streemo-video-call-sdk 0.1.2 → 0.2.3

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.
Files changed (48) hide show
  1. package/README.md +46 -111
  2. package/dist/client.d.ts +35 -10
  3. package/dist/client.js +275 -4
  4. package/dist/client.js.map +1 -1
  5. package/dist/index.d.ts +475 -15
  6. package/dist/index.js +2377 -7
  7. package/dist/index.js.map +1 -1
  8. package/dist/sdkConfig-C9Fey7R2.d.ts +42 -0
  9. package/dist/styles.css +198 -0
  10. package/package.json +4 -3
  11. package/dist/ChatPanel.d.ts +0 -19
  12. package/dist/ChatPanel.d.ts.map +0 -1
  13. package/dist/ChatPanel.js +0 -78
  14. package/dist/ChatPanel.js.map +0 -1
  15. package/dist/VideoCallWidget.d.ts +0 -17
  16. package/dist/VideoCallWidget.d.ts.map +0 -1
  17. package/dist/VideoCallWidget.js +0 -20
  18. package/dist/VideoCallWidget.js.map +0 -1
  19. package/dist/VideoTile.d.ts +0 -15
  20. package/dist/VideoTile.d.ts.map +0 -1
  21. package/dist/VideoTile.js +0 -76
  22. package/dist/VideoTile.js.map +0 -1
  23. package/dist/auth.d.ts +0 -10
  24. package/dist/auth.d.ts.map +0 -1
  25. package/dist/auth.js +0 -11
  26. package/dist/auth.js.map +0 -1
  27. package/dist/client.d.ts.map +0 -1
  28. package/dist/index.d.ts.map +0 -1
  29. package/dist/sdkConfig.d.ts +0 -9
  30. package/dist/sdkConfig.d.ts.map +0 -1
  31. package/dist/sdkConfig.js +0 -17
  32. package/dist/sdkConfig.js.map +0 -1
  33. package/dist/sdkErrors.d.ts +0 -8
  34. package/dist/sdkErrors.d.ts.map +0 -1
  35. package/dist/sdkErrors.js +0 -32
  36. package/dist/sdkErrors.js.map +0 -1
  37. package/dist/signalingClient.d.ts +0 -33
  38. package/dist/signalingClient.d.ts.map +0 -1
  39. package/dist/signalingClient.js +0 -95
  40. package/dist/signalingClient.js.map +0 -1
  41. package/dist/tokenProvider.d.ts +0 -16
  42. package/dist/tokenProvider.d.ts.map +0 -1
  43. package/dist/tokenProvider.js +0 -89
  44. package/dist/tokenProvider.js.map +0 -1
  45. package/dist/useWebRTCCall.d.ts +0 -60
  46. package/dist/useWebRTCCall.d.ts.map +0 -1
  47. package/dist/useWebRTCCall.js +0 -1055
  48. package/dist/useWebRTCCall.js.map +0 -1
package/README.md CHANGED
@@ -1,138 +1,73 @@
1
- # @streemo-video-call-sdk
1
+ # streemo-video-call-sdk
2
2
 
3
- React SDK and signaling client to connect any website to your Streemo video-chat signaling server.
3
+ Production-ready React UI kit and headless hooks for Streemo realtime chat and video calls.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- npm install @streemo-video-call-sdk
8
+ npm install streemo-video-call-sdk
9
9
  ```
10
10
 
11
- ## React usage (auto room token from server)
11
+ ## Quick start (under 5 minutes)
12
12
 
13
13
  ```tsx
