@fishjam-cloud/ts-client 0.7.0 → 0.7.2
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/README.md +20 -8
- package/dist/src/reconnection.js +1 -1
- package/dist/src/webrtc/CommandsQueue.d.ts +2 -2
- package/dist/src/webrtc/CommandsQueue.js +4 -3
- package/dist/src/webrtc/tracks/Local.js +13 -7
- package/dist/src/webrtc/tracks/LocalTrack.js +17 -15
- package/dist/src/webrtc/tracks/Remote.js +7 -7
- package/dist/src/webrtc/tracks/muteTrackUtils.d.ts +3 -0
- package/dist/src/webrtc/tracks/muteTrackUtils.js +19 -0
- package/dist/src/webrtc/types.d.ts +12 -3
- package/dist/src/webrtc/webRTCEndpoint.js +4 -10
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
# Fishjam TS Client
|
|
6
6
|
|
|
7
|
-
TypeScript client library for [Fishjam Cloud](https://
|
|
7
|
+
TypeScript client library for [Fishjam Cloud](https://fishjam.io).
|
|
8
|
+
|
|
9
|
+
> [!WARNING]
|
|
10
|
+
> This SDK is not stable yet. We recommend to use
|
|
11
|
+
> [React Client](https://github.com/fishjam-cloud/web-client-sdk/tree/main/packages/react-client) for Fishjam Cloud
|
|
12
|
+
> services.
|
|
8
13
|
|
|
9
14
|
## Documentation
|
|
10
15
|
|
|
@@ -33,10 +38,11 @@ yarn @fishjam-cloud/ts-client
|
|
|
33
38
|
|
|
34
39
|
Prerequisites:
|
|
35
40
|
|
|
36
|
-
- Account on [Fishjam Cloud](https://
|
|
41
|
+
- Account on [Fishjam Cloud](https://https://fishjam.io) with App configured.
|
|
37
42
|
- Created room and token of peer in that room. You can use Room Manager to create room and peer token.
|
|
38
43
|
|
|
39
|
-
The following code snippet is based on the
|
|
44
|
+
The following code snippet is based on the
|
|
45
|
+
[minimal](https://github.com/fishjam-cloud/web-client-sdk/tree/main/examples/ts-client/minimal/) example.
|
|
40
46
|
|
|
41
47
|
```ts
|
|
42
48
|
import { FishjamClient, WebRTCEndpoint } from '@fishjam-cloud/ts-client';
|
|
@@ -119,12 +125,18 @@ async function startScreenSharing(webrtc: WebRTCEndpoint) {
|
|
|
119
125
|
|
|
120
126
|
## Examples
|
|
121
127
|
|
|
122
|
-
For more examples, see the [examples](
|
|
128
|
+
For more examples, see the [examples](https://github.com/fishjam-cloud/web-client-sdk/tree/main/examples/ts-client/)
|
|
129
|
+
folder.
|
|
130
|
+
|
|
131
|
+
## License
|
|
123
132
|
|
|
124
|
-
|
|
133
|
+
Licensed under the [Apache License, Version 2.0](LICENSE)
|
|
125
134
|
|
|
126
|
-
|
|
135
|
+
## Fishjam Cloud is created by Software Mansion
|
|
127
136
|
|
|
128
|
-
[
|
|
137
|
+
Since 2012 [Software Mansion](https://swmansion.com) is a software agency with experience in building web and mobile
|
|
138
|
+
apps. We are Core React Native Contributors and experts in dealing with all kinds of React Native issues. We can help
|
|
139
|
+
you build your next dream product –
|
|
140
|
+
[Hire us](https://swmansion.com/contact/projects?utm_source=fishjam&utm_medium=web-readme).
|
|
129
141
|
|
|
130
|
-
|
|
142
|
+
[](https://swmansion.com/contact/projects?utm_source=fishjam&utm_medium=web-readme)
|
package/dist/src/reconnection.js
CHANGED
|
@@ -2,10 +2,10 @@ import type { Deferred } from './deferred';
|
|
|
2
2
|
import type { LocalTrackManager } from './tracks/LocalTrackManager';
|
|
3
3
|
import type { ConnectionManager } from './ConnectionManager';
|
|
4
4
|
export type Command = {
|
|
5
|
-
handler: () => void
|
|
5
|
+
handler: () => Promise<void>;
|
|
6
6
|
parse?: () => void;
|
|
7
7
|
resolutionNotifier: Deferred<void>;
|
|
8
|
-
resolve: 'after-renegotiation' | '
|
|
8
|
+
resolve: 'after-renegotiation' | 'on-handler-resolve';
|
|
9
9
|
};
|
|
10
10
|
export declare class CommandsQueue<EndpointMetadata, TrackMetadata> {
|
|
11
11
|
private readonly localTrackManager;
|
|
@@ -56,11 +56,12 @@ export class CommandsQueue {
|
|
|
56
56
|
this.commandResolutionNotifier = command.resolutionNotifier;
|
|
57
57
|
this.handleCommand(command);
|
|
58
58
|
};
|
|
59
|
-
handleCommand = (command) => {
|
|
59
|
+
handleCommand = async (command) => {
|
|
60
60
|
try {
|
|
61
61
|
command.parse?.();
|
|
62
|
-
command.handler();
|
|
63
|
-
if (command.resolve === '
|
|
62
|
+
const promise = command.handler();
|
|
63
|
+
if (command.resolve === 'on-handler-resolve') {
|
|
64
|
+
await promise;
|
|
64
65
|
this.resolvePreviousCommand();
|
|
65
66
|
this.processNextCommand();
|
|
66
67
|
}
|
|
@@ -13,8 +13,14 @@ export class Local {
|
|
|
13
13
|
localEndpoint = {
|
|
14
14
|
id: '',
|
|
15
15
|
type: 'webrtc',
|
|
16
|
-
metadata:
|
|
17
|
-
|
|
16
|
+
metadata: {
|
|
17
|
+
peer: undefined,
|
|
18
|
+
server: undefined,
|
|
19
|
+
},
|
|
20
|
+
rawMetadata: {
|
|
21
|
+
peer: undefined,
|
|
22
|
+
server: undefined,
|
|
23
|
+
},
|
|
18
24
|
tracks: new Map(),
|
|
19
25
|
};
|
|
20
26
|
endpointMetadataParser;
|
|
@@ -96,15 +102,15 @@ export class Local {
|
|
|
96
102
|
};
|
|
97
103
|
setEndpointMetadata = (metadata) => {
|
|
98
104
|
try {
|
|
99
|
-
this.localEndpoint.metadata = this.endpointMetadataParser(metadata);
|
|
105
|
+
this.localEndpoint.metadata.peer = this.endpointMetadataParser(metadata);
|
|
100
106
|
this.localEndpoint.metadataParsingError = undefined;
|
|
101
107
|
}
|
|
102
108
|
catch (error) {
|
|
103
|
-
this.localEndpoint.metadata = undefined;
|
|
109
|
+
this.localEndpoint.metadata.peer = undefined;
|
|
104
110
|
this.localEndpoint.metadataParsingError = error;
|
|
105
111
|
throw error;
|
|
106
112
|
}
|
|
107
|
-
this.localEndpoint.rawMetadata = metadata;
|
|
113
|
+
this.localEndpoint.rawMetadata.peer = metadata;
|
|
108
114
|
};
|
|
109
115
|
getEndpoint = () => {
|
|
110
116
|
return this.localEndpoint;
|
|
@@ -149,11 +155,11 @@ export class Local {
|
|
|
149
155
|
});
|
|
150
156
|
};
|
|
151
157
|
updateEndpointMetadata = (metadata) => {
|
|
152
|
-
this.localEndpoint.metadata = this.endpointMetadataParser(metadata);
|
|
158
|
+
this.localEndpoint.metadata.peer = this.endpointMetadataParser(metadata);
|
|
153
159
|
this.localEndpoint.rawMetadata = this.localEndpoint.metadata;
|
|
154
160
|
this.localEndpoint.metadataParsingError = undefined;
|
|
155
161
|
const mediaEvent = generateMediaEvent('updateEndpointMetadata', {
|
|
156
|
-
metadata: this.localEndpoint.metadata,
|
|
162
|
+
metadata: this.localEndpoint.metadata.peer,
|
|
157
163
|
});
|
|
158
164
|
this.sendMediaEvent(mediaEvent);
|
|
159
165
|
this.emit('localEndpointMetadataChanged', {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { generateCustomEvent
|
|
1
|
+
import { generateCustomEvent } from '../mediaEvent';
|
|
2
2
|
import { defaultBitrates, defaultSimulcastBitrates, UNLIMITED_BANDWIDTH } from '../bitrate';
|
|
3
3
|
import { getEncodingParameters } from './encodings';
|
|
4
4
|
import { createTransceiverConfig } from './transceivers';
|
|
5
|
+
import { emitMutableEvents, getActionType } from './muteTrackUtils';
|
|
5
6
|
/**
|
|
6
7
|
* This is a wrapper over `TrackContext` that adds additional properties such as:
|
|
7
8
|
* - `MLineId`: required to generate sdpOffer
|
|
@@ -99,6 +100,7 @@ export class LocalTrack {
|
|
|
99
100
|
replaceTrack = async (newTrack, webrtc) => {
|
|
100
101
|
const trackId = this.id;
|
|
101
102
|
const stream = this.trackContext.stream;
|
|
103
|
+
const oldTrack = this.trackContext.track;
|
|
102
104
|
if (!this.sender)
|
|
103
105
|
throw Error('There is no RTCRtpSender for this track id!');
|
|
104
106
|
stream?.getTracks().forEach((track) => {
|
|
@@ -107,25 +109,25 @@ export class LocalTrack {
|
|
|
107
109
|
if (newTrack) {
|
|
108
110
|
stream?.addTrack(newTrack);
|
|
109
111
|
}
|
|
110
|
-
|
|
111
|
-
const mediaEvent = generateMediaEvent('muteTrack', { trackId: trackId });
|
|
112
|
-
webrtc.sendMediaEvent(mediaEvent);
|
|
113
|
-
webrtc.emit('localTrackMuted', { trackId: trackId });
|
|
114
|
-
}
|
|
115
|
-
else if (!this.trackContext.track && newTrack) {
|
|
116
|
-
const mediaEvent = generateMediaEvent('unmuteTrack', {
|
|
117
|
-
trackId: trackId,
|
|
118
|
-
});
|
|
119
|
-
webrtc.sendMediaEvent(mediaEvent);
|
|
120
|
-
webrtc.emit('localTrackUnmuted', { trackId: trackId });
|
|
121
|
-
}
|
|
112
|
+
this.trackContext.track = newTrack;
|
|
122
113
|
this.mediaStreamTrackId = newTrack?.id ?? null;
|
|
114
|
+
const action = getActionType(this.trackContext.track, newTrack);
|
|
115
|
+
if (action === 'mute' || action === 'unmute') {
|
|
116
|
+
emitMutableEvents(action, webrtc, trackId);
|
|
117
|
+
}
|
|
123
118
|
try {
|
|
124
119
|
await this.sender.replaceTrack(newTrack);
|
|
125
|
-
this.trackContext.track = newTrack;
|
|
126
120
|
}
|
|
127
121
|
catch (_error) {
|
|
128
|
-
//
|
|
122
|
+
// rollback: emit opposite events and revert internal state
|
|
123
|
+
if (action === 'mute') {
|
|
124
|
+
emitMutableEvents('unmute', webrtc, trackId);
|
|
125
|
+
}
|
|
126
|
+
else if (action === 'unmute') {
|
|
127
|
+
emitMutableEvents('mute', webrtc, trackId);
|
|
128
|
+
}
|
|
129
|
+
this.trackContext.track = oldTrack;
|
|
130
|
+
this.mediaStreamTrackId = oldTrack?.id ?? null;
|
|
129
131
|
}
|
|
130
132
|
};
|
|
131
133
|
setTrackBandwidth = (bandwidth) => {
|
|
@@ -56,13 +56,13 @@ export class Remote {
|
|
|
56
56
|
const newEndpoint = {
|
|
57
57
|
id: endpoint.id,
|
|
58
58
|
type: endpoint.type,
|
|
59
|
-
metadata: undefined,
|
|
60
|
-
rawMetadata: undefined,
|
|
59
|
+
metadata: { peer: undefined, server: undefined },
|
|
60
|
+
rawMetadata: { peer: undefined, server: undefined },
|
|
61
61
|
metadataParsingError: undefined,
|
|
62
62
|
tracks: new Map(),
|
|
63
63
|
};
|
|
64
64
|
// mutation in place
|
|
65
|
-
this.updateEndpointMetadata(newEndpoint, endpoint
|
|
65
|
+
this.updateEndpointMetadata(newEndpoint, endpoint?.metadata?.peer);
|
|
66
66
|
this.addEndpoint(newEndpoint);
|
|
67
67
|
this.addTracks(newEndpoint.id, endpoint.tracks, endpoint.trackIdToMetadata);
|
|
68
68
|
if (sendNotification) {
|
|
@@ -77,19 +77,19 @@ export class Remote {
|
|
|
77
77
|
if (!endpoint)
|
|
78
78
|
throw new Error(`Endpoint ${data.id} not found`);
|
|
79
79
|
// mutation in place
|
|
80
|
-
this.updateEndpointMetadata(endpoint, data.metadata);
|
|
80
|
+
this.updateEndpointMetadata(endpoint, data.metadata.peer);
|
|
81
81
|
this.emit('endpointUpdated', endpoint);
|
|
82
82
|
};
|
|
83
83
|
updateEndpointMetadata = (endpoint, metadata) => {
|
|
84
84
|
try {
|
|
85
|
-
endpoint.metadata = this.endpointMetadataParser(metadata);
|
|
85
|
+
endpoint.metadata.peer = this.endpointMetadataParser(metadata);
|
|
86
86
|
endpoint.metadataParsingError = undefined;
|
|
87
87
|
}
|
|
88
88
|
catch (error) {
|
|
89
|
-
endpoint.metadata = undefined;
|
|
89
|
+
endpoint.metadata.peer = undefined;
|
|
90
90
|
endpoint.metadataParsingError = error;
|
|
91
91
|
}
|
|
92
|
-
endpoint.rawMetadata = metadata;
|
|
92
|
+
endpoint.rawMetadata.peer = metadata;
|
|
93
93
|
};
|
|
94
94
|
removeRemoteEndpoint = (endpointId) => {
|
|
95
95
|
const endpoint = this.remoteEndpoints[endpointId];
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { WebRTCEndpoint } from '../webRTCEndpoint';
|
|
2
|
+
export declare function emitMutableEvents<EndpointMetadata, TrackMetadata>(action: 'mute' | 'unmute', webrtc: WebRTCEndpoint<EndpointMetadata, TrackMetadata>, trackId: string): void;
|
|
3
|
+
export declare function getActionType(currentTrack: MediaStreamTrack | null, newTrack: MediaStreamTrack | null): 'mute' | 'unmute' | 'replace';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { generateMediaEvent } from '../mediaEvent';
|
|
2
|
+
export function emitMutableEvents(action, webrtc, trackId) {
|
|
3
|
+
const mediaEventType = action === 'mute' ? `muteTrack` : `unmuteTrack`;
|
|
4
|
+
const localEventType = action === 'mute' ? `localTrackMuted` : `localTrackUnmuted`;
|
|
5
|
+
const mediaEvent = generateMediaEvent(mediaEventType, { trackId: trackId });
|
|
6
|
+
webrtc.sendMediaEvent(mediaEvent);
|
|
7
|
+
webrtc.emit(localEventType, { trackId: trackId });
|
|
8
|
+
}
|
|
9
|
+
export function getActionType(currentTrack, newTrack) {
|
|
10
|
+
if (currentTrack && !newTrack) {
|
|
11
|
+
return 'mute';
|
|
12
|
+
}
|
|
13
|
+
else if (!currentTrack && newTrack) {
|
|
14
|
+
return 'unmute';
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return 'replace';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -268,7 +268,10 @@ export interface WebRTCEndpointEvents<EndpointMetadata, TrackMetadata> {
|
|
|
268
268
|
encoding: Encoding;
|
|
269
269
|
}) => void;
|
|
270
270
|
localEndpointMetadataChanged: (event: {
|
|
271
|
-
metadata:
|
|
271
|
+
metadata: {
|
|
272
|
+
peer?: EndpointMetadata;
|
|
273
|
+
server?: unknown;
|
|
274
|
+
};
|
|
272
275
|
}) => void;
|
|
273
276
|
localTrackMetadataChanged: (event: {
|
|
274
277
|
trackId: string;
|
|
@@ -294,8 +297,14 @@ export interface Endpoint<EndpointMetadata, TrackMetadata> {
|
|
|
294
297
|
/**
|
|
295
298
|
* Any information that was provided in {@link WebRTCEndpoint.connect}.
|
|
296
299
|
*/
|
|
297
|
-
metadata
|
|
298
|
-
|
|
300
|
+
metadata: {
|
|
301
|
+
peer?: EndpointMetadata;
|
|
302
|
+
server: any;
|
|
303
|
+
};
|
|
304
|
+
rawMetadata: {
|
|
305
|
+
peer?: any;
|
|
306
|
+
server: any;
|
|
307
|
+
};
|
|
299
308
|
metadataParsingError?: any;
|
|
300
309
|
/**
|
|
301
310
|
* List of tracks that are sent by the endpoint.
|
|
@@ -327,9 +327,7 @@ export class WebRTCEndpoint extends EventEmitter {
|
|
|
327
327
|
metadata = parsedMetadata;
|
|
328
328
|
stream.addTrack(track);
|
|
329
329
|
this.commandsQueue.pushCommand({
|
|
330
|
-
handler: () =>
|
|
331
|
-
this.localTrackManager.addTrackHandler(trackId, track, stream, parsedMetadata, simulcastConfig, maxBandwidth);
|
|
332
|
-
},
|
|
330
|
+
handler: async () => this.localTrackManager.addTrackHandler(trackId, track, stream, parsedMetadata, simulcastConfig, maxBandwidth),
|
|
333
331
|
parse: () => this.localTrackManager.parseAddTrack(track, simulcastConfig, maxBandwidth),
|
|
334
332
|
resolve: 'after-renegotiation',
|
|
335
333
|
resolutionNotifier,
|
|
@@ -397,11 +395,9 @@ export class WebRTCEndpoint extends EventEmitter {
|
|
|
397
395
|
async replaceTrack(trackId, newTrack) {
|
|
398
396
|
const resolutionNotifier = new Deferred();
|
|
399
397
|
this.commandsQueue.pushCommand({
|
|
400
|
-
handler: () =>
|
|
401
|
-
this.localTrackManager.replaceTrackHandler(this, trackId, newTrack);
|
|
402
|
-
},
|
|
398
|
+
handler: () => this.localTrackManager.replaceTrackHandler(this, trackId, newTrack),
|
|
403
399
|
resolutionNotifier,
|
|
404
|
-
resolve: '
|
|
400
|
+
resolve: 'on-handler-resolve',
|
|
405
401
|
});
|
|
406
402
|
return resolutionNotifier.promise.then(() => {
|
|
407
403
|
this.emit('localTrackReplaced', { trackId, track: newTrack });
|
|
@@ -466,9 +462,7 @@ export class WebRTCEndpoint extends EventEmitter {
|
|
|
466
462
|
async removeTrack(trackId) {
|
|
467
463
|
const resolutionNotifier = new Deferred();
|
|
468
464
|
this.commandsQueue.pushCommand({
|
|
469
|
-
handler: () =>
|
|
470
|
-
this.localTrackManager.removeTrackHandler(trackId);
|
|
471
|
-
},
|
|
465
|
+
handler: async () => this.localTrackManager.removeTrackHandler(trackId),
|
|
472
466
|
resolutionNotifier,
|
|
473
467
|
resolve: 'after-renegotiation',
|
|
474
468
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishjam-cloud/ts-client",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Typescript client library for Fishjam Cloud",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Fishjam Cloud Team",
|
|
@@ -51,22 +51,22 @@
|
|
|
51
51
|
"uuid": "^10.0.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@playwright/test": "^1.47.
|
|
54
|
+
"@playwright/test": "^1.47.2",
|
|
55
55
|
"@types/events": "^3.0.3",
|
|
56
|
-
"@types/node": "^22.
|
|
56
|
+
"@types/node": "^22.7.5",
|
|
57
57
|
"@types/uuid": "^10.0.0",
|
|
58
|
-
"@vitest/coverage-v8": "^2.1.
|
|
58
|
+
"@vitest/coverage-v8": "^2.1.2",
|
|
59
59
|
"fake-mediastreamtrack": "^1.2.0",
|
|
60
60
|
"husky": "^9.1.6",
|
|
61
61
|
"lint-staged": "^15.2.10",
|
|
62
62
|
"react": "^18.2.0",
|
|
63
|
-
"ts-proto": "^2.2.
|
|
63
|
+
"ts-proto": "^2.2.3",
|
|
64
64
|
"typed-emitter": "^2.1.0",
|
|
65
|
-
"typedoc": "^0.26.
|
|
65
|
+
"typedoc": "^0.26.8",
|
|
66
66
|
"typedoc-plugin-external-resolver": "^1.0.3",
|
|
67
|
-
"typedoc-plugin-mdn-links": "^3.2
|
|
67
|
+
"typedoc-plugin-mdn-links": "^3.3.2",
|
|
68
68
|
"typescript": "^5.6.2",
|
|
69
|
-
"vitest": "^2.1.
|
|
69
|
+
"vitest": "^2.1.2",
|
|
70
70
|
"zod": "^3.23.6"
|
|
71
71
|
},
|
|
72
72
|
"lint-staged": {
|