connectbase-client 0.6.29 → 0.6.30

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/dist/index.js CHANGED
@@ -1760,6 +1760,15 @@ var RealtimeAPI = class {
1760
1760
  this.streamSessions = /* @__PURE__ */ new Map();
1761
1761
  this.stateHandlers = [];
1762
1762
  this.errorHandlers = [];
1763
+ // Presence handlers
1764
+ this.presenceHandlers = [];
1765
+ this.presenceSubscriptions = /* @__PURE__ */ new Map();
1766
+ // Typing handlers
1767
+ this.typingHandlers = /* @__PURE__ */ new Map();
1768
+ // Read receipt handlers
1769
+ this.readReceiptHandlers = /* @__PURE__ */ new Map();
1770
+ // 연결 중일 때 대기하는 Promise들
1771
+ this.connectPromise = null;
1763
1772
  this.http = http;
1764
1773
  this.socketUrl = socketUrl;
1765
1774
  this.clientId = this.generateClientId();
@@ -1779,14 +1788,22 @@ var RealtimeAPI = class {
1779
1788
  * - userId: 사용자 식별자 (표시용)
1780
1789
  */
1781
1790
  async connect(options = {}) {
1782
- if (this.state === "connected" || this.state === "connecting") {
1791
+ if (this.state === "connected") {
1783
1792
  return;
1784
1793
  }
1794
+ if (this.state === "connecting" && this.connectPromise) {
1795
+ return this.connectPromise;
1796
+ }
1785
1797
  this.options = { ...this.options, ...options };
1786
1798
  if (options.userId) {
1787
1799
  this.userId = options.userId;
1788
1800
  }
1789
- return this.doConnect();
1801
+ this.connectPromise = this.doConnect();
1802
+ try {
1803
+ await this.connectPromise;
1804
+ } finally {
1805
+ this.connectPromise = null;
1806
+ }
1790
1807
  }
1791
1808
  /**
1792
1809
  * 연결 해제
@@ -1804,6 +1821,20 @@ var RealtimeAPI = class {
1804
1821
  });
1805
1822
  this.pendingRequests.clear();
1806
1823
  this.subscriptions.clear();
1824
+ this.streamSessions.forEach((session) => {
1825
+ if (session.handlers.onError) {
1826
+ session.handlers.onError(new Error("Connection closed"));
1827
+ }
1828
+ });
1829
+ this.streamSessions.clear();
1830
+ this.presenceHandlers = [];
1831
+ this.presenceSubscriptions.clear();
1832
+ this.typingHandlers.clear();
1833
+ this.readReceiptHandlers.clear();
1834
+ this._connectionId = null;
1835
+ this._appId = null;
1836
+ this.retryCount = 0;
1837
+ this.connectPromise = null;
1807
1838
  }
1808
1839
  /**
1809
1840
  * 카테고리 구독
@@ -1839,6 +1870,10 @@ var RealtimeAPI = class {
1839
1870
  },
1840
1871
  onMessage: (handler) => {
1841
1872
  handlers.push(handler);
1873
+ return () => {
1874
+ const idx = handlers.indexOf(handler);
1875
+ if (idx > -1) handlers.splice(idx, 1);
1876
+ };
1842
1877
  }
1843
1878
  };
1844
1879
  return subscription;
@@ -1946,21 +1981,26 @@ var RealtimeAPI = class {
1946
1981
  handlers,
1947
1982
  requestId
1948
1983
  });
1949
- this.sendRaw({
1950
- category: "",
1951
- action: "stream",
1952
- data: {
1953
- provider: options.provider,
1954
- model: options.model,
1955
- messages,
1956
- system: options.system,
1957
- temperature: options.temperature,
1958
- max_tokens: options.maxTokens,
1959
- session_id: sessionId,
1960
- metadata: options.metadata
1961
- },
1962
- request_id: requestId
1963
- });
1984
+ try {
1985
+ this.sendRaw({
1986
+ category: "",
1987
+ action: "stream",
1988
+ data: {
1989
+ provider: options.provider,
1990
+ model: options.model,
1991
+ messages,
1992
+ system: options.system,
1993
+ temperature: options.temperature,
1994
+ max_tokens: options.maxTokens,
1995
+ session_id: sessionId,
1996
+ metadata: options.metadata
1997
+ },
1998
+ request_id: requestId
1999
+ });
2000
+ } catch (e) {
2001
+ this.streamSessions.delete(sessionId);
2002
+ throw e;
2003
+ }
1964
2004
  return {
1965
2005
  sessionId,
1966
2006
  stop: async () => {
@@ -2016,6 +2056,344 @@ var RealtimeAPI = class {
2016
2056
  if (idx > -1) this.errorHandlers.splice(idx, 1);
2017
2057
  };
2018
2058
  }
2059
+ // ========== Presence API ==========
2060
+ /**
2061
+ * 프레전스 상태 설정
2062
+ * @param status 상태 (online, away, busy, offline)
2063
+ * @param options 추가 옵션 (device, metadata)
2064
+ *
2065
+ * @example
2066
+ * ```typescript
2067
+ * await client.realtime.setPresence('online', {
2068
+ * device: 'web',
2069
+ * metadata: { nickname: 'John' }
2070
+ * })
2071
+ * ```
2072
+ */
2073
+ async setPresence(status, options = {}) {
2074
+ if (this.state !== "connected") {
2075
+ throw new Error("Not connected");
2076
+ }
2077
+ const requestId = this.generateRequestId();
2078
+ await this.sendRequest({
2079
+ category: "",
2080
+ action: "presence_set",
2081
+ data: {
2082
+ status,
2083
+ device: options.device,
2084
+ metadata: options.metadata
2085
+ },
2086
+ request_id: requestId
2087
+ });
2088
+ }
2089
+ /**
2090
+ * 연결 해제 시 자동으로 설정될 프레전스 상태 지정
2091
+ * @param status 연결 해제 시 변경할 상태
2092
+ * @param metadata 연결 해제 시 업데이트할 메타데이터
2093
+ *
2094
+ * @example
2095
+ * ```typescript
2096
+ * // 연결이 끊기면 자동으로 offline으로 변경
2097
+ * await client.realtime.setPresenceOnDisconnect('offline')
2098
+ * ```
2099
+ */
2100
+ async setPresenceOnDisconnect(status, metadata) {
2101
+ if (this.state !== "connected") {
2102
+ throw new Error("Not connected");
2103
+ }
2104
+ const requestId = this.generateRequestId();
2105
+ await this.sendRequest({
2106
+ category: "",
2107
+ action: "presence_on_disconnect",
2108
+ data: { status, metadata },
2109
+ request_id: requestId
2110
+ });
2111
+ }
2112
+ /**
2113
+ * 특정 사용자의 프레전스 조회
2114
+ * @param userId 조회할 사용자 ID
2115
+ * @returns 프레전스 정보
2116
+ *
2117
+ * @example
2118
+ * ```typescript
2119
+ * const presence = await client.realtime.getPresence('user-123')
2120
+ * console.log(presence.status) // 'online'
2121
+ * ```
2122
+ */
2123
+ async getPresence(userId) {
2124
+ if (this.state !== "connected") {
2125
+ throw new Error("Not connected");
2126
+ }
2127
+ const requestId = this.generateRequestId();
2128
+ const response = await this.sendRequest({
2129
+ category: "",
2130
+ action: "presence_get",
2131
+ data: { user_id: userId },
2132
+ request_id: requestId
2133
+ });
2134
+ return {
2135
+ userId: response.user_id,
2136
+ status: response.status,
2137
+ lastSeen: response.last_seen,
2138
+ device: response.device,
2139
+ metadata: response.metadata
2140
+ };
2141
+ }
2142
+ /**
2143
+ * 여러 사용자의 프레전스 조회
2144
+ * @param userIds 조회할 사용자 ID 목록
2145
+ * @returns 사용자별 프레전스 정보
2146
+ *
2147
+ * @example
2148
+ * ```typescript
2149
+ * const result = await client.realtime.getPresenceMany(['user-1', 'user-2'])
2150
+ * console.log(result.users['user-1'].status)
2151
+ * ```
2152
+ */
2153
+ async getPresenceMany(userIds) {
2154
+ if (this.state !== "connected") {
2155
+ throw new Error("Not connected");
2156
+ }
2157
+ const requestId = this.generateRequestId();
2158
+ const response = await this.sendRequest({
2159
+ category: "",
2160
+ action: "presence_get_many",
2161
+ data: { user_ids: userIds },
2162
+ request_id: requestId
2163
+ });
2164
+ const users = {};
2165
+ for (const [id, data] of Object.entries(response.users)) {
2166
+ users[id] = {
2167
+ userId: data.user_id,
2168
+ status: data.status,
2169
+ lastSeen: data.last_seen,
2170
+ device: data.device,
2171
+ metadata: data.metadata
2172
+ };
2173
+ }
2174
+ return { users };
2175
+ }
2176
+ /**
2177
+ * 특정 사용자의 프레전스 변경 구독
2178
+ * @param userId 구독할 사용자 ID
2179
+ * @param handler 프레전스 변경 핸들러
2180
+ * @returns 구독 해제 함수
2181
+ *
2182
+ * @example
2183
+ * ```typescript
2184
+ * const unsubscribe = await client.realtime.subscribePresence('user-123', (presence) => {
2185
+ * console.log(`${presence.userId} is now ${presence.status}`)
2186
+ * })
2187
+ *
2188
+ * // 나중에 구독 해제
2189
+ * unsubscribe()
2190
+ * ```
2191
+ */
2192
+ async subscribePresence(userId, handler) {
2193
+ if (this.state !== "connected") {
2194
+ throw new Error("Not connected");
2195
+ }
2196
+ if (!this.presenceSubscriptions.has(userId)) {
2197
+ const requestId = this.generateRequestId();
2198
+ await this.sendRequest({
2199
+ category: "",
2200
+ action: "presence_subscribe",
2201
+ data: { user_id: userId },
2202
+ request_id: requestId
2203
+ });
2204
+ this.presenceSubscriptions.set(userId, []);
2205
+ }
2206
+ const handlers = this.presenceSubscriptions.get(userId);
2207
+ handlers.push(handler);
2208
+ return () => {
2209
+ const idx = handlers.indexOf(handler);
2210
+ if (idx > -1) handlers.splice(idx, 1);
2211
+ if (handlers.length === 0) {
2212
+ this.presenceSubscriptions.delete(userId);
2213
+ if (this.state === "connected") {
2214
+ const requestId = this.generateRequestId();
2215
+ this.sendRequest({
2216
+ category: "",
2217
+ action: "presence_unsubscribe",
2218
+ data: { user_id: userId },
2219
+ request_id: requestId
2220
+ }).catch((e) => {
2221
+ this.log(`Failed to unsubscribe presence for ${userId}: ${e}`);
2222
+ });
2223
+ }
2224
+ }
2225
+ };
2226
+ }
2227
+ /**
2228
+ * 전체 프레전스 변경 이벤트 구독
2229
+ * @param handler 프레전스 변경 핸들러
2230
+ * @returns 구독 해제 함수
2231
+ *
2232
+ * @example
2233
+ * ```typescript
2234
+ * const unsubscribe = client.realtime.onPresenceChange((presence) => {
2235
+ * if (presence.eventType === 'join') {
2236
+ * console.log(`${presence.userId} joined`)
2237
+ * }
2238
+ * })
2239
+ * ```
2240
+ */
2241
+ onPresenceChange(handler) {
2242
+ this.presenceHandlers.push(handler);
2243
+ return () => {
2244
+ const idx = this.presenceHandlers.indexOf(handler);
2245
+ if (idx > -1) this.presenceHandlers.splice(idx, 1);
2246
+ };
2247
+ }
2248
+ // ========== Typing API ==========
2249
+ /**
2250
+ * 타이핑 시작 알림
2251
+ * @param roomId 타이핑 중인 룸/채널 ID (category 이름)
2252
+ *
2253
+ * @example
2254
+ * ```typescript
2255
+ * // 입력 시작 시
2256
+ * input.addEventListener('focus', () => {
2257
+ * client.realtime.startTyping('chat-room-1')
2258
+ * })
2259
+ * ```
2260
+ */
2261
+ async startTyping(roomId) {
2262
+ if (this.state !== "connected") {
2263
+ throw new Error("Not connected");
2264
+ }
2265
+ const requestId = this.generateRequestId();
2266
+ await this.sendRequest({
2267
+ category: "",
2268
+ action: "typing_start",
2269
+ data: { room_id: roomId },
2270
+ request_id: requestId
2271
+ });
2272
+ }
2273
+ /**
2274
+ * 타이핑 종료 알림
2275
+ * @param roomId 타이핑을 종료할 룸/채널 ID
2276
+ *
2277
+ * @example
2278
+ * ```typescript
2279
+ * // 입력 종료 또는 메시지 전송 시
2280
+ * input.addEventListener('blur', () => {
2281
+ * client.realtime.stopTyping('chat-room-1')
2282
+ * })
2283
+ * ```
2284
+ */
2285
+ async stopTyping(roomId) {
2286
+ if (this.state !== "connected") {
2287
+ throw new Error("Not connected");
2288
+ }
2289
+ const requestId = this.generateRequestId();
2290
+ await this.sendRequest({
2291
+ category: "",
2292
+ action: "typing_stop",
2293
+ data: { room_id: roomId },
2294
+ request_id: requestId
2295
+ });
2296
+ }
2297
+ /**
2298
+ * 타이핑 상태 변경 구독
2299
+ * @param roomId 구독할 룸/채널 ID
2300
+ * @param handler 타이핑 상태 변경 핸들러
2301
+ * @returns 구독 해제 함수
2302
+ *
2303
+ * @example
2304
+ * ```typescript
2305
+ * const unsubscribe = await client.realtime.onTypingChange('chat-room-1', (typing) => {
2306
+ * if (typing.users.length > 0) {
2307
+ * indicator.textContent = `${typing.users.join(', ')} is typing...`
2308
+ * } else {
2309
+ * indicator.textContent = ''
2310
+ * }
2311
+ * })
2312
+ * ```
2313
+ */
2314
+ async onTypingChange(roomId, handler) {
2315
+ if (this.state !== "connected") {
2316
+ throw new Error("Not connected");
2317
+ }
2318
+ if (!this.typingHandlers.has(roomId)) {
2319
+ const requestId = this.generateRequestId();
2320
+ await this.sendRequest({
2321
+ category: "",
2322
+ action: "typing_subscribe",
2323
+ data: { room_id: roomId },
2324
+ request_id: requestId
2325
+ });
2326
+ this.typingHandlers.set(roomId, []);
2327
+ }
2328
+ const handlers = this.typingHandlers.get(roomId);
2329
+ handlers.push(handler);
2330
+ return () => {
2331
+ const idx = handlers.indexOf(handler);
2332
+ if (idx > -1) handlers.splice(idx, 1);
2333
+ if (handlers.length === 0) {
2334
+ this.typingHandlers.delete(roomId);
2335
+ if (this.state === "connected") {
2336
+ const requestId = this.generateRequestId();
2337
+ this.sendRequest({
2338
+ category: "",
2339
+ action: "typing_unsubscribe",
2340
+ data: { room_id: roomId },
2341
+ request_id: requestId
2342
+ }).catch((e) => {
2343
+ this.log(`Failed to unsubscribe typing for ${roomId}: ${e}`);
2344
+ });
2345
+ }
2346
+ }
2347
+ };
2348
+ }
2349
+ // ========== Read Receipt API ==========
2350
+ /**
2351
+ * 메시지 읽음 표시
2352
+ * @param category 카테고리 이름
2353
+ * @param messageIds 읽음 처리할 메시지 ID 목록
2354
+ *
2355
+ * @example
2356
+ * ```typescript
2357
+ * // 메시지를 읽었을 때
2358
+ * await client.realtime.markRead('chat-room-1', ['msg-1', 'msg-2'])
2359
+ * ```
2360
+ */
2361
+ async markRead(category, messageIds) {
2362
+ if (this.state !== "connected") {
2363
+ throw new Error("Not connected");
2364
+ }
2365
+ const requestId = this.generateRequestId();
2366
+ await this.sendRequest({
2367
+ category,
2368
+ action: "mark_read",
2369
+ data: { message_ids: messageIds },
2370
+ request_id: requestId
2371
+ });
2372
+ }
2373
+ /**
2374
+ * 읽음 확인 이벤트 구독
2375
+ * @param category 구독할 카테고리 이름
2376
+ * @param handler 읽음 확인 핸들러
2377
+ * @returns 구독 해제 함수
2378
+ *
2379
+ * @example
2380
+ * ```typescript
2381
+ * const unsubscribe = client.realtime.onReadReceipt('chat-room-1', (receipt) => {
2382
+ * console.log(`${receipt.readerId} read messages: ${receipt.messageIds.join(', ')}`)
2383
+ * })
2384
+ * ```
2385
+ */
2386
+ onReadReceipt(category, handler) {
2387
+ if (!this.readReceiptHandlers.has(category)) {
2388
+ this.readReceiptHandlers.set(category, []);
2389
+ }
2390
+ const handlers = this.readReceiptHandlers.get(category);
2391
+ handlers.push(handler);
2392
+ return () => {
2393
+ const idx = handlers.indexOf(handler);
2394
+ if (idx > -1) handlers.splice(idx, 1);
2395
+ };
2396
+ }
2019
2397
  // Private methods
2020
2398
  async doConnect() {
2021
2399
  return new Promise((resolve, reject) => {
@@ -2207,26 +2585,174 @@ var RealtimeAPI = class {
2207
2585
  }
2208
2586
  break;
2209
2587
  }
2588
+ // Presence events
2589
+ case "presence":
2590
+ case "presence_status": {
2591
+ const data = msg.data;
2592
+ const presence = {
2593
+ userId: data.user_id,
2594
+ status: data.status,
2595
+ lastSeen: data.last_seen,
2596
+ device: data.device,
2597
+ metadata: data.metadata,
2598
+ eventType: data.event_type
2599
+ };
2600
+ this.presenceHandlers.forEach((h) => h(presence));
2601
+ const userHandlers = this.presenceSubscriptions.get(data.user_id);
2602
+ if (userHandlers) {
2603
+ userHandlers.forEach((h) => h(presence));
2604
+ }
2605
+ if (msg.request_id) {
2606
+ const pending = this.pendingRequests.get(msg.request_id);
2607
+ if (pending) {
2608
+ clearTimeout(pending.timeout);
2609
+ pending.resolve(msg.data);
2610
+ this.pendingRequests.delete(msg.request_id);
2611
+ }
2612
+ }
2613
+ break;
2614
+ }
2615
+ // Typing events
2616
+ case "typing": {
2617
+ const data = msg.data;
2618
+ const typing = {
2619
+ roomId: data.room_id,
2620
+ users: data.users
2621
+ };
2622
+ const typingHandlers = this.typingHandlers.get(data.room_id);
2623
+ if (typingHandlers) {
2624
+ typingHandlers.forEach((h) => h(typing));
2625
+ }
2626
+ break;
2627
+ }
2628
+ // Read receipt events
2629
+ case "read_receipt": {
2630
+ const data = msg.data;
2631
+ const receipt = {
2632
+ category: data.category,
2633
+ messageIds: data.message_ids,
2634
+ readerId: data.reader_id,
2635
+ readAt: data.read_at
2636
+ };
2637
+ const receiptHandlers = this.readReceiptHandlers.get(data.category);
2638
+ if (receiptHandlers) {
2639
+ receiptHandlers.forEach((h) => h(receipt));
2640
+ }
2641
+ break;
2642
+ }
2210
2643
  }
2211
2644
  }
2212
2645
  handleDisconnect() {
2213
2646
  this.ws = null;
2214
2647
  this._connectionId = null;
2648
+ this.streamSessions.forEach((session) => {
2649
+ if (session.handlers.onError) {
2650
+ session.handlers.onError(new Error("Connection lost"));
2651
+ }
2652
+ });
2653
+ this.streamSessions.clear();
2654
+ const previousSubscriptions = /* @__PURE__ */ new Map();
2655
+ for (const [category, sub] of this.subscriptions) {
2656
+ previousSubscriptions.set(category, [...sub.handlers]);
2657
+ }
2658
+ this.subscriptions.clear();
2659
+ const previousPresenceSubscriptions = /* @__PURE__ */ new Map();
2660
+ for (const [userId, handlers] of this.presenceSubscriptions) {
2661
+ previousPresenceSubscriptions.set(userId, [...handlers]);
2662
+ }
2663
+ this.presenceSubscriptions.clear();
2664
+ const previousTypingHandlers = /* @__PURE__ */ new Map();
2665
+ for (const [roomId, handlers] of this.typingHandlers) {
2666
+ previousTypingHandlers.set(roomId, [...handlers]);
2667
+ }
2668
+ this.typingHandlers.clear();
2215
2669
  if (this.retryCount < this.options.maxRetries) {
2216
2670
  this.state = "reconnecting";
2217
2671
  this.notifyStateChange();
2218
2672
  this.retryCount++;
2219
- setTimeout(() => {
2220
- this.doConnect().catch((e) => {
2673
+ const delay = Math.min(
2674
+ this.options.retryInterval * Math.pow(2, this.retryCount - 1),
2675
+ 3e4
2676
+ );
2677
+ setTimeout(async () => {
2678
+ try {
2679
+ await this.doConnect();
2680
+ this.log("Reconnected successfully, restoring subscriptions...");
2681
+ await this.restoreSubscriptions(
2682
+ previousSubscriptions,
2683
+ previousPresenceSubscriptions,
2684
+ previousTypingHandlers
2685
+ );
2686
+ } catch (e) {
2221
2687
  console.error("[Realtime] Reconnect failed:", e);
2222
- });
2223
- }, this.options.retryInterval * this.retryCount);
2688
+ }
2689
+ }, delay);
2224
2690
  } else {
2225
2691
  this.state = "disconnected";
2226
2692
  this.notifyStateChange();
2227
2693
  this.notifyError(new Error("Connection lost. Max retries exceeded."));
2228
2694
  }
2229
2695
  }
2696
+ /**
2697
+ * 이전 구독 상태를 복구합니다.
2698
+ * 재연결 성공 후 호출됩니다.
2699
+ */
2700
+ async restoreSubscriptions(previousSubscriptions, previousPresenceSubscriptions, previousTypingHandlers) {
2701
+ for (const [category, handlers] of previousSubscriptions) {
2702
+ try {
2703
+ this.log(`Restoring subscription: ${category}`);
2704
+ const requestId = this.generateRequestId();
2705
+ const response = await this.sendRequest({
2706
+ category,
2707
+ action: "subscribe",
2708
+ request_id: requestId
2709
+ });
2710
+ const info = {
2711
+ category: response.category,
2712
+ persist: response.persist,
2713
+ historyCount: response.history_count,
2714
+ readReceipt: response.read_receipt
2715
+ };
2716
+ this.subscriptions.set(category, { info, handlers });
2717
+ this.log(`Restored subscription: ${category}`);
2718
+ } catch (e) {
2719
+ console.error(`[Realtime] Failed to restore subscription for ${category}:`, e);
2720
+ this.notifyError(new Error(`Failed to restore subscription: ${category}`));
2721
+ }
2722
+ }
2723
+ for (const [userId, handlers] of previousPresenceSubscriptions) {
2724
+ try {
2725
+ this.log(`Restoring presence subscription: ${userId}`);
2726
+ const requestId = this.generateRequestId();
2727
+ await this.sendRequest({
2728
+ category: "",
2729
+ action: "presence_subscribe",
2730
+ data: { user_id: userId },
2731
+ request_id: requestId
2732
+ });
2733
+ this.presenceSubscriptions.set(userId, handlers);
2734
+ this.log(`Restored presence subscription: ${userId}`);
2735
+ } catch (e) {
2736
+ console.error(`[Realtime] Failed to restore presence subscription for ${userId}:`, e);
2737
+ }
2738
+ }
2739
+ for (const [roomId, handlers] of previousTypingHandlers) {
2740
+ try {
2741
+ this.log(`Restoring typing subscription: ${roomId}`);
2742
+ const requestId = this.generateRequestId();
2743
+ await this.sendRequest({
2744
+ category: "",
2745
+ action: "typing_subscribe",
2746
+ data: { room_id: roomId },
2747
+ request_id: requestId
2748
+ });
2749
+ this.typingHandlers.set(roomId, handlers);
2750
+ this.log(`Restored typing subscription: ${roomId}`);
2751
+ } catch (e) {
2752
+ console.error(`[Realtime] Failed to restore typing subscription for ${roomId}:`, e);
2753
+ }
2754
+ }
2755
+ }
2230
2756
  sendRequest(message) {
2231
2757
  return new Promise((resolve, reject) => {
2232
2758
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
@@ -2236,7 +2762,7 @@ var RealtimeAPI = class {
2236
2762
  const timeout = setTimeout(() => {
2237
2763
  this.pendingRequests.delete(message.request_id);
2238
2764
  reject(new Error("Request timeout"));
2239
- }, 3e4);
2765
+ }, this.options.timeout);
2240
2766
  this.pendingRequests.set(message.request_id, {
2241
2767
  resolve,
2242
2768
  reject,