@fishjam-cloud/ts-client 0.6.0 → 0.7.1
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/FishjamClient.d.ts +6 -1
- package/dist/src/FishjamClient.js +5 -1
- package/dist/src/connectEventsHandler.d.ts +2 -0
- package/dist/src/connectEventsHandler.js +22 -0
- package/dist/src/reconnection.js +1 -1
- package/dist/src/webrtc/ConnectionManager.d.ts +0 -2
- package/dist/src/webrtc/ConnectionManager.js +0 -11
- package/dist/src/webrtc/tracks/Local.d.ts +0 -1
- package/dist/src/webrtc/tracks/Local.js +13 -17
- package/dist/src/webrtc/tracks/LocalTrack.d.ts +1 -1
- package/dist/src/webrtc/tracks/LocalTrackManager.js +0 -1
- package/dist/src/webrtc/tracks/Remote.js +7 -7
- package/dist/src/webrtc/types.d.ts +12 -3
- package/dist/src/webrtc/webRTCEndpoint.js +0 -2
- package/package.json +11 -11
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)
|
|
@@ -12,6 +12,11 @@ export type Component<PeerMetadata, TrackMetadata> = Omit<Endpoint<PeerMetadata,
|
|
|
12
12
|
* Events emitted by the client with their arguments.
|
|
13
13
|
*/
|
|
14
14
|
export interface MessageEvents<PeerMetadata, TrackMetadata> {
|
|
15
|
+
/**
|
|
16
|
+
* Emitted when connect method invoked
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
connectionStarted: () => void;
|
|
15
20
|
/**
|
|
16
21
|
* Emitted when the websocket connection is closed
|
|
17
22
|
*
|
|
@@ -210,7 +215,7 @@ export declare class FishjamClient<PeerMetadata, TrackMetadata> extends FishjamC
|
|
|
210
215
|
*
|
|
211
216
|
* @param {ConnectConfig} config - Configuration object for the client
|
|
212
217
|
*/
|
|
213
|
-
connect(config: ConnectConfig<PeerMetadata>): void
|
|
218
|
+
connect(config: ConnectConfig<PeerMetadata>): Promise<void>;
|
|
214
219
|
private initConnection;
|
|
215
220
|
private getUrl;
|
|
216
221
|
private initWebsocket;
|
|
@@ -3,6 +3,7 @@ import { EventEmitter } from 'events';
|
|
|
3
3
|
import { PeerMessage } from './protos';
|
|
4
4
|
import { ReconnectManager } from './reconnection';
|
|
5
5
|
import { isAuthError } from './auth';
|
|
6
|
+
import { connectEventsHandler } from './connectEventsHandler';
|
|
6
7
|
const STATISTICS_INTERVAL = 10_000;
|
|
7
8
|
const isPeer = (endpoint) => endpoint.type === 'webrtc';
|
|
8
9
|
const isComponent = (endpoint) => endpoint.type === 'recording' ||
|
|
@@ -77,10 +78,13 @@ export class FishjamClient extends EventEmitter {
|
|
|
77
78
|
*
|
|
78
79
|
* @param {ConnectConfig} config - Configuration object for the client
|
|
79
80
|
*/
|
|
80
|
-
connect(config) {
|
|
81
|
+
async connect(config) {
|
|
82
|
+
this.emit('connectionStarted');
|
|
83
|
+
const result = connectEventsHandler(this);
|
|
81
84
|
this.reconnectManager.reset(config.peerMetadata);
|
|
82
85
|
this.connectConfig = config;
|
|
83
86
|
this.initConnection(config.peerMetadata);
|
|
87
|
+
return result;
|
|
84
88
|
}
|
|
85
89
|
async initConnection(peerMetadata) {
|
|
86
90
|
if (this.status === 'initialized') {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function connectEventsHandler(fishjamClient) {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
const onSuccess = () => {
|
|
4
|
+
clearCallbacks();
|
|
5
|
+
resolve();
|
|
6
|
+
};
|
|
7
|
+
const onError = () => {
|
|
8
|
+
clearCallbacks();
|
|
9
|
+
reject();
|
|
10
|
+
};
|
|
11
|
+
fishjamClient.on('joined', onSuccess);
|
|
12
|
+
fishjamClient.on('joinError', onError);
|
|
13
|
+
fishjamClient.on('authError', onError);
|
|
14
|
+
fishjamClient.on('socketError', onError);
|
|
15
|
+
const clearCallbacks = () => {
|
|
16
|
+
fishjamClient.removeListener('joined', onSuccess);
|
|
17
|
+
fishjamClient.removeListener('joinError', onError);
|
|
18
|
+
fishjamClient.removeListener('authError', onError);
|
|
19
|
+
fishjamClient.removeListener('socketError', onError);
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
}
|
package/dist/src/reconnection.js
CHANGED
|
@@ -15,14 +15,12 @@ export declare class ConnectionManager {
|
|
|
15
15
|
*/
|
|
16
16
|
private getIceServers;
|
|
17
17
|
getConnection: () => RTCPeerConnection;
|
|
18
|
-
setTransceiversToReadOnly: () => void;
|
|
19
18
|
addTransceiversIfNeeded: (serverTracks: Map<string, number>) => void;
|
|
20
19
|
private getNeededTransceiversTypes;
|
|
21
20
|
addTransceiver: (track: MediaStreamTrack, transceiverConfig: RTCRtpTransceiverInit) => void;
|
|
22
21
|
setOnTrackReady: (onTrackReady: (event: RTCTrackEvent) => void) => void;
|
|
23
22
|
setRemoteDescription: (data: RTCSessionDescriptionInit) => Promise<void>;
|
|
24
23
|
isTrackInUse: (track: MediaStreamTrack) => boolean;
|
|
25
|
-
setTransceiverDirection: () => void;
|
|
26
24
|
removeTrack: (sender: RTCRtpSender) => void;
|
|
27
25
|
findSender: (mediaStreamTrackId: MediaStreamTrackId) => RTCRtpSender;
|
|
28
26
|
addIceCandidate: (iceCandidate: RTCIceCandidate) => Promise<void>;
|
|
@@ -34,9 +34,6 @@ export class ConnectionManager {
|
|
|
34
34
|
getConnection = () => {
|
|
35
35
|
return this.connection;
|
|
36
36
|
};
|
|
37
|
-
setTransceiversToReadOnly = () => {
|
|
38
|
-
this.connection.getTransceivers().forEach((transceiver) => (transceiver.direction = 'sendonly'));
|
|
39
|
-
};
|
|
40
37
|
addTransceiversIfNeeded = (serverTracks) => {
|
|
41
38
|
const recvTransceivers = this.connection.getTransceivers().filter((elem) => elem.direction === 'recvonly');
|
|
42
39
|
['audio', 'video']
|
|
@@ -58,14 +55,6 @@ export class ConnectionManager {
|
|
|
58
55
|
await this.connection.setRemoteDescription(data);
|
|
59
56
|
};
|
|
60
57
|
isTrackInUse = (track) => this.connection.getSenders().some((val) => val.track === track);
|
|
61
|
-
setTransceiverDirection = () => {
|
|
62
|
-
this.connection
|
|
63
|
-
.getTransceivers()
|
|
64
|
-
.filter((transceiver) => transceiver.direction === 'sendrecv')
|
|
65
|
-
.forEach((transceiver) => {
|
|
66
|
-
transceiver.direction = 'sendonly';
|
|
67
|
-
});
|
|
68
|
-
};
|
|
69
58
|
removeTrack = (sender) => {
|
|
70
59
|
this.connection.removeTrack(sender);
|
|
71
60
|
};
|
|
@@ -28,7 +28,6 @@ export declare class Local<EndpointMetadata, TrackMetadata> {
|
|
|
28
28
|
updateConnection: (connection: ConnectionManager) => void;
|
|
29
29
|
createSdpOfferEvent: (offer: RTCSessionDescriptionInit) => MediaEvent;
|
|
30
30
|
addTrack: (connection: ConnectionManager | undefined, trackId: string, track: MediaStreamTrack, stream: MediaStream, trackMetadata: TrackMetadata | undefined, simulcastConfig: SimulcastConfig, maxBandwidth: TrackBandwidthLimit) => LocalTrack<EndpointMetadata, TrackMetadata>;
|
|
31
|
-
disableAllLocalTrackEncodings: () => Promise<void>;
|
|
32
31
|
getTrackByMidOrNull: (mid: string) => LocalTrack<EndpointMetadata, TrackMetadata> | null;
|
|
33
32
|
removeTrack: (trackId: TrackId) => void;
|
|
34
33
|
replaceTrack: (webrtc: WebRTCEndpoint<EndpointMetadata, TrackMetadata>, trackId: TrackId, newTrack: MediaStreamTrack | null) => Promise<void>;
|
|
@@ -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;
|
|
@@ -74,16 +80,6 @@ export class Local {
|
|
|
74
80
|
this.localTracks[trackId] = trackManager;
|
|
75
81
|
return trackManager;
|
|
76
82
|
};
|
|
77
|
-
disableAllLocalTrackEncodings = async () => {
|
|
78
|
-
// TODO: Why are we disabling already disabled encodings?
|
|
79
|
-
// I think this part of the code is invoked to mutate RTCRtpSender.getParameters().encodings.active.
|
|
80
|
-
// We should probably implement a separate method for that, because disabling already disabled tracks seems weird.
|
|
81
|
-
for (const [trackId, trackManager] of Object.entries(this.localTracks)) {
|
|
82
|
-
for (const encoding of trackManager.getDisabledEncodings()) {
|
|
83
|
-
await this.disableLocalTrackEncoding(trackId, encoding);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
83
|
getTrackByMidOrNull = (mid) => {
|
|
88
84
|
return Object.values(this.localTracks).find((track) => track.mLineId === mid) ?? null;
|
|
89
85
|
};
|
|
@@ -106,15 +102,15 @@ export class Local {
|
|
|
106
102
|
};
|
|
107
103
|
setEndpointMetadata = (metadata) => {
|
|
108
104
|
try {
|
|
109
|
-
this.localEndpoint.metadata = this.endpointMetadataParser(metadata);
|
|
105
|
+
this.localEndpoint.metadata.peer = this.endpointMetadataParser(metadata);
|
|
110
106
|
this.localEndpoint.metadataParsingError = undefined;
|
|
111
107
|
}
|
|
112
108
|
catch (error) {
|
|
113
|
-
this.localEndpoint.metadata = undefined;
|
|
109
|
+
this.localEndpoint.metadata.peer = undefined;
|
|
114
110
|
this.localEndpoint.metadataParsingError = error;
|
|
115
111
|
throw error;
|
|
116
112
|
}
|
|
117
|
-
this.localEndpoint.rawMetadata = metadata;
|
|
113
|
+
this.localEndpoint.rawMetadata.peer = metadata;
|
|
118
114
|
};
|
|
119
115
|
getEndpoint = () => {
|
|
120
116
|
return this.localEndpoint;
|
|
@@ -159,11 +155,11 @@ export class Local {
|
|
|
159
155
|
});
|
|
160
156
|
};
|
|
161
157
|
updateEndpointMetadata = (metadata) => {
|
|
162
|
-
this.localEndpoint.metadata = this.endpointMetadataParser(metadata);
|
|
158
|
+
this.localEndpoint.metadata.peer = this.endpointMetadataParser(metadata);
|
|
163
159
|
this.localEndpoint.rawMetadata = this.localEndpoint.metadata;
|
|
164
160
|
this.localEndpoint.metadataParsingError = undefined;
|
|
165
161
|
const mediaEvent = generateMediaEvent('updateEndpointMetadata', {
|
|
166
|
-
metadata: this.localEndpoint.metadata,
|
|
162
|
+
metadata: this.localEndpoint.metadata.peer,
|
|
167
163
|
});
|
|
168
164
|
this.sendMediaEvent(mediaEvent);
|
|
169
165
|
this.emit('localEndpointMetadataChanged', {
|
|
@@ -58,5 +58,5 @@ export declare class LocalTrack<EndpointMetadata, TrackMetadata> implements Trac
|
|
|
58
58
|
setMLineId: (mLineId: MLineId) => void;
|
|
59
59
|
private isNotSimulcastTrack;
|
|
60
60
|
getTrackBitrates: () => Bitrates;
|
|
61
|
-
createTrackVariantBitratesEvent: () => import("
|
|
61
|
+
createTrackVariantBitratesEvent: () => import("..").MediaEvent;
|
|
62
62
|
}
|
|
@@ -48,7 +48,6 @@ export class LocalTrackManager {
|
|
|
48
48
|
const trackManager = this.local.addTrack(this.connection, trackId, track, stream, trackMetadata, simulcastConfig, maxBandwidth);
|
|
49
49
|
if (this.connection) {
|
|
50
50
|
trackManager.addTrackToConnection();
|
|
51
|
-
this.connection.setTransceiverDirection();
|
|
52
51
|
}
|
|
53
52
|
const mediaEvent = generateCustomEvent({ type: 'renegotiateTracks' });
|
|
54
53
|
this.sendMediaEvent(mediaEvent);
|
|
@@ -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];
|
|
@@ -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.
|
|
@@ -265,7 +265,6 @@ export class WebRTCEndpoint extends EventEmitter {
|
|
|
265
265
|
});
|
|
266
266
|
try {
|
|
267
267
|
await this.connectionManager.setRemoteDescription(data);
|
|
268
|
-
await this.local.disableAllLocalTrackEncodings();
|
|
269
268
|
}
|
|
270
269
|
catch (err) {
|
|
271
270
|
console.error(err);
|
|
@@ -626,7 +625,6 @@ export class WebRTCEndpoint extends EventEmitter {
|
|
|
626
625
|
connection.getConnection().addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
|
|
627
626
|
this.commandsQueue.initConnection(connection);
|
|
628
627
|
this.local.addAllTracksToConnection();
|
|
629
|
-
connection.setTransceiversToReadOnly();
|
|
630
628
|
}
|
|
631
629
|
this.localTrackManager.updateSenders();
|
|
632
630
|
const tracks = new Map(Object.entries(offerData.data.tracksTypes));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishjam-cloud/ts-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
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.
|
|
54
|
+
"@playwright/test": "^1.47.2",
|
|
55
55
|
"@types/events": "^3.0.3",
|
|
56
|
-
"@types/node": "^22.4
|
|
56
|
+
"@types/node": "^22.7.4",
|
|
57
57
|
"@types/uuid": "^10.0.0",
|
|
58
|
-
"@vitest/coverage-v8": "^2.
|
|
58
|
+
"@vitest/coverage-v8": "^2.1.1",
|
|
59
59
|
"fake-mediastreamtrack": "^1.2.0",
|
|
60
|
-
"husky": "^9.1.
|
|
61
|
-
"lint-staged": "^15.2.
|
|
60
|
+
"husky": "^9.1.6",
|
|
61
|
+
"lint-staged": "^15.2.10",
|
|
62
62
|
"react": "^18.2.0",
|
|
63
|
-
"ts-proto": "^2.
|
|
63
|
+
"ts-proto": "^2.2.1",
|
|
64
64
|
"typed-emitter": "^2.1.0",
|
|
65
|
-
"typedoc": "^0.26.
|
|
65
|
+
"typedoc": "^0.26.7",
|
|
66
66
|
"typedoc-plugin-external-resolver": "^1.0.3",
|
|
67
|
-
"typedoc-plugin-mdn-links": "^3.2
|
|
68
|
-
"typescript": "^5.
|
|
69
|
-
"vitest": "^2.
|
|
67
|
+
"typedoc-plugin-mdn-links": "^3.3.2",
|
|
68
|
+
"typescript": "^5.6.2",
|
|
69
|
+
"vitest": "^2.1.1",
|
|
70
70
|
"zod": "^3.23.6"
|
|
71
71
|
},
|
|
72
72
|
"lint-staged": {
|