@stream-io/video-client 0.0.1-alpha.7
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/LICENSE +219 -0
- package/README.md +14 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +14663 -0
- package/dist/index.js.map +1 -0
- package/dist/src/Batcher.d.ts +12 -0
- package/dist/src/CallDropScheduler.d.ts +44 -0
- package/dist/src/StreamSfuClient.d.ts +25 -0
- package/dist/src/StreamVideoClient.d.ts +145 -0
- package/dist/src/__tests__/StreamVideoClient.test.d.ts +1 -0
- package/dist/src/config/defaultConfigs.d.ts +2 -0
- package/dist/src/config/types.d.ts +29 -0
- package/dist/src/coordinator/StreamCoordinatorClient.d.ts +19 -0
- package/dist/src/coordinator/connection/base64.d.ts +2 -0
- package/dist/src/coordinator/connection/client.d.ts +174 -0
- package/dist/src/coordinator/connection/connection.d.ts +139 -0
- package/dist/src/coordinator/connection/connection_fallback.d.ts +38 -0
- package/dist/src/coordinator/connection/errors.d.ts +16 -0
- package/dist/src/coordinator/connection/events.d.ts +7 -0
- package/dist/src/coordinator/connection/insights.d.ts +58 -0
- package/dist/src/coordinator/connection/signing.d.ts +30 -0
- package/dist/src/coordinator/connection/token_manager.d.ts +39 -0
- package/dist/src/coordinator/connection/types.d.ts +96 -0
- package/dist/src/coordinator/connection/utils.d.ts +25 -0
- package/dist/src/devices.d.ts +79 -0
- package/dist/src/events/call.d.ts +26 -0
- package/dist/src/events/internal.d.ts +8 -0
- package/dist/src/events/participant.d.ts +21 -0
- package/dist/src/events/speaker.d.ts +10 -0
- package/dist/src/gen/coordinator/index.d.ts +1664 -0
- package/dist/src/gen/google/protobuf/descriptor.d.ts +1650 -0
- package/dist/src/gen/google/protobuf/duration.d.ts +113 -0
- package/dist/src/gen/google/protobuf/struct.d.ts +184 -0
- package/dist/src/gen/google/protobuf/timestamp.d.ts +158 -0
- package/dist/src/gen/video/coordinator/broadcast_v1/broadcast.d.ts +66 -0
- package/dist/src/gen/video/coordinator/call_v1/call.d.ts +254 -0
- package/dist/src/gen/video/coordinator/client_v1_rpc/client_rpc.client.d.ts +351 -0
- package/dist/src/gen/video/coordinator/client_v1_rpc/client_rpc.d.ts +1488 -0
- package/dist/src/gen/video/coordinator/client_v1_rpc/envelopes.d.ts +143 -0
- package/dist/src/gen/video/coordinator/client_v1_rpc/websocket.d.ts +292 -0
- package/dist/src/gen/video/coordinator/edge_v1/edge.d.ts +183 -0
- package/dist/src/gen/video/coordinator/event_v1/event.d.ts +411 -0
- package/dist/src/gen/video/coordinator/geofence_v1/geofence.d.ts +63 -0
- package/dist/src/gen/video/coordinator/member_v1/member.d.ts +59 -0
- package/dist/src/gen/video/coordinator/participant_v1/participant.d.ts +103 -0
- package/dist/src/gen/video/coordinator/push_v1/push.d.ts +240 -0
- package/dist/src/gen/video/coordinator/stat_v1/stat.d.ts +308 -0
- package/dist/src/gen/video/coordinator/user_v1/user.d.ts +112 -0
- package/dist/src/gen/video/coordinator/utils_v1/utils.d.ts +47 -0
- package/dist/src/gen/video/sfu/event/events.d.ts +736 -0
- package/dist/src/gen/video/sfu/models/models.d.ts +460 -0
- package/dist/src/gen/video/sfu/signal_rpc/signal.client.d.ts +89 -0
- package/dist/src/gen/video/sfu/signal_rpc/signal.d.ts +320 -0
- package/dist/src/helpers/browsers.d.ts +8 -0
- package/dist/src/helpers/sound-detector.d.ts +34 -0
- package/dist/src/rpc/createClient.d.ts +10 -0
- package/dist/src/rpc/index.d.ts +2 -0
- package/dist/src/rpc/latency.d.ts +9 -0
- package/dist/src/rtc/Call.d.ts +180 -0
- package/dist/src/rtc/CallMetadata.d.ts +9 -0
- package/dist/src/rtc/Dispatcher.d.ts +9 -0
- package/dist/src/rtc/IceTrickleBuffer.d.ts +11 -0
- package/dist/src/rtc/callEventHandlers.d.ts +5 -0
- package/dist/src/rtc/codecs.d.ts +2 -0
- package/dist/src/rtc/helpers/iceCandidate.d.ts +2 -0
- package/dist/src/rtc/helpers/tracks.d.ts +3 -0
- package/dist/src/rtc/publisher.d.ts +53 -0
- package/dist/src/rtc/signal.d.ts +5 -0
- package/dist/src/rtc/subscriber.d.ts +7 -0
- package/dist/src/rtc/types.d.ts +84 -0
- package/dist/src/rtc/videoLayers.d.ts +17 -0
- package/dist/src/stats/coordinator-stats-reporter.d.ts +10 -0
- package/dist/src/stats/state-store-stats-reporter.d.ts +57 -0
- package/dist/src/stats/types.d.ts +42 -0
- package/dist/src/store/index.d.ts +2 -0
- package/dist/src/store/rxUtils.d.ts +18 -0
- package/dist/src/store/stateStore.d.ts +182 -0
- package/generate-openapi.sh +32 -0
- package/index.ts +30 -0
- package/openapitools.json +7 -0
- package/package.json +54 -0
- package/rollup.config.mjs +48 -0
- package/src/Batcher.ts +43 -0
- package/src/CallDropScheduler.ts +192 -0
- package/src/StreamSfuClient.ts +185 -0
- package/src/StreamVideoClient.ts +487 -0
- package/src/__tests__/StreamVideoClient.test.ts +83 -0
- package/src/config/defaultConfigs.ts +15 -0
- package/src/config/types.ts +30 -0
- package/src/coordinator/StreamCoordinatorClient.ts +111 -0
- package/src/coordinator/connection/base64.ts +80 -0
- package/src/coordinator/connection/client.ts +815 -0
- package/src/coordinator/connection/connection.ts +750 -0
- package/src/coordinator/connection/connection_fallback.ts +239 -0
- package/src/coordinator/connection/errors.ts +70 -0
- package/src/coordinator/connection/events.ts +10 -0
- package/src/coordinator/connection/insights.ts +88 -0
- package/src/coordinator/connection/signing.ts +104 -0
- package/src/coordinator/connection/token_manager.ts +160 -0
- package/src/coordinator/connection/types.ts +120 -0
- package/src/coordinator/connection/utils.ts +148 -0
- package/src/devices.ts +266 -0
- package/src/events/call.ts +166 -0
- package/src/events/internal.ts +47 -0
- package/src/events/participant.ts +97 -0
- package/src/events/speaker.ts +62 -0
- package/src/gen/coordinator/index.ts +1653 -0
- package/src/gen/google/protobuf/descriptor.ts +3466 -0
- package/src/gen/google/protobuf/duration.ts +232 -0
- package/src/gen/google/protobuf/struct.ts +481 -0
- package/src/gen/google/protobuf/timestamp.ts +291 -0
- package/src/gen/video/coordinator/broadcast_v1/broadcast.ts +154 -0
- package/src/gen/video/coordinator/call_v1/call.ts +651 -0
- package/src/gen/video/coordinator/client_v1_rpc/client_rpc.client.ts +463 -0
- package/src/gen/video/coordinator/client_v1_rpc/client_rpc.ts +3819 -0
- package/src/gen/video/coordinator/client_v1_rpc/envelopes.ts +424 -0
- package/src/gen/video/coordinator/client_v1_rpc/websocket.ts +719 -0
- package/src/gen/video/coordinator/edge_v1/edge.ts +532 -0
- package/src/gen/video/coordinator/event_v1/event.ts +1171 -0
- package/src/gen/video/coordinator/geofence_v1/geofence.ts +128 -0
- package/src/gen/video/coordinator/member_v1/member.ts +138 -0
- package/src/gen/video/coordinator/participant_v1/participant.ts +261 -0
- package/src/gen/video/coordinator/push_v1/push.ts +651 -0
- package/src/gen/video/coordinator/stat_v1/stat.ts +656 -0
- package/src/gen/video/coordinator/user_v1/user.ts +277 -0
- package/src/gen/video/coordinator/utils_v1/utils.ts +98 -0
- package/src/gen/video/sfu/event/events.ts +1962 -0
- package/src/gen/video/sfu/models/models.ts +1062 -0
- package/src/gen/video/sfu/signal_rpc/signal.client.ts +108 -0
- package/src/gen/video/sfu/signal_rpc/signal.ts +906 -0
- package/src/helpers/browsers.ts +13 -0
- package/src/helpers/sound-detector.ts +85 -0
- package/src/rpc/createClient.ts +50 -0
- package/src/rpc/index.ts +2 -0
- package/src/rpc/latency.ts +43 -0
- package/src/rtc/Call.ts +585 -0
- package/src/rtc/CallMetadata.ts +24 -0
- package/src/rtc/Dispatcher.ts +46 -0
- package/src/rtc/IceTrickleBuffer.ts +21 -0
- package/src/rtc/callEventHandlers.ts +37 -0
- package/src/rtc/codecs.ts +61 -0
- package/src/rtc/helpers/iceCandidate.ts +16 -0
- package/src/rtc/helpers/tracks.ts +18 -0
- package/src/rtc/publisher.ts +305 -0
- package/src/rtc/signal.ts +34 -0
- package/src/rtc/subscriber.ts +85 -0
- package/src/rtc/types.ts +105 -0
- package/src/rtc/videoLayers.ts +103 -0
- package/src/stats/coordinator-stats-reporter.ts +167 -0
- package/src/stats/state-store-stats-reporter.ts +364 -0
- package/src/stats/types.ts +46 -0
- package/src/store/index.ts +2 -0
- package/src/store/rxUtils.ts +42 -0
- package/src/store/stateStore.ts +341 -0
- package/tsconfig.json +25 -0
- package/typedoc.json +11 -0
- package/vite.config.ts +11 -0
|
@@ -0,0 +1,815 @@
|
|
|
1
|
+
import axios, {
|
|
2
|
+
AxiosError,
|
|
3
|
+
AxiosHeaders,
|
|
4
|
+
AxiosInstance,
|
|
5
|
+
AxiosRequestConfig,
|
|
6
|
+
AxiosResponse,
|
|
7
|
+
} from 'axios';
|
|
8
|
+
import https from 'https';
|
|
9
|
+
import WebSocket from 'isomorphic-ws';
|
|
10
|
+
import { StableWSConnection } from './connection';
|
|
11
|
+
import { DevToken } from './signing';
|
|
12
|
+
import { TokenManager } from './token_manager';
|
|
13
|
+
import { WSConnectionFallback } from './connection_fallback';
|
|
14
|
+
import { isErrorResponse, isWSFailure } from './errors';
|
|
15
|
+
import {
|
|
16
|
+
chatCodes,
|
|
17
|
+
isFunction,
|
|
18
|
+
isOnline,
|
|
19
|
+
randomId,
|
|
20
|
+
retryInterval,
|
|
21
|
+
sleep,
|
|
22
|
+
} from './utils';
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
APIErrorResponse,
|
|
26
|
+
ConnectAPIResponse,
|
|
27
|
+
ErrorFromResponse,
|
|
28
|
+
Event,
|
|
29
|
+
EventHandler,
|
|
30
|
+
Logger,
|
|
31
|
+
OwnUserResponse,
|
|
32
|
+
StreamClientOptions,
|
|
33
|
+
TokenOrProvider,
|
|
34
|
+
UserResponse,
|
|
35
|
+
} from './types';
|
|
36
|
+
import { InsightMetrics, postInsights } from './insights';
|
|
37
|
+
|
|
38
|
+
function isString(x: unknown): x is string {
|
|
39
|
+
return typeof x === 'string' || x instanceof String;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class StreamClient {
|
|
43
|
+
_user?: OwnUserResponse | UserResponse;
|
|
44
|
+
anonymous: boolean;
|
|
45
|
+
persistUserOnConnectionFailure?: boolean;
|
|
46
|
+
axiosInstance: AxiosInstance;
|
|
47
|
+
baseURL?: string;
|
|
48
|
+
browser: boolean;
|
|
49
|
+
cleaningIntervalRef?: NodeJS.Timeout;
|
|
50
|
+
clientID?: string;
|
|
51
|
+
key: string;
|
|
52
|
+
listeners: Record<string, Array<(event: Event) => void>>;
|
|
53
|
+
logger: Logger;
|
|
54
|
+
|
|
55
|
+
node: boolean;
|
|
56
|
+
options: StreamClientOptions;
|
|
57
|
+
secret?: string;
|
|
58
|
+
setUserPromise: ConnectAPIResponse | null;
|
|
59
|
+
tokenManager: TokenManager;
|
|
60
|
+
user?: OwnUserResponse | UserResponse;
|
|
61
|
+
userAgent?: string;
|
|
62
|
+
userID?: string;
|
|
63
|
+
wsBaseURL?: string;
|
|
64
|
+
wsConnection: StableWSConnection | null;
|
|
65
|
+
wsFallback?: WSConnectionFallback;
|
|
66
|
+
wsPromise: ConnectAPIResponse | null;
|
|
67
|
+
consecutiveFailures: number;
|
|
68
|
+
insightMetrics: InsightMetrics;
|
|
69
|
+
defaultWSTimeoutWithFallback: number;
|
|
70
|
+
defaultWSTimeout: number;
|
|
71
|
+
private nextRequestAbortController: AbortController | null = null;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Initialize a client.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} key - the api key
|
|
77
|
+
* @param {string} [secret] - the api secret
|
|
78
|
+
* @param {StreamClientOptions} [options] - additional options, here you can pass custom options to axios instance
|
|
79
|
+
* @param {boolean} [options.browser] - enforce the client to be in browser mode
|
|
80
|
+
* @param {boolean} [options.warmUp] - default to false, if true, client will open a connection as soon as possible to speed up following requests
|
|
81
|
+
* @param {Logger} [options.Logger] - custom logger
|
|
82
|
+
* @param {number} [options.timeout] - default to 3000
|
|
83
|
+
* @param {httpsAgent} [options.httpsAgent] - custom httpsAgent, in node it's default to https.agent()
|
|
84
|
+
* @example <caption>initialize the client in user mode</caption>
|
|
85
|
+
* new StreamChat('api_key')
|
|
86
|
+
* @example <caption>initialize the client in user mode with options</caption>
|
|
87
|
+
* new StreamChat('api_key', { warmUp:true, timeout:5000 })
|
|
88
|
+
* @example <caption>secret is optional and only used in server side mode</caption>
|
|
89
|
+
* new StreamChat('api_key', "secret", { httpsAgent: customAgent })
|
|
90
|
+
*/
|
|
91
|
+
constructor(key: string, options?: StreamClientOptions);
|
|
92
|
+
constructor(key: string, secret?: string, options?: StreamClientOptions);
|
|
93
|
+
constructor(
|
|
94
|
+
key: string,
|
|
95
|
+
secretOrOptions?: StreamClientOptions | string,
|
|
96
|
+
options?: StreamClientOptions,
|
|
97
|
+
) {
|
|
98
|
+
// set the key
|
|
99
|
+
this.key = key;
|
|
100
|
+
this.listeners = {};
|
|
101
|
+
|
|
102
|
+
// set the secret
|
|
103
|
+
if (secretOrOptions && isString(secretOrOptions)) {
|
|
104
|
+
this.secret = secretOrOptions;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// set the options... and figure out defaults...
|
|
108
|
+
const inputOptions = options
|
|
109
|
+
? options
|
|
110
|
+
: secretOrOptions && !isString(secretOrOptions)
|
|
111
|
+
? secretOrOptions
|
|
112
|
+
: ({
|
|
113
|
+
browser: typeof window !== 'undefined',
|
|
114
|
+
} as Partial<StreamClientOptions>);
|
|
115
|
+
|
|
116
|
+
this.browser = inputOptions.browser || typeof window !== 'undefined';
|
|
117
|
+
this.node = !this.browser;
|
|
118
|
+
|
|
119
|
+
this.options = {
|
|
120
|
+
timeout: 3000,
|
|
121
|
+
withCredentials: false, // making sure cookies are not sent
|
|
122
|
+
warmUp: false,
|
|
123
|
+
...inputOptions,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (this.node && !this.options.httpsAgent) {
|
|
127
|
+
this.options.httpsAgent = new https.Agent({
|
|
128
|
+
keepAlive: true,
|
|
129
|
+
keepAliveMsecs: 3000,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.axiosInstance = axios.create(this.options);
|
|
134
|
+
|
|
135
|
+
this.setBaseURL(this.options.baseURL || 'https://chat.stream-io-api.com');
|
|
136
|
+
|
|
137
|
+
if (typeof process !== 'undefined' && process.env.STREAM_LOCAL_TEST_RUN) {
|
|
138
|
+
this.setBaseURL('http://localhost:3030');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (typeof process !== 'undefined' && process.env.STREAM_LOCAL_TEST_HOST) {
|
|
142
|
+
this.setBaseURL('http://' + process.env.STREAM_LOCAL_TEST_HOST);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// WS connection is initialized when setUser is called
|
|
146
|
+
this.wsConnection = null;
|
|
147
|
+
this.wsPromise = null;
|
|
148
|
+
this.setUserPromise = null;
|
|
149
|
+
|
|
150
|
+
// mapping between channel groups and configs
|
|
151
|
+
this.anonymous = false;
|
|
152
|
+
this.persistUserOnConnectionFailure =
|
|
153
|
+
this.options?.persistUserOnConnectionFailure;
|
|
154
|
+
|
|
155
|
+
// If its a server-side client, then lets initialize the tokenManager, since token will be
|
|
156
|
+
// generated from secret.
|
|
157
|
+
this.tokenManager = new TokenManager(this.secret);
|
|
158
|
+
this.consecutiveFailures = 0;
|
|
159
|
+
this.insightMetrics = new InsightMetrics();
|
|
160
|
+
|
|
161
|
+
this.defaultWSTimeoutWithFallback = 6000;
|
|
162
|
+
this.defaultWSTimeout = 15000;
|
|
163
|
+
|
|
164
|
+
this.logger = isFunction(inputOptions.logger)
|
|
165
|
+
? inputOptions.logger
|
|
166
|
+
: () => null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
devToken(userID: string) {
|
|
170
|
+
return DevToken(userID);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
getAuthType() {
|
|
174
|
+
return this.anonymous ? 'anonymous' : 'jwt';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
setBaseURL(baseURL: string) {
|
|
178
|
+
this.baseURL = baseURL;
|
|
179
|
+
this.wsBaseURL = this.baseURL
|
|
180
|
+
.replace('http', 'ws')
|
|
181
|
+
.replace(':3030', ':8800');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
_getConnectionID = () =>
|
|
185
|
+
this.wsConnection?.connectionID || this.wsFallback?.connectionID;
|
|
186
|
+
|
|
187
|
+
_hasConnectionID = () => Boolean(this._getConnectionID());
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* connectUser - Set the current user and open a WebSocket connection
|
|
191
|
+
*
|
|
192
|
+
* @param {OwnUserResponse | UserResponse} user Data about this user. IE {name: "john"}
|
|
193
|
+
* @param {TokenOrProvider} userTokenOrProvider Token or provider
|
|
194
|
+
*
|
|
195
|
+
* @return {ConnectAPIResponse} Returns a promise that resolves when the connection is setup
|
|
196
|
+
*/
|
|
197
|
+
connectUser = async (
|
|
198
|
+
user: OwnUserResponse | UserResponse,
|
|
199
|
+
userTokenOrProvider: TokenOrProvider,
|
|
200
|
+
) => {
|
|
201
|
+
if (!user.id) {
|
|
202
|
+
throw new Error('The "id" field on the user is missing');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Calling connectUser multiple times is potentially the result of a bad integration, however,
|
|
207
|
+
* If the user id remains the same we don't throw error
|
|
208
|
+
*/
|
|
209
|
+
if (this.userID === user.id && this.setUserPromise) {
|
|
210
|
+
console.warn(
|
|
211
|
+
'Consecutive calls to connectUser is detected, ideally you should only call this function once in your app.',
|
|
212
|
+
);
|
|
213
|
+
return this.setUserPromise;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (this.userID) {
|
|
217
|
+
throw new Error(
|
|
218
|
+
'Use client.disconnect() before trying to connect as a different user. connectUser was called twice.',
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (
|
|
223
|
+
(this._isUsingServerAuth() || this.node) &&
|
|
224
|
+
!this.options.allowServerSideConnect
|
|
225
|
+
) {
|
|
226
|
+
console.warn(
|
|
227
|
+
'Please do not use connectUser server side. connectUser impacts MAU and concurrent connection usage and thus your bill. If you have a valid use-case, add "allowServerSideConnect: true" to the client options to disable this warning.',
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// we generate the client id client side
|
|
232
|
+
this.userID = user.id;
|
|
233
|
+
this.anonymous = false;
|
|
234
|
+
|
|
235
|
+
const setTokenPromise = this._setToken(user, userTokenOrProvider);
|
|
236
|
+
this._setUser(user);
|
|
237
|
+
|
|
238
|
+
const wsPromise = this.openConnection();
|
|
239
|
+
|
|
240
|
+
this.setUserPromise = Promise.all([setTokenPromise, wsPromise]).then(
|
|
241
|
+
(result) => result[1], // We only return connection promise;
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
return await this.setUserPromise;
|
|
246
|
+
} catch (err) {
|
|
247
|
+
if (this.persistUserOnConnectionFailure) {
|
|
248
|
+
// cleanup client to allow the user to retry connectUser again
|
|
249
|
+
this.closeConnection();
|
|
250
|
+
} else {
|
|
251
|
+
this.disconnectUser();
|
|
252
|
+
}
|
|
253
|
+
throw err;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
_setToken = (user: UserResponse, userTokenOrProvider: TokenOrProvider) =>
|
|
258
|
+
this.tokenManager.setTokenOrProvider(userTokenOrProvider, user);
|
|
259
|
+
|
|
260
|
+
_setUser(user: OwnUserResponse | UserResponse) {
|
|
261
|
+
/**
|
|
262
|
+
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
263
|
+
* It contains reserved properties and own user properties which are not present in `this._user`.
|
|
264
|
+
*/
|
|
265
|
+
this.user = user;
|
|
266
|
+
this.userID = user.id;
|
|
267
|
+
// this one is actually used for requests. This is a copy of current user provided to `connectUser` function.
|
|
268
|
+
this._user = { ...user };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Disconnects the websocket connection, without removing the user set on client.
|
|
273
|
+
* client.closeConnection will not trigger default auto-retry mechanism for reconnection. You need
|
|
274
|
+
* to call client.openConnection to reconnect to websocket.
|
|
275
|
+
*
|
|
276
|
+
* This is mainly useful on mobile side. You can only receive push notifications
|
|
277
|
+
* if you don't have active websocket connection.
|
|
278
|
+
* So when your app goes to background, you can call `client.closeConnection`.
|
|
279
|
+
* And when app comes back to foreground, call `client.openConnection`.
|
|
280
|
+
*
|
|
281
|
+
* @param timeout Max number of ms, to wait for close event of websocket, before forcefully assuming succesful disconnection.
|
|
282
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
|
283
|
+
*/
|
|
284
|
+
closeConnection = async (timeout?: number) => {
|
|
285
|
+
if (this.cleaningIntervalRef != null) {
|
|
286
|
+
clearInterval(this.cleaningIntervalRef);
|
|
287
|
+
this.cleaningIntervalRef = undefined;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
await Promise.all([
|
|
291
|
+
this.wsConnection?.disconnect(timeout),
|
|
292
|
+
this.wsFallback?.disconnect(timeout),
|
|
293
|
+
]);
|
|
294
|
+
return Promise.resolve();
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Creates a new WebSocket connection with the current user. Returns empty promise, if there is an active connection
|
|
299
|
+
*/
|
|
300
|
+
openConnection = async () => {
|
|
301
|
+
if (!this.userID) {
|
|
302
|
+
throw Error(
|
|
303
|
+
'User is not set on client, use client.connectUser or client.connectAnonymousUser instead',
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (this.wsConnection?.isConnecting && this.wsPromise) {
|
|
308
|
+
this.logger(
|
|
309
|
+
'info',
|
|
310
|
+
'client:openConnection() - connection already in progress',
|
|
311
|
+
{
|
|
312
|
+
tags: ['connection', 'client'],
|
|
313
|
+
},
|
|
314
|
+
);
|
|
315
|
+
return this.wsPromise;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (
|
|
319
|
+
(this.wsConnection?.isHealthy || this.wsFallback?.isHealthy()) &&
|
|
320
|
+
this._hasConnectionID()
|
|
321
|
+
) {
|
|
322
|
+
this.logger(
|
|
323
|
+
'info',
|
|
324
|
+
'client:openConnection() - openConnection called twice, healthy connection already exists',
|
|
325
|
+
{
|
|
326
|
+
tags: ['connection', 'client'],
|
|
327
|
+
},
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
return Promise.resolve();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
this.clientID = `${this.userID}--${randomId()}`;
|
|
334
|
+
this.wsPromise = this.connect();
|
|
335
|
+
return this.wsPromise;
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
_normalizeDate = (before: Date | string | null): string | null => {
|
|
339
|
+
if (before instanceof Date) {
|
|
340
|
+
before = before.toISOString();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (before === '') {
|
|
344
|
+
throw new Error(
|
|
345
|
+
"Don't pass blank string for since, use null instead if resetting the token revoke",
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return before;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Disconnects the websocket and removes the user from client.
|
|
354
|
+
*
|
|
355
|
+
* @param timeout Max number of ms, to wait for close event of websocket, before forcefully assuming successful disconnection.
|
|
356
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
|
357
|
+
*/
|
|
358
|
+
disconnectUser = async (timeout?: number) => {
|
|
359
|
+
this.logger('info', 'client:disconnect() - Disconnecting the client', {
|
|
360
|
+
tags: ['connection', 'client'],
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// remove the user specific fields
|
|
364
|
+
delete this.user;
|
|
365
|
+
delete this._user;
|
|
366
|
+
delete this.userID;
|
|
367
|
+
|
|
368
|
+
this.anonymous = false;
|
|
369
|
+
|
|
370
|
+
const closePromise = this.closeConnection(timeout);
|
|
371
|
+
// reset token manager
|
|
372
|
+
setTimeout(this.tokenManager.reset); // delay reseting to use token for disconnect calls
|
|
373
|
+
|
|
374
|
+
// close the WS connection
|
|
375
|
+
return closePromise;
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* connectAnonymousUser - Set an anonymous user and open a WebSocket connection
|
|
380
|
+
*/
|
|
381
|
+
connectAnonymousUser = () => {
|
|
382
|
+
if (
|
|
383
|
+
(this._isUsingServerAuth() || this.node) &&
|
|
384
|
+
!this.options.allowServerSideConnect
|
|
385
|
+
) {
|
|
386
|
+
console.warn(
|
|
387
|
+
'Please do not use connectUser server side. connectUser impacts MAU and concurrent connection usage and thus your bill. If you have a valid use-case, add "allowServerSideConnect: true" to the client options to disable this warning.',
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
this.anonymous = true;
|
|
392
|
+
this.userID = randomId();
|
|
393
|
+
const anonymousUser = {
|
|
394
|
+
id: this.userID,
|
|
395
|
+
anon: true,
|
|
396
|
+
} as UserResponse;
|
|
397
|
+
|
|
398
|
+
this._setToken(anonymousUser, '');
|
|
399
|
+
this._setUser(anonymousUser);
|
|
400
|
+
|
|
401
|
+
return this.openConnection();
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* on - Listen to events on all channels and users your watching
|
|
406
|
+
*
|
|
407
|
+
* client.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
|
|
408
|
+
* or
|
|
409
|
+
* client.on(event => {console.log(event.type)})
|
|
410
|
+
*
|
|
411
|
+
* @param {EventHandler | string} callbackOrEventName The event type to listen for (optional)
|
|
412
|
+
* @param {EventHandler} [callbackOrNothing] The callback to call
|
|
413
|
+
*
|
|
414
|
+
* @return {Function} Returns a function which, when called, unsubscribes the event handler.
|
|
415
|
+
*/
|
|
416
|
+
on(
|
|
417
|
+
callbackOrEventName: EventHandler | string,
|
|
418
|
+
callbackOrNothing?: EventHandler,
|
|
419
|
+
) {
|
|
420
|
+
const key = callbackOrNothing ? (callbackOrEventName as string) : 'all';
|
|
421
|
+
const callback = callbackOrNothing
|
|
422
|
+
? callbackOrNothing
|
|
423
|
+
: (callbackOrEventName as EventHandler);
|
|
424
|
+
if (!(key in this.listeners)) {
|
|
425
|
+
this.listeners[key] = [];
|
|
426
|
+
}
|
|
427
|
+
this.logger('info', `Attaching listener for ${key} event`, {
|
|
428
|
+
tags: ['event', 'client'],
|
|
429
|
+
});
|
|
430
|
+
this.listeners[key].push(callback);
|
|
431
|
+
|
|
432
|
+
return () => {
|
|
433
|
+
this.off(key, callback);
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* off - Remove the event handler
|
|
439
|
+
*
|
|
440
|
+
*/
|
|
441
|
+
off(
|
|
442
|
+
callbackOrEventName: EventHandler | string,
|
|
443
|
+
callbackOrNothing?: EventHandler,
|
|
444
|
+
) {
|
|
445
|
+
const key = callbackOrNothing ? (callbackOrEventName as string) : 'all';
|
|
446
|
+
const callback = callbackOrNothing
|
|
447
|
+
? callbackOrNothing
|
|
448
|
+
: (callbackOrEventName as EventHandler);
|
|
449
|
+
if (!(key in this.listeners)) {
|
|
450
|
+
this.listeners[key] = [];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
this.logger('info', `Removing listener for ${key} event`, {
|
|
454
|
+
tags: ['event', 'client'],
|
|
455
|
+
});
|
|
456
|
+
this.listeners[key] = this.listeners[key].filter(
|
|
457
|
+
(value) => value !== callback,
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
_logApiRequest(
|
|
462
|
+
type: string,
|
|
463
|
+
url: string,
|
|
464
|
+
data: unknown,
|
|
465
|
+
config: AxiosRequestConfig & {
|
|
466
|
+
config?: AxiosRequestConfig & { maxBodyLength?: number };
|
|
467
|
+
},
|
|
468
|
+
) {
|
|
469
|
+
this.logger('info', `client: ${type} - Request - ${url}`, {
|
|
470
|
+
tags: ['api', 'api_request', 'client'],
|
|
471
|
+
url,
|
|
472
|
+
payload: data,
|
|
473
|
+
config,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
_logApiResponse<T>(type: string, url: string, response: AxiosResponse<T>) {
|
|
478
|
+
this.logger(
|
|
479
|
+
'info',
|
|
480
|
+
`client:${type} - Response - url: ${url} > status ${response.status}`,
|
|
481
|
+
{
|
|
482
|
+
tags: ['api', 'api_response', 'client'],
|
|
483
|
+
url,
|
|
484
|
+
response,
|
|
485
|
+
},
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
_logApiError(type: string, url: string, error: unknown) {
|
|
490
|
+
this.logger('error', `client:${type} - Error - url: ${url}`, {
|
|
491
|
+
tags: ['api', 'api_response', 'client'],
|
|
492
|
+
url,
|
|
493
|
+
error,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
doAxiosRequest = async <T>(
|
|
498
|
+
type: string,
|
|
499
|
+
url: string,
|
|
500
|
+
data?: unknown,
|
|
501
|
+
options: AxiosRequestConfig & {
|
|
502
|
+
config?: AxiosRequestConfig & { maxBodyLength?: number };
|
|
503
|
+
} = {},
|
|
504
|
+
): Promise<T> => {
|
|
505
|
+
await this.tokenManager.tokenReady();
|
|
506
|
+
const requestConfig = this._enrichAxiosOptions(options);
|
|
507
|
+
try {
|
|
508
|
+
let response: AxiosResponse<T>;
|
|
509
|
+
this._logApiRequest(type, url, data, requestConfig);
|
|
510
|
+
switch (type) {
|
|
511
|
+
case 'get':
|
|
512
|
+
response = await this.axiosInstance.get(url, requestConfig);
|
|
513
|
+
break;
|
|
514
|
+
case 'delete':
|
|
515
|
+
response = await this.axiosInstance.delete(url, requestConfig);
|
|
516
|
+
break;
|
|
517
|
+
case 'post':
|
|
518
|
+
response = await this.axiosInstance.post(url, data, requestConfig);
|
|
519
|
+
break;
|
|
520
|
+
case 'put':
|
|
521
|
+
response = await this.axiosInstance.put(url, data, requestConfig);
|
|
522
|
+
break;
|
|
523
|
+
case 'patch':
|
|
524
|
+
response = await this.axiosInstance.patch(url, data, requestConfig);
|
|
525
|
+
break;
|
|
526
|
+
case 'options':
|
|
527
|
+
response = await this.axiosInstance.options(url, requestConfig);
|
|
528
|
+
break;
|
|
529
|
+
default:
|
|
530
|
+
throw new Error('Invalid request type');
|
|
531
|
+
}
|
|
532
|
+
this._logApiResponse<T>(type, url, response);
|
|
533
|
+
this.consecutiveFailures = 0;
|
|
534
|
+
return this.handleResponse(response);
|
|
535
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
536
|
+
} catch (e: any /**TODO: generalize error types */) {
|
|
537
|
+
e.client_request_id = requestConfig.headers?.['x-client-request-id'];
|
|
538
|
+
this._logApiError(type, url, e);
|
|
539
|
+
this.consecutiveFailures += 1;
|
|
540
|
+
if (e.response) {
|
|
541
|
+
/** connection_fallback depends on this token expiration logic */
|
|
542
|
+
if (
|
|
543
|
+
e.response.data.code === chatCodes.TOKEN_EXPIRED &&
|
|
544
|
+
!this.tokenManager.isStatic()
|
|
545
|
+
) {
|
|
546
|
+
if (this.consecutiveFailures > 1) {
|
|
547
|
+
await sleep(retryInterval(this.consecutiveFailures));
|
|
548
|
+
}
|
|
549
|
+
await this.tokenManager.loadToken();
|
|
550
|
+
return await this.doAxiosRequest<T>(type, url, data, options);
|
|
551
|
+
}
|
|
552
|
+
return this.handleResponse(e.response);
|
|
553
|
+
} else {
|
|
554
|
+
throw e as AxiosError<APIErrorResponse>;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
get<T>(url: string, params?: AxiosRequestConfig['params']) {
|
|
560
|
+
return this.doAxiosRequest<T>('get', url, null, {
|
|
561
|
+
params,
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
put<T>(url: string, data?: unknown) {
|
|
566
|
+
return this.doAxiosRequest<T>('put', url, data);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
post<T>(url: string, data?: unknown) {
|
|
570
|
+
return this.doAxiosRequest<T>('post', url, data);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
patch<T>(url: string, data?: unknown) {
|
|
574
|
+
return this.doAxiosRequest<T>('patch', url, data);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
delete<T>(url: string, params?: AxiosRequestConfig['params']) {
|
|
578
|
+
return this.doAxiosRequest<T>('delete', url, null, {
|
|
579
|
+
params,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
errorFromResponse(
|
|
584
|
+
response: AxiosResponse<APIErrorResponse>,
|
|
585
|
+
): ErrorFromResponse<APIErrorResponse> {
|
|
586
|
+
let err: ErrorFromResponse<APIErrorResponse>;
|
|
587
|
+
err = new ErrorFromResponse(
|
|
588
|
+
`StreamChat error HTTP code: ${response.status}`,
|
|
589
|
+
);
|
|
590
|
+
if (response.data && response.data.code) {
|
|
591
|
+
err = new Error(
|
|
592
|
+
`StreamChat error code ${response.data.code}: ${response.data.message}`,
|
|
593
|
+
);
|
|
594
|
+
err.code = response.data.code;
|
|
595
|
+
}
|
|
596
|
+
err.response = response;
|
|
597
|
+
err.status = response.status;
|
|
598
|
+
return err;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
handleResponse<T>(response: AxiosResponse<T>) {
|
|
602
|
+
const data = response.data;
|
|
603
|
+
if (isErrorResponse(response)) {
|
|
604
|
+
throw this.errorFromResponse(response);
|
|
605
|
+
}
|
|
606
|
+
return data;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
dispatchEvent = (event: Event) => {
|
|
610
|
+
if (!event.received_at) event.received_at = new Date();
|
|
611
|
+
|
|
612
|
+
this._callClientListeners(event);
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
handleEvent = (messageEvent: WebSocket.MessageEvent) => {
|
|
616
|
+
// dispatch the event to the channel listeners
|
|
617
|
+
const jsonString = messageEvent.data as string;
|
|
618
|
+
const event = JSON.parse(jsonString) as Event;
|
|
619
|
+
this.dispatchEvent(event);
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
_callClientListeners = (event: Event) => {
|
|
623
|
+
const client = this;
|
|
624
|
+
// gather and call the listeners
|
|
625
|
+
const listeners: Array<(event: Event) => void> = [];
|
|
626
|
+
if (client.listeners.all) {
|
|
627
|
+
listeners.push(...client.listeners.all);
|
|
628
|
+
}
|
|
629
|
+
if (client.listeners[event.type]) {
|
|
630
|
+
listeners.push(...client.listeners[event.type]);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// call the event and send it to the listeners
|
|
634
|
+
for (const listener of listeners) {
|
|
635
|
+
listener(event);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* @private
|
|
641
|
+
*/
|
|
642
|
+
async connect() {
|
|
643
|
+
if (!this.userID || !this._user) {
|
|
644
|
+
throw Error(
|
|
645
|
+
'Call connectUser or connectAnonymousUser before starting the connection',
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
if (!this.wsBaseURL) {
|
|
649
|
+
throw Error('Websocket base url not set');
|
|
650
|
+
}
|
|
651
|
+
if (!this.clientID) {
|
|
652
|
+
throw Error('clientID is not set');
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (
|
|
656
|
+
!this.wsConnection &&
|
|
657
|
+
(this.options.warmUp || this.options.enableInsights)
|
|
658
|
+
) {
|
|
659
|
+
this._sayHi();
|
|
660
|
+
}
|
|
661
|
+
// The StableWSConnection handles all the reconnection logic.
|
|
662
|
+
if (this.options.wsConnection && this.node) {
|
|
663
|
+
// Intentionally avoiding adding ts generics on wsConnection in options since its only useful for unit test purpose.
|
|
664
|
+
(this.options.wsConnection as unknown as StableWSConnection).setClient(
|
|
665
|
+
this,
|
|
666
|
+
);
|
|
667
|
+
this.wsConnection = this.options
|
|
668
|
+
.wsConnection as unknown as StableWSConnection;
|
|
669
|
+
} else {
|
|
670
|
+
this.wsConnection = new StableWSConnection(this);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
try {
|
|
674
|
+
// if fallback is used before, continue using it instead of waiting for WS to fail
|
|
675
|
+
if (this.wsFallback) {
|
|
676
|
+
return await this.wsFallback.connect();
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// if WSFallback is enabled, ws connect should timeout faster so fallback can try
|
|
680
|
+
return await this.wsConnection.connect(
|
|
681
|
+
this.options.enableWSFallback
|
|
682
|
+
? this.defaultWSTimeoutWithFallback
|
|
683
|
+
: this.defaultWSTimeout,
|
|
684
|
+
);
|
|
685
|
+
} catch (err) {
|
|
686
|
+
// run fallback only if it's WS/Network error and not a normal API error
|
|
687
|
+
// make sure browser is online before even trying the longpoll
|
|
688
|
+
// @ts-ignore
|
|
689
|
+
if (this.options.enableWSFallback && isWSFailure(err) && isOnline()) {
|
|
690
|
+
this.logger(
|
|
691
|
+
'info',
|
|
692
|
+
'client:connect() - WS failed, fallback to longpoll',
|
|
693
|
+
{ tags: ['connection', 'client'] },
|
|
694
|
+
);
|
|
695
|
+
this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
|
|
696
|
+
|
|
697
|
+
this.wsConnection._destroyCurrentWSConnection();
|
|
698
|
+
this.wsConnection.disconnect().then(); // close WS so no retry
|
|
699
|
+
this.wsFallback = new WSConnectionFallback(this);
|
|
700
|
+
return await this.wsFallback.connect();
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
throw err;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Check the connectivity with server for warmup purpose.
|
|
709
|
+
*
|
|
710
|
+
* @private
|
|
711
|
+
*/
|
|
712
|
+
_sayHi() {
|
|
713
|
+
const client_request_id = randomId();
|
|
714
|
+
const opts = {
|
|
715
|
+
headers: AxiosHeaders.from({
|
|
716
|
+
'x-client-request-id': client_request_id,
|
|
717
|
+
}),
|
|
718
|
+
};
|
|
719
|
+
this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
|
|
720
|
+
if (this.options.enableInsights) {
|
|
721
|
+
postInsights('http_hi_failed', {
|
|
722
|
+
api_key: this.key,
|
|
723
|
+
err: e,
|
|
724
|
+
client_request_id,
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
getUserAgent() {
|
|
731
|
+
return (
|
|
732
|
+
this.userAgent ||
|
|
733
|
+
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${
|
|
734
|
+
process.env.PKG_VERSION
|
|
735
|
+
}`
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
setUserAgent(userAgent: string) {
|
|
740
|
+
this.userAgent = userAgent;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* _isUsingServerAuth - Returns true if we're using server side auth
|
|
745
|
+
*/
|
|
746
|
+
_isUsingServerAuth = () => !!this.secret;
|
|
747
|
+
|
|
748
|
+
_enrichAxiosOptions(
|
|
749
|
+
options: AxiosRequestConfig & { config?: AxiosRequestConfig } = {
|
|
750
|
+
params: {},
|
|
751
|
+
headers: {},
|
|
752
|
+
config: {},
|
|
753
|
+
},
|
|
754
|
+
): AxiosRequestConfig {
|
|
755
|
+
const token = this._getToken();
|
|
756
|
+
const authorization = token ? { Authorization: token } : undefined;
|
|
757
|
+
let signal: AbortSignal | null = null;
|
|
758
|
+
if (this.nextRequestAbortController !== null) {
|
|
759
|
+
signal = this.nextRequestAbortController.signal;
|
|
760
|
+
this.nextRequestAbortController = null;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
if (!options.headers?.['x-client-request-id']) {
|
|
764
|
+
options.headers = {
|
|
765
|
+
...options.headers,
|
|
766
|
+
'x-client-request-id': randomId(),
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
return {
|
|
771
|
+
params: {
|
|
772
|
+
user_id: this.userID,
|
|
773
|
+
connection_id: this._getConnectionID(),
|
|
774
|
+
api_key: this.key,
|
|
775
|
+
...options.params,
|
|
776
|
+
},
|
|
777
|
+
headers: {
|
|
778
|
+
...authorization,
|
|
779
|
+
'stream-auth-type': this.getAuthType(),
|
|
780
|
+
'X-Stream-Client': this.getUserAgent(),
|
|
781
|
+
...options.headers,
|
|
782
|
+
},
|
|
783
|
+
...(signal ? { signal } : {}),
|
|
784
|
+
...options.config,
|
|
785
|
+
...this.options.axiosRequestConfig,
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
_getToken() {
|
|
790
|
+
if (!this.tokenManager || this.anonymous) return null;
|
|
791
|
+
|
|
792
|
+
return this.tokenManager.getToken();
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* encode ws url payload
|
|
797
|
+
* @private
|
|
798
|
+
* @returns json string
|
|
799
|
+
*/
|
|
800
|
+
_buildWSPayload = (client_request_id?: string) => {
|
|
801
|
+
return JSON.stringify({
|
|
802
|
+
user_id: this.userID,
|
|
803
|
+
user_details: this._user,
|
|
804
|
+
// device: this.options.device,
|
|
805
|
+
client_request_id,
|
|
806
|
+
});
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* creates an abort controller that will be used by the next HTTP Request.
|
|
811
|
+
*/
|
|
812
|
+
createAbortControllerForNextRequest() {
|
|
813
|
+
return (this.nextRequestAbortController = new AbortController());
|
|
814
|
+
}
|
|
815
|
+
}
|