connectbase-client 5.12.6 → 6.0.0

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.d.mts CHANGED
@@ -5144,6 +5144,14 @@ interface NativeBridgeInterface {
5144
5144
  rewarded: boolean;
5145
5145
  }>;
5146
5146
  };
5147
+ /** 모바일 전용: 네이티브 인앱 브라우저 세션으로 여는 OAuth (ASWebAuthenticationSession/Custom Tabs) */
5148
+ oauth?: {
5149
+ signIn: (authUrl: string, callbackScheme: string) => Promise<{
5150
+ url: string;
5151
+ }>;
5152
+ /** 이 패키징 앱의 OAuth 콜백 URL 스킴 (`<scheme>://oauth/callback`). 동기 함수 */
5153
+ getCallbackScheme: () => string;
5154
+ };
5147
5155
  system?: {
5148
5156
  getInfo: () => Promise<SystemInfo>;
5149
5157
  getMemory: () => Promise<MemoryInfo>;
@@ -5173,6 +5181,7 @@ interface NativeBridgeInterface {
5173
5181
  readImage: () => Promise<string | null>;
5174
5182
  writeImage: (dataURL: string) => Promise<void>;
5175
5183
  clear: () => Promise<void>;
5184
+ has: (format: string) => Promise<boolean>;
5176
5185
  };
5177
5186
  shell?: {
5178
5187
  openExternal: (url: string) => Promise<{
@@ -5242,6 +5251,22 @@ interface NativeBridgeInterface {
5242
5251
  getAppDir?: () => Promise<string>;
5243
5252
  };
5244
5253
  onDeepLink?: (callback: (url: string) => void) => () => void;
5254
+ /** 모바일 전용: 앱에 연결된 Connect Base Storage 로 파일 업로드 (file_storage.type=connectbase 일 때만 존재) */
5255
+ connectbase?: {
5256
+ uploadFile: (uri: string, filename?: string, mimeType?: string) => Promise<{
5257
+ id: string;
5258
+ name: string;
5259
+ url: string;
5260
+ path: string;
5261
+ storageId: string;
5262
+ }>;
5263
+ };
5264
+ /**
5265
+ * 데스크톱 전용: 저수준 이벤트 구독 (`off` 는 해당 채널의 모든 리스너를 제거한다 — 콜백
5266
+ * 단위 해제가 아니다). `deep-link` 는 `onDeepLink` 로도 동일하게 구독할 수 있다.
5267
+ */
5268
+ on?: (channel: "deep-link" | "window-focus" | "window-blur" | "update-available" | "update-downloaded", callback: (...args: unknown[]) => void) => void;
5269
+ off?: (channel: "deep-link" | "window-focus" | "window-blur" | "update-available" | "update-downloaded") => void;
5245
5270
  }
5246
5271
  type Platform = "web" | "mobile" | "desktop";
5247
5272
  /** 패키징 앱 정보 (`cb.native.getAppInfo()`) */
@@ -5459,6 +5484,10 @@ declare class NativeAPI {
5459
5484
  * 이미지 읽기 (데스크톱 전용, Data URL 반환)
5460
5485
  */
5461
5486
  readImage: () => Promise<string | null>;
5487
+ /**
5488
+ * 클립보드에 특정 형식(MIME 타입, 예: 'text/plain', 'image/png')의 데이터가 있는지 확인 (데스크톱 전용)
5489
+ */
5490
+ has: (format: string) => Promise<boolean>;
5462
5491
  };
5463
5492
  /**
5464
5493
  * 크로스 플랫폼 파일 시스템 API
@@ -5573,6 +5602,47 @@ declare class NativeAPI {
5573
5602
  */
5574
5603
  openExternal: (url: string) => Promise<boolean>;
5575
5604
  };
5605
+ /**
5606
+ * 모바일 전용: 네이티브 인앱 브라우저 세션으로 여는 OAuth
5607
+ *
5608
+ * WebView 안에서 소셜 로그인 팝업을 열면 세션/쿠키가 격리돼 실패하는 경우가 많다.
5609
+ * 패키징 앱(RN)에서는 시스템 브라우저 세션(iOS ASWebAuthenticationSession /
5610
+ * Android Custom Tabs)으로 열고 앱 URL 스킴 콜백으로 결과를 받는다.
5611
+ * 웹/데스크톱에서는 지원하지 않는다 — 일반 팝업/리다이렉트 OAuth 플로우를 쓸 것.
5612
+ *
5613
+ * @example
5614
+ * ```typescript
5615
+ * if (cb.native.getPlatform() === 'mobile') {
5616
+ * const result = await cb.native.oauth.signIn(authorizeUrl)
5617
+ * if (result) window.location.href = result.url // 콜백 URL 로 계속 진행
5618
+ * }
5619
+ * ```
5620
+ */
5621
+ oauth: {
5622
+ /**
5623
+ * OAuth 인가 URL 을 네이티브 인앱 브라우저 세션으로 열고 콜백 URL 을 기다린다.
5624
+ * callbackScheme 을 생략하면 이 패키징 앱의 스킴(getCallbackScheme())을 사용한다.
5625
+ * 사용자가 취소했거나 미지원 플랫폼이면 null.
5626
+ */
5627
+ signIn: (authUrl: string, callbackScheme?: string) => Promise<{
5628
+ url: string;
5629
+ } | null>;
5630
+ /** 이 패키징 앱의 OAuth 콜백 URL 스킴(`<scheme>://oauth/callback`). 미지원 플랫폼은 null (동기) */
5631
+ getCallbackScheme: () => string | null;
5632
+ };
5633
+ /**
5634
+ * 모바일 전용: 패키징 앱에 연결된 Connect Base Storage 로 파일 업로드.
5635
+ * 콘솔 패키징 설정에서 "파일 저장 위치" 를 Connect Base Storage 로 선택한 경우에만 지원한다.
5636
+ */
5637
+ connectbase: {
5638
+ uploadFile: (uri: string, filename?: string, mimeType?: string) => Promise<{
5639
+ id: string;
5640
+ name: string;
5641
+ url: string;
5642
+ path: string;
5643
+ storageId: string;
5644
+ } | null>;
5645
+ };
5576
5646
  /**
5577
5647
  * 데스크톱 전용 창 제어 API
5578
5648
  */
@@ -5584,6 +5654,19 @@ declare class NativeAPI {
5584
5654
  isMaximized: () => Promise<boolean>;
5585
5655
  setTitle: (title: string) => Promise<void>;
5586
5656
  setFullScreen: (flag: boolean) => Promise<void>;
5657
+ isFullScreen: () => Promise<boolean>;
5658
+ /** 창 크기 설정 (데스크톱 전용) */
5659
+ setSize: (width: number, height: number) => Promise<void>;
5660
+ /** 창 크기 조회 (데스크톱 전용, 미지원 플랫폼은 null) */
5661
+ getSize: () => Promise<[number, number] | null>;
5662
+ /** 창 위치 설정 (데스크톱 전용) */
5663
+ setPosition: (x: number, y: number) => Promise<void>;
5664
+ /** 창 위치 조회 (데스크톱 전용, 미지원 플랫폼은 null) */
5665
+ getPosition: () => Promise<[number, number] | null>;
5666
+ /** 화면 중앙으로 이동 (데스크톱 전용) */
5667
+ center: () => Promise<void>;
5668
+ /** 항상 위에 표시 설정 (데스크톱 전용) */
5669
+ setAlwaysOnTop: (flag: boolean) => Promise<void>;
5587
5670
  };
5588
5671
  /**
5589
5672
  * 데스크톱 전용 시스템 정보 API
@@ -5591,6 +5674,7 @@ declare class NativeAPI {
5591
5674
  system: {
5592
5675
  getInfo: () => Promise<SystemInfo | null>;
5593
5676
  getMemory: () => Promise<MemoryInfo | null>;
5677
+ getCPU: () => Promise<CPUInfo | null>;
5594
5678
  };
5595
5679
  /**
5596
5680
  * 모바일 전용 생체인증 API
@@ -7319,9 +7403,31 @@ declare class RealtimeAPI {
7319
7403
  */
7320
7404
  getState(): ConnectionState;
7321
7405
  /**
7322
- * 연결 여부 확인
7406
+ * pub/sub 사용 가능 여부.
7407
+ *
7408
+ * WebSocket 으로 연결됐을 때만 true 다. SSE fallback 모드에서는 `subscribe` /
7409
+ * `sendMessage` / `setPresence` / typing 이 전부 예외를 던지므로, 여기서 true 를
7410
+ * 반환하면 앱이 "연결됨"으로 오판하고 아무것도 오가지 않는 채 조용히 멈춘다
7411
+ * (platform-issue 01a01e6a-3cb1).
7412
+ *
7413
+ * AI 스트리밍만 필요하다면 {@link isStreamReady} 를, 다운그레이드 여부를 알고 싶다면
7414
+ * {@link isDegraded} 를 쓴다.
7323
7415
  */
7324
7416
  isConnected(): boolean;
7417
+ /**
7418
+ * SSE fallback 으로 다운그레이드된 상태인지 여부.
7419
+ *
7420
+ * true 면 연결은 살아 있지만 단방향이라 `realtime.stream()` 만 동작한다. 사용자에게
7421
+ * "제한된 네트워크라 일부 기능을 쓸 수 없다" 고 안내할 지점이다.
7422
+ */
7423
+ isDegraded(): boolean;
7424
+ /**
7425
+ * AI 스트리밍(`realtime.stream()`) 사용 가능 여부.
7426
+ *
7427
+ * WS 든 SSE fallback 이든 연결만 서 있으면 true 다. 종전 `isConnected()` 와 같은
7428
+ * 판정이므로, 스트리밍 게이팅에 `isConnected()` 를 쓰던 코드는 이쪽으로 옮긴다.
7429
+ */
7430
+ isStreamReady(): boolean;
7325
7431
  /**
7326
7432
  * 상태 변경 핸들러 등록
7327
7433
  */
@@ -9963,6 +10069,14 @@ interface ICEServer {
9963
10069
  /** ICE 서버 목록 응답 */
9964
10070
  interface ICEServersResponse {
9965
10071
  ice_servers: ICEServer[];
10072
+ /**
10073
+ * ephemeral TURN 자격증명의 만료 시각(Unix epoch 초).
10074
+ *
10075
+ * 서버가 `TURN_SHARED_SECRET` 으로 시간제한 자격증명을 발급한 경우에만 0 이 아니다.
10076
+ * STUN 전용 구성에서는 0 — 만료가 없다는 뜻이다. 통화가 길어질 수 있으면 이 시각
10077
+ * 전에 `getICEServers()` 를 다시 불러 새 자격증명을 받는다.
10078
+ */
10079
+ expires_at?: number;
9966
10080
  }
9967
10081
  /**
9968
10082
  * 채널별 통계 (룸 ID 의 `channel:room` 컨벤션 기준 집계).
@@ -10038,6 +10152,16 @@ declare class WebRTCAPI {
10038
10152
  private reconnectAttempts;
10039
10153
  private maxReconnectAttempts;
10040
10154
  private reconnectTimeout;
10155
+ /**
10156
+ * 시그널링 소켓의 세대(generation) 토큰.
10157
+ *
10158
+ * 소켓을 열 때마다 1 증가하며, 각 핸들러는 자기가 열릴 때의 세대를 클로저에 담아둔다.
10159
+ * 세대가 밀린 핸들러는 "옛 소켓의 지연 이벤트" 이므로 인스턴스 상태를 건드리지 않는다.
10160
+ * 참조(`this.ws`)만 끊는 것으로는 부족하다 — 핸들러 클로저가 인스턴스를 캡처하고 있어
10161
+ * close 이벤트가 뒤늦게 도착하면 새 연결을 'disconnected' 로 덮어쓰고 새로 만든 피어
10162
+ * 연결까지 정리해버린다.
10163
+ */
10164
+ private wsGeneration;
10041
10165
  private currentRoomId;
10042
10166
  private currentPeerId;
10043
10167
  private currentUserId;
@@ -10074,6 +10198,14 @@ declare class WebRTCAPI {
10074
10198
  */
10075
10199
  connect(options: WebRTCConnectOptions): Promise<void>;
10076
10200
  private connectWebSocket;
10201
+ /**
10202
+ * 소켓에 붙은 핸들러를 전부 떼어낸다.
10203
+ *
10204
+ * `this.ws = null` 로 참조만 끊으면 클로저가 살아남아 close 이벤트가 계속 들어온다.
10205
+ * 세대 토큰이 그 이벤트를 무시해주긴 하지만, 핸들러 자체를 떼면 인스턴스 참조가 끊겨
10206
+ * 소켓이 GC 될 때까지 인스턴스를 붙잡고 있지 않는다.
10207
+ */
10208
+ private static detachSocket;
10077
10209
  private buildWebSocketUrl;
10078
10210
  private handleSignalingMessage;
10079
10211
  private createPeerConnection;
@@ -10088,6 +10220,44 @@ declare class WebRTCAPI {
10088
10220
  * WebRTC 연결 해제
10089
10221
  */
10090
10222
  disconnect(): void;
10223
+ /**
10224
+ * 현재 세션의 룸을 교체한다.
10225
+ *
10226
+ * `disconnect()` 후 곧바로 `connect()` 하는 패턴을 안전하게 감싼 것이다. 옛 소켓의
10227
+ * close 이벤트가 새 연결의 'connecting' 구간에 떨어져도 세대 토큰이 걸러낸다.
10228
+ * 생략한 옵션은 현재 세션 값을 그대로 이어받는다.
10229
+ *
10230
+ * @example
10231
+ * ```typescript
10232
+ * await cb.webrtc.switchRoom('channel:room-b')
10233
+ * ```
10234
+ */
10235
+ switchRoom(roomId: string, options?: Omit<WebRTCConnectOptions, "roomId">): Promise<void>;
10236
+ /**
10237
+ * 소켓, 룸, 이벤트 리스너, 피어 연결을 **독립적으로** 갖는 새 WebRTC 세션을 만든다.
10238
+ *
10239
+ * `cb.webrtc` 는 인스턴스 하나에 소켓 하나/룸 하나라, 한 앱에 WebRTC 기능이 둘 이상이면
10240
+ * (예: 공간 음성채팅 + 1:1 통화) 서로 연결을 뺏고 이벤트가 교차한다. 기능마다 세션을
10241
+ * 하나씩 만들면 각자의 소켓과 핸들러를 갖는다.
10242
+ *
10243
+ * 인증 정보(HttpClient)와 서버 주소, appId 는 부모와 공유한다.
10244
+ *
10245
+ * @example
10246
+ * ```typescript
10247
+ * const voice = cb.webrtc.createSession()
10248
+ * const call = cb.webrtc.createSession()
10249
+ *
10250
+ * voice.onRemoteStream((peerId, stream) => attachToSpatialAudio(peerId, stream))
10251
+ * call.onRemoteStream((peerId, stream) => showCallScreen(stream))
10252
+ *
10253
+ * await voice.connect({ roomId: 'voice:lobby', localStream: mic })
10254
+ * await call.connect({ roomId: 'call:alice-bob', localStream: mic })
10255
+ *
10256
+ * // 각 세션은 독립적으로 끊는다
10257
+ * call.disconnect()
10258
+ * ```
10259
+ */
10260
+ createSession(): WebRTCAPI;
10091
10261
  /**
10092
10262
  * 현재 연결 상태 조회
10093
10263
  */
package/dist/index.d.ts CHANGED
@@ -5144,6 +5144,14 @@ interface NativeBridgeInterface {
5144
5144
  rewarded: boolean;
5145
5145
  }>;
5146
5146
  };
5147
+ /** 모바일 전용: 네이티브 인앱 브라우저 세션으로 여는 OAuth (ASWebAuthenticationSession/Custom Tabs) */
5148
+ oauth?: {
5149
+ signIn: (authUrl: string, callbackScheme: string) => Promise<{
5150
+ url: string;
5151
+ }>;
5152
+ /** 이 패키징 앱의 OAuth 콜백 URL 스킴 (`<scheme>://oauth/callback`). 동기 함수 */
5153
+ getCallbackScheme: () => string;
5154
+ };
5147
5155
  system?: {
5148
5156
  getInfo: () => Promise<SystemInfo>;
5149
5157
  getMemory: () => Promise<MemoryInfo>;
@@ -5173,6 +5181,7 @@ interface NativeBridgeInterface {
5173
5181
  readImage: () => Promise<string | null>;
5174
5182
  writeImage: (dataURL: string) => Promise<void>;
5175
5183
  clear: () => Promise<void>;
5184
+ has: (format: string) => Promise<boolean>;
5176
5185
  };
5177
5186
  shell?: {
5178
5187
  openExternal: (url: string) => Promise<{
@@ -5242,6 +5251,22 @@ interface NativeBridgeInterface {
5242
5251
  getAppDir?: () => Promise<string>;
5243
5252
  };
5244
5253
  onDeepLink?: (callback: (url: string) => void) => () => void;
5254
+ /** 모바일 전용: 앱에 연결된 Connect Base Storage 로 파일 업로드 (file_storage.type=connectbase 일 때만 존재) */
5255
+ connectbase?: {
5256
+ uploadFile: (uri: string, filename?: string, mimeType?: string) => Promise<{
5257
+ id: string;
5258
+ name: string;
5259
+ url: string;
5260
+ path: string;
5261
+ storageId: string;
5262
+ }>;
5263
+ };
5264
+ /**
5265
+ * 데스크톱 전용: 저수준 이벤트 구독 (`off` 는 해당 채널의 모든 리스너를 제거한다 — 콜백
5266
+ * 단위 해제가 아니다). `deep-link` 는 `onDeepLink` 로도 동일하게 구독할 수 있다.
5267
+ */
5268
+ on?: (channel: "deep-link" | "window-focus" | "window-blur" | "update-available" | "update-downloaded", callback: (...args: unknown[]) => void) => void;
5269
+ off?: (channel: "deep-link" | "window-focus" | "window-blur" | "update-available" | "update-downloaded") => void;
5245
5270
  }
5246
5271
  type Platform = "web" | "mobile" | "desktop";
5247
5272
  /** 패키징 앱 정보 (`cb.native.getAppInfo()`) */
@@ -5459,6 +5484,10 @@ declare class NativeAPI {
5459
5484
  * 이미지 읽기 (데스크톱 전용, Data URL 반환)
5460
5485
  */
5461
5486
  readImage: () => Promise<string | null>;
5487
+ /**
5488
+ * 클립보드에 특정 형식(MIME 타입, 예: 'text/plain', 'image/png')의 데이터가 있는지 확인 (데스크톱 전용)
5489
+ */
5490
+ has: (format: string) => Promise<boolean>;
5462
5491
  };
5463
5492
  /**
5464
5493
  * 크로스 플랫폼 파일 시스템 API
@@ -5573,6 +5602,47 @@ declare class NativeAPI {
5573
5602
  */
5574
5603
  openExternal: (url: string) => Promise<boolean>;
5575
5604
  };
5605
+ /**
5606
+ * 모바일 전용: 네이티브 인앱 브라우저 세션으로 여는 OAuth
5607
+ *
5608
+ * WebView 안에서 소셜 로그인 팝업을 열면 세션/쿠키가 격리돼 실패하는 경우가 많다.
5609
+ * 패키징 앱(RN)에서는 시스템 브라우저 세션(iOS ASWebAuthenticationSession /
5610
+ * Android Custom Tabs)으로 열고 앱 URL 스킴 콜백으로 결과를 받는다.
5611
+ * 웹/데스크톱에서는 지원하지 않는다 — 일반 팝업/리다이렉트 OAuth 플로우를 쓸 것.
5612
+ *
5613
+ * @example
5614
+ * ```typescript
5615
+ * if (cb.native.getPlatform() === 'mobile') {
5616
+ * const result = await cb.native.oauth.signIn(authorizeUrl)
5617
+ * if (result) window.location.href = result.url // 콜백 URL 로 계속 진행
5618
+ * }
5619
+ * ```
5620
+ */
5621
+ oauth: {
5622
+ /**
5623
+ * OAuth 인가 URL 을 네이티브 인앱 브라우저 세션으로 열고 콜백 URL 을 기다린다.
5624
+ * callbackScheme 을 생략하면 이 패키징 앱의 스킴(getCallbackScheme())을 사용한다.
5625
+ * 사용자가 취소했거나 미지원 플랫폼이면 null.
5626
+ */
5627
+ signIn: (authUrl: string, callbackScheme?: string) => Promise<{
5628
+ url: string;
5629
+ } | null>;
5630
+ /** 이 패키징 앱의 OAuth 콜백 URL 스킴(`<scheme>://oauth/callback`). 미지원 플랫폼은 null (동기) */
5631
+ getCallbackScheme: () => string | null;
5632
+ };
5633
+ /**
5634
+ * 모바일 전용: 패키징 앱에 연결된 Connect Base Storage 로 파일 업로드.
5635
+ * 콘솔 패키징 설정에서 "파일 저장 위치" 를 Connect Base Storage 로 선택한 경우에만 지원한다.
5636
+ */
5637
+ connectbase: {
5638
+ uploadFile: (uri: string, filename?: string, mimeType?: string) => Promise<{
5639
+ id: string;
5640
+ name: string;
5641
+ url: string;
5642
+ path: string;
5643
+ storageId: string;
5644
+ } | null>;
5645
+ };
5576
5646
  /**
5577
5647
  * 데스크톱 전용 창 제어 API
5578
5648
  */
@@ -5584,6 +5654,19 @@ declare class NativeAPI {
5584
5654
  isMaximized: () => Promise<boolean>;
5585
5655
  setTitle: (title: string) => Promise<void>;
5586
5656
  setFullScreen: (flag: boolean) => Promise<void>;
5657
+ isFullScreen: () => Promise<boolean>;
5658
+ /** 창 크기 설정 (데스크톱 전용) */
5659
+ setSize: (width: number, height: number) => Promise<void>;
5660
+ /** 창 크기 조회 (데스크톱 전용, 미지원 플랫폼은 null) */
5661
+ getSize: () => Promise<[number, number] | null>;
5662
+ /** 창 위치 설정 (데스크톱 전용) */
5663
+ setPosition: (x: number, y: number) => Promise<void>;
5664
+ /** 창 위치 조회 (데스크톱 전용, 미지원 플랫폼은 null) */
5665
+ getPosition: () => Promise<[number, number] | null>;
5666
+ /** 화면 중앙으로 이동 (데스크톱 전용) */
5667
+ center: () => Promise<void>;
5668
+ /** 항상 위에 표시 설정 (데스크톱 전용) */
5669
+ setAlwaysOnTop: (flag: boolean) => Promise<void>;
5587
5670
  };
5588
5671
  /**
5589
5672
  * 데스크톱 전용 시스템 정보 API
@@ -5591,6 +5674,7 @@ declare class NativeAPI {
5591
5674
  system: {
5592
5675
  getInfo: () => Promise<SystemInfo | null>;
5593
5676
  getMemory: () => Promise<MemoryInfo | null>;
5677
+ getCPU: () => Promise<CPUInfo | null>;
5594
5678
  };
5595
5679
  /**
5596
5680
  * 모바일 전용 생체인증 API
@@ -7319,9 +7403,31 @@ declare class RealtimeAPI {
7319
7403
  */
7320
7404
  getState(): ConnectionState;
7321
7405
  /**
7322
- * 연결 여부 확인
7406
+ * pub/sub 사용 가능 여부.
7407
+ *
7408
+ * WebSocket 으로 연결됐을 때만 true 다. SSE fallback 모드에서는 `subscribe` /
7409
+ * `sendMessage` / `setPresence` / typing 이 전부 예외를 던지므로, 여기서 true 를
7410
+ * 반환하면 앱이 "연결됨"으로 오판하고 아무것도 오가지 않는 채 조용히 멈춘다
7411
+ * (platform-issue 01a01e6a-3cb1).
7412
+ *
7413
+ * AI 스트리밍만 필요하다면 {@link isStreamReady} 를, 다운그레이드 여부를 알고 싶다면
7414
+ * {@link isDegraded} 를 쓴다.
7323
7415
  */
7324
7416
  isConnected(): boolean;
7417
+ /**
7418
+ * SSE fallback 으로 다운그레이드된 상태인지 여부.
7419
+ *
7420
+ * true 면 연결은 살아 있지만 단방향이라 `realtime.stream()` 만 동작한다. 사용자에게
7421
+ * "제한된 네트워크라 일부 기능을 쓸 수 없다" 고 안내할 지점이다.
7422
+ */
7423
+ isDegraded(): boolean;
7424
+ /**
7425
+ * AI 스트리밍(`realtime.stream()`) 사용 가능 여부.
7426
+ *
7427
+ * WS 든 SSE fallback 이든 연결만 서 있으면 true 다. 종전 `isConnected()` 와 같은
7428
+ * 판정이므로, 스트리밍 게이팅에 `isConnected()` 를 쓰던 코드는 이쪽으로 옮긴다.
7429
+ */
7430
+ isStreamReady(): boolean;
7325
7431
  /**
7326
7432
  * 상태 변경 핸들러 등록
7327
7433
  */
@@ -9963,6 +10069,14 @@ interface ICEServer {
9963
10069
  /** ICE 서버 목록 응답 */
9964
10070
  interface ICEServersResponse {
9965
10071
  ice_servers: ICEServer[];
10072
+ /**
10073
+ * ephemeral TURN 자격증명의 만료 시각(Unix epoch 초).
10074
+ *
10075
+ * 서버가 `TURN_SHARED_SECRET` 으로 시간제한 자격증명을 발급한 경우에만 0 이 아니다.
10076
+ * STUN 전용 구성에서는 0 — 만료가 없다는 뜻이다. 통화가 길어질 수 있으면 이 시각
10077
+ * 전에 `getICEServers()` 를 다시 불러 새 자격증명을 받는다.
10078
+ */
10079
+ expires_at?: number;
9966
10080
  }
9967
10081
  /**
9968
10082
  * 채널별 통계 (룸 ID 의 `channel:room` 컨벤션 기준 집계).
@@ -10038,6 +10152,16 @@ declare class WebRTCAPI {
10038
10152
  private reconnectAttempts;
10039
10153
  private maxReconnectAttempts;
10040
10154
  private reconnectTimeout;
10155
+ /**
10156
+ * 시그널링 소켓의 세대(generation) 토큰.
10157
+ *
10158
+ * 소켓을 열 때마다 1 증가하며, 각 핸들러는 자기가 열릴 때의 세대를 클로저에 담아둔다.
10159
+ * 세대가 밀린 핸들러는 "옛 소켓의 지연 이벤트" 이므로 인스턴스 상태를 건드리지 않는다.
10160
+ * 참조(`this.ws`)만 끊는 것으로는 부족하다 — 핸들러 클로저가 인스턴스를 캡처하고 있어
10161
+ * close 이벤트가 뒤늦게 도착하면 새 연결을 'disconnected' 로 덮어쓰고 새로 만든 피어
10162
+ * 연결까지 정리해버린다.
10163
+ */
10164
+ private wsGeneration;
10041
10165
  private currentRoomId;
10042
10166
  private currentPeerId;
10043
10167
  private currentUserId;
@@ -10074,6 +10198,14 @@ declare class WebRTCAPI {
10074
10198
  */
10075
10199
  connect(options: WebRTCConnectOptions): Promise<void>;
10076
10200
  private connectWebSocket;
10201
+ /**
10202
+ * 소켓에 붙은 핸들러를 전부 떼어낸다.
10203
+ *
10204
+ * `this.ws = null` 로 참조만 끊으면 클로저가 살아남아 close 이벤트가 계속 들어온다.
10205
+ * 세대 토큰이 그 이벤트를 무시해주긴 하지만, 핸들러 자체를 떼면 인스턴스 참조가 끊겨
10206
+ * 소켓이 GC 될 때까지 인스턴스를 붙잡고 있지 않는다.
10207
+ */
10208
+ private static detachSocket;
10077
10209
  private buildWebSocketUrl;
10078
10210
  private handleSignalingMessage;
10079
10211
  private createPeerConnection;
@@ -10088,6 +10220,44 @@ declare class WebRTCAPI {
10088
10220
  * WebRTC 연결 해제
10089
10221
  */
10090
10222
  disconnect(): void;
10223
+ /**
10224
+ * 현재 세션의 룸을 교체한다.
10225
+ *
10226
+ * `disconnect()` 후 곧바로 `connect()` 하는 패턴을 안전하게 감싼 것이다. 옛 소켓의
10227
+ * close 이벤트가 새 연결의 'connecting' 구간에 떨어져도 세대 토큰이 걸러낸다.
10228
+ * 생략한 옵션은 현재 세션 값을 그대로 이어받는다.
10229
+ *
10230
+ * @example
10231
+ * ```typescript
10232
+ * await cb.webrtc.switchRoom('channel:room-b')
10233
+ * ```
10234
+ */
10235
+ switchRoom(roomId: string, options?: Omit<WebRTCConnectOptions, "roomId">): Promise<void>;
10236
+ /**
10237
+ * 소켓, 룸, 이벤트 리스너, 피어 연결을 **독립적으로** 갖는 새 WebRTC 세션을 만든다.
10238
+ *
10239
+ * `cb.webrtc` 는 인스턴스 하나에 소켓 하나/룸 하나라, 한 앱에 WebRTC 기능이 둘 이상이면
10240
+ * (예: 공간 음성채팅 + 1:1 통화) 서로 연결을 뺏고 이벤트가 교차한다. 기능마다 세션을
10241
+ * 하나씩 만들면 각자의 소켓과 핸들러를 갖는다.
10242
+ *
10243
+ * 인증 정보(HttpClient)와 서버 주소, appId 는 부모와 공유한다.
10244
+ *
10245
+ * @example
10246
+ * ```typescript
10247
+ * const voice = cb.webrtc.createSession()
10248
+ * const call = cb.webrtc.createSession()
10249
+ *
10250
+ * voice.onRemoteStream((peerId, stream) => attachToSpatialAudio(peerId, stream))
10251
+ * call.onRemoteStream((peerId, stream) => showCallScreen(stream))
10252
+ *
10253
+ * await voice.connect({ roomId: 'voice:lobby', localStream: mic })
10254
+ * await call.connect({ roomId: 'call:alice-bob', localStream: mic })
10255
+ *
10256
+ * // 각 세션은 독립적으로 끊는다
10257
+ * call.disconnect()
10258
+ * ```
10259
+ */
10260
+ createSession(): WebRTCAPI;
10091
10261
  /**
10092
10262
  * 현재 연결 상태 조회
10093
10263
  */