@vibexnpm/talkx 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +327 -0
- package/dist/index.d.ts +1771 -0
- package/dist/talkflow-sdk.esm.js +2 -0
- package/dist/talkflow-sdk.esm.js.map +1 -0
- package/dist/talkflow-sdk.standalone.js +2 -0
- package/dist/talkflow-sdk.standalone.js.map +1 -0
- package/dist/talkflow-sdk.umd.js +2 -0
- package/dist/talkflow-sdk.umd.js.map +1 -0
- package/package.json +51 -0
- package/src/TalkFlowClient.js +481 -0
- package/src/chat/ChatClient.js +2221 -0
- package/src/constants.js +411 -0
- package/src/core/ConnectionManager.js +517 -0
- package/src/index.js +97 -0
- package/src/push/PushManager.js +893 -0
- package/src/talkflow/delegates.js +112 -0
- package/src/talkflow/eventForwarding.js +93 -0
- package/src/talkflow/session.js +355 -0
- package/src/utils/ApiClient.js +305 -0
- package/src/utils/EventEmitter.js +113 -0
- package/src/utils/Logger.js +88 -0
- package/src/utils/jwtUtils.js +213 -0
- package/src/webrtc/MediaStreamManager.js +478 -0
- package/src/webrtc/PeerConnectionManager.js +467 -0
- package/src/webrtc/WebRTCClient.js +1041 -0
- package/types/index.d.ts +1771 -0
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,1771 @@
|
|
|
1
|
+
// Type definitions for @vibexnpm/talkflow
|
|
2
|
+
// TalkFlow SDK - Chat & WebRTC unified client
|
|
3
|
+
//
|
|
4
|
+
// 본 파일은 공개 API 의 정적 타입 계약을 정의합니다.
|
|
5
|
+
// 소스 ({@code src/*.js}) 는 JavaScript + JSDoc 으로 작성되어 있으며,
|
|
6
|
+
// 여기 정의는 README 에 명시된 공개 API 를 기준으로 유지합니다.
|
|
7
|
+
//
|
|
8
|
+
// 수정 시 주의: 소스의 공개 메서드/이벤트가 추가되거나 시그니처가 바뀌면 이 파일도 함께 갱신.
|
|
9
|
+
//
|
|
10
|
+
// ESM (package.json "type": "module") — default export + named exports.
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Constants (enum-like readonly records)
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
export const ConnectionState: {
|
|
17
|
+
readonly DISCONNECTED: 'disconnected';
|
|
18
|
+
readonly CONNECTING: 'connecting';
|
|
19
|
+
readonly CONNECTED: 'connected';
|
|
20
|
+
readonly RECONNECTING: 'reconnecting';
|
|
21
|
+
readonly ERROR: 'error';
|
|
22
|
+
};
|
|
23
|
+
export type ConnectionStateValue = typeof ConnectionState[keyof typeof ConnectionState];
|
|
24
|
+
|
|
25
|
+
export const ErrorTypes: {
|
|
26
|
+
readonly CONNECTION_FAILED: 'CONNECTION_FAILED';
|
|
27
|
+
readonly CONNECTION_LOST: 'CONNECTION_LOST';
|
|
28
|
+
readonly CONNECTION_TIMEOUT: 'CONNECTION_TIMEOUT';
|
|
29
|
+
readonly JWT_INVALID: 'JWT_INVALID';
|
|
30
|
+
readonly JWT_EXPIRED: 'JWT_EXPIRED';
|
|
31
|
+
readonly JWT_PARSE_FAILED: 'JWT_PARSE_FAILED';
|
|
32
|
+
readonly UNAUTHORIZED: 'UNAUTHORIZED';
|
|
33
|
+
readonly API_ERROR: 'API_ERROR';
|
|
34
|
+
readonly API_TIMEOUT: 'API_TIMEOUT';
|
|
35
|
+
readonly CHAT_ROOM_NOT_FOUND: 'CHAT_ROOM_NOT_FOUND';
|
|
36
|
+
readonly CHAT_MESSAGE_FAILED: 'CHAT_MESSAGE_FAILED';
|
|
37
|
+
readonly CHAT_SUBSCRIPTION_FAILED: 'CHAT_SUBSCRIPTION_FAILED';
|
|
38
|
+
readonly MEDIA_ACCESS_DENIED: 'MEDIA_ACCESS_DENIED';
|
|
39
|
+
readonly SCREEN_SHARE_DENIED: 'SCREEN_SHARE_DENIED';
|
|
40
|
+
readonly PEER_CONNECTION_FAILED: 'PEER_CONNECTION_FAILED';
|
|
41
|
+
readonly ICE_CONNECTION_FAILED: 'ICE_CONNECTION_FAILED';
|
|
42
|
+
readonly SIGNALING_FAILED: 'SIGNALING_FAILED';
|
|
43
|
+
readonly DEVICE_SWITCH_FAILED: 'DEVICE_SWITCH_FAILED';
|
|
44
|
+
readonly ENUMERATE_DEVICES_FAILED: 'ENUMERATE_DEVICES_FAILED';
|
|
45
|
+
readonly CALL_ROOM_NOT_FOUND: 'CALL_ROOM_NOT_FOUND';
|
|
46
|
+
readonly INVALID_STATE: 'INVALID_STATE';
|
|
47
|
+
readonly UNKNOWN_ERROR: 'UNKNOWN_ERROR';
|
|
48
|
+
};
|
|
49
|
+
export type ErrorTypeValue = typeof ErrorTypes[keyof typeof ErrorTypes];
|
|
50
|
+
|
|
51
|
+
export const SignalTypes: {
|
|
52
|
+
readonly CALL_OFFER: 'call_offer';
|
|
53
|
+
readonly CALL_ANSWER: 'call_answer';
|
|
54
|
+
readonly ICE_CANDIDATE: 'ice_candidate';
|
|
55
|
+
readonly JOIN_ROOM: 'join_room';
|
|
56
|
+
readonly LEAVE_ROOM: 'leave_room';
|
|
57
|
+
readonly PEER_JOINED: 'peer_joined';
|
|
58
|
+
readonly PEER_LEFT: 'peer_left';
|
|
59
|
+
readonly VIDEO_STATE_CHANGED: 'video_state_changed';
|
|
60
|
+
readonly AUDIO_STATE_CHANGED: 'audio_state_changed';
|
|
61
|
+
readonly SCREEN_SHARE_STARTED: 'screen_share_started';
|
|
62
|
+
readonly SCREEN_SHARE_ENDED: 'screen_share_ended';
|
|
63
|
+
readonly CALL_REQUEST: 'call_request';
|
|
64
|
+
readonly CALL_ACCEPT: 'call_accept';
|
|
65
|
+
readonly CALL_REJECT: 'call_reject';
|
|
66
|
+
readonly CALL_CANCEL: 'call_cancel';
|
|
67
|
+
readonly CALL_END: 'call_end';
|
|
68
|
+
readonly CALL_INVITATION: 'call_invitation';
|
|
69
|
+
readonly CALL_BUSY: 'call_busy';
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* SDK 가 런타임에 export 하는 ChatMessageType 상수.
|
|
74
|
+
* <p><b>런타임 소스와 정확히 일치</b>: {@code src/constants.js} 에 정의된 6개 값만 포함.
|
|
75
|
+
* {@code DOCUMENT / GALLERY} 는 SDK 상수에 없음 — 서버 응답 메시지의 {@code chatMessageType}
|
|
76
|
+
* 필드로는 수신될 수 있으므로 {@link ChatMessageTypeValue} 는 해당 값을 포함한 union.</p>
|
|
77
|
+
*/
|
|
78
|
+
export const ChatMessageType: {
|
|
79
|
+
readonly TEXT: 'TEXT';
|
|
80
|
+
readonly IMAGE: 'IMAGE';
|
|
81
|
+
readonly FILE: 'FILE';
|
|
82
|
+
readonly VIDEO: 'VIDEO';
|
|
83
|
+
readonly AUDIO: 'AUDIO';
|
|
84
|
+
readonly SYSTEM: 'SYSTEM';
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 서버가 보낼 수 있는 메시지 타입 값 전체.
|
|
89
|
+
* <p>SDK 상수 ({@link ChatMessageType}) 의 6개 값 + 서버에서만 쓰는 {@code DOCUMENT / GALLERY}.
|
|
90
|
+
* 수신 {@link ChatMessage#chatMessageType} 비교 시 이 union 을 사용.</p>
|
|
91
|
+
*/
|
|
92
|
+
export type ChatMessageTypeValue =
|
|
93
|
+
| 'TEXT' | 'IMAGE' | 'FILE' | 'VIDEO' | 'AUDIO' | 'SYSTEM'
|
|
94
|
+
| 'DOCUMENT' | 'GALLERY';
|
|
95
|
+
|
|
96
|
+
export const ChatRoomType: {
|
|
97
|
+
readonly DIRECT: 'DIRECT';
|
|
98
|
+
readonly GROUP: 'GROUP';
|
|
99
|
+
readonly PRIVATE_GROUP: 'PRIVATE_GROUP';
|
|
100
|
+
/** 팀 채팅방 — 초대 전용 + 입장 시점 무관 전체 히스토리. 비참여자에게 목록 비노출. 비밀번호 없음. */
|
|
101
|
+
readonly TEAM: 'TEAM';
|
|
102
|
+
};
|
|
103
|
+
export type ChatRoomTypeValue = typeof ChatRoomType[keyof typeof ChatRoomType];
|
|
104
|
+
|
|
105
|
+
export const AssistantMode: {
|
|
106
|
+
readonly GENERAL: 'GENERAL';
|
|
107
|
+
readonly PEOPLE_ONLY: 'PEOPLE_ONLY';
|
|
108
|
+
readonly CALL_ONLY: 'CALL_ONLY';
|
|
109
|
+
};
|
|
110
|
+
export type AssistantModeValue = typeof AssistantMode[keyof typeof AssistantMode];
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 방의 AI 사용 방식 (상위 SSOT) — 서버 {@code RoomAiType} enum 과 일치.
|
|
114
|
+
* <p>{@link CreateGroupRoomData.roomAiType} 으로 방 생성 시 의도를 명시. 미지정 시 서버가 키 권한 + 페르소나로 파생.</p>
|
|
115
|
+
*/
|
|
116
|
+
export const RoomAiType: {
|
|
117
|
+
/** AI 미사용 — 순수 사람 채팅. persona/assistantMode 동시 지정 불가. */
|
|
118
|
+
readonly NONE: 'NONE';
|
|
119
|
+
/** 다중 페르소나, LLM 이 판단해 호출. {@code assistantMode} 로 세부 제어. (현재 유일 활성) */
|
|
120
|
+
readonly PERSONA_MULTI: 'PERSONA_MULTI';
|
|
121
|
+
/** PM + 백스테이지. PM 단일 응답 구현됨 — {@code engagementIntensity} 로 개입 강도 제어. 백스테이지 위임은 후속. */
|
|
122
|
+
readonly PM_BACKSTAGE: 'PM_BACKSTAGE';
|
|
123
|
+
};
|
|
124
|
+
export type RoomAiTypeValue = typeof RoomAiType[keyof typeof RoomAiType];
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* PM_BACKSTAGE 방의 PM 개입 강도 — 서버 {@code EngagementIntensity} enum 과 일치.
|
|
128
|
+
* <p>PM_BACKSTAGE 방 전용(그 외 타입에 지정 시 서버 거절). 14인 PERSONA_MULTI 는 쿨다운+Planner 자동이라 무관.</p>
|
|
129
|
+
*/
|
|
130
|
+
export const EngagementIntensity: {
|
|
131
|
+
/** 보수적 — 명확한 질문/도움 요청에만 PM 응답. */
|
|
132
|
+
readonly QUIET: 'QUIET';
|
|
133
|
+
/** 기본 — 업무 신호에 응답, 잡담엔 침묵. */
|
|
134
|
+
readonly NORMAL: 'NORMAL';
|
|
135
|
+
/** 적극 — 가벼운 신호에도 응답. */
|
|
136
|
+
readonly ACTIVE: 'ACTIVE';
|
|
137
|
+
};
|
|
138
|
+
export type EngagementIntensityValue = typeof EngagementIntensity[keyof typeof EngagementIntensity];
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* PM 프롬프트 레이어 차원 — 서버 {@code PmPromptLayerScope} enum 과 일치.
|
|
142
|
+
* <p>전역 PM 프롬프트 위에 합성되는 추가 지시문 2종 (합성 순서: 전역 → PROJECT → ROOM).
|
|
143
|
+
* SDK 가 다루는 것은 ROOM(방 레이어)이며 PROJECT(회사 레이어)는 어드민 콘솔 영역.</p>
|
|
144
|
+
*/
|
|
145
|
+
export const PmPromptLayerScope: {
|
|
146
|
+
/** 회사(프로젝트) 레이어 — 어드민 콘솔(PROJECT_ADMIN) 관리. */
|
|
147
|
+
readonly PROJECT: 'PROJECT';
|
|
148
|
+
/** 방 레이어 — 방 주인(OWNER) 관리. PM_BACKSTAGE 방 전용. */
|
|
149
|
+
readonly ROOM: 'ROOM';
|
|
150
|
+
};
|
|
151
|
+
export type PmPromptLayerScopeValue = typeof PmPromptLayerScope[keyof typeof PmPromptLayerScope];
|
|
152
|
+
|
|
153
|
+
/** PM 프롬프트 레이어 수정 주체 (감사 추적) — 서버 {@code PmPromptLayerEditorType} enum 과 일치. */
|
|
154
|
+
export const PmPromptLayerEditorType: {
|
|
155
|
+
/** 플랫폼 운영자 (대리 수정). */
|
|
156
|
+
readonly SUPER_ADMIN: 'SUPER_ADMIN';
|
|
157
|
+
/** 회사 관리자 — PROJECT 레이어. */
|
|
158
|
+
readonly PROJECT_ADMIN: 'PROJECT_ADMIN';
|
|
159
|
+
/** 방 주인(OWNER) — ROOM 레이어. */
|
|
160
|
+
readonly ROOM_OWNER: 'ROOM_OWNER';
|
|
161
|
+
};
|
|
162
|
+
export type PmPromptLayerEditorTypeValue = typeof PmPromptLayerEditorType[keyof typeof PmPromptLayerEditorType];
|
|
163
|
+
|
|
164
|
+
/** 방 AI 메타 — 현재 키로 생성 가능한 roomAiType 목록 ({@link ChatClient.getRoomAiMeta}). 키 권한으로 결정. */
|
|
165
|
+
export interface RoomAiMetaResponse {
|
|
166
|
+
/** 생성 가능한 roomAiType — {@code 'NONE'} 항상, {@code 'PERSONA_MULTI'}/{@code 'PM_BACKSTAGE'} 는 권한 보유 시.
|
|
167
|
+
* 고른 타입은 {@link CreateGroupRoomData.roomAiType} 으로 명시 전송 필수 (미전송 시 PM 전용 키는 NONE 으로 파생). */
|
|
168
|
+
availableRoomAiTypes: RoomAiTypeValue[];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 메시지 발신자 타입 — 서버 {@code SenderType} enum 과 일치.
|
|
173
|
+
*
|
|
174
|
+
* <p>USER 메시지는 응답에서 senderType 이 생략될 수 있음 (서버가 {@code NON_NULL} 직렬화).
|
|
175
|
+
* 어시스턴트 메시지에만 {@code 'ASSISTANT'} 값으로 표시 — 클라이언트는 이 값으로 어시 메시지 마킹.</p>
|
|
176
|
+
*/
|
|
177
|
+
export const SenderType: {
|
|
178
|
+
readonly USER: 'USER';
|
|
179
|
+
readonly ASSISTANT: 'ASSISTANT';
|
|
180
|
+
};
|
|
181
|
+
export type SenderTypeValue = typeof SenderType[keyof typeof SenderType];
|
|
182
|
+
|
|
183
|
+
/** 어시스턴트 페르소나 분류 — 서버 {@code PersonaRole} enum 과 일치 (14개). {@code PM} 은 PM_BACKSTAGE 단일 PM 전용. */
|
|
184
|
+
export const PersonaRole: {
|
|
185
|
+
readonly LEGAL_ADVISOR: 'LEGAL_ADVISOR';
|
|
186
|
+
readonly MARKETING: 'MARKETING';
|
|
187
|
+
readonly PRODUCT_PLANNING: 'PRODUCT_PLANNING';
|
|
188
|
+
readonly HR: 'HR';
|
|
189
|
+
readonly FINANCE: 'FINANCE';
|
|
190
|
+
readonly CUSTOMER_SUPPORT: 'CUSTOMER_SUPPORT';
|
|
191
|
+
readonly SALES: 'SALES';
|
|
192
|
+
readonly ENGINEERING: 'ENGINEERING';
|
|
193
|
+
readonly DATA_ANALYST: 'DATA_ANALYST';
|
|
194
|
+
readonly PROJECT_MANAGEMENT: 'PROJECT_MANAGEMENT';
|
|
195
|
+
readonly RESEARCH: 'RESEARCH';
|
|
196
|
+
readonly TRANSLATION: 'TRANSLATION';
|
|
197
|
+
readonly DESIGN: 'DESIGN';
|
|
198
|
+
/** PM_BACKSTAGE 전용 — PM Front 단일 응답 (PERSONA_MULTI 14인과 별개). */
|
|
199
|
+
readonly PM: 'PM';
|
|
200
|
+
};
|
|
201
|
+
export type PersonaRoleValue = typeof PersonaRole[keyof typeof PersonaRole];
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 채팅 요약 포맷 — 서버 {@code SummarizeFormat} enum 과 일치 (5개).
|
|
205
|
+
* {@link ChatClient.summarizeWithAssistant} 의 {@code format} 인자. 미지정 시 서버 기본값 {@code SHORT}.
|
|
206
|
+
*/
|
|
207
|
+
export const SummarizeFormat: {
|
|
208
|
+
/** 회의록 — 참석/안건/핵심 논의/결정사항. */
|
|
209
|
+
readonly MINUTES: 'MINUTES';
|
|
210
|
+
/** 짧은 요약 — 몇 문장 핵심. 기본값. */
|
|
211
|
+
readonly SHORT: 'SHORT';
|
|
212
|
+
/** 시간순 타임라인. */
|
|
213
|
+
readonly TIMELINE: 'TIMELINE';
|
|
214
|
+
/** 액션 아이템 — task/owner/기한. */
|
|
215
|
+
readonly ACTIONS: 'ACTIONS';
|
|
216
|
+
/** 옵션 비교 — 논의된 선택지별 장단점. */
|
|
217
|
+
readonly OPTIONS: 'OPTIONS';
|
|
218
|
+
};
|
|
219
|
+
export type SummarizeFormatValue = typeof SummarizeFormat[keyof typeof SummarizeFormat];
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 어시스턴트 응답 — {@link ChatClient.getAssistants} 반환 항목.
|
|
223
|
+
*
|
|
224
|
+
* <p>방 생성 시 {@code id} 를 {@link CreateGroupRoomData#invitedAssistantPersonaIds} 로 전달.</p>
|
|
225
|
+
*/
|
|
226
|
+
export interface AssistantPersonaResponse {
|
|
227
|
+
/** 어시스턴트 식별자. 방에 추가할 때 이 값을 전달. */
|
|
228
|
+
id: string;
|
|
229
|
+
/** UI 표시명. 멘션 대상 (예: {@code @법률자문관}). */
|
|
230
|
+
displayName: string;
|
|
231
|
+
/** 영문 alias. 멘션 대상 (예: {@code @legal}). */
|
|
232
|
+
mentionKey: string;
|
|
233
|
+
/** 분류 (UI 카테고리 / 정렬 등). */
|
|
234
|
+
role: PersonaRoleValue;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 어시스턴트 도구 실행 결과 — 서버 {@code AssistantToolResult}.
|
|
239
|
+
* {@link ChatClient.summarizeWithAssistant} / {@link ChatClient.translateWithAssistant} 반환.
|
|
240
|
+
*/
|
|
241
|
+
export interface AssistantToolResult {
|
|
242
|
+
/**
|
|
243
|
+
* 실행 결과.
|
|
244
|
+
* <ul>
|
|
245
|
+
* <li>{@code 'EMITTED'} — 어시스턴트 메시지 생성됨 ({@code messageId} 존재).</li>
|
|
246
|
+
* <li>{@code 'SKIPPED'} — 정책상 미실행 ({@code detail} 에 사유).</li>
|
|
247
|
+
* <li>{@code 'FAILED'} — 실행 실패 ({@code detail} 에 사유).</li>
|
|
248
|
+
* </ul>
|
|
249
|
+
*/
|
|
250
|
+
outcome: 'EMITTED' | 'SKIPPED' | 'FAILED';
|
|
251
|
+
/** 생성된 메시지 ID. {@code outcome === 'EMITTED'} 일 때만 존재, 그 외 null. */
|
|
252
|
+
messageId: string | null;
|
|
253
|
+
/** SKIPPED / FAILED 사유. EMITTED 일 때 null. */
|
|
254
|
+
detail: string | null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** {@link ChatClient.summarizeWithAssistant} 옵션 — 서버 {@code SummarizeToolRequest}. */
|
|
258
|
+
export interface SummarizeToolRequest {
|
|
259
|
+
/** 요약을 수행할 어시스턴트 ID ({@link AssistantPersonaResponse.id}). 필수. */
|
|
260
|
+
personaId: string;
|
|
261
|
+
/** 요약 포맷. 미지정 시 서버 기본값 {@code SHORT}. */
|
|
262
|
+
format?: SummarizeFormatValue;
|
|
263
|
+
/** 요약 대상 최근 메시지 수 (1~50). 미지정 시 서버 기본값. */
|
|
264
|
+
messageCount?: number;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** {@link ChatClient.translateWithAssistant} 옵션 — 서버 {@code TranslateToolRequest}. */
|
|
268
|
+
export interface TranslateToolRequest {
|
|
269
|
+
/** 번역을 수행할 어시스턴트 ID ({@link AssistantPersonaResponse.id}). 필수. */
|
|
270
|
+
personaId: string;
|
|
271
|
+
/** 번역 대상 메시지의 {@code messageId}. 필수. */
|
|
272
|
+
sourceMessageId: string;
|
|
273
|
+
/** 목표 언어 (예: {@code 'en'}). 미지정 시 서버 정책. */
|
|
274
|
+
targetLang?: string;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* PM 프롬프트 레이어 — 서버 {@code PmPromptLayerResponse}.
|
|
279
|
+
*
|
|
280
|
+
* <p>전역 PM 프롬프트에 <b>합성</b>되는 추가 지시문 (교체 아님). SDK 의 방 레이어 메서드
|
|
281
|
+
* ({@link ChatClient.getRoomPmPrompt} 등)는 {@code scope === 'ROOM'} 인 레이어만 다룬다.
|
|
282
|
+
* PM_BACKSTAGE 방 전용 — PM(role=PM) 응답에만 반영.</p>
|
|
283
|
+
*/
|
|
284
|
+
export interface PmPromptLayerResponse {
|
|
285
|
+
/** 레이어 식별자. */
|
|
286
|
+
id: string;
|
|
287
|
+
/** 레이어 차원 — 방 레이어 API 에서는 항상 {@code 'ROOM'}. */
|
|
288
|
+
scope: PmPromptLayerScopeValue;
|
|
289
|
+
/** 소속 프로젝트(회사) ID. */
|
|
290
|
+
projectId: string;
|
|
291
|
+
/** 방 ID — {@code scope === 'ROOM'} 일 때 존재. PROJECT 레이어는 필드 생략(서버 NON_NULL 직렬화). */
|
|
292
|
+
roomId?: string;
|
|
293
|
+
/** 레이어 본문 (방 레이어 한도 2,000자). */
|
|
294
|
+
content: string;
|
|
295
|
+
/** 합성 포함 여부 — {@code false} 면 보존된 채 합성에서만 제외(soft delete). */
|
|
296
|
+
active: boolean;
|
|
297
|
+
/** 현재 라이브 본문에 해당하는 활성 버전 번호. 이력이 없는 레거시 데이터는 생략될 수 있다. */
|
|
298
|
+
activeVersion?: number;
|
|
299
|
+
/** 마지막 수정 주체. */
|
|
300
|
+
editorType: PmPromptLayerEditorTypeValue;
|
|
301
|
+
/** 마지막 수정 시각 (ISO-8601 문자열). */
|
|
302
|
+
updatedAt: string;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** PM 프롬프트 레이어 버전 이력 항목 — 서버 {@code PmPromptLayerVersionResponse}. */
|
|
306
|
+
export interface PmPromptLayerVersionResponse {
|
|
307
|
+
/** 버전 번호 (1부터 증가). {@link ChatClient.activateRoomPmPromptVersion} 에 전달. */
|
|
308
|
+
version: number;
|
|
309
|
+
/** 해당 버전의 본문. */
|
|
310
|
+
content: string;
|
|
311
|
+
/** 현재 활성 버전이면 {@code true}(layerId 당 1개). 실제 합성 적용 여부는 단건 응답의 active 와 함께 판단. */
|
|
312
|
+
active: boolean;
|
|
313
|
+
/** 해당 버전을 만든 주체. */
|
|
314
|
+
editorType: PmPromptLayerEditorTypeValue;
|
|
315
|
+
/** 버전 생성 시각 (ISO-8601 문자열). */
|
|
316
|
+
createdAt: string;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* PM 프롬프트 합성 미리보기 — 서버 {@code PmPromptPreviewResponse}.
|
|
321
|
+
* <p>전역(+회사)(+방) 레이어 합성 결과 SYSTEM 본문. 컨텍스트 정책 등 런타임 부착분은 미포함.</p>
|
|
322
|
+
*/
|
|
323
|
+
export interface PmPromptPreviewResponse {
|
|
324
|
+
/** 합성된 최종 프롬프트 본문. */
|
|
325
|
+
composedPrompt: string;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* {@link ChatClient.inviteToGroupRoom} 의 신규 형식 입력. 회원 / 어시스턴트 동시 초대 지원.
|
|
330
|
+
* 둘 중 하나 이상 필수.
|
|
331
|
+
*/
|
|
332
|
+
export interface InviteToRoomData {
|
|
333
|
+
/** 초대할 회원 ID 배열 (max 50). */
|
|
334
|
+
userIds?: string[];
|
|
335
|
+
/** 추가할 어시스턴트 ID 배열 (max 10). {@link ChatClient.getAssistants} 결과의 {@code id}. */
|
|
336
|
+
assistantPersonaIds?: string[];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export const RoomListEventType: {
|
|
340
|
+
readonly MESSAGE_RECEIVED: 'MESSAGE_RECEIVED';
|
|
341
|
+
readonly MESSAGE_DELETED: 'MESSAGE_DELETED';
|
|
342
|
+
readonly MESSAGE_UPDATED: 'MESSAGE_UPDATED';
|
|
343
|
+
readonly ROOM_CREATED: 'ROOM_CREATED';
|
|
344
|
+
readonly ROOM_JOINED: 'ROOM_JOINED';
|
|
345
|
+
readonly ROOM_LEFT: 'ROOM_LEFT';
|
|
346
|
+
readonly ROOM_UPDATED: 'ROOM_UPDATED';
|
|
347
|
+
readonly ROOM_KICKED: 'ROOM_KICKED';
|
|
348
|
+
readonly ROOM_BANNED: 'ROOM_BANNED';
|
|
349
|
+
readonly MESSAGE_RETENTION_CLEANUP: 'MESSAGE_RETENTION_CLEANUP';
|
|
350
|
+
};
|
|
351
|
+
export type RoomListEventTypeValue = typeof RoomListEventType[keyof typeof RoomListEventType];
|
|
352
|
+
|
|
353
|
+
export const LogLevel: {
|
|
354
|
+
readonly DEBUG: 0;
|
|
355
|
+
readonly INFO: 1;
|
|
356
|
+
readonly WARN: 2;
|
|
357
|
+
readonly ERROR: 3;
|
|
358
|
+
readonly NONE: 4;
|
|
359
|
+
};
|
|
360
|
+
export type LogLevelValue = typeof LogLevel[keyof typeof LogLevel];
|
|
361
|
+
|
|
362
|
+
export const Environment: {
|
|
363
|
+
readonly DEVELOPMENT: 'development';
|
|
364
|
+
readonly STAGING: 'staging';
|
|
365
|
+
readonly PRODUCTION: 'production';
|
|
366
|
+
};
|
|
367
|
+
export type EnvironmentValue = typeof Environment[keyof typeof Environment];
|
|
368
|
+
|
|
369
|
+
export const Endpoints: Record<EnvironmentValue, { serverUrl: string; wsEndpoint: string }>;
|
|
370
|
+
|
|
371
|
+
export const WebSocketPaths: {
|
|
372
|
+
readonly SOCKJS_ENDPOINT: string;
|
|
373
|
+
readonly NATIVE_ENDPOINT: string;
|
|
374
|
+
readonly APP_PREFIX: string;
|
|
375
|
+
readonly TOPIC_PREFIX: string;
|
|
376
|
+
readonly QUEUE_PREFIX: string;
|
|
377
|
+
readonly USER_PREFIX: string;
|
|
378
|
+
getChatDestination(roomId: string): string;
|
|
379
|
+
getChatReadDestination(roomId: string): string;
|
|
380
|
+
getChatTypingDestination(roomId: string): string;
|
|
381
|
+
readonly ROOM_LIST_USER_DESTINATION: string;
|
|
382
|
+
getWebRTCDestination(roomId: string): string;
|
|
383
|
+
getWebRTCUserDestination(): string;
|
|
384
|
+
readonly CHAT_SEND: string;
|
|
385
|
+
readonly CHAT_READ: string;
|
|
386
|
+
readonly CHAT_TYPING: string;
|
|
387
|
+
readonly WEBRTC_SIGNAL: string;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
export const DefaultConfig: {
|
|
391
|
+
readonly environment: EnvironmentValue;
|
|
392
|
+
readonly reconnectDelay: number;
|
|
393
|
+
readonly maxReconnectAttempts: number;
|
|
394
|
+
readonly heartbeatIncoming: number;
|
|
395
|
+
readonly heartbeatOutgoing: number;
|
|
396
|
+
readonly apiTimeout: number;
|
|
397
|
+
readonly iceServers: RTCIceServer[];
|
|
398
|
+
readonly logLevel: LogLevelValue;
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
export function getServerUrl(env: EnvironmentValue | string): string;
|
|
402
|
+
|
|
403
|
+
// ============================================================================
|
|
404
|
+
// Domain / DTO types
|
|
405
|
+
// ============================================================================
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* JSON 호환 값 — string / number / boolean / null / 중첩 배열·객체.
|
|
409
|
+
* <p>{@code Date} / {@code Map} / {@code Set} / 함수 / {@code undefined} / 순환 객체는 컴파일 타임에 거부.</p>
|
|
410
|
+
*/
|
|
411
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
412
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* 고객사 UI 용 opaque pass-through metadata.
|
|
416
|
+
*
|
|
417
|
+
* <p>서버는 해석하지 않고 저장 / 응답·이벤트로 그대로 반환한다. 스티커 / 커스텀 첨부 타입 /
|
|
418
|
+
* 렌더링 힌트처럼 SDK·서버가 의미를 몰라도 되는 값을 자유 schema 로 주고받는 용도.</p>
|
|
419
|
+
*
|
|
420
|
+
* <p><b>제약</b>:</p>
|
|
421
|
+
* <ul>
|
|
422
|
+
* <li>JSON 직렬화 가능한 값만 — {@link JsonValue} 로 컴파일 타임 강제</li>
|
|
423
|
+
* <li>키에 {@code "."} 포함 또는 {@code "$"} 시작 금지 — Mongo BSON 제약. 위반 시 서버 400</li>
|
|
424
|
+
* <li>권장 사이즈: 파일 1개당 직렬화 후 1-2KB. 서버 상한 4KB 초과 시 400</li>
|
|
425
|
+
* <li>멱등 retry 시 첫 호출의 metadata 가 유지됨</li>
|
|
426
|
+
* </ul>
|
|
427
|
+
*/
|
|
428
|
+
export type FileCustomMetadata = { [key: string]: JsonValue };
|
|
429
|
+
|
|
430
|
+
/** 파일 메타데이터 (이미지/영상/오디오/문서). */
|
|
431
|
+
export interface FileMetaData {
|
|
432
|
+
fileUrl: string;
|
|
433
|
+
fileName?: string;
|
|
434
|
+
fileSize?: number;
|
|
435
|
+
contentType?: string;
|
|
436
|
+
imageInfo?: {
|
|
437
|
+
width?: number;
|
|
438
|
+
height?: number;
|
|
439
|
+
/**
|
|
440
|
+
* 업로드 시 자동 생성된 JPEG 썸네일 URL (최대 변 300px, 품질 85%, EXIF 회전/알파 배경 처리 완료).
|
|
441
|
+
* 생성 실패 (초고해상도 / 서버 부하 / 디코드 오류) 시 undefined —
|
|
442
|
+
* 클라는 `thumbnailUrl ?? fileUrl` fallback 으로 원본 사용.
|
|
443
|
+
*/
|
|
444
|
+
thumbnailUrl?: string;
|
|
445
|
+
};
|
|
446
|
+
videoInfo?: {
|
|
447
|
+
duration?: number;
|
|
448
|
+
width?: number;
|
|
449
|
+
height?: number;
|
|
450
|
+
thumbnailUrl?: string;
|
|
451
|
+
};
|
|
452
|
+
audioInfo?: {
|
|
453
|
+
duration?: number;
|
|
454
|
+
};
|
|
455
|
+
documentInfo?: {
|
|
456
|
+
pageCount?: number;
|
|
457
|
+
thumbnailUrl?: string;
|
|
458
|
+
};
|
|
459
|
+
/**
|
|
460
|
+
* 고객사 pass-through metadata. {@link FileCustomMetadata} 참고.
|
|
461
|
+
*
|
|
462
|
+
* <p>업로드 응답에는 보통 비어 있으며, 고객사가 메시지 전송 시 채워서 보내면
|
|
463
|
+
* 메시지 응답·수신 이벤트의 {@code fileMetaDataList[i].metadata} 로 동일 값이 돌아온다.</p>
|
|
464
|
+
*/
|
|
465
|
+
metadata?: FileCustomMetadata;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/** 리액션 항목 — 이모지 + 누른 사용자들. */
|
|
469
|
+
export interface ReactionResponse {
|
|
470
|
+
emoji: string;
|
|
471
|
+
users: Array<{
|
|
472
|
+
userId: string;
|
|
473
|
+
nickName?: string | null;
|
|
474
|
+
profileUrl?: string | null;
|
|
475
|
+
}>;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/** 답글 대상 원본 메시지 snapshot. */
|
|
479
|
+
export interface ReplyToSnapshot {
|
|
480
|
+
messageId: string;
|
|
481
|
+
senderId: string;
|
|
482
|
+
senderName: string;
|
|
483
|
+
contentPreview: string;
|
|
484
|
+
messageType: ChatMessageTypeValue;
|
|
485
|
+
sentAt: number;
|
|
486
|
+
fileUrl?: string | null;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* 실시간 WebSocket 이벤트에만 실리는 이벤트 종류 식별자.
|
|
491
|
+
* <p>REST 히스토리 응답에는 이 필드가 없음. 클라이언트는 실시간 분기에만 사용.</p>
|
|
492
|
+
*/
|
|
493
|
+
export type ChatMessageEventTypeValue =
|
|
494
|
+
| 'MESSAGE_CREATED'
|
|
495
|
+
| 'MESSAGE_UPDATED'
|
|
496
|
+
| 'MESSAGE_DELETED'
|
|
497
|
+
| 'REACTION_CHANGED'
|
|
498
|
+
| 'LINK_PREVIEW_ATTACHED'
|
|
499
|
+
| 'MESSAGE_TRANSLATED';
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* 링크 미리보기 snapshot.
|
|
503
|
+
* <p>REST/WebSocket 공통. 서버가 비동기로 채우며, OG 파싱 실패 시 도메인 + favicon 기반
|
|
504
|
+
* 최소 카드가 내려올 수 있다. 유효하지 않거나 안전하지 않은 URL 은 필드 자체가 부재.</p>
|
|
505
|
+
*/
|
|
506
|
+
export interface LinkPreviewResponse {
|
|
507
|
+
url: string;
|
|
508
|
+
title?: string | null;
|
|
509
|
+
description?: string | null;
|
|
510
|
+
imageUrl?: string | null;
|
|
511
|
+
siteName?: string | null;
|
|
512
|
+
type?: string | null;
|
|
513
|
+
previewSource?: 'OG' | 'FAVICON_FALLBACK' | null;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** 수동 링크 미리보기 조회 응답. */
|
|
517
|
+
export interface FetchLinkPreviewResponse extends LinkPreviewResponse {
|
|
518
|
+
finalUrl: string;
|
|
519
|
+
fetchedAt: string;
|
|
520
|
+
/** @deprecated 서버는 imageUrl 을 사용합니다. */
|
|
521
|
+
image?: string | null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** 단일 메시지 DTO. */
|
|
525
|
+
export interface ChatMessage {
|
|
526
|
+
id: string;
|
|
527
|
+
messageId: string;
|
|
528
|
+
roomId: string;
|
|
529
|
+
userId: string;
|
|
530
|
+
/**
|
|
531
|
+
* 발신자 타입. {@code 'USER'} 메시지에는 서버가 이 필드를 응답에서 생략 (NON_NULL).
|
|
532
|
+
* {@code 'ASSISTANT'} 면 {@code userId} 가 페르소나 ID 이며 클라이언트는 어시 메시지로 마킹.
|
|
533
|
+
*/
|
|
534
|
+
senderType?: SenderTypeValue;
|
|
535
|
+
/** 발신자 표시명. 서버가 응답 시 enrich (NON_NULL — 없으면 생략). */
|
|
536
|
+
nickName?: string;
|
|
537
|
+
/** 발신자 프로필 이미지 URL. */
|
|
538
|
+
profileUrl?: string | null;
|
|
539
|
+
/** 발신자의 프로젝트 스코프 사용자 ID. */
|
|
540
|
+
projectUserId?: string;
|
|
541
|
+
content: string | null;
|
|
542
|
+
chatMessageType: ChatMessageTypeValue;
|
|
543
|
+
chatType: string;
|
|
544
|
+
roomType: ChatRoomTypeValue;
|
|
545
|
+
sentAt: number;
|
|
546
|
+
unreadCount?: number | null;
|
|
547
|
+
fileMetaDataList?: FileMetaData[] | null;
|
|
548
|
+
deletedStatus?: 'DELETED' | null;
|
|
549
|
+
edited?: boolean;
|
|
550
|
+
editedAt?: number | null;
|
|
551
|
+
reactions?: ReactionResponse[];
|
|
552
|
+
replyTo?: ReplyToSnapshot | null;
|
|
553
|
+
/** WebSocket 이벤트에만 존재 — REST 응답에는 없음. */
|
|
554
|
+
eventType?: ChatMessageEventTypeValue;
|
|
555
|
+
/** Open Graph 미리보기. 서버가 비동기 첨부 성공 시 값 존재, 실패/미첨부 시 null/undefined. */
|
|
556
|
+
linkPreview?: LinkPreviewResponse | null;
|
|
557
|
+
/** 감지된 원문 언어 (BCP-47). 서버가 비동기 번역 후 채움 (다국어). 미번역 시 부재. */
|
|
558
|
+
sourceLang?: string | null;
|
|
559
|
+
/**
|
|
560
|
+
* 언어 코드 → 번역문 맵. 서버가 비동기 번역 후 채우고 {@code MESSAGE_TRANSLATED} 로 패치.
|
|
561
|
+
* <p>표시: {@code translations[myLang] ?? content} (원문 토글 권장). 같은 언어/원문 수신자는 키 부재 →
|
|
562
|
+
* {@code content} fallback. {@code MESSAGE_UPDATED}(편집) 수신 시 서버가 비워 보내므로 교체/클리어 필요.</p>
|
|
563
|
+
*/
|
|
564
|
+
translations?: Record<string, string> | null;
|
|
565
|
+
/** 번역 당시 content 해시 — stale 판별용 (내부). */
|
|
566
|
+
translationsOf?: string | null;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/** 채팅방 참가자. */
|
|
570
|
+
export interface ParticipantResponse {
|
|
571
|
+
userId: string;
|
|
572
|
+
nickName?: string;
|
|
573
|
+
profileUrl?: string | null;
|
|
574
|
+
projectUserId?: string;
|
|
575
|
+
/**
|
|
576
|
+
* 참가자 역할 — 서버 {@code ParticipantRole} enum 과 일치.
|
|
577
|
+
* <ul>
|
|
578
|
+
* <li>{@code OWNER} — 방 주인 (그룹방 생성자, 방당 1명). 방장 기능 + PM 프롬프트 레이어 쓰기 전용 권한.</li>
|
|
579
|
+
* <li>{@code ADMIN} — 위임 관리자. 방장 기능(설정/추방/차단/고정/초대)은 OWNER 와 동일하나
|
|
580
|
+
* PM 프롬프트 레이어 쓰기는 불가. (승급 API 는 후속 — 현재 신규 부여 경로 없음)</li>
|
|
581
|
+
* <li>{@code MEMBER} — 일반 사용자</li>
|
|
582
|
+
* <li>{@code ASSISTANT} — 어시스턴트 페르소나 ({@code userId} = personaId). UI 에서 봇 배지 등 별도 표시 권장.</li>
|
|
583
|
+
* </ul>
|
|
584
|
+
* <p>기존(OWNER 도입 전) 생성 방의 생성자는 서버 부팅 시 backfill 로 OWNER 전환된다 —
|
|
585
|
+
* 과도기 동안 {@code ADMIN} 생성자가 보일 수 있으나 권한은 동일.</p>
|
|
586
|
+
*/
|
|
587
|
+
role: 'OWNER' | 'ADMIN' | 'MEMBER' | 'ASSISTANT';
|
|
588
|
+
joinedAt: string;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** 채팅방 last message preview. */
|
|
592
|
+
export interface LastMessageResponse {
|
|
593
|
+
messageId: string;
|
|
594
|
+
senderId: string;
|
|
595
|
+
senderName: string;
|
|
596
|
+
senderProfileUrl?: string | null;
|
|
597
|
+
content: string;
|
|
598
|
+
messageType: string;
|
|
599
|
+
sentAt: string;
|
|
600
|
+
/**
|
|
601
|
+
* 파일 메시지일 때 첫 파일의 사용자 metadata (opaque pass-through).
|
|
602
|
+
* <p>고객사가 파일 업로드 시 함께 보낸 데이터 (스티커 ID 등) 가 채팅방 리스트에서도 식별 가능하도록 보존.
|
|
603
|
+
* 텍스트/시스템 메시지에서는 undefined.</p>
|
|
604
|
+
*/
|
|
605
|
+
metadata?: Record<string, unknown> | null;
|
|
606
|
+
/**
|
|
607
|
+
* IMAGE / GALLERY / VIDEO / DOCUMENT 메시지의 첫 파일 썸네일 URL.
|
|
608
|
+
* <p>채팅방 리스트에서 카톡식 작은 미리보기 이미지로 사용. 텍스트/AUDIO/FILE/SYSTEM 또는
|
|
609
|
+
* 썸네일 생성 실패 시 undefined.</p>
|
|
610
|
+
*/
|
|
611
|
+
thumbnailUrl?: string | null;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/** 채팅방 DTO. */
|
|
615
|
+
export interface ChatRoomResponse {
|
|
616
|
+
id: string;
|
|
617
|
+
projectId: string;
|
|
618
|
+
roomType: ChatRoomTypeValue;
|
|
619
|
+
/** 어시스턴트 참여 모드. 구버전 서버 응답에서는 없을 수 있음. */
|
|
620
|
+
assistantMode?: AssistantModeValue;
|
|
621
|
+
/** 방 AI 사용 방식 (상위 SSOT). 구버전 서버 응답에서는 없을 수 있음. */
|
|
622
|
+
roomAiType?: RoomAiTypeValue;
|
|
623
|
+
/** PM 개입 강도 (PM_BACKSTAGE 방 전용). 그 외 방 타입/구버전 응답에서는 없을 수 있음. */
|
|
624
|
+
engagementIntensity?: EngagementIntensityValue;
|
|
625
|
+
roomName: string;
|
|
626
|
+
description?: string;
|
|
627
|
+
creatorId: string;
|
|
628
|
+
unreadCount?: number | null;
|
|
629
|
+
isParticipating?: boolean | null;
|
|
630
|
+
activeParticipantCount: number;
|
|
631
|
+
messageRetentionHours?: number | null;
|
|
632
|
+
anyoneCanInvite: boolean;
|
|
633
|
+
pinnedMessageId?: string | null;
|
|
634
|
+
participants?: ParticipantResponse[];
|
|
635
|
+
lastMessage?: LastMessageResponse | null;
|
|
636
|
+
lastMessageAt?: string | null;
|
|
637
|
+
createdAt: string;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/** 커서 페이지네이션 응답. */
|
|
641
|
+
export interface CursorPageResponse<T> {
|
|
642
|
+
content: T[];
|
|
643
|
+
size: number;
|
|
644
|
+
hasNext: boolean;
|
|
645
|
+
lastId?: string | null;
|
|
646
|
+
lastSortValue?: number | string | null;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* 서버 공통 성공 응답 래퍼 — 일부 메서드({@link ChatClient.getMessages})는 unwrap 하지 않고
|
|
651
|
+
* 이 래퍼째 반환한다 (기존 소비자의 {@code result.data.*} 접근 호환 유지).
|
|
652
|
+
*/
|
|
653
|
+
export interface ApiEnvelope<T> {
|
|
654
|
+
/** 실제 페이로드. */
|
|
655
|
+
data: T;
|
|
656
|
+
/** 서버 응답 코드/메시지 (존재 시). */
|
|
657
|
+
code?: string;
|
|
658
|
+
message?: string;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// ============================================================================
|
|
662
|
+
// Event payloads
|
|
663
|
+
// ============================================================================
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* ROOM_UPDATED 이벤트의 방 메타 snapshot.
|
|
667
|
+
* <p>클라이언트는 {@link #roomListRoomUpdated} 수신 시 {@code event.room} 을 로컬 방 객체에 그대로 merge 하면 됨.
|
|
668
|
+
* 재조회(getRoomInfo) 불필요.</p>
|
|
669
|
+
*/
|
|
670
|
+
export interface RoomMeta {
|
|
671
|
+
roomName: string;
|
|
672
|
+
description: string | null;
|
|
673
|
+
messageRetentionHours: number | null;
|
|
674
|
+
anyoneCanInvite: boolean;
|
|
675
|
+
pinnedMessageId: string | null;
|
|
676
|
+
roomType: ChatRoomTypeValue;
|
|
677
|
+
/** ROOM_UPDATED 메타에 포함되는 어시스턴트 참여 모드. */
|
|
678
|
+
assistantMode?: AssistantModeValue;
|
|
679
|
+
/** ROOM_UPDATED 메타의 방 AI 사용 방식 (상위 SSOT). */
|
|
680
|
+
roomAiType?: RoomAiTypeValue;
|
|
681
|
+
/** ROOM_UPDATED 메타의 PM 개입 강도 (PM_BACKSTAGE 방 전용). */
|
|
682
|
+
engagementIntensity?: EngagementIntensityValue;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/** 채팅방 리스트 실시간 업데이트 이벤트. */
|
|
686
|
+
export interface RoomListUpdateEvent {
|
|
687
|
+
eventType: RoomListEventTypeValue;
|
|
688
|
+
roomId: string;
|
|
689
|
+
actorId?: string;
|
|
690
|
+
cutoffTime?: number;
|
|
691
|
+
/** {@code ROOM_UPDATED} 전용 — 저장된 최신 메타 snapshot. 다른 eventType 에서는 undefined. */
|
|
692
|
+
room?: RoomMeta;
|
|
693
|
+
/**
|
|
694
|
+
* {@code MESSAGE_RECEIVED} 의 파일 메시지에 한해 채워지는 첫 파일의 사용자 metadata.
|
|
695
|
+
* 그 외 이벤트 또는 텍스트/시스템 메시지에서는 undefined.
|
|
696
|
+
*/
|
|
697
|
+
lastMessageMetadata?: Record<string, unknown> | null;
|
|
698
|
+
/**
|
|
699
|
+
* {@code MESSAGE_RECEIVED} 의 IMAGE/GALLERY/VIDEO/DOCUMENT 메시지에 한해 채워지는 썸네일 URL.
|
|
700
|
+
* 클라가 채팅방 리스트에서 카톡식 작은 미리보기로 사용.
|
|
701
|
+
*/
|
|
702
|
+
lastMessageThumbnailUrl?: string | null;
|
|
703
|
+
/**
|
|
704
|
+
* {@code MESSAGE_RECEIVED} / {@code MESSAGE_DELETED} / {@code MESSAGE_UPDATED} 일 때 메시지 ID.
|
|
705
|
+
* SDK 내부에서 같은 메시지의 중복 수신 (서버측 멱등 race 복구 등) dedup 용도. 다른 eventType 에서는 undefined.
|
|
706
|
+
*/
|
|
707
|
+
messageId?: string;
|
|
708
|
+
[extra: string]: unknown;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/** 포그라운드 푸시 알림 payload. */
|
|
712
|
+
export interface PushNotificationPayload {
|
|
713
|
+
title: string;
|
|
714
|
+
body: string;
|
|
715
|
+
data?: {
|
|
716
|
+
roomId?: string;
|
|
717
|
+
[k: string]: string | undefined;
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* 타이핑 이벤트.
|
|
723
|
+
*
|
|
724
|
+
* 두 종류 — `senderType` 으로 분기:
|
|
725
|
+
* - `USER` (default): 다른 사용자의 키 입력 표시. 호출자 클라이언트가 `startTyping()` 호출 시 발신.
|
|
726
|
+
* - `ASSISTANT`: 어시스턴트 응답 준비 중 신호. 서버는 `typing=true` 만 발행하며 `typing=false` 는 SDK 가
|
|
727
|
+
* (1) 어시 메시지 도착 시 즉시 (2) client-side timeout (default 30초) 만료 시 자동 emit.
|
|
728
|
+
* `userId` 는 generic marker (`__assistant__`), `userName` 은 "AI" 통칭.
|
|
729
|
+
*/
|
|
730
|
+
export interface TypingEvent {
|
|
731
|
+
roomId: string;
|
|
732
|
+
userId: string;
|
|
733
|
+
userName: string;
|
|
734
|
+
typing: boolean;
|
|
735
|
+
senderType: 'USER' | 'ASSISTANT';
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// ============================================================================
|
|
739
|
+
// Options — constructor & method
|
|
740
|
+
// ============================================================================
|
|
741
|
+
|
|
742
|
+
export interface TalkFlowClientOptions {
|
|
743
|
+
/** Client API 키 (브라우저 노출 안전). */
|
|
744
|
+
apiKey: string;
|
|
745
|
+
/** 프로젝트 ID. */
|
|
746
|
+
projectId: string;
|
|
747
|
+
/** JWT 토큰 — 고객사 backend 에서 발급 받아 전달. */
|
|
748
|
+
jwtToken?: string;
|
|
749
|
+
/** 환경. 기본 'production'. */
|
|
750
|
+
env?: EnvironmentValue | string;
|
|
751
|
+
/** 서버 URL 직접 지정 (env 무시). */
|
|
752
|
+
serverUrl?: string;
|
|
753
|
+
/** SockJS 사용 여부. 기본 true. */
|
|
754
|
+
useSockJS?: boolean;
|
|
755
|
+
/** 재연결 지연 (ms). 기본 5000. */
|
|
756
|
+
reconnectDelay?: number;
|
|
757
|
+
/** 최대 재연결 시도. 기본 10. */
|
|
758
|
+
maxReconnectAttempts?: number;
|
|
759
|
+
/** WebRTC ICE 서버. */
|
|
760
|
+
iceServers?: RTCIceServer[];
|
|
761
|
+
/** connect() 시 채팅방 리스트 자동 구독. 기본 true. */
|
|
762
|
+
autoSubscribeRoomList?: boolean;
|
|
763
|
+
/** 로그 레벨. 기본 WARN. */
|
|
764
|
+
logLevel?: LogLevelValue;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
export interface ConnectOptions {
|
|
768
|
+
/** connect 와 동시에 푸시 활성화. 기본 false. */
|
|
769
|
+
enablePush?: boolean;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
export interface EnablePushOptions {
|
|
773
|
+
/** Firebase 앱 설정 (고급). 기본 TalkFlow 내장. */
|
|
774
|
+
firebaseConfig?: Record<string, unknown>;
|
|
775
|
+
/** FCM VAPID 공개키. 기본 TalkFlow 내장. */
|
|
776
|
+
vapidKey?: string;
|
|
777
|
+
/** 서비스 워커 경로. 기본 '/firebase-messaging-sw.js'. */
|
|
778
|
+
serviceWorkerPath?: string;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
export interface CursorPageParams {
|
|
782
|
+
/** 페이지 크기. 기본 50, max 100. */
|
|
783
|
+
size?: number;
|
|
784
|
+
/** 마지막 항목 ID. */
|
|
785
|
+
lastId?: string;
|
|
786
|
+
/** 마지막 정렬값 (epoch millis 등). */
|
|
787
|
+
lastSortValue?: number | string;
|
|
788
|
+
/** 방 리스트에서 타입 필터. */
|
|
789
|
+
type?: ChatRoomTypeValue;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* 그룹 채팅방 생성 요청 body.
|
|
794
|
+
* <p>런타임 ({@code ChatClient.createGroupRoom}) 에서 실제로 읽는 필드와 이름/기본값 일치.</p>
|
|
795
|
+
*/
|
|
796
|
+
export interface CreateGroupRoomData {
|
|
797
|
+
/** 방 이름. 필수. max 100자. */
|
|
798
|
+
roomName: string;
|
|
799
|
+
/** 설명. 선택. max 500자. */
|
|
800
|
+
description?: string;
|
|
801
|
+
/** 초대할 사용자 내부 ID 배열. 선택. max 50명. */
|
|
802
|
+
invitedUserIds?: string[];
|
|
803
|
+
/**
|
|
804
|
+
* 방 타입. 생략 시 {@code 'GROUP'}.
|
|
805
|
+
* <p>{@code 'TEAM'} = 초대 전용(직접 join 불가) + 멤버는 입장 시점 무관 전체 히스토리 열람 +
|
|
806
|
+
* 비참여자에게 탐색/전체 목록 비노출. 비밀번호 금지.</p>
|
|
807
|
+
*/
|
|
808
|
+
roomType?: ChatRoomTypeValue;
|
|
809
|
+
/** 비밀번호. {@code roomType === 'PRIVATE_GROUP'} 일 때만 의미 있고, 이 경우 필수 (SDK 가 사전 검증). 4~50자. {@code 'GROUP'}/{@code 'TEAM'} 에는 금지. */
|
|
810
|
+
password?: string;
|
|
811
|
+
/** 메시지 보관 기간 (시간). 1~8760. 미설정 시 무제한. */
|
|
812
|
+
messageRetentionHours?: number;
|
|
813
|
+
/**
|
|
814
|
+
* 방 생성 시 함께 추가할 어시스턴트 페르소나 ID 배열. max 10개.
|
|
815
|
+
* {@link ChatClient.getAssistants} 결과의 {@code id} 를 전달.
|
|
816
|
+
*/
|
|
817
|
+
invitedAssistantPersonaIds?: string[];
|
|
818
|
+
/** 어시스턴트 참여 모드. 생략 시 서버 기본값 {@code GENERAL}. */
|
|
819
|
+
assistantMode?: AssistantModeValue;
|
|
820
|
+
/**
|
|
821
|
+
* 방 AI 사용 방식 (intent). 미지정 시 서버가 API 키 권한 + 첨부 페르소나로 파생 (레거시 호환).
|
|
822
|
+
* <p>{@code 'NONE'} 이면 {@code invitedAssistantPersonaIds}/{@code assistantMode} 동시 지정 불가 (서버 400).
|
|
823
|
+
* {@code 'PM_BACKSTAGE'} 는 PM 자동부착 — {@code invitedAssistantPersonaIds}/{@code assistantMode} 지정 불가,
|
|
824
|
+
* 대신 {@code engagementIntensity} 로 개입 강도 제어. 권한 없는 값 지정 시 403.</p>
|
|
825
|
+
* <p><b>함정:</b> PERSONA_MULTI 권한이 없는 PM 전용 키는 생략 시 파생이 {@code 'NONE'}(사람 방)으로 떨어진다
|
|
826
|
+
* (PM_BACKSTAGE 자동 승격 없음). {@link ChatClient.getRoomAiMeta} 로 확인한 타입을 반드시 명시 전송할 것.</p>
|
|
827
|
+
*/
|
|
828
|
+
roomAiType?: RoomAiTypeValue;
|
|
829
|
+
/**
|
|
830
|
+
* PM 개입 강도. {@code 'PM_BACKSTAGE'} 방 전용 — 그 외 타입에 지정 시 서버 거절. 미지정 시 서버 기본값 {@code NORMAL}.
|
|
831
|
+
*/
|
|
832
|
+
engagementIntensity?: EngagementIntensityValue;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* 그룹 채팅방 설정 수정 body. null/undefined 필드는 변경 안 함.
|
|
837
|
+
* <p>런타임 ({@code ChatClient.updateGroupRoom}) 이 data 를 그대로 PUT 하므로 다른 필드는 무시됨.</p>
|
|
838
|
+
*/
|
|
839
|
+
export interface UpdateGroupRoomData {
|
|
840
|
+
roomName?: string;
|
|
841
|
+
description?: string;
|
|
842
|
+
/**
|
|
843
|
+
* 공개/비공개 전환 ({@code 'GROUP'} ↔ {@code 'PRIVATE_GROUP'}). 생략 시 변경 안 함.
|
|
844
|
+
* <p>{@code 'PRIVATE_GROUP'} 으로 전환 시 {@code password} 필수, {@code 'GROUP'} 으로 전환 시
|
|
845
|
+
* 기존 비밀번호는 서버가 제거한다. partial update 라 비번 필수 검증은 현재 방 상태 기준으로 서버가 수행한다
|
|
846
|
+
* ({@code 'PRIVATE_GROUP'} 전환에는 항상 새 {@code password} 필요 — 기존 해시 재사용 불가).</p>
|
|
847
|
+
* <p>{@code 'TEAM'} 은 전환 대상/타깃 모두 불가 — 타깃으로 보내면 400, TEAM 방 자체에
|
|
848
|
+
* roomType/password 변경 요청도 400 (멤버 히스토리 가시성 시맨틱 충돌로 미지원).</p>
|
|
849
|
+
*/
|
|
850
|
+
roomType?: ChatRoomTypeValue;
|
|
851
|
+
/** PRIVATE_GROUP 비밀번호 변경/설정 시 사용. 공백만으로는 설정 불가. 4~50자. */
|
|
852
|
+
password?: string;
|
|
853
|
+
messageRetentionHours?: number;
|
|
854
|
+
/** 누구나 초대 가능 여부. */
|
|
855
|
+
anyoneCanInvite?: boolean;
|
|
856
|
+
/** 어시스턴트 참여 모드. */
|
|
857
|
+
assistantMode?: AssistantModeValue;
|
|
858
|
+
/** PM 개입 강도 ({@code 'PM_BACKSTAGE'} 방 전용). PM 참여 레벨 변경 — 키 PM_BACKSTAGE 권한 필요. */
|
|
859
|
+
engagementIntensity?: EngagementIntensityValue;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
export interface SendMessageData {
|
|
863
|
+
/** @deprecated SDK 가 내부에서 UUID 를 자동 생성합니다. 지정해도 런타임에서 무시됩니다. */
|
|
864
|
+
messageId?: string;
|
|
865
|
+
message?: string;
|
|
866
|
+
fileInfos?: FileMetaData[];
|
|
867
|
+
separateFiles?: boolean;
|
|
868
|
+
replyToMessageId?: string;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
export interface SendMessageResult {
|
|
872
|
+
duplicate: boolean;
|
|
873
|
+
messages: ChatMessage[];
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Optimistic prediction 검증 메타데이터 — {@link OptimisticSendMessageResult} 에 첨부.
|
|
878
|
+
*
|
|
879
|
+
* <p>{@code predictionMatched: false} 면 서버의 파일 분류 규칙이 SDK 와 어긋난 것이며,
|
|
880
|
+
* 호출자는 placeholder 들을 제거하고 {@code actualMessageIds} 기준으로 재구성해야 한다.</p>
|
|
881
|
+
*/
|
|
882
|
+
export interface OptimisticPredictionInfo {
|
|
883
|
+
/** SDK 가 호출 시점에 예측한 messageId 배열. */
|
|
884
|
+
predictedMessageIds: string[];
|
|
885
|
+
/** 서버 응답의 실제 messageId 배열 — {@code result.messages.map(m => m.messageId)} 와 동일. */
|
|
886
|
+
actualMessageIds: string[];
|
|
887
|
+
/** 길이 + 값/순서가 모두 일치하는지 여부. {@code false} 면 drift 발생 — fallback reconcile 필요. */
|
|
888
|
+
predictionMatched: boolean;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Optimistic 계열 메서드의 promise 가 resolve 하는 타입.
|
|
893
|
+
* <p>일반 {@link SendMessageResult} 에 {@code optimistic} 메타데이터가 항상 첨부된다.</p>
|
|
894
|
+
*/
|
|
895
|
+
export interface OptimisticSendMessageResult extends SendMessageResult {
|
|
896
|
+
optimistic: OptimisticPredictionInfo;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Optimistic UI 핸들 — {@code sendMessageOptimistic} / {@code sendTextMessageOptimistic} /
|
|
901
|
+
* {@code sendReplyOptimistic} / {@code sendFileMessageOptimistic} 의 반환 타입.
|
|
902
|
+
*
|
|
903
|
+
* <p>SDK 가 서버의 messageId derivation 규칙 (텍스트=base, 파일=base#N) 을 mirror 하여
|
|
904
|
+
* 응답 {@code messages} 배열과 1-1 매칭되는 ID 배열을 promise 시작 전 동기 반환한다.</p>
|
|
905
|
+
*
|
|
906
|
+
* <p><b>Drift 방어</b>: 서버 파일 분류 규칙이 변경되면 prediction 이 어긋날 수 있으며,
|
|
907
|
+
* 이 경우 SDK 가 warn 로그를 남기고 promise 결과의 {@code optimistic.predictionMatched: false}
|
|
908
|
+
* 로 알린다. 호출자는 {@code result.optimistic.actualMessageIds} 로 fallback reconcile 한다.</p>
|
|
909
|
+
*/
|
|
910
|
+
export interface OptimisticSendMessageHandle {
|
|
911
|
+
/** SDK 가 생성한 base messageId — 서버 멱등 키. 응답 첫 메시지가 텍스트면 이 값과 동일. */
|
|
912
|
+
baseMessageId: string;
|
|
913
|
+
/**
|
|
914
|
+
* 예측된 전체 messageId 배열 — 응답 {@code messages} 와 같은 순서/길이.
|
|
915
|
+
* <ul>
|
|
916
|
+
* <li>텍스트 단건: {@code [base]}</li>
|
|
917
|
+
* <li>파일 1개 (text 없음): {@code [base]}</li>
|
|
918
|
+
* <li>텍스트 + 파일들: {@code [base, base#0, base#1, ...]}</li>
|
|
919
|
+
* <li>분리 다파일 (text 없음): {@code [base#0, base#1, ...]}</li>
|
|
920
|
+
* </ul>
|
|
921
|
+
*/
|
|
922
|
+
messageIds: string[];
|
|
923
|
+
/**
|
|
924
|
+
* 실제 전송 promise — 일반 결과 + {@code optimistic} 메타데이터.
|
|
925
|
+
* {@code result.optimistic.predictionMatched} 가 {@code false} 면 drift fallback 필요.
|
|
926
|
+
*/
|
|
927
|
+
promise: Promise<OptimisticSendMessageResult>;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* 파일 업로드 진행률 payload.
|
|
932
|
+
* <p>런타임 {@code ChatClient.uploadFile} 이 {@code onProgress} 에 넘기는 단일 객체 인자.</p>
|
|
933
|
+
*/
|
|
934
|
+
export interface UploadProgressEvent {
|
|
935
|
+
loaded: number;
|
|
936
|
+
total: number;
|
|
937
|
+
percent: number;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* {@code ChatClient.uploadFile} 옵션 — 단일 파일 업로드.
|
|
942
|
+
*/
|
|
943
|
+
export interface FileUploadOptions {
|
|
944
|
+
/**
|
|
945
|
+
* 단일 객체 인자를 받는 콜백 — 런타임 구현과 일치.
|
|
946
|
+
* <p>이전 공개 예시 "두 인자 (loaded, total)" 는 잘못된 계약이었으며 수정됨.</p>
|
|
947
|
+
*/
|
|
948
|
+
onProgress?: (event: UploadProgressEvent) => void;
|
|
949
|
+
signal?: AbortSignal;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* 다중 파일 진행률 payload — 단일 파일 {@link UploadProgressEvent} 에 {@code fileIndex} 추가.
|
|
954
|
+
*/
|
|
955
|
+
export interface SendFileMessageProgressEvent extends UploadProgressEvent {
|
|
956
|
+
/** 병렬 업로드 중인 파일의 0-based index. 단일 파일도 0. */
|
|
957
|
+
fileIndex: number;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* {@code ChatClient.sendFileMessage} 옵션 — 업로드 + 메시지 전송 통합.
|
|
962
|
+
* <p>진행률 콜백 이름이 {@code onUploadProgress} 로 {@link FileUploadOptions#onProgress} 와 다름.
|
|
963
|
+
* 이 계약은 런타임 구현과 일치.</p>
|
|
964
|
+
*/
|
|
965
|
+
export interface SendFileMessageOptions {
|
|
966
|
+
/** 함께 보낼 텍스트. */
|
|
967
|
+
message?: string;
|
|
968
|
+
/** 파일별 개별 메시지 분리 여부. */
|
|
969
|
+
separateFiles?: boolean;
|
|
970
|
+
/** 답글 대상 원본 메시지의 서버 messageId. */
|
|
971
|
+
replyToMessageId?: string;
|
|
972
|
+
/** 업로드 중 취소용 AbortSignal. */
|
|
973
|
+
signal?: AbortSignal;
|
|
974
|
+
/**
|
|
975
|
+
* 파일별 진행률 콜백 — {@code fileIndex} 포함 단일 객체 인자.
|
|
976
|
+
* 이름이 {@code onProgress} 가 아님에 주의.
|
|
977
|
+
*/
|
|
978
|
+
onUploadProgress?: (event: SendFileMessageProgressEvent) => void;
|
|
979
|
+
/**
|
|
980
|
+
* 고객사 pass-through metadata — 업로드 결과 {@link FileMetaData} 의 {@code metadata} 에 병합되어 전송된다.
|
|
981
|
+
*
|
|
982
|
+
* <ul>
|
|
983
|
+
* <li>객체 1개: 모든 파일에 동일 metadata 부여 (broadcast)</li>
|
|
984
|
+
* <li>배열: index 별 파일에 매핑. 빈 슬롯 ({@code null} / {@code undefined}) 은 해당 파일에 metadata 미부여</li>
|
|
985
|
+
* </ul>
|
|
986
|
+
*
|
|
987
|
+
* <p>{@code separateFiles: false} 로 묶이는 경우에도 각 파일의 {@code metadata} 는 자신의 값을 유지하므로,
|
|
988
|
+
* 그룹 메시지의 {@code fileMetaDataList[i].metadata} 가 i 별로 다를 수 있다.</p>
|
|
989
|
+
*/
|
|
990
|
+
metadata?: FileCustomMetadata | Array<FileCustomMetadata | undefined | null>;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/** 메시지 삭제 종류. */
|
|
994
|
+
export type DeleteType = 'ALL' | 'SELF';
|
|
995
|
+
|
|
996
|
+
// ============================================================================
|
|
997
|
+
// EventEmitter 기반 — on/off/emit
|
|
998
|
+
// ============================================================================
|
|
999
|
+
|
|
1000
|
+
export interface SdkEventEmitter<EventMap> {
|
|
1001
|
+
on<K extends keyof EventMap>(event: K, listener: (payload: EventMap[K]) => void): () => void;
|
|
1002
|
+
once<K extends keyof EventMap>(event: K, listener: (payload: EventMap[K]) => void): () => void;
|
|
1003
|
+
off<K extends keyof EventMap>(event: K, listener: (payload: EventMap[K]) => void): void;
|
|
1004
|
+
emit<K extends keyof EventMap>(event: K, ...payload: EventMap[K] extends void ? [] : [EventMap[K]]): void;
|
|
1005
|
+
removeAllListeners(event?: keyof EventMap): void;
|
|
1006
|
+
listenerCount(event: keyof EventMap): number;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* 각 클래스의 이벤트 payload 는 소스에서 실제 {@code this.emit(...)} 호출되는 그대로 반영.
|
|
1011
|
+
* 런타임과 일치 보장이 목표이며, 새로 발견한 이벤트는 이 파일에 추가해야 함.
|
|
1012
|
+
*
|
|
1013
|
+
* <p><b>주의</b>: TalkFlowClient / ChatClient / WebRTCClient 는 각자 독립 이벤트 맵을 가짐.
|
|
1014
|
+
* 같은 이벤트 이름이라도 다른 클래스에서는 payload 가 다를 수 있음 (특히 ChatClient 의
|
|
1015
|
+
* {@code message} vs TalkFlowClient 의 {@code chatMessage}).</p>
|
|
1016
|
+
*/
|
|
1017
|
+
export interface ChatMessageReceivedPayload {
|
|
1018
|
+
roomId: string;
|
|
1019
|
+
message: ChatMessage;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
export interface MemberChangePayload {
|
|
1023
|
+
roomId: string;
|
|
1024
|
+
members: Array<{ userId: string; nickname?: string; profileUrl?: string | null }>;
|
|
1025
|
+
participantCount: number;
|
|
1026
|
+
timestamp?: number;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/** 방 영구 차단 사용자 정보 (getBannedMembers 응답). */
|
|
1030
|
+
export interface BannedMemberInfo {
|
|
1031
|
+
userId: string;
|
|
1032
|
+
nickname: string | null;
|
|
1033
|
+
profileUrl: string | null;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* 읽음 이벤트 payload — {@code ChatClient._handleReadEvent} 가 실제로 emit 하는 shape.
|
|
1038
|
+
* 배치/단일 이벤트 둘 다 동일 필드 이름 사용.
|
|
1039
|
+
*/
|
|
1040
|
+
export interface MessageReadEventPayload {
|
|
1041
|
+
roomId: string;
|
|
1042
|
+
messageId: string;
|
|
1043
|
+
/** 읽은 사용자. 배치 이벤트의 경우 이벤트 전체에 대한 공통 값. */
|
|
1044
|
+
userId?: string;
|
|
1045
|
+
/** 해당 메시지의 남은 미읽음 사용자 수. 런타임 필드명 {@code remainingUnreadCount}. */
|
|
1046
|
+
remainingUnreadCount?: number;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
export interface RetentionCleanupPayload {
|
|
1050
|
+
roomId: string;
|
|
1051
|
+
cutoffTime: number;
|
|
1052
|
+
eventType?: 'MESSAGE_RETENTION_CLEANUP';
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* TalkFlowClient 이벤트 맵 — 런타임 {@code TalkFlowClient.js} 의 {@code this.emit(...)} 호출 기반.
|
|
1057
|
+
*
|
|
1058
|
+
* <p><b>Connection 관련</b>: stateChange, connected, disconnected, reconnecting, connectionError, tokenSet, loggedOut</p>
|
|
1059
|
+
* <p><b>Chat 관련 (ChatClient 이벤트를 이 이름으로 re-emit)</b>: chatMessage, newChatMessage, messageUpdated, messageDeleted, reactionChanged, linkPreviewAttached, messageRead, typing, memberJoined, memberLeft, roomSubscribed, roomUnsubscribed, roomListSubscribed, roomListUnsubscribed, roomListUpdate, roomListMessage, roomListCreated, roomListJoined, roomListLeft, roomListSelfLeft, roomListRoomUpdated, retentionCleanup</p>
|
|
1060
|
+
* <p><b>Push 관련</b>: pushEnabled, pushFailed, pushNotification</p>
|
|
1061
|
+
* <p><b>WebRTC 관련</b>: localStreamStarted, localStreamStopped, remoteTrack, screenShareStarted, screenShareEnded, deviceChange, mediaStateChanged, callStarted, callEnded, callRequested, callAccepted, callRejected, callCancelled, callInvitation, callBusy, incomingCall, incomingCallWhileBusy, userJoined, userLeft, participantLeft, participantMediaState, peerConnected, peerDisconnected, peerClosed, webrtcError</p>
|
|
1062
|
+
*/
|
|
1063
|
+
export interface TalkFlowClientEvents {
|
|
1064
|
+
// Connection
|
|
1065
|
+
stateChange: { state: ConnectionStateValue; prevState: ConnectionStateValue };
|
|
1066
|
+
connected: unknown;
|
|
1067
|
+
disconnected: unknown;
|
|
1068
|
+
reconnecting: { attempt: number };
|
|
1069
|
+
connectionError: unknown;
|
|
1070
|
+
tokenSet: { userId: string };
|
|
1071
|
+
loggedOut: void;
|
|
1072
|
+
|
|
1073
|
+
// Chat (re-emit from ChatClient with prefixed names)
|
|
1074
|
+
chatMessage: ChatMessageReceivedPayload;
|
|
1075
|
+
newChatMessage: ChatMessageReceivedPayload;
|
|
1076
|
+
/** 메시지 수정 (eventType=MESSAGE_UPDATED) — 이름 변환 없이 그대로 re-emit. */
|
|
1077
|
+
messageUpdated: ChatMessageReceivedPayload;
|
|
1078
|
+
/** "모두에게 삭제" (eventType=MESSAGE_DELETED). */
|
|
1079
|
+
messageDeleted: ChatMessageReceivedPayload;
|
|
1080
|
+
/** 이모지 리액션 토글 (eventType=REACTION_CHANGED). */
|
|
1081
|
+
reactionChanged: ChatMessageReceivedPayload;
|
|
1082
|
+
/** OG 링크 미리보기 서버 비동기 첨부 완료 (eventType=LINK_PREVIEW_ATTACHED). */
|
|
1083
|
+
linkPreviewAttached: ChatMessageReceivedPayload;
|
|
1084
|
+
/** 다국어 번역 서버 비동기 첨부 완료 (eventType=MESSAGE_TRANSLATED) — message.translations 머지. */
|
|
1085
|
+
messageTranslated: ChatMessageReceivedPayload;
|
|
1086
|
+
messageRead: MessageReadEventPayload;
|
|
1087
|
+
typing: TypingEvent;
|
|
1088
|
+
memberJoined: MemberChangePayload;
|
|
1089
|
+
memberLeft: MemberChangePayload;
|
|
1090
|
+
roomSubscribed: { roomId: string };
|
|
1091
|
+
roomUnsubscribed: { roomId: string };
|
|
1092
|
+
roomListSubscribed: Record<string, unknown>;
|
|
1093
|
+
roomListUnsubscribed: Record<string, unknown>;
|
|
1094
|
+
roomListUpdate: RoomListUpdateEvent;
|
|
1095
|
+
roomListMessage: RoomListUpdateEvent;
|
|
1096
|
+
roomListMessageDeleted: RoomListUpdateEvent;
|
|
1097
|
+
/** 현재 lastMessage 가 편집됨 (MESSAGE_UPDATED) — preview 새 content 로 갱신. */
|
|
1098
|
+
roomListMessageUpdated: RoomListUpdateEvent;
|
|
1099
|
+
roomListCreated: RoomListUpdateEvent;
|
|
1100
|
+
roomListJoined: RoomListUpdateEvent;
|
|
1101
|
+
roomListLeft: RoomListUpdateEvent;
|
|
1102
|
+
roomListSelfLeft: RoomListUpdateEvent;
|
|
1103
|
+
/** 방장이 누군가를 단순 추방함 (ROOM_KICKED). members 에 추방된 사용자. */
|
|
1104
|
+
roomListKicked: RoomListUpdateEvent;
|
|
1105
|
+
/** 본인이 추방당함 — UI 에서 "추방됨" 표시 + 리스트 제거. */
|
|
1106
|
+
roomListSelfKicked: RoomListUpdateEvent;
|
|
1107
|
+
/** 방장이 누군가를 영구 차단함 (ROOM_BANNED). members 에 차단된 사용자. */
|
|
1108
|
+
roomListBanned: RoomListUpdateEvent;
|
|
1109
|
+
/** 본인이 영구 차단당함. */
|
|
1110
|
+
roomListSelfBanned: RoomListUpdateEvent;
|
|
1111
|
+
roomListRoomUpdated: RoomListUpdateEvent;
|
|
1112
|
+
retentionCleanup: RetentionCleanupPayload;
|
|
1113
|
+
|
|
1114
|
+
// Push
|
|
1115
|
+
pushEnabled: void;
|
|
1116
|
+
/**
|
|
1117
|
+
* 푸시 활성화 실패. {@code enablePushNotifications()} 가 어떤 사유든 실패할 때 emit.
|
|
1118
|
+
*
|
|
1119
|
+
* <p>호출자가 await 결과를 받지 않는 fire-and-forget 패턴 (예: {@code connect(jwt, { enablePush: true })})
|
|
1120
|
+
* 으로 호출했을 때, 실패 사실을 알리는 유일한 통보 채널이다.</p>
|
|
1121
|
+
*
|
|
1122
|
+
* <p><b>중요</b>: 자동 활성화 경로를 쓰는 경우 {@code connect()} <b>전에</b> 리스너를 등록해야
|
|
1123
|
+
* 즉시 emit 되는 실패 (예: PERMISSION_DENIED) 를 놓치지 않는다.</p>
|
|
1124
|
+
*/
|
|
1125
|
+
pushFailed: PushFailedPayload;
|
|
1126
|
+
pushNotification: PushNotificationPayload;
|
|
1127
|
+
|
|
1128
|
+
// WebRTC (re-emit from WebRTCClient)
|
|
1129
|
+
localStreamStarted: unknown;
|
|
1130
|
+
localStreamStopped: unknown;
|
|
1131
|
+
remoteTrack: unknown;
|
|
1132
|
+
screenShareStarted: unknown;
|
|
1133
|
+
screenShareEnded: unknown;
|
|
1134
|
+
deviceChange: unknown;
|
|
1135
|
+
mediaStateChanged: unknown;
|
|
1136
|
+
callStarted: unknown;
|
|
1137
|
+
callEnded: unknown;
|
|
1138
|
+
callRequested: unknown;
|
|
1139
|
+
callAccepted: unknown;
|
|
1140
|
+
callRejected: unknown;
|
|
1141
|
+
callCancelled: unknown;
|
|
1142
|
+
callInvitation: unknown;
|
|
1143
|
+
callBusy: unknown;
|
|
1144
|
+
incomingCall: unknown;
|
|
1145
|
+
incomingCallWhileBusy: unknown;
|
|
1146
|
+
userJoined: unknown;
|
|
1147
|
+
userLeft: unknown;
|
|
1148
|
+
participantLeft: unknown;
|
|
1149
|
+
participantMediaState: unknown;
|
|
1150
|
+
peerConnected: unknown;
|
|
1151
|
+
peerDisconnected: unknown;
|
|
1152
|
+
peerClosed: unknown;
|
|
1153
|
+
webrtcError: unknown;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* ChatClient 가 직접 emit 하는 이벤트 맵.
|
|
1158
|
+
* <p>TalkFlowClient 를 거치지 않고 {@code client.chat.on(...)} 로 직접 구독할 때 사용.
|
|
1159
|
+
* TalkFlowClient 이벤트와 <b>이름/payload 가 다를 수 있음</b> — 특히 {@code message} vs {@code chatMessage}.</p>
|
|
1160
|
+
*/
|
|
1161
|
+
export interface ChatClientEvents {
|
|
1162
|
+
message: ChatMessageReceivedPayload;
|
|
1163
|
+
newMessage: ChatMessageReceivedPayload;
|
|
1164
|
+
/** 메시지 수정 (eventType=MESSAGE_UPDATED). */
|
|
1165
|
+
messageUpdated: ChatMessageReceivedPayload;
|
|
1166
|
+
/** "모두에게 삭제" (eventType=MESSAGE_DELETED). */
|
|
1167
|
+
messageDeleted: ChatMessageReceivedPayload;
|
|
1168
|
+
/** 이모지 리액션 토글 (eventType=REACTION_CHANGED). */
|
|
1169
|
+
reactionChanged: ChatMessageReceivedPayload;
|
|
1170
|
+
/** OG 링크 미리보기 서버 비동기 첨부 완료 (eventType=LINK_PREVIEW_ATTACHED). */
|
|
1171
|
+
linkPreviewAttached: ChatMessageReceivedPayload;
|
|
1172
|
+
/** 다국어 번역 서버 비동기 첨부 완료 (eventType=MESSAGE_TRANSLATED) — message.translations 머지. */
|
|
1173
|
+
messageTranslated: ChatMessageReceivedPayload;
|
|
1174
|
+
roomSubscribed: { roomId: string };
|
|
1175
|
+
roomUnsubscribed: { roomId: string };
|
|
1176
|
+
memberJoined: MemberChangePayload;
|
|
1177
|
+
memberLeft: MemberChangePayload;
|
|
1178
|
+
roomListSubscribed: Record<string, unknown>;
|
|
1179
|
+
roomListUnsubscribed: Record<string, unknown>;
|
|
1180
|
+
roomListUpdate: RoomListUpdateEvent;
|
|
1181
|
+
roomListMessage: RoomListUpdateEvent;
|
|
1182
|
+
roomListMessageDeleted: RoomListUpdateEvent;
|
|
1183
|
+
/** 현재 lastMessage 가 편집됨 (MESSAGE_UPDATED) — preview 새 content 로 갱신. */
|
|
1184
|
+
roomListMessageUpdated: RoomListUpdateEvent;
|
|
1185
|
+
roomListCreated: RoomListUpdateEvent;
|
|
1186
|
+
roomListJoined: RoomListUpdateEvent;
|
|
1187
|
+
roomListLeft: RoomListUpdateEvent;
|
|
1188
|
+
roomListSelfLeft: RoomListUpdateEvent;
|
|
1189
|
+
/** 방장이 누군가를 단순 추방함 (ROOM_KICKED). */
|
|
1190
|
+
roomListKicked: RoomListUpdateEvent;
|
|
1191
|
+
/** 본인이 추방당함. */
|
|
1192
|
+
roomListSelfKicked: RoomListUpdateEvent;
|
|
1193
|
+
/** 방장이 누군가를 영구 차단함 (ROOM_BANNED). */
|
|
1194
|
+
roomListBanned: RoomListUpdateEvent;
|
|
1195
|
+
/** 본인이 영구 차단당함. */
|
|
1196
|
+
roomListSelfBanned: RoomListUpdateEvent;
|
|
1197
|
+
roomListRoomUpdated: RoomListUpdateEvent;
|
|
1198
|
+
retentionCleanup: RetentionCleanupPayload;
|
|
1199
|
+
messageRead: MessageReadEventPayload;
|
|
1200
|
+
typing: TypingEvent;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* WebRTCClient 가 직접 emit 하는 이벤트 맵.
|
|
1205
|
+
*/
|
|
1206
|
+
export interface WebRTCClientEvents {
|
|
1207
|
+
localStreamStarted: unknown;
|
|
1208
|
+
localStreamStopped: unknown;
|
|
1209
|
+
screenShareStarted: unknown;
|
|
1210
|
+
screenShareEnded: unknown;
|
|
1211
|
+
remoteTrack: unknown;
|
|
1212
|
+
peerConnected: unknown;
|
|
1213
|
+
peerDisconnected: unknown;
|
|
1214
|
+
peerClosed: unknown;
|
|
1215
|
+
error: unknown;
|
|
1216
|
+
callStarted: unknown;
|
|
1217
|
+
callEnded: unknown;
|
|
1218
|
+
callRequested: unknown;
|
|
1219
|
+
callAccepted: unknown;
|
|
1220
|
+
callRejected: unknown;
|
|
1221
|
+
callCancelled: unknown;
|
|
1222
|
+
callInvitation: unknown;
|
|
1223
|
+
callBusy: unknown;
|
|
1224
|
+
incomingCall: unknown;
|
|
1225
|
+
incomingCallWhileBusy: unknown;
|
|
1226
|
+
userJoined: unknown;
|
|
1227
|
+
userLeft: unknown;
|
|
1228
|
+
participantLeft: unknown;
|
|
1229
|
+
participantMediaState: unknown;
|
|
1230
|
+
mediaStateChanged: unknown;
|
|
1231
|
+
deviceChange: unknown;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// ============================================================================
|
|
1235
|
+
// Sub-clients
|
|
1236
|
+
// ============================================================================
|
|
1237
|
+
|
|
1238
|
+
export class ChatClient implements SdkEventEmitter<ChatClientEvents> {
|
|
1239
|
+
on<K extends keyof ChatClientEvents>(event: K, listener: (payload: ChatClientEvents[K]) => void): () => void;
|
|
1240
|
+
once<K extends keyof ChatClientEvents>(event: K, listener: (payload: ChatClientEvents[K]) => void): () => void;
|
|
1241
|
+
off<K extends keyof ChatClientEvents>(event: K, listener: (payload: ChatClientEvents[K]) => void): void;
|
|
1242
|
+
emit<K extends keyof ChatClientEvents>(event: K, ...payload: ChatClientEvents[K] extends void ? [] : [ChatClientEvents[K]]): void;
|
|
1243
|
+
removeAllListeners(event?: keyof ChatClientEvents): void;
|
|
1244
|
+
listenerCount(event: keyof ChatClientEvents): number;
|
|
1245
|
+
|
|
1246
|
+
getRooms(params?: CursorPageParams): Promise<CursorPageResponse<ChatRoomResponse>>;
|
|
1247
|
+
getRoom(roomId: string): Promise<ChatRoomResponse>;
|
|
1248
|
+
getRoomInfo(roomId: string): Promise<ChatRoomResponse>;
|
|
1249
|
+
/** 내 방 언어 설정 (다국어) — 'ko'/'en' 등 BCP-47 / 'OFF'(원문) / null·''(해제). 갱신된 방 반환. */
|
|
1250
|
+
setMyRoomLanguage(roomId: string, language: string | null): Promise<ChatRoomResponse>;
|
|
1251
|
+
createOneToOneRoom(friendId: string): Promise<ChatRoomResponse>;
|
|
1252
|
+
createGroupRoom(data: CreateGroupRoomData): Promise<ChatRoomResponse>;
|
|
1253
|
+
/**
|
|
1254
|
+
* 호출자 프로젝트에서 사용 가능한 어시스턴트 페르소나 리스트.
|
|
1255
|
+
* GLOBAL + PROJECT + OVERRIDE (OVERRIDE 가 GLOBAL 가림).
|
|
1256
|
+
*/
|
|
1257
|
+
getAssistants(): Promise<AssistantPersonaResponse[]>;
|
|
1258
|
+
/** 현재 키로 생성 가능한 roomAiType 조회 (discovery). */
|
|
1259
|
+
getRoomAiMeta(): Promise<RoomAiMetaResponse>;
|
|
1260
|
+
/** 어시스턴트 메시지 평점 등록 (1~5점). 어시 메시지({@code senderType === 'ASSISTANT'})에만 의미. */
|
|
1261
|
+
rateAssistantMessage(roomId: string, messageId: string, rating: number, comment?: string): Promise<void>;
|
|
1262
|
+
/** 어시스턴트로 대화 요약 (버튼 진입점, 동기). */
|
|
1263
|
+
summarizeWithAssistant(roomId: string, options: SummarizeToolRequest): Promise<AssistantToolResult>;
|
|
1264
|
+
/** 어시스턴트로 특정 메시지 번역 (버튼 진입점, 동기). */
|
|
1265
|
+
translateWithAssistant(roomId: string, options: TranslateToolRequest): Promise<AssistantToolResult>;
|
|
1266
|
+
/** 방 PM 프롬프트 레이어 조회 — PM_BACKSTAGE 방 전용, 참여자 전체. 미등록 시 404. */
|
|
1267
|
+
getRoomPmPrompt(roomId: string): Promise<PmPromptLayerResponse>;
|
|
1268
|
+
/** 방 PM 프롬프트 레이어 등록/수정 (upsert, 새 버전 자동 기록) — 방 주인(OWNER) 전용. content 1~2,000자. */
|
|
1269
|
+
upsertRoomPmPrompt(roomId: string, content: string): Promise<PmPromptLayerResponse>;
|
|
1270
|
+
/** 방 PM 프롬프트 레이어 합성 복귀 — 방 주인(OWNER) 전용. */
|
|
1271
|
+
activateRoomPmPrompt(roomId: string): Promise<PmPromptLayerResponse>;
|
|
1272
|
+
/** 방 PM 프롬프트 레이어 합성 제외 (soft delete — 본문/이력 보존) — 방 주인(OWNER) 전용. */
|
|
1273
|
+
deactivateRoomPmPrompt(roomId: string): Promise<PmPromptLayerResponse>;
|
|
1274
|
+
/** 방 PM 프롬프트 레이어 버전 이력 — 참여자 전체. */
|
|
1275
|
+
getRoomPmPromptVersions(roomId: string): Promise<PmPromptLayerVersionResponse[]>;
|
|
1276
|
+
/** 방 PM 프롬프트 레이어 버전 활성화 (active 포인터 이동 — 새 버전 미기록) — 방 주인(OWNER) 전용. */
|
|
1277
|
+
activateRoomPmPromptVersion(roomId: string, version: number): Promise<PmPromptLayerResponse>;
|
|
1278
|
+
/** PM 프롬프트 합성 미리보기 (전역+회사+방) — 참여자 전체. */
|
|
1279
|
+
previewRoomPmPrompt(roomId: string): Promise<PmPromptPreviewResponse>;
|
|
1280
|
+
joinGroupRoom(roomId: string, password?: string): Promise<ChatRoomResponse>;
|
|
1281
|
+
leaveRoom(roomId: string): Promise<void>;
|
|
1282
|
+
updateGroupRoom(roomId: string, data: UpdateGroupRoomData): Promise<ChatRoomResponse>;
|
|
1283
|
+
inviteToGroupRoom(roomId: string, userIds: string[]): Promise<ChatRoomResponse>;
|
|
1284
|
+
inviteToGroupRoom(roomId: string, data: InviteToRoomData): Promise<ChatRoomResponse>;
|
|
1285
|
+
|
|
1286
|
+
/** 단순 추방 — 재입장 가능. 방 관리자(OWNER/ADMIN) 만. */
|
|
1287
|
+
kickMember(roomId: string, userId: string): Promise<void>;
|
|
1288
|
+
/** 추방 + 영구 차단. 방 관리자(OWNER/ADMIN) 만. */
|
|
1289
|
+
banMember(roomId: string, userId: string): Promise<void>;
|
|
1290
|
+
/** 영구 차단 해제. 방 관리자(OWNER/ADMIN) 만. */
|
|
1291
|
+
unbanMember(roomId: string, userId: string): Promise<void>;
|
|
1292
|
+
/** 차단 사용자 목록 조회. 방장(ADMIN) 만. */
|
|
1293
|
+
getBannedMembers(roomId: string): Promise<BannedMemberInfo[]>;
|
|
1294
|
+
|
|
1295
|
+
getAvailableGroupRooms(params?: CursorPageParams): Promise<CursorPageResponse<ChatRoomResponse>>;
|
|
1296
|
+
getAllGroupRooms(params?: CursorPageParams): Promise<CursorPageResponse<ChatRoomResponse>>;
|
|
1297
|
+
|
|
1298
|
+
enterRoom(roomId: string): Promise<ChatRoomResponse>;
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* 메시지 목록 (커서 페이징) — 반환은 envelope 째({@code result.data.content}).
|
|
1302
|
+
* <p>타임스탬프(sentAt/editedAt/replyTo.sentAt)는 SDK 가 epoch millis(number)로 정규화한다 —
|
|
1303
|
+
* WebSocket 이벤트와 동일 형 (REST 원시 응답은 ISO 문자열).</p>
|
|
1304
|
+
*/
|
|
1305
|
+
getMessages(roomId: string, params?: CursorPageParams): Promise<ApiEnvelope<CursorPageResponse<ChatMessage>>>;
|
|
1306
|
+
fetchLinkPreview(url: string): Promise<FetchLinkPreviewResponse | null>;
|
|
1307
|
+
sendMessage(roomId: string, data: SendMessageData): Promise<SendMessageResult>;
|
|
1308
|
+
/** Optimistic UI 버전 — 응답 전 base/예측 messageIds 동기 반환. */
|
|
1309
|
+
sendMessageOptimistic(roomId: string, data: SendMessageData): OptimisticSendMessageHandle;
|
|
1310
|
+
sendTextMessage(roomId: string, message: string): Promise<SendMessageResult>;
|
|
1311
|
+
sendTextMessageOptimistic(roomId: string, message: string): OptimisticSendMessageHandle;
|
|
1312
|
+
sendReply(roomId: string, message: string, replyToMessageId: string): Promise<SendMessageResult>;
|
|
1313
|
+
sendReplyOptimistic(roomId: string, message: string, replyToMessageId: string): OptimisticSendMessageHandle;
|
|
1314
|
+
|
|
1315
|
+
uploadFile(roomId: string, file: File, options?: FileUploadOptions): Promise<FileMetaData>;
|
|
1316
|
+
sendFileMessage(roomId: string, files: File | File[], options?: SendFileMessageOptions): Promise<SendMessageResult>;
|
|
1317
|
+
/**
|
|
1318
|
+
* 파일 메시지 Optimistic UI 버전 — 업로드 시작 전에 messageIds 를 동기 반환.
|
|
1319
|
+
* 파일 검증 실패는 promise 가 만들어지기 전 동기 throw.
|
|
1320
|
+
*/
|
|
1321
|
+
sendFileMessageOptimistic(roomId: string, files: File | File[], options?: SendFileMessageOptions): OptimisticSendMessageHandle;
|
|
1322
|
+
|
|
1323
|
+
editMessage(roomId: string, messageId: string, message: string): Promise<void>;
|
|
1324
|
+
deleteMessage(roomId: string, messageId: string, deleteType?: DeleteType): Promise<void>;
|
|
1325
|
+
/** 메시지 읽음 처리 — sync. 연결 상태 or send 결과에 따라 boolean 반환. */
|
|
1326
|
+
markAsRead(roomId: string, messageId: string): boolean;
|
|
1327
|
+
pinMessage(roomId: string, messageId: string): Promise<void>;
|
|
1328
|
+
unpinMessage(roomId: string): Promise<void>;
|
|
1329
|
+
|
|
1330
|
+
/**
|
|
1331
|
+
* 이모지 리액션 토글.
|
|
1332
|
+
* 한 사용자는 메시지당 하나의 이모지만 보유. 같은 이모지 재호출 시 해제, 다른 이모지 시 교체.
|
|
1333
|
+
*/
|
|
1334
|
+
toggleReaction(roomId: string, messageId: string, emoji: string): Promise<void>;
|
|
1335
|
+
|
|
1336
|
+
/** STOMP 3 개 destination 구독 — 내부 connectionManager.subscribe 가 async. */
|
|
1337
|
+
subscribeRoom(roomId: string): Promise<void>;
|
|
1338
|
+
unsubscribeRoom(roomId: string): void;
|
|
1339
|
+
unsubscribeAllRooms(): void;
|
|
1340
|
+
setActiveRoom(roomId: string): void;
|
|
1341
|
+
clearActiveRoom(): void;
|
|
1342
|
+
getActiveRoom(): string | null;
|
|
1343
|
+
/** 방 리스트 전역 구독 — 내부 connectionManager.subscribe 가 async. */
|
|
1344
|
+
subscribeRoomList(): Promise<void>;
|
|
1345
|
+
unsubscribeRoomList(): void;
|
|
1346
|
+
isRoomListSubscribed(): boolean;
|
|
1347
|
+
getSubscribedRooms(): string[];
|
|
1348
|
+
isSubscribed(roomId: string): boolean;
|
|
1349
|
+
|
|
1350
|
+
startTyping(roomId: string): void;
|
|
1351
|
+
stopTyping(roomId: string): void;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
export class WebRTCClient implements SdkEventEmitter<WebRTCClientEvents> {
|
|
1355
|
+
on<K extends keyof WebRTCClientEvents>(event: K, listener: (payload: WebRTCClientEvents[K]) => void): () => void;
|
|
1356
|
+
once<K extends keyof WebRTCClientEvents>(event: K, listener: (payload: WebRTCClientEvents[K]) => void): () => void;
|
|
1357
|
+
off<K extends keyof WebRTCClientEvents>(event: K, listener: (payload: WebRTCClientEvents[K]) => void): void;
|
|
1358
|
+
emit<K extends keyof WebRTCClientEvents>(event: K, ...payload: WebRTCClientEvents[K] extends void ? [] : [WebRTCClientEvents[K]]): void;
|
|
1359
|
+
removeAllListeners(event?: keyof WebRTCClientEvents): void;
|
|
1360
|
+
listenerCount(event: keyof WebRTCClientEvents): number;
|
|
1361
|
+
|
|
1362
|
+
initializeIceServers(): Promise<RTCIceServer[]>;
|
|
1363
|
+
getTurnCredentials(): Promise<{ iceServers: RTCIceServer[] }>;
|
|
1364
|
+
|
|
1365
|
+
createCallRoom(data: unknown): Promise<unknown>;
|
|
1366
|
+
getCallRoom(roomId: string): Promise<unknown>;
|
|
1367
|
+
joinCallRoomApi(roomId: string): Promise<unknown>;
|
|
1368
|
+
leaveCallRoomApi(roomId: string): Promise<unknown>;
|
|
1369
|
+
|
|
1370
|
+
/** 수신 call 구독 활성화 — 내부 WebSocket subscribe 가 async. */
|
|
1371
|
+
enableIncomingCalls(): Promise<void>;
|
|
1372
|
+
disableIncomingCalls(): void;
|
|
1373
|
+
isIncomingCallsEnabled(): boolean;
|
|
1374
|
+
|
|
1375
|
+
startCall(options: { targetUserId?: string; roomId?: string; mediaConstraints?: MediaStreamConstraints }): Promise<void>;
|
|
1376
|
+
callUser(targetUserId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
|
|
1377
|
+
acceptCall(callerId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
|
|
1378
|
+
rejectCall(callerId: string): void;
|
|
1379
|
+
cancelCall(): void;
|
|
1380
|
+
endCall(): void;
|
|
1381
|
+
|
|
1382
|
+
toggleVideo(): boolean;
|
|
1383
|
+
toggleAudio(): boolean;
|
|
1384
|
+
setVideoEnabled(enabled: boolean): void;
|
|
1385
|
+
setAudioEnabled(enabled: boolean): void;
|
|
1386
|
+
startScreenShare(): Promise<MediaStream>;
|
|
1387
|
+
stopScreenShare(): void;
|
|
1388
|
+
getLocalStream(): MediaStream | null;
|
|
1389
|
+
|
|
1390
|
+
getDevices(): Promise<{ videoInputs: MediaDeviceInfo[]; audioInputs: MediaDeviceInfo[]; audioOutputs: MediaDeviceInfo[] }>;
|
|
1391
|
+
switchDevice(deviceId: string, kind: 'video' | 'audio' | 'audiooutput'): Promise<void>;
|
|
1392
|
+
startDeviceChangeDetection(): void;
|
|
1393
|
+
stopDeviceChangeDetection(): void;
|
|
1394
|
+
applyVideoConstraints(constraints: MediaTrackConstraints): Promise<void>;
|
|
1395
|
+
getVideoSettings(): MediaTrackSettings | null;
|
|
1396
|
+
getAudioSettings(): MediaTrackSettings | null;
|
|
1397
|
+
|
|
1398
|
+
isInCall(): boolean;
|
|
1399
|
+
getCurrentRoom(): string | null;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/** 푸시 디바이스 응답 (내 디바이스 목록). deviceToken 은 보안상 미포함. */
|
|
1403
|
+
export interface PushDeviceInfo {
|
|
1404
|
+
deviceId: string;
|
|
1405
|
+
deviceType: string;
|
|
1406
|
+
enabled: boolean;
|
|
1407
|
+
/** ISO-8601 문자열. */
|
|
1408
|
+
createdAt: string;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* 브라우저 푸시 권한 상태.
|
|
1413
|
+
*
|
|
1414
|
+
* <ul>
|
|
1415
|
+
* <li>{@code 'granted'} — 이미 허용. enable 호출 시 권한창 안 뜨고 토큰 발급 진행.</li>
|
|
1416
|
+
* <li>{@code 'default'} — 아직 묻지 않음. enable 호출 시 권한창 표시.</li>
|
|
1417
|
+
* <li>{@code 'denied'} — 거부됨. enable 호출 시 즉시 PERMISSION_DENIED 로 실패.</li>
|
|
1418
|
+
* <li>{@code 'unsupported'} — Notification / Service Worker 미지원 환경 (Safari iOS 등).</li>
|
|
1419
|
+
* </ul>
|
|
1420
|
+
*/
|
|
1421
|
+
export type PushPermissionState = 'granted' | 'denied' | 'default' | 'unsupported';
|
|
1422
|
+
|
|
1423
|
+
/**
|
|
1424
|
+
* 푸시 활성화 실패 사유 분류.
|
|
1425
|
+
*
|
|
1426
|
+
* <p>{@code 'NO_TOKEN'} 은 SDK 측 사전 가드 (JWT 미설정), 나머지는 PushManager 단에서 발생.</p>
|
|
1427
|
+
*/
|
|
1428
|
+
export type PushErrorReason =
|
|
1429
|
+
| 'UNSUPPORTED_BROWSER'
|
|
1430
|
+
| 'FIREBASE_NOT_INSTALLED'
|
|
1431
|
+
| 'SW_REGISTER_FAILED'
|
|
1432
|
+
| 'PERMISSION_DENIED'
|
|
1433
|
+
| 'TOKEN_FAILED'
|
|
1434
|
+
| 'SERVER_REGISTER_FAILED'
|
|
1435
|
+
| 'NO_TOKEN'
|
|
1436
|
+
| 'UNKNOWN';
|
|
1437
|
+
|
|
1438
|
+
/** {@code 'pushFailed'} 이벤트 페이로드. */
|
|
1439
|
+
export interface PushFailedPayload {
|
|
1440
|
+
reason: PushErrorReason;
|
|
1441
|
+
error: Error;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
/**
|
|
1445
|
+
* {@code enablePushNotifications()} 의 반환 결과.
|
|
1446
|
+
*
|
|
1447
|
+
* <p>throw 하지 않고 항상 이 객체를 Promise 로 반환한다 — 호출자가 {@code .ok} 로 분기.</p>
|
|
1448
|
+
*/
|
|
1449
|
+
export type EnablePushResult =
|
|
1450
|
+
| { ok: true; alreadyEnabled?: boolean }
|
|
1451
|
+
| { ok: false; reason: PushErrorReason; error: Error };
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* 푸시 활성화 과정에서 발생한 분류된 에러.
|
|
1455
|
+
*
|
|
1456
|
+
* <p>{@code error.code} 로 사유 분기 가능. 호출자는 {@code error.message} 문자열 매칭 대신
|
|
1457
|
+
* {@code error.code} 를 사용해야 안정적이다.</p>
|
|
1458
|
+
*/
|
|
1459
|
+
export class PushError extends Error {
|
|
1460
|
+
readonly name: 'PushError';
|
|
1461
|
+
readonly code: PushErrorReason;
|
|
1462
|
+
readonly cause?: unknown;
|
|
1463
|
+
constructor(code: PushErrorReason, message: string, cause?: unknown);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
/**
|
|
1467
|
+
* {@link PushErrorReason} 의 런타임 값 enum.
|
|
1468
|
+
*
|
|
1469
|
+
* <p>JS 사용자가 문자열 리터럴 대신 상수로 분기할 때 사용:
|
|
1470
|
+
* {@code if (result.reason === PushErrorCode.PERMISSION_DENIED) ...}</p>
|
|
1471
|
+
*
|
|
1472
|
+
* <p>{@code 'NO_TOKEN'}, {@code 'UNKNOWN'} 은 PushManager 외부에서 부여되는 코드라
|
|
1473
|
+
* 이 enum 에 포함되지 않는다 — {@link PushErrorReason} 타입에만 존재.</p>
|
|
1474
|
+
*/
|
|
1475
|
+
export const PushErrorCode: {
|
|
1476
|
+
readonly UNSUPPORTED_BROWSER: 'UNSUPPORTED_BROWSER';
|
|
1477
|
+
readonly FIREBASE_NOT_INSTALLED: 'FIREBASE_NOT_INSTALLED';
|
|
1478
|
+
readonly SW_REGISTER_FAILED: 'SW_REGISTER_FAILED';
|
|
1479
|
+
readonly PERMISSION_DENIED: 'PERMISSION_DENIED';
|
|
1480
|
+
readonly TOKEN_FAILED: 'TOKEN_FAILED';
|
|
1481
|
+
readonly SERVER_REGISTER_FAILED: 'SERVER_REGISTER_FAILED';
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1484
|
+
export class PushManager {
|
|
1485
|
+
consumePendingRoom(): Promise<string | null>;
|
|
1486
|
+
reset(): void;
|
|
1487
|
+
getToken(): string | null;
|
|
1488
|
+
isEnabled(): boolean;
|
|
1489
|
+
/** 내 디바이스 목록 조회 (UI 관리용). */
|
|
1490
|
+
getMyDevices(): Promise<PushDeviceInfo[]>;
|
|
1491
|
+
/** 특정 디바이스의 푸시 수신 여부 토글. */
|
|
1492
|
+
setDeviceEnabled(deviceId: string, enabled: boolean): Promise<void>;
|
|
1493
|
+
/** 현재 디바이스의 푸시 수신 여부 토글. */
|
|
1494
|
+
setCurrentDeviceEnabled(enabled: boolean): Promise<void>;
|
|
1495
|
+
static consumePendingRoom(projectId?: string): Promise<string | null>;
|
|
1496
|
+
/**
|
|
1497
|
+
* 현재 브라우저의 푸시 권한 상태. 인스턴스 없이도 호출 가능.
|
|
1498
|
+
*/
|
|
1499
|
+
static getPermissionState(): PushPermissionState;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
// ============================================================================
|
|
1503
|
+
// Main client
|
|
1504
|
+
// ============================================================================
|
|
1505
|
+
|
|
1506
|
+
export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEvents> {
|
|
1507
|
+
constructor(options: TalkFlowClientOptions);
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* 채팅 서브클라이언트.
|
|
1511
|
+
* <p><b>JWT 토큰이 설정된 이후에만 non-null</b>. 런타임에서 생성자 또는 {@code setToken()} /
|
|
1512
|
+
* {@code connect(jwt)} 호출 전까지 {@code null}. 접근 전에 반드시 null 체크 또는
|
|
1513
|
+
* {@code connect()} / {@code setToken()} 완료 후 사용.</p>
|
|
1514
|
+
*/
|
|
1515
|
+
readonly chat: ChatClient | null;
|
|
1516
|
+
|
|
1517
|
+
/**
|
|
1518
|
+
* WebRTC 서브클라이언트.
|
|
1519
|
+
* <p><b>JWT 토큰이 설정된 이후에만 non-null</b>. 동작 조건은 {@link #chat} 과 동일.</p>
|
|
1520
|
+
*/
|
|
1521
|
+
readonly webrtc: WebRTCClient | null;
|
|
1522
|
+
|
|
1523
|
+
/**
|
|
1524
|
+
* Push 서브클라이언트.
|
|
1525
|
+
* <p><b>{@link #enablePushNotifications} 호출 후에만 non-null</b>.
|
|
1526
|
+
* 기본 상태는 {@code null}. 런타임 속성 이름은 {@code pushManager} — TypeScript 에도 동일 이름.</p>
|
|
1527
|
+
*/
|
|
1528
|
+
readonly pushManager: PushManager | null;
|
|
1529
|
+
|
|
1530
|
+
/** JWT 에서 추출된 userId. 토큰이 없거나 파싱 실패 시 null. */
|
|
1531
|
+
readonly userId: string | null;
|
|
1532
|
+
|
|
1533
|
+
// EventEmitter
|
|
1534
|
+
on<K extends keyof TalkFlowClientEvents>(event: K, listener: (payload: TalkFlowClientEvents[K]) => void): () => void;
|
|
1535
|
+
once<K extends keyof TalkFlowClientEvents>(event: K, listener: (payload: TalkFlowClientEvents[K]) => void): () => void;
|
|
1536
|
+
off<K extends keyof TalkFlowClientEvents>(event: K, listener: (payload: TalkFlowClientEvents[K]) => void): void;
|
|
1537
|
+
emit<K extends keyof TalkFlowClientEvents>(event: K, ...payload: TalkFlowClientEvents[K] extends void ? [] : [TalkFlowClientEvents[K]]): void;
|
|
1538
|
+
removeAllListeners(event?: keyof TalkFlowClientEvents): void;
|
|
1539
|
+
listenerCount(event: keyof TalkFlowClientEvents): number;
|
|
1540
|
+
|
|
1541
|
+
// Connection lifecycle
|
|
1542
|
+
connect(jwt?: string, options?: ConnectOptions): Promise<void>;
|
|
1543
|
+
disconnect(): Promise<void>;
|
|
1544
|
+
logout(): Promise<void>;
|
|
1545
|
+
setToken(token: string): Promise<void>;
|
|
1546
|
+
updateToken(newToken: string): Promise<void>;
|
|
1547
|
+
isConnected(): boolean;
|
|
1548
|
+
getState(): ConnectionStateValue;
|
|
1549
|
+
hasToken(): boolean;
|
|
1550
|
+
|
|
1551
|
+
// 다국어 (Multilingual)
|
|
1552
|
+
/** 내 기본 선호 언어 설정 (전 방 공통) — BCP-47 / 'OFF'(원문) / null·''(미설정). 방별은 setMyRoomLanguage. */
|
|
1553
|
+
setPreferredLanguage(language: string | null): Promise<unknown>;
|
|
1554
|
+
/** 브라우저 locale 감지 — navigator.language 를 BCP-47 소문자로. 비브라우저/미가용 시 null. */
|
|
1555
|
+
static detectBrowserLanguage(): string | null;
|
|
1556
|
+
/** 표시 헬퍼 — message.translations[myLang] ?? message.content. */
|
|
1557
|
+
static displayText(message: ChatMessage, myLang?: string): string | null;
|
|
1558
|
+
|
|
1559
|
+
// Push
|
|
1560
|
+
/**
|
|
1561
|
+
* 웹 푸시 알림 활성화 — Firebase 초기화, 권한 요청, 토큰 발급, 서버 등록까지 일괄 수행.
|
|
1562
|
+
*
|
|
1563
|
+
* <p>throw 하지 않으며, 실패는 결과 객체로 반환된다. fire-and-forget (await 미사용) 으로 호출해도
|
|
1564
|
+
* unhandled rejection 이 발생하지 않는다. 호출 패턴 가이드는 README "푸시 알림" 섹션 참고.</p>
|
|
1565
|
+
*/
|
|
1566
|
+
enablePushNotifications(options?: EnablePushOptions): Promise<EnablePushResult>;
|
|
1567
|
+
/**
|
|
1568
|
+
* 호출 전에 사용 — 브라우저 권한 상태에 따라 UI 분기.
|
|
1569
|
+
* pushManager 인스턴스 없이도 호출 가능.
|
|
1570
|
+
*/
|
|
1571
|
+
getPushPermissionState(): PushPermissionState;
|
|
1572
|
+
/** SDK 내부 푸시 활성화 상태. 페이지 로드 직후엔 항상 false. 권한 상태는 {@link #getPushPermissionState}. */
|
|
1573
|
+
isPushEnabled(): boolean;
|
|
1574
|
+
/** 현재 발급된 FCM 토큰. enable 호출 후에만 non-null. */
|
|
1575
|
+
getPushToken(): string | null;
|
|
1576
|
+
/** 푸시 상태 초기화 — 로그아웃 또는 사용자 전환 시 호출. logout() 내부에서 자동 호출됨. */
|
|
1577
|
+
resetPush(): void;
|
|
1578
|
+
consumePendingRoom(): Promise<string | null>;
|
|
1579
|
+
/** 현재 디바이스의 푸시 수신 여부 토글. enable 선행 안 됐으면 undefined 반환. */
|
|
1580
|
+
setCurrentDeviceEnabled(enabled: boolean): Promise<void> | undefined;
|
|
1581
|
+
/** 특정 디바이스 토글 (관리 UI 용). enable 선행 안 됐으면 undefined 반환. */
|
|
1582
|
+
setDeviceEnabled(deviceId: string, enabled: boolean): Promise<void> | undefined;
|
|
1583
|
+
/** 내 푸시 디바이스 목록 조회. enable 선행 안 됐으면 undefined 반환. */
|
|
1584
|
+
getMyDevices(): Promise<PushDeviceInfo[]> | undefined;
|
|
1585
|
+
|
|
1586
|
+
// Chat delegations
|
|
1587
|
+
getRooms(params?: CursorPageParams): Promise<CursorPageResponse<ChatRoomResponse>>;
|
|
1588
|
+
getRoom(roomId: string): Promise<ChatRoomResponse>;
|
|
1589
|
+
getRoomInfo(roomId: string): Promise<ChatRoomResponse>;
|
|
1590
|
+
/** 내 방 언어 설정 (다국어) — 'ko'/'en' 등 BCP-47 / 'OFF'(원문) / null·''(해제). 갱신된 방 반환. */
|
|
1591
|
+
setMyRoomLanguage(roomId: string, language: string | null): Promise<ChatRoomResponse>;
|
|
1592
|
+
createOneToOneRoom(friendId: string): Promise<ChatRoomResponse>;
|
|
1593
|
+
createGroupRoom(data: CreateGroupRoomData): Promise<ChatRoomResponse>;
|
|
1594
|
+
/**
|
|
1595
|
+
* 호출자 프로젝트에서 사용 가능한 어시스턴트 페르소나 리스트.
|
|
1596
|
+
* GLOBAL + PROJECT + OVERRIDE (OVERRIDE 가 GLOBAL 가림).
|
|
1597
|
+
*/
|
|
1598
|
+
getAssistants(): Promise<AssistantPersonaResponse[]>;
|
|
1599
|
+
/** 현재 키로 생성 가능한 roomAiType 조회 (discovery). */
|
|
1600
|
+
getRoomAiMeta(): Promise<RoomAiMetaResponse>;
|
|
1601
|
+
/** 어시스턴트 메시지 평점 등록 (1~5점). 어시 메시지({@code senderType === 'ASSISTANT'})에만 의미. */
|
|
1602
|
+
rateAssistantMessage(roomId: string, messageId: string, rating: number, comment?: string): Promise<void>;
|
|
1603
|
+
/** 어시스턴트로 대화 요약 (버튼 진입점, 동기). */
|
|
1604
|
+
summarizeWithAssistant(roomId: string, options: SummarizeToolRequest): Promise<AssistantToolResult>;
|
|
1605
|
+
/** 어시스턴트로 특정 메시지 번역 (버튼 진입점, 동기). */
|
|
1606
|
+
translateWithAssistant(roomId: string, options: TranslateToolRequest): Promise<AssistantToolResult>;
|
|
1607
|
+
/** 방 PM 프롬프트 레이어 조회 — PM_BACKSTAGE 방 전용, 참여자 전체. 미등록 시 404. */
|
|
1608
|
+
getRoomPmPrompt(roomId: string): Promise<PmPromptLayerResponse>;
|
|
1609
|
+
/** 방 PM 프롬프트 레이어 등록/수정 (upsert, 새 버전 자동 기록) — 방 주인(OWNER) 전용. content 1~2,000자. */
|
|
1610
|
+
upsertRoomPmPrompt(roomId: string, content: string): Promise<PmPromptLayerResponse>;
|
|
1611
|
+
/** 방 PM 프롬프트 레이어 합성 복귀 — 방 주인(OWNER) 전용. */
|
|
1612
|
+
activateRoomPmPrompt(roomId: string): Promise<PmPromptLayerResponse>;
|
|
1613
|
+
/** 방 PM 프롬프트 레이어 합성 제외 (soft delete — 본문/이력 보존) — 방 주인(OWNER) 전용. */
|
|
1614
|
+
deactivateRoomPmPrompt(roomId: string): Promise<PmPromptLayerResponse>;
|
|
1615
|
+
/** 방 PM 프롬프트 레이어 버전 이력 — 참여자 전체. */
|
|
1616
|
+
getRoomPmPromptVersions(roomId: string): Promise<PmPromptLayerVersionResponse[]>;
|
|
1617
|
+
/** 방 PM 프롬프트 레이어 버전 활성화 (active 포인터 이동 — 새 버전 미기록) — 방 주인(OWNER) 전용. */
|
|
1618
|
+
activateRoomPmPromptVersion(roomId: string, version: number): Promise<PmPromptLayerResponse>;
|
|
1619
|
+
/** PM 프롬프트 합성 미리보기 (전역+회사+방) — 참여자 전체. */
|
|
1620
|
+
previewRoomPmPrompt(roomId: string): Promise<PmPromptPreviewResponse>;
|
|
1621
|
+
joinGroupRoom(roomId: string, password?: string): Promise<ChatRoomResponse>;
|
|
1622
|
+
leaveRoom(roomId: string): Promise<void>;
|
|
1623
|
+
updateGroupRoom(roomId: string, data: UpdateGroupRoomData): Promise<ChatRoomResponse>;
|
|
1624
|
+
inviteToGroupRoom(roomId: string, userIds: string[]): Promise<ChatRoomResponse>;
|
|
1625
|
+
inviteToGroupRoom(roomId: string, data: InviteToRoomData): Promise<ChatRoomResponse>;
|
|
1626
|
+
|
|
1627
|
+
/** 단순 추방 — 재입장 가능. 방 관리자(OWNER/ADMIN) 만. */
|
|
1628
|
+
kickMember(roomId: string, userId: string): Promise<void>;
|
|
1629
|
+
/** 추방 + 영구 차단. 방 관리자(OWNER/ADMIN) 만. */
|
|
1630
|
+
banMember(roomId: string, userId: string): Promise<void>;
|
|
1631
|
+
/** 영구 차단 해제. 방 관리자(OWNER/ADMIN) 만. */
|
|
1632
|
+
unbanMember(roomId: string, userId: string): Promise<void>;
|
|
1633
|
+
/** 차단 사용자 목록 조회. 방장(ADMIN) 만. */
|
|
1634
|
+
getBannedMembers(roomId: string): Promise<BannedMemberInfo[]>;
|
|
1635
|
+
|
|
1636
|
+
getAvailableGroupRooms(params?: CursorPageParams): Promise<CursorPageResponse<ChatRoomResponse>>;
|
|
1637
|
+
getAllGroupRooms(params?: CursorPageParams): Promise<CursorPageResponse<ChatRoomResponse>>;
|
|
1638
|
+
|
|
1639
|
+
enterRoom(roomId: string): Promise<ChatRoomResponse>;
|
|
1640
|
+
|
|
1641
|
+
/**
|
|
1642
|
+
* 메시지 목록 (커서 페이징) — 반환은 envelope 째({@code result.data.content}).
|
|
1643
|
+
* <p>타임스탬프(sentAt/editedAt/replyTo.sentAt)는 SDK 가 epoch millis(number)로 정규화한다 —
|
|
1644
|
+
* WebSocket 이벤트와 동일 형 (REST 원시 응답은 ISO 문자열).</p>
|
|
1645
|
+
*/
|
|
1646
|
+
getMessages(roomId: string, params?: CursorPageParams): Promise<ApiEnvelope<CursorPageResponse<ChatMessage>>>;
|
|
1647
|
+
fetchLinkPreview(url: string): Promise<FetchLinkPreviewResponse | null>;
|
|
1648
|
+
sendMessage(roomId: string, data: SendMessageData): Promise<SendMessageResult>;
|
|
1649
|
+
/** Optimistic UI 버전 — 응답 전 base/예측 messageIds 동기 반환. */
|
|
1650
|
+
sendMessageOptimistic(roomId: string, data: SendMessageData): OptimisticSendMessageHandle;
|
|
1651
|
+
sendTextMessage(roomId: string, message: string): Promise<SendMessageResult>;
|
|
1652
|
+
sendTextMessageOptimistic(roomId: string, message: string): OptimisticSendMessageHandle;
|
|
1653
|
+
sendReply(roomId: string, message: string, replyToMessageId: string): Promise<SendMessageResult>;
|
|
1654
|
+
sendReplyOptimistic(roomId: string, message: string, replyToMessageId: string): OptimisticSendMessageHandle;
|
|
1655
|
+
|
|
1656
|
+
uploadFile(roomId: string, file: File, options?: FileUploadOptions): Promise<FileMetaData>;
|
|
1657
|
+
sendFileMessage(roomId: string, files: File | File[], options?: SendFileMessageOptions): Promise<SendMessageResult>;
|
|
1658
|
+
/**
|
|
1659
|
+
* 파일 메시지 Optimistic UI 버전 — 업로드 시작 전에 messageIds 를 동기 반환.
|
|
1660
|
+
* 파일 검증 실패는 promise 가 만들어지기 전 동기 throw.
|
|
1661
|
+
*/
|
|
1662
|
+
sendFileMessageOptimistic(roomId: string, files: File | File[], options?: SendFileMessageOptions): OptimisticSendMessageHandle;
|
|
1663
|
+
|
|
1664
|
+
editMessage(roomId: string, messageId: string, message: string): Promise<void>;
|
|
1665
|
+
deleteMessage(roomId: string, messageId: string, deleteType?: DeleteType): Promise<void>;
|
|
1666
|
+
/** 메시지 읽음 처리 — sync. 연결 상태 or send 결과에 따라 boolean 반환. */
|
|
1667
|
+
markAsRead(roomId: string, messageId: string): boolean;
|
|
1668
|
+
pinMessage(roomId: string, messageId: string): Promise<void>;
|
|
1669
|
+
unpinMessage(roomId: string): Promise<void>;
|
|
1670
|
+
|
|
1671
|
+
toggleReaction(roomId: string, messageId: string, emoji: string): Promise<void>;
|
|
1672
|
+
|
|
1673
|
+
/** STOMP 3 개 destination 구독 — 내부 connectionManager.subscribe 가 async. */
|
|
1674
|
+
subscribeRoom(roomId: string): Promise<void>;
|
|
1675
|
+
unsubscribeRoom(roomId: string): void;
|
|
1676
|
+
unsubscribeAllRooms(): void;
|
|
1677
|
+
setActiveRoom(roomId: string): void;
|
|
1678
|
+
clearActiveRoom(): void;
|
|
1679
|
+
getActiveRoom(): string | null;
|
|
1680
|
+
/** 방 리스트 전역 구독 — 내부 connectionManager.subscribe 가 async. */
|
|
1681
|
+
subscribeRoomList(): Promise<void>;
|
|
1682
|
+
unsubscribeRoomList(): void;
|
|
1683
|
+
isRoomListSubscribed(): boolean;
|
|
1684
|
+
getSubscribedRooms(): string[];
|
|
1685
|
+
isSubscribed(roomId: string): boolean;
|
|
1686
|
+
|
|
1687
|
+
startTyping(roomId: string): void;
|
|
1688
|
+
stopTyping(roomId: string): void;
|
|
1689
|
+
|
|
1690
|
+
// WebRTC delegations
|
|
1691
|
+
initializeIceServers(): Promise<RTCIceServer[]>;
|
|
1692
|
+
getTurnCredentials(): Promise<{ iceServers: RTCIceServer[] }>;
|
|
1693
|
+
|
|
1694
|
+
createCallRoom(data: unknown): Promise<unknown>;
|
|
1695
|
+
getCallRoom(roomId: string): Promise<unknown>;
|
|
1696
|
+
joinCallRoomApi(roomId: string): Promise<unknown>;
|
|
1697
|
+
leaveCallRoomApi(roomId: string): Promise<unknown>;
|
|
1698
|
+
|
|
1699
|
+
/** 수신 call 구독 활성화 — 내부 WebSocket subscribe 가 async. */
|
|
1700
|
+
enableIncomingCalls(): Promise<void>;
|
|
1701
|
+
disableIncomingCalls(): void;
|
|
1702
|
+
isIncomingCallsEnabled(): boolean;
|
|
1703
|
+
|
|
1704
|
+
startCall(options: { targetUserId?: string; roomId?: string; mediaConstraints?: MediaStreamConstraints }): Promise<void>;
|
|
1705
|
+
callUser(targetUserId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
|
|
1706
|
+
acceptCall(callerId: string, mediaConstraints?: MediaStreamConstraints): Promise<void>;
|
|
1707
|
+
rejectCall(callerId: string): void;
|
|
1708
|
+
cancelCall(): void;
|
|
1709
|
+
endCall(): void;
|
|
1710
|
+
|
|
1711
|
+
toggleVideo(): boolean;
|
|
1712
|
+
toggleAudio(): boolean;
|
|
1713
|
+
setVideoEnabled(enabled: boolean): void;
|
|
1714
|
+
setAudioEnabled(enabled: boolean): void;
|
|
1715
|
+
startScreenShare(): Promise<MediaStream>;
|
|
1716
|
+
stopScreenShare(): void;
|
|
1717
|
+
getLocalStream(): MediaStream | null;
|
|
1718
|
+
|
|
1719
|
+
getDevices(): Promise<{ videoInputs: MediaDeviceInfo[]; audioInputs: MediaDeviceInfo[]; audioOutputs: MediaDeviceInfo[] }>;
|
|
1720
|
+
switchDevice(deviceId: string, kind: 'video' | 'audio' | 'audiooutput'): Promise<void>;
|
|
1721
|
+
startDeviceChangeDetection(): void;
|
|
1722
|
+
stopDeviceChangeDetection(): void;
|
|
1723
|
+
applyVideoConstraints(constraints: MediaTrackConstraints): Promise<void>;
|
|
1724
|
+
getVideoSettings(): MediaTrackSettings | null;
|
|
1725
|
+
getAudioSettings(): MediaTrackSettings | null;
|
|
1726
|
+
|
|
1727
|
+
isInCall(): boolean;
|
|
1728
|
+
getCurrentRoom(): string | null;
|
|
1729
|
+
|
|
1730
|
+
// Cleanup
|
|
1731
|
+
destroy(): Promise<void>;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// ============================================================================
|
|
1735
|
+
// Utility functions (exported)
|
|
1736
|
+
// ============================================================================
|
|
1737
|
+
|
|
1738
|
+
export function validateAndParseJWT(
|
|
1739
|
+
token: string,
|
|
1740
|
+
options?: { bufferSeconds?: number; validateExpiry?: boolean }
|
|
1741
|
+
): { userId: string; payload: Record<string, unknown> };
|
|
1742
|
+
export function extractUserIdFromJWT(token: string): string | null;
|
|
1743
|
+
export function isJWTExpired(token: string): boolean;
|
|
1744
|
+
export function getJWTRemainingTime(token: string): number;
|
|
1745
|
+
export function decodeJWTPayload(token: string): Record<string, unknown> | null;
|
|
1746
|
+
|
|
1747
|
+
// ============================================================================
|
|
1748
|
+
// EventEmitter / Logger (utility classes)
|
|
1749
|
+
// ============================================================================
|
|
1750
|
+
|
|
1751
|
+
export class EventEmitter<EventMap = Record<string, unknown>> implements SdkEventEmitter<EventMap> {
|
|
1752
|
+
on<K extends keyof EventMap>(event: K, listener: (payload: EventMap[K]) => void): () => void;
|
|
1753
|
+
once<K extends keyof EventMap>(event: K, listener: (payload: EventMap[K]) => void): () => void;
|
|
1754
|
+
off<K extends keyof EventMap>(event: K, listener: (payload: EventMap[K]) => void): void;
|
|
1755
|
+
emit<K extends keyof EventMap>(event: K, ...payload: EventMap[K] extends void ? [] : [EventMap[K]]): void;
|
|
1756
|
+
removeAllListeners(event?: keyof EventMap): void;
|
|
1757
|
+
listenerCount(event: keyof EventMap): number;
|
|
1758
|
+
eventNames(): Array<keyof EventMap>;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
export class Logger {
|
|
1762
|
+
constructor(level: LogLevelValue, tag: string);
|
|
1763
|
+
debug(...args: unknown[]): void;
|
|
1764
|
+
info(...args: unknown[]): void;
|
|
1765
|
+
warn(...args: unknown[]): void;
|
|
1766
|
+
error(...args: unknown[]): void;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
export class ConnectionManager {}
|
|
1770
|
+
export class MediaStreamManager {}
|
|
1771
|
+
export class PeerConnectionManager {}
|