14
- import { useState } from 'react'
15
- import { VideoCallWidget, initVideoSDK } from '@streemo-video-call-sdk'
16
- import '@streemo-video-call-sdk/styles.css'
17
-
18
- initVideoSDK({
19
- auth: {
20
- clientId: 'YOUR_CLIENT_ID',
21
- appId: 'YOUR_APP_ID',
22
- },
14
+ import {
15
+ StreemoClient,
16
+ StreemoProvider,
17
+ StreemoTheme,
18
+ Chat,
19
+ ChannelList,
20
+ Channel,
21
+ MessageList,
22
+ MessageInput,
23
+ TypingIndicator,
24
+ } from 'streemo-video-call-sdk'
25
+ import 'streemo-video-call-sdk/styles.css'
26
+
27
+ const client = new StreemoClient({
28
+ apiKey: 'YOUR_APP_ID',
29
+ userToken: 'USER_TOKEN',
30
+ user: { id: 'u-1', name: 'Alice' },
31
+ baseUrl: 'https://api.streemo.ru',
23
32
  })
24
33
 
25
- export function MyCallPage() {
26
- const [enabled, setEnabled] = useState(false)
27
-
34
+ export function App() {
28
35
  return (
29
- <div>
30
- {!enabled && <button onClick={() => setEnabled(true)}>Enable camera and microphone</button>}
31
- <VideoCallWidget
32
- roomId="ROOM_ID"
33
- userId="USER_ID"
34
- userName="Alice"
35
- enabled={enabled}
36
- />
37
- </div>
36
+ <StreemoProvider client={client}>
37
+ <StreemoTheme theme={{ primary: '#635bff', radius: '10px' }}>
38
+ <Chat>
39
+ <ChannelList />
40
+ <Channel>
41
+ <MessageList />
42
+ <TypingIndicator />
43
+ <MessageInput />
44
+ </Channel>
45
+ </Chat>
46
+ </StreemoTheme>
47
+ </StreemoProvider>
38
48
  )
39
49
  }
40
50
  ```
41
51
 
42
- The widget will call your server automatically:
43
-
44
- - `POST /v1/rooms/:roomId/join`
45
- - `POST /v1/rooms/:roomId/token`
46
-
47
- Then it opens WS connection to `/v1/ws` and starts signaling.
48
-
49
- If `apiBaseUrl` is not provided, SDK uses `https://api.streemo.ru` by default.
50
- If `auth` is not passed into hook/widget/client, SDK uses values from `initVideoSDK(...)`.
51
-
52
- ## React usage (custom tokenProvider)
52
+ ## Call UI
53
53
 
54
54
  ```tsx
55
- import { useWebRTCCall } from '@streemo-video-call-sdk'
56
-
57
- const call = useWebRTCCall({
58
- roomId,
59
- userId,
60
- enabled: true,
61
- wsBaseUrl: 'wss://your-domain.com',
62
- tokenProvider: async ({ roomId }) => {
63
- // custom flow if your backend differs
64
- const response = await fetch(`/api/my-room-token?roomId=${roomId}`)
65
- const data = await response.json()
66
- return data.token
67
- },
68
- })
69
- ```
70
-
71
- ## Non-React usage (signaling-only entrypoint)
72
-
73
- ```ts
74
- import { VideoSignalingClient } from '@streemo-video-call-sdk/client'
55
+ import { CallRoom, ParticipantGrid, CallControls } from 'streemo-video-call-sdk'
75
56
 
76
- const client = new VideoSignalingClient({
77
- roomId: 'ROOM_ID',
78
- userId: 'USER_ID',
79
- apiBaseUrl: 'https://your-domain.com',
80
- auth: {
81
- authToken: 'PRE_ISSUED_AUTH_TOKEN',
82
- clientId: 'YOUR_CLIENT_ID',
83
- appId: 'YOUR_APP_ID',
84
- },
85
- onMessage: (msg) => console.log('signal', msg),
86
- })
87
-
88
- await client.connect()
89
- client.send({ type: 'join', payload: { joinedAt: new Date().toISOString() } })
57
+ export function SupportCall() {
58
+ return (
59
+ <CallRoom roomId="support-call">
60
+ <ParticipantGrid />
61
+ <CallControls />
62
+ </CallRoom>
63
+ )
64
+ }
90
65
  ```
91
66
 
92
- ## Server compatibility
93
-
94
- The SDK expects your server to support:
95
-
96
- - `GET /v1/ws?roomId=<id>&token=<jwt>` WebSocket endpoint
97
- - `POST /v1/rooms/:roomId/join` endpoint
98
- - `POST /v1/rooms/:roomId/token` endpoint returning `{ "token": "..." }`
99
- - Signaling message types:
100
- - `welcome`
101
- - `join` / `participant_joined`
102
- - `participant_left`
103
- - `offer`
104
- - `answer`
105
- - `ice_candidate`
106
-
107
- By default, SDK sends auth headers:
108
-
109
- - `X-Client-Id: <clientId>`
110
- - `X-App-Id: <appId>`
111
- - `X-Guest-Id: <userId>`
112
-
113
- `Authorization` header is optional. If `auth.authToken` is provided, SDK also sends `Authorization: Bearer <authToken>`.
114
- You can override header names in `auth` config (`authHeader`, `clientIdHeader`, `appIdHeader`).
115
-
116
- ## Auth error mapping for UI
117
-
118
- SDK normalizes server auth errors into typed codes:
119
-
120
- - `invalid_app`
121
- - `invalid_client`
122
- - `expired_token`
123
- - `unauthorized`
124
- - `forbidden`
125
-
126
- Helpers:
127
-
128
- - `mapSDKErrorToUIMessage(error)` from `@streemo-video-call-sdk`
129
- - `mapSDKErrorToUIMessage(error)` from `@streemo-video-call-sdk/client`
130
-
131
- ## Publish to npm
67
+ ## Build and publish
132
68
 
133
69
  ```bash
134
70
  npm run clean
135
- npm install
136
71
  npm run build
137
72
  npm publish --access public
138
73
  ```
package/dist/client.d.ts CHANGED
@@ -1,10 +1,35 @@
1
- export { VideoSignalingClient } from './signalingClient';
2
- export type { SignalingClientConfig, SignalMessage } from './signalingClient';
3
- export { createServerTokenProvider, defaultWsBaseUrlFromApi, defaultApiBaseUrl } from './tokenProvider';
4
- export type { TokenProvider, TokenProviderContext, ServerTokenProviderOptions } from './tokenProvider';
5
- export type { ServerAuthConfig } from './auth';
6
- export { SDKError, mapSDKErrorToUIMessage } from './sdkErrors';
7
- export type { SDKErrorCode } from './sdkErrors';
8
- export { initVideoSDK, getVideoSDKConfig } from './sdkConfig';
9
- export type { VideoSDKConfig } from './sdkConfig';
10
- //# sourceMappingURL=client.d.ts.map
1
+ import { T as TokenProvider, S as ServerAuthConfig } from './sdkConfig-C9Fey7R2.js';
2
+ export { a as SDKError, b as SDKErrorCode, c as ServerTokenProviderOptions, d as TokenProviderContext, V as VideoSDKConfig, e as createServerTokenProvider, f as defaultApiBaseUrl, g as defaultWsBaseUrlFromApi, h as getVideoSDKConfig, i as initVideoSDK, m as mapSDKErrorToUIMessage } from './sdkConfig-C9Fey7R2.js';
3
+
4
+ type SignalMessage = {
5
+ type: string;
6
+ roomId: string;
7
+ userId: string;
8
+ targetUserId?: string;
9
+ payload?: unknown;
10
+ };
11
+ type SignalingClientConfig = {
12
+ roomId: string;
13
+ userId: string;
14
+ roomToken?: string;
15
+ wsBaseUrl?: string;
16
+ tokenProvider?: TokenProvider;
17
+ apiBaseUrl?: string;
18
+ auth?: ServerAuthConfig;
19
+ onOpen?: () => void;
20
+ onClose?: (event: CloseEvent) => void;
21
+ onError?: (event: Event) => void;
22
+ onMessage?: (message: SignalMessage) => void;
23
+ };
24
+ declare class VideoSignalingClient {
25
+ private readonly config;
26
+ private ws;
27
+ private connected;
28
+ constructor(config: SignalingClientConfig);
29
+ isConnected(): boolean;
30
+ connect(): Promise<void>;
31
+ send(message: Omit<SignalMessage, 'roomId' | 'userId'>): void;
32
+ disconnect(): void;
33
+ }
34
+
35
+ export { ServerAuthConfig, type SignalMessage, type SignalingClientConfig, TokenProvider, VideoSignalingClient };
package/dist/client.js CHANGED
@@ -1,5 +1,276 @@
1
- export { VideoSignalingClient } from './signalingClient';
2
- export { createServerTokenProvider, defaultWsBaseUrlFromApi, defaultApiBaseUrl } from './tokenProvider';
3
- export { SDKError, mapSDKErrorToUIMessage } from './sdkErrors';
4
- export { initVideoSDK, getVideoSDKConfig } from './sdkConfig';
1
+ // src/auth.ts
2
+ function buildAuthHeaders(auth) {
3
+ const headers = {
4
+ [auth.appIdHeader ?? "X-App-Id"]: auth.appId,
5
+ [auth.authHeader ?? "Authorization"]: `Bearer ${auth.authToken}`
6
+ };
7
+ if (auth.clientId) {
8
+ headers[auth.clientIdHeader ?? "X-Client-Id"] = auth.clientId;
9
+ }
10
+ return headers;
11
+ }
12
+
13
+ // src/sdkErrors.ts
14
+ var SDKError = class extends Error {
15
+ code;
16
+ status;
17
+ constructor(code, message, status) {
18
+ super(message);
19
+ this.code = code;
20
+ this.status = status;
21
+ this.name = "SDKError";
22
+ }
23
+ };
24
+ function mapSDKErrorToUIMessage(err) {
25
+ if (!(err instanceof SDKError)) {
26
+ return "Unexpected error while connecting to call server.";
27
+ }
28
+ switch (err.code) {
29
+ case "invalid_app":
30
+ return "Application is not allowed to use this video service.";
31
+ case "invalid_client":
32
+ return "Client is not recognized by video service.";
33
+ case "expired_token":
34
+ return "Authorization token is expired. Please sign in again.";
35
+ case "unauthorized":
36
+ return "Authorization failed. Please check your credentials.";
37
+ case "forbidden":
38
+ return "Access is forbidden for this application/client.";
39
+ case "rate_limited":
40
+ return "Organization rate limit exceeded. Please retry later.";
41
+ case "network_error":
42
+ return "Network error while connecting to video service.";
43
+ default:
44
+ return err.message || "Unknown video service error.";
45
+ }
46
+ }
47
+
48
+ // src/tokenProvider.ts
49
+ function mapServerError(status, serverMessage) {
50
+ const message = serverMessage.toLowerCase();
51
+ if (status === 403 && message.includes("app id")) {
52
+ return new SDKError("invalid_app", "invalid_app", status);
53
+ }
54
+ if (status === 403 && message.includes("auth token")) {
55
+ return new SDKError("unauthorized", "invalid_auth_token", status);
56
+ }
57
+ if (status === 403 && message.includes("client id")) {
58
+ return new SDKError("invalid_client", "invalid_client", status);
59
+ }
60
+ if (status === 401 && message.includes("expired token")) {
61
+ return new SDKError("expired_token", "expired_token", status);
62
+ }
63
+ if (status === 401) {
64
+ return new SDKError("unauthorized", "unauthorized", status);
65
+ }
66
+ if (status === 403) {
67
+ return new SDKError("forbidden", "forbidden", status);
68
+ }
69
+ if (status === 429) {
70
+ return new SDKError("rate_limited", "rate_limited", status);
71
+ }
72
+ return new SDKError("unknown_error", serverMessage || "unknown_error", status);
73
+ }
74
+ async function fetchJson(input, init) {
75
+ let response;
76
+ try {
77
+ response = await fetch(input, init);
78
+ } catch {
79
+ throw new SDKError("network_error", "network_error");
80
+ }
81
+ if (!response.ok) {
82
+ let serverMessage = response.statusText;
83
+ try {
84
+ const payload = await response.json();
85
+ serverMessage = payload.error || payload.message || serverMessage;
86
+ } catch {
87
+ const text = await response.text();
88
+ if (text) serverMessage = text;
89
+ }
90
+ throw mapServerError(response.status, serverMessage);
91
+ }
92
+ return response.json();
93
+ }
94
+ function defaultWsBaseUrlFromApi(apiBaseUrl) {
95
+ const normalized = apiBaseUrl.replace(/\/+$/, "");
96
+ if (normalized.startsWith("https://")) {
97
+ return normalized.replace(/^https:\/\//, "wss://");
98
+ }
99
+ if (normalized.startsWith("http://")) {
100
+ return normalized.replace(/^http:\/\//, "ws://");
101
+ }
102
+ return normalized;
103
+ }
104
+ function defaultApiBaseUrl() {
105
+ if (typeof window !== "undefined" && window.location?.origin) {
106
+ return window.location.origin;
107
+ }
108
+ return "";
109
+ }
110
+ function createServerTokenProvider(options) {
111
+ const externalUserRegisterPath = options.externalUserRegisterPath ?? "/v1/external-users/register";
112
+ const joinPathBuilder = options.joinPathBuilder ?? ((roomId) => `/v1/rooms/${roomId}/join`);
113
+ const tokenPathBuilder = options.tokenPathBuilder ?? ((roomId) => `/v1/rooms/${roomId}/token`);
114
+ const externalUserMap = /* @__PURE__ */ new Map();
115
+ return async ({ roomId, userId }) => {
116
+ const base = (options.apiBaseUrl || defaultApiBaseUrl()).replace(/\/+$/, "");
117
+ if (!base) {
118
+ throw new SDKError("unknown_error", "api_base_url_required");
119
+ }
120
+ const headers = {
121
+ "Content-Type": "application/json",
122
+ ...buildAuthHeaders(options.auth)
123
+ };
124
+ let internalUserID = externalUserMap.get(userId);
125
+ if (!internalUserID) {
126
+ const registration = await fetchJson(`${base}${externalUserRegisterPath}`, {
127
+ method: "POST",
128
+ headers,
129
+ body: JSON.stringify({
130
+ externalId: userId
131
+ })
132
+ });
133
+ if (!registration.userId) {
134
+ throw new SDKError("unknown_error", "external_user_registration_failed");
135
+ }
136
+ internalUserID = registration.userId;
137
+ externalUserMap.set(userId, internalUserID);
138
+ }
139
+ const roomHeaders = {
140
+ ...headers,
141
+ "X-Guest-Id": internalUserID
142
+ };
143
+ await fetchJson(`${base}${joinPathBuilder(roomId)}`, {
144
+ method: "POST",
145
+ headers: roomHeaders,
146
+ body: "{}"
147
+ });
148
+ const tokenResponse = await fetchJson(`${base}${tokenPathBuilder(roomId)}`, {
149
+ method: "POST",
150
+ headers: roomHeaders
151
+ });
152
+ if (!tokenResponse.token) {
153
+ throw new Error("Room token is empty");
154
+ }
155
+ return tokenResponse.token;
156
+ };
157
+ }
158
+
159
+ // src/sdkConfig.ts
160
+ var DEFAULT_API_BASE_URL = "https://api.streemo.ru";
161
+ var DEFAULT_WS_BASE_URL = "wss://api.streemo.ru";
162
+ var globalConfig = {
163
+ apiBaseUrl: DEFAULT_API_BASE_URL,
164
+ wsBaseUrl: DEFAULT_WS_BASE_URL
165
+ };
166
+ function initVideoSDK(config) {
167
+ globalConfig = {
168
+ ...globalConfig,
169
+ ...config,
170
+ auth: config.auth ? { ...globalConfig.auth ?? {}, ...config.auth } : globalConfig.auth
171
+ };
172
+ }
173
+ function getVideoSDKConfig() {
174
+ return globalConfig;
175
+ }
176
+
177
+ // src/signalingClient.ts
178
+ function resolveTokenProvider(config) {
179
+ const sdkConfig = getVideoSDKConfig();
180
+ const apiBaseUrl = config.apiBaseUrl ?? sdkConfig.apiBaseUrl;
181
+ const auth = config.auth ?? sdkConfig.auth;
182
+ if (config.tokenProvider) return config.tokenProvider;
183
+ if (auth) {
184
+ return createServerTokenProvider({
185
+ apiBaseUrl: apiBaseUrl ?? defaultApiBaseUrl(),
186
+ auth
187
+ });
188
+ }
189
+ return null;
190
+ }
191
+ async function resolveRoomToken(config) {
192
+ if (config.roomToken) return config.roomToken;
193
+ const provider = resolveTokenProvider(config);
194
+ if (!provider) {
195
+ throw new Error("roomToken or tokenProvider/apiBaseUrl+auth is required");
196
+ }
197
+ const ctx = {
198
+ roomId: config.roomId,
199
+ userId: config.userId
200
+ };
201
+ return provider(ctx);
202
+ }
203
+ function resolveWsBaseUrl(config) {
204
+ const sdkConfig = getVideoSDKConfig();
205
+ if (config.wsBaseUrl) return config.wsBaseUrl.replace(/\/+$/, "");
206
+ if (sdkConfig.wsBaseUrl) return sdkConfig.wsBaseUrl.replace(/\/+$/, "");
207
+ const apiBaseUrl = config.apiBaseUrl ?? sdkConfig.apiBaseUrl ?? defaultApiBaseUrl();
208
+ if (apiBaseUrl) return defaultWsBaseUrlFromApi(apiBaseUrl);
209
+ throw new Error("wsBaseUrl or apiBaseUrl is required");
210
+ }
211
+ var VideoSignalingClient = class {
212
+ config;
213
+ ws = null;
214
+ connected = false;
215
+ constructor(config) {
216
+ this.config = config;
217
+ }
218
+ isConnected() {
219
+ return this.connected;
220
+ }
221
+ async connect() {
222
+ if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
223
+ return;
224
+ }
225
+ const roomToken = await resolveRoomToken(this.config);
226
+ const wsBase = resolveWsBaseUrl(this.config);
227
+ const wsURL = `${wsBase}/v1/ws?roomId=${encodeURIComponent(this.config.roomId)}&token=${encodeURIComponent(roomToken)}`;
228
+ await new Promise((resolve, reject) => {
229
+ const ws = new WebSocket(wsURL);
230
+ this.ws = ws;
231
+ ws.onopen = () => {
232
+ this.connected = true;
233
+ this.config.onOpen?.();
234
+ resolve();
235
+ };
236
+ ws.onclose = (event) => {
237
+ this.connected = false;
238
+ this.config.onClose?.(event);
239
+ };
240
+ ws.onerror = (event) => {
241
+ this.config.onError?.(event);
242
+ reject(new Error("WebSocket connection error"));
243
+ };
244
+ ws.onmessage = (event) => {
245
+ const message = JSON.parse(event.data);
246
+ this.config.onMessage?.(message);
247
+ };
248
+ });
249
+ }
250
+ send(message) {
251
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
252
+ this.ws.send(
253
+ JSON.stringify({
254
+ roomId: this.config.roomId,
255
+ userId: this.config.userId,
256
+ ...message
257
+ })
258
+ );
259
+ }
260
+ disconnect() {
261
+ this.ws?.close();
262
+ this.ws = null;
263
+ this.connected = false;
264
+ }
265
+ };
266
+ export {
267
+ SDKError,
268
+ VideoSignalingClient,
269
+ createServerTokenProvider,
270
+ defaultApiBaseUrl,
271
+ defaultWsBaseUrlFromApi,
272
+ getVideoSDKConfig,
273
+ initVideoSDK,
274
+ mapSDKErrorToUIMessage
275
+ };
5
276
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAExD,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAGvG,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAE9D,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"sources":["../src/auth.ts","../src/sdkErrors.ts","../src/tokenProvider.ts","../src/sdkConfig.ts","../src/signalingClient.ts"],"sourcesContent":["export type ServerAuthConfig = {\n authToken: string\n clientId?: string\n appId: string\n authHeader?: string\n clientIdHeader?: string\n appIdHeader?: string\n}\n\nexport function buildAuthHeaders(auth: ServerAuthConfig): Record<string, string> {\n const headers: Record<string, string> = {\n [auth.appIdHeader ?? 'X-App-Id']: auth.appId,\n [auth.authHeader ?? 'Authorization']: `Bearer ${auth.authToken}`,\n }\n if (auth.clientId) {\n headers[auth.clientIdHeader ?? 'X-Client-Id'] = auth.clientId\n }\n return headers\n}\n","export type SDKErrorCode =\n | 'invalid_app'\n | 'invalid_client'\n | 'expired_token'\n | 'unauthorized'\n | 'forbidden'\n | 'rate_limited'\n | 'network_error'\n | 'unknown_error'\n\nexport class SDKError extends Error {\n code: SDKErrorCode\n status?: number\n\n constructor(code: SDKErrorCode, message: string, status?: number) {\n super(message)\n this.code = code\n this.status = status\n this.name = 'SDKError'\n }\n}\n\nexport function mapSDKErrorToUIMessage(err: unknown): string {\n if (!(err instanceof SDKError)) {\n return 'Unexpected error while connecting to call server.'\n }\n\n switch (err.code) {\n case 'invalid_app':\n return 'Application is not allowed to use this video service.'\n case 'invalid_client':\n return 'Client is not recognized by video service.'\n case 'expired_token':\n return 'Authorization token is expired. Please sign in again.'\n case 'unauthorized':\n return 'Authorization failed. Please check your credentials.'\n case 'forbidden':\n return 'Access is forbidden for this application/client.'\n case 'rate_limited':\n return 'Organization rate limit exceeded. Please retry later.'\n case 'network_error':\n return 'Network error while connecting to video service.'\n default:\n return err.message || 'Unknown video service error.'\n }\n}\n","import { buildAuthHeaders, type ServerAuthConfig } from './auth'\nimport { SDKError } from './sdkErrors'\n\nexport type TokenProviderContext = {\n roomId: string\n userId: string\n}\n\nexport type TokenProvider = (ctx: TokenProviderContext) => Promise<string>\n\nexport type ServerTokenProviderOptions = {\n apiBaseUrl?: string\n auth: ServerAuthConfig\n externalUserRegisterPath?: string\n joinPathBuilder?: (roomId: string) => string\n tokenPathBuilder?: (roomId: string) => string\n}\n\ntype ServerErrorPayload = {\n error?: string\n message?: string\n}\n\nfunction mapServerError(status: number, serverMessage: string): SDKError {\n const message = serverMessage.toLowerCase()\n if (status === 403 && message.includes('app id')) {\n return new SDKError('invalid_app', 'invalid_app', status)\n }\n if (status === 403 && message.includes('auth token')) {\n return new SDKError('unauthorized', 'invalid_auth_token', status)\n }\n if (status === 403 && message.includes('client id')) {\n return new SDKError('invalid_client', 'invalid_client', status)\n }\n if (status === 401 && message.includes('expired token')) {\n return new SDKError('expired_token', 'expired_token', status)\n }\n if (status === 401) {\n return new SDKError('unauthorized', 'unauthorized', status)\n }\n if (status === 403) {\n return new SDKError('forbidden', 'forbidden', status)\n }\n if (status === 429) {\n return new SDKError('rate_limited', 'rate_limited', status)\n }\n return new SDKError('unknown_error', serverMessage || 'unknown_error', status)\n}\n\nasync function fetchJson<T>(input: RequestInfo | URL, init: RequestInit): Promise<T> {\n let response: Response\n try {\n response = await fetch(input, init)\n } catch {\n throw new SDKError('network_error', 'network_error')\n }\n if (!response.ok) {\n let serverMessage = response.statusText\n try {\n const payload = (await response.json()) as ServerErrorPayload\n serverMessage = payload.error || payload.message || serverMessage\n } catch {\n const text = await response.text()\n if (text) serverMessage = text\n }\n throw mapServerError(response.status, serverMessage)\n }\n return response.json() as Promise<T>\n}\n\nexport function defaultWsBaseUrlFromApi(apiBaseUrl: string): string {\n const normalized = apiBaseUrl.replace(/\\/+$/, '')\n if (normalized.startsWith('https://')) {\n return normalized.replace(/^https:\\/\\//, 'wss://')\n }\n if (normalized.startsWith('http://')) {\n return normalized.replace(/^http:\\/\\//, 'ws://')\n }\n return normalized\n}\n\nexport function defaultApiBaseUrl(): string {\n if (typeof window !== 'undefined' && window.location?.origin) {\n return window.location.origin\n }\n return ''\n}\n\nexport function createServerTokenProvider(options: ServerTokenProviderOptions): TokenProvider {\n const externalUserRegisterPath = options.externalUserRegisterPath ?? '/v1/external-users/register'\n const joinPathBuilder = options.joinPathBuilder ?? ((roomId: string) => `/v1/rooms/${roomId}/join`)\n const tokenPathBuilder = options.tokenPathBuilder ?? ((roomId: string) => `/v1/rooms/${roomId}/token`)\n const externalUserMap = new Map<string, string>()\n\n return async ({ roomId, userId }) => {\n const base = (options.apiBaseUrl || defaultApiBaseUrl()).replace(/\\/+$/, '')\n if (!base) {\n throw new SDKError('unknown_error', 'api_base_url_required')\n }\n const headers = {\n 'Content-Type': 'application/json',\n ...buildAuthHeaders(options.auth),\n }\n\n let internalUserID = externalUserMap.get(userId)\n if (!internalUserID) {\n const registration = await fetchJson<{ userId: string }>(`${base}${externalUserRegisterPath}`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n externalId: userId,\n }),\n })\n if (!registration.userId) {\n throw new SDKError('unknown_error', 'external_user_registration_failed')\n }\n internalUserID = registration.userId\n externalUserMap.set(userId, internalUserID)\n }\n const roomHeaders = {\n ...headers,\n 'X-Guest-Id': internalUserID,\n }\n\n await fetchJson<{ ok?: boolean }>(`${base}${joinPathBuilder(roomId)}`, {\n method: 'POST',\n headers: roomHeaders,\n body: '{}',\n })\n\n const tokenResponse = await fetchJson<{ token: string }>(`${base}${tokenPathBuilder(roomId)}`, {\n method: 'POST',\n headers: roomHeaders,\n })\n\n if (!tokenResponse.token) {\n throw new Error('Room token is empty')\n }\n return tokenResponse.token\n }\n}\n","import type { ServerAuthConfig } from './auth'\n\nexport type VideoSDKConfig = {\n apiBaseUrl?: string\n wsBaseUrl?: string\n auth?: ServerAuthConfig\n}\n\nconst DEFAULT_API_BASE_URL = 'https://api.streemo.ru'\nconst DEFAULT_WS_BASE_URL = 'wss://api.streemo.ru'\n\nlet globalConfig: VideoSDKConfig = {\n apiBaseUrl: DEFAULT_API_BASE_URL,\n wsBaseUrl: DEFAULT_WS_BASE_URL,\n}\n\nexport function initVideoSDK(config: VideoSDKConfig): void {\n globalConfig = {\n ...globalConfig,\n ...config,\n auth: config.auth ? { ...(globalConfig.auth ?? {}), ...config.auth } : globalConfig.auth,\n }\n}\n\nexport function getVideoSDKConfig(): VideoSDKConfig {\n return globalConfig\n}\n\n","import {\n createServerTokenProvider,\n defaultApiBaseUrl,\n defaultWsBaseUrlFromApi,\n type TokenProvider,\n type TokenProviderContext,\n} from './tokenProvider'\nimport type { ServerAuthConfig } from './auth'\nimport { getVideoSDKConfig } from './sdkConfig'\n\nexport type SignalMessage = {\n type: string\n roomId: string\n userId: string\n targetUserId?: string\n payload?: unknown\n}\n\nexport type SignalingClientConfig = {\n roomId: string\n userId: string\n roomToken?: string\n wsBaseUrl?: string\n tokenProvider?: TokenProvider\n apiBaseUrl?: string\n auth?: ServerAuthConfig\n onOpen?: () => void\n onClose?: (event: CloseEvent) => void\n onError?: (event: Event) => void\n onMessage?: (message: SignalMessage) => void\n}\n\nfunction resolveTokenProvider(config: SignalingClientConfig): TokenProvider | null {\n const sdkConfig = getVideoSDKConfig()\n const apiBaseUrl = config.apiBaseUrl ?? sdkConfig.apiBaseUrl\n const auth = config.auth ?? sdkConfig.auth\n if (config.tokenProvider) return config.tokenProvider\n if (auth) {\n return createServerTokenProvider({\n apiBaseUrl: apiBaseUrl ?? defaultApiBaseUrl(),\n auth,\n })\n }\n return null\n}\n\nasync function resolveRoomToken(config: SignalingClientConfig): Promise<string> {\n if (config.roomToken) return config.roomToken\n const provider = resolveTokenProvider(config)\n if (!provider) {\n throw new Error('roomToken or tokenProvider/apiBaseUrl+auth is required')\n }\n const ctx: TokenProviderContext = {\n roomId: config.roomId,\n userId: config.userId,\n }\n return provider(ctx)\n}\n\nfunction resolveWsBaseUrl(config: SignalingClientConfig): string {\n const sdkConfig = getVideoSDKConfig()\n if (config.wsBaseUrl) return config.wsBaseUrl.replace(/\\/+$/, '')\n if (sdkConfig.wsBaseUrl) return sdkConfig.wsBaseUrl.replace(/\\/+$/, '')\n const apiBaseUrl = config.apiBaseUrl ?? sdkConfig.apiBaseUrl ?? defaultApiBaseUrl()\n if (apiBaseUrl) return defaultWsBaseUrlFromApi(apiBaseUrl)\n throw new Error('wsBaseUrl or apiBaseUrl is required')\n}\n\nexport class VideoSignalingClient {\n private readonly config: SignalingClientConfig\n private ws: WebSocket | null = null\n private connected = false\n\n constructor(config: SignalingClientConfig) {\n this.config = config\n }\n\n isConnected(): boolean {\n return this.connected\n }\n\n async connect(): Promise<void> {\n if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {\n return\n }\n\n const roomToken = await resolveRoomToken(this.config)\n const wsBase = resolveWsBaseUrl(this.config)\n const wsURL = `${wsBase}/v1/ws?roomId=${encodeURIComponent(this.config.roomId)}&token=${encodeURIComponent(roomToken)}`\n\n await new Promise<void>((resolve, reject) => {\n const ws = new WebSocket(wsURL)\n this.ws = ws\n\n ws.onopen = () => {\n this.connected = true\n this.config.onOpen?.()\n resolve()\n }\n ws.onclose = (event) => {\n this.connected = false\n this.config.onClose?.(event)\n }\n ws.onerror = (event) => {\n this.config.onError?.(event)\n reject(new Error('WebSocket connection error'))\n }\n ws.onmessage = (event) => {\n const message = JSON.parse(event.data) as SignalMessage\n this.config.onMessage?.(message)\n }\n })\n }\n\n send(message: Omit<SignalMessage, 'roomId' | 'userId'>): void {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return\n this.ws.send(\n JSON.stringify({\n roomId: this.config.roomId,\n userId: this.config.userId,\n ...message,\n }),\n )\n }\n\n disconnect(): void {\n this.ws?.close()\n this.ws = null\n this.connected = false\n }\n}\n"],"mappings":"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","names":[]}