@vibexnpm/talkx 5.6.0 → 6.1.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.ts +49 -3
- package/dist/talkflow-sdk.esm.js +1 -1
- package/dist/talkflow-sdk.esm.js.map +1 -1
- package/dist/talkflow-sdk.standalone.js +1 -1
- package/dist/talkflow-sdk.standalone.js.map +1 -1
- package/dist/talkflow-sdk.umd.js +1 -1
- package/dist/talkflow-sdk.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/TalkFlowClient.js +38 -93
- package/src/chat/ChatClient.js +1 -1
- package/src/index.js +11 -19
- package/src/talkflow/session.js +3 -3
- package/types/index.d.ts +49 -3
package/package.json
CHANGED
package/src/TalkFlowClient.js
CHANGED
|
@@ -35,15 +35,11 @@ class TalkFlowClient extends EventEmitter {
|
|
|
35
35
|
* @param {number} [options.logLevel=LogLevel.WARN] - 로그 레벨
|
|
36
36
|
*
|
|
37
37
|
* @example
|
|
38
|
-
* // 1단계:
|
|
39
|
-
* //
|
|
40
|
-
* //
|
|
41
|
-
* // Body: { userId: 'user-123', nickname: '홍길동' }
|
|
42
|
-
* // Response: { accessToken, refreshToken }
|
|
38
|
+
* // 1단계: 로그인 — 클라이언트 생성 «전» 에 부른다. ck/projectId/JWT 를 한 번에 받는다.
|
|
39
|
+
* // const { clientKey, projectId, accessToken } = await TalkFlowClient.loginUser({
|
|
40
|
+
* // serverUrl, email, password });
|
|
43
41
|
*
|
|
44
|
-
* // 2단계:
|
|
45
|
-
*
|
|
46
|
-
* // 3단계: 브라우저에서 SDK 초기화
|
|
42
|
+
* // 2단계: 받은 값으로 SDK 초기화
|
|
47
43
|
* const client = new TalkFlowClient({
|
|
48
44
|
* apiKey: 'CLIENT_KEY', // 브라우저에 노출되는 Client Key
|
|
49
45
|
* projectId: 'your-project-id',
|
|
@@ -174,24 +170,13 @@ class TalkFlowClient extends EventEmitter {
|
|
|
174
170
|
throw new Error('projectId is required');
|
|
175
171
|
}
|
|
176
172
|
|
|
177
|
-
// Server Key (sk-)
|
|
178
|
-
//
|
|
179
|
-
// - staging/production: 경고 + 강력 안내
|
|
173
|
+
// Server Key (sk-) 는 SDK 가 쓸 일이 없다 — 유일한 용도였던 registerUser(/users/auth) 가
|
|
174
|
+
// 사라졌다. 그래도 들어오면 잘못된 키를 넣은 것이므로 알린다.
|
|
180
175
|
if (options.apiKey.startsWith('sk-')) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
'프로덕션 배포 전에 반드시 Client Key (ck-) 로 교체하세요.'
|
|
186
|
-
);
|
|
187
|
-
} else {
|
|
188
|
-
console.error(
|
|
189
|
-
'🚨 [TalkFlow] Server Key (sk-) 가 비개발 환경 (' + env + ') 에서 감지되었습니다. ' +
|
|
190
|
-
'보안 위험: Server Key 를 브라우저에 포함하면 공격자가 임의 사용자로 JWT 를 발급받을 수 있습니다. ' +
|
|
191
|
-
'반드시 Client Key (ck-) 로 교체하세요. ' +
|
|
192
|
-
'서버에서도 프로덕션 모드에서 Server Key 의 브라우저 호출을 차단합니다.'
|
|
193
|
-
);
|
|
194
|
-
}
|
|
176
|
+
console.error(
|
|
177
|
+
'🚨 [TalkFlow] Server Key (sk-) 로 SDK 를 초기화했습니다. ' +
|
|
178
|
+
'SDK 는 Client Key (ck-) 만 사용합니다 — loginUser() 응답의 clientKey 를 그대로 쓰세요.'
|
|
179
|
+
);
|
|
195
180
|
}
|
|
196
181
|
}
|
|
197
182
|
|
|
@@ -240,7 +225,7 @@ class TalkFlowClient extends EventEmitter {
|
|
|
240
225
|
/**
|
|
241
226
|
* 회원 로그인 — 이메일/비밀번호로 채팅 접속 일체를 받는다. **클라이언트 생성 전에 호출한다.**
|
|
242
227
|
*
|
|
243
|
-
* <p>
|
|
228
|
+
* <p>Server Key 도 고객사 BFF 도 필요 없다. 응답의
|
|
244
229
|
* {@code clientKey}/{@code projectId}/{@code accessToken} 이 그대로 SDK 초기화 입력이다.</p>
|
|
245
230
|
*
|
|
246
231
|
* <p><b>회사 선택 단계가 없다</b>: 로그인 키가 전역 유일이라 이메일 하나로 회원과 회사가
|
|
@@ -357,64 +342,6 @@ class TalkFlowClient extends EventEmitter {
|
|
|
357
342
|
return this.apiClient.put('/api/v1/me/password', { currentPassword, newPassword });
|
|
358
343
|
}
|
|
359
344
|
|
|
360
|
-
/**
|
|
361
|
-
* 사용자 인증 (등록/로그인).
|
|
362
|
-
* 인증 성공 시 반환된 JWT 토큰을 자동으로 설정합니다.
|
|
363
|
-
*
|
|
364
|
-
* <h3>⚠️ DEPRECATED — {@link TalkFlowClient.loginUser} 로 대체</h3>
|
|
365
|
-
* <p>고객사 앱이 자기 회원 ID 를 들고 오던 경로다. 서버에서 {@code POST /api/v1/users/auth} 가
|
|
366
|
-
* 제거될 예정이며 그때 이 메서드도 함께 사라진다. 신규 코드는 {@code loginUser} 를 쓴다.</p>
|
|
367
|
-
*
|
|
368
|
-
* <h3>⚠️ Server API Key 전용</h3>
|
|
369
|
-
* <p>이 메서드는 JWT 를 발급하는 엔드포인트 ({@code POST /api/v1/users/auth}) 를 호출합니다.
|
|
370
|
-
* <strong>프로덕션 환경에서는 Client API Key 로 이 메서드를 호출하면 서버가 403 을 반환합니다.</strong></p>
|
|
371
|
-
*
|
|
372
|
-
* <ul>
|
|
373
|
-
* <li><strong>개발/프로토타입</strong>: Server Key 로 SDK 를 초기화한 뒤 이 메서드를 사용하면 빠르게 테스트 가능</li>
|
|
374
|
-
* <li><strong>프로덕션</strong>: 고객사 backend 에서 Server Key 로 직접 {@code POST /api/v1/users/auth} 를 호출 →
|
|
375
|
-
* JWT 를 받아 SDK 에 {@code setToken(jwt)} 으로 전달. 이 메서드를 브라우저에서 쓰지 마세요.</li>
|
|
376
|
-
* </ul>
|
|
377
|
-
*
|
|
378
|
-
* <p>자세한 내용은 README 의 "프로덕션 인증 플로우" 섹션 참고.</p>
|
|
379
|
-
*
|
|
380
|
-
* @param {Object} userData
|
|
381
|
-
* @param {string} userData.userId - 사용자 ID (필수, 고객사 회원 ID)
|
|
382
|
-
* @param {string} userData.nickname - 닉네임 (필수, max 100자)
|
|
383
|
-
* @param {string} [userData.profileImageUrl] - 프로필 이미지 URL (max 500자)
|
|
384
|
-
* @param {Object} [userData.metadata] - 추가 메타데이터
|
|
385
|
-
* @returns {Promise<Object>} 인증 결과 (토큰 포함)
|
|
386
|
-
*
|
|
387
|
-
* @example
|
|
388
|
-
* // 개발 환경 — Server Key 로 빠른 테스트
|
|
389
|
-
* const client = new TalkFlowClient({
|
|
390
|
-
* apiKey: 'SERVER_KEY',
|
|
391
|
-
* projectId: 'your-project-id',
|
|
392
|
-
* env: 'development'
|
|
393
|
-
* });
|
|
394
|
-
* await client.registerUser({ userId: 'user-123', nickname: '홍길동' });
|
|
395
|
-
* await client.connect();
|
|
396
|
-
*/
|
|
397
|
-
async registerUser(userData) {
|
|
398
|
-
// 다국어: navigator.language 자동 시드 — 호출자가 preferredLanguage 를 명시하지 않은 경우에만.
|
|
399
|
-
// 서버 /users/auth 는 seed-if-absent (기존 값 비클로버) 라 매 인증마다 덮어쓰지 않아 안전.
|
|
400
|
-
const payload = { ...userData };
|
|
401
|
-
if (payload.preferredLanguage === undefined) {
|
|
402
|
-
const detected = TalkFlowClient.detectBrowserLanguage();
|
|
403
|
-
if (detected) {
|
|
404
|
-
payload.preferredLanguage = detected;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const response = await this.apiClient.post('/api/v1/users/auth', payload);
|
|
409
|
-
|
|
410
|
-
if (response.data && response.data.accessToken) {
|
|
411
|
-
await this.setToken(response.data.accessToken);
|
|
412
|
-
this.logger.info('User authenticated, token auto-set');
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return response;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
345
|
/**
|
|
419
346
|
* 사용자 정보 수정.
|
|
420
347
|
*
|
|
@@ -468,7 +395,9 @@ class TalkFlowClient extends EventEmitter {
|
|
|
468
395
|
* 지역 변형을 그대로 보내면 명시 요청({@code lang: 'en'})과 키가 분열되어 같은 언어 이중 생성·조회 miss 가
|
|
469
396
|
* 발생한다. 서버 {@code LanguageCode} 도 동일 규칙으로 축약하므로 어느 쪽으로 들어와도 같은 값이 된다.</p>
|
|
470
397
|
*
|
|
471
|
-
* <p
|
|
398
|
+
* <p>예전에는 {@code registerUser} 가 이 값을 자동으로 실어 보냈는데 그 경로가 사라졌다.
|
|
399
|
+
* 지금은 호출자가 직접 감지해 {@code PUT /api/v1/users/update} 의 {@code preferredLanguage}
|
|
400
|
+
* 로 넣어야 한다 — 넣지 않으면 사용자 기본 언어가 미설정으로 남아 자동 번역이 서버 기본값을 쓴다.</p>
|
|
472
401
|
* @returns {string|null}
|
|
473
402
|
*/
|
|
474
403
|
static detectBrowserLanguage() {
|
|
@@ -522,16 +451,32 @@ class TalkFlowClient extends EventEmitter {
|
|
|
522
451
|
}
|
|
523
452
|
|
|
524
453
|
/**
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
454
|
+
* 회사 조직도 + 인원 — <b>방 만들 때 «조직으로 초대»</b> 가 쓴다.
|
|
455
|
+
*
|
|
456
|
+
* <p>조직을 고르면 <b>하위 조직 인원까지</b> 초대된다. 그래서 각 노드의 {@code memberCount} 는
|
|
457
|
+
* 자손을 포함한 누적이고, {@code members} 는 그 노드 <b>직속</b>만이다 — 누적 목록이 필요하면
|
|
458
|
+
* {@code children} 을 따라 내려가며 모은다(상위마다 중복해 실으면 응답이 제곱으로 커진다).</p>
|
|
459
|
+
*
|
|
460
|
+
* <p>부서가 없는 회원은 {@code unassignedMembers} 에 있다. 퇴사·차단 회원은 어디에도 없다.</p>
|
|
461
|
+
*
|
|
462
|
+
* @returns {Promise<Object>} `{tree, unassignedMembers, totalMemberCount, membersOmitted}`
|
|
463
|
+
*
|
|
464
|
+
* @example
|
|
465
|
+
* const dir = await client.getCompanyDirectory();
|
|
466
|
+
* // 조직 하나를 고르면 그 자손까지 전부 모은다
|
|
467
|
+
* const collect = (node) => [...node.members, ...node.children.flatMap(collect)];
|
|
528
468
|
*/
|
|
529
|
-
async
|
|
530
|
-
|
|
469
|
+
async getCompanyDirectory() {
|
|
470
|
+
this._checkToken();
|
|
471
|
+
return this.apiClient.get('/api/v1/company/directory');
|
|
531
472
|
}
|
|
532
473
|
|
|
533
474
|
/**
|
|
534
475
|
* 사용자 목록 조회
|
|
476
|
+
*
|
|
477
|
+
* <p>응답 행에 {@code phone}/{@code orgNodeId}/{@code orgName} 이 포함된다 — 부서명은
|
|
478
|
+
* 저장값이 아니라 조회 시점에 조직 마스터에서 해소한 값이다.</p>
|
|
479
|
+
*
|
|
535
480
|
* @param {Object} [params]
|
|
536
481
|
* @param {number} [params.size=50] - 페이지 크기 (1-100)
|
|
537
482
|
* @param {string} [params.lastId] - 마지막 사용자 ID
|
|
@@ -565,9 +510,9 @@ class TalkFlowClient extends EventEmitter {
|
|
|
565
510
|
_checkToken() {
|
|
566
511
|
if (!this.options.jwtToken) {
|
|
567
512
|
throw new Error(
|
|
568
|
-
'JWT token is required for this operation.
|
|
569
|
-
'(
|
|
570
|
-
'See README "
|
|
513
|
+
'JWT token is required for this operation. Sign in with ' +
|
|
514
|
+
'TalkFlowClient.loginUser({ serverUrl, email, password }) and pass the ' +
|
|
515
|
+
'returned accessToken via setToken(). See README "로그인".'
|
|
571
516
|
);
|
|
572
517
|
}
|
|
573
518
|
}
|
package/src/chat/ChatClient.js
CHANGED
|
@@ -376,7 +376,7 @@ class ChatClient extends EventEmitter {
|
|
|
376
376
|
|
|
377
377
|
/**
|
|
378
378
|
* 1:1 채팅방 생성/조회
|
|
379
|
-
* @param {string} friendId - 상대방 사용자 ID (
|
|
379
|
+
* @param {string} friendId - 상대방 사용자 ID (서버 내부 {@code users._id} — 참가자 목록의 userId)
|
|
380
380
|
* @returns {Promise<Object>}
|
|
381
381
|
*/
|
|
382
382
|
async createOneToOneRoom(friendId) {
|
package/src/index.js
CHANGED
|
@@ -10,35 +10,27 @@
|
|
|
10
10
|
* 자세한 내용은 README 의 "프로덕션 인증 플로우" 섹션 참고.
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
|
-
* // ===
|
|
13
|
+
* // === 시작하기 — 이메일/비밀번호 로그인 ===
|
|
14
14
|
* import TalkFlowClient from '@talkflow/sdk';
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* // 1단계: 로그인. 클라이언트 생성 «전» 에 부른다 — ck/projectId/JWT 를 한 번에 받는다.
|
|
17
|
+
* const { clientKey, projectId, accessToken } = await TalkFlowClient.loginUser({
|
|
18
|
+
* serverUrl: 'https://chat.apiorbit.net',
|
|
19
|
+
* email: 'me@example.com',
|
|
20
|
+
* password: '••••••••'
|
|
20
21
|
* });
|
|
21
22
|
*
|
|
22
|
-
*
|
|
23
|
-
* await client.connect();
|
|
24
|
-
* await client.chat.sendTextMessage('room-id', '안녕하세요!');
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* // === 프로덕션 — Client Key + Backend JWT 발급 ===
|
|
28
|
-
* // 1단계: 고객사 backend 에서 Server Key 로 JWT 발급
|
|
29
|
-
* // POST /api/v1/users/auth (X-API-KEY: SERVER_KEY)
|
|
30
|
-
* // → { accessToken }
|
|
31
|
-
*
|
|
32
|
-
* // 2단계: 브라우저 SDK 는 Client Key + JWT 로 초기화
|
|
23
|
+
* // 2단계: 받은 값 그대로 초기화
|
|
33
24
|
* const client = new TalkFlowClient({
|
|
34
|
-
* apiKey:
|
|
35
|
-
* projectId
|
|
36
|
-
* jwtToken:
|
|
25
|
+
* apiKey: clientKey, // ck- (브라우저 노출 안전)
|
|
26
|
+
* projectId,
|
|
27
|
+
* jwtToken: accessToken,
|
|
37
28
|
* env: 'production'
|
|
38
29
|
* });
|
|
39
30
|
*
|
|
40
31
|
* await client.connect();
|
|
41
32
|
* await client.chat.sendTextMessage('room-id', '안녕하세요!');
|
|
33
|
+
* await client.chat.sendTextMessage('room-id', '안녕하세요!');
|
|
42
34
|
*/
|
|
43
35
|
|
|
44
36
|
// 메인 클라이언트
|
package/src/talkflow/session.js
CHANGED
|
@@ -62,9 +62,9 @@ export function applySessionMethods(TalkFlowClient) {
|
|
|
62
62
|
|
|
63
63
|
if (!this.options.jwtToken) {
|
|
64
64
|
throw new Error(
|
|
65
|
-
'JWT token is required.
|
|
66
|
-
'
|
|
67
|
-
'
|
|
65
|
+
'JWT token is required. Sign in with TalkFlowClient.loginUser({ serverUrl, ' +
|
|
66
|
+
'email, password }) and pass the returned accessToken to the SDK via the ' +
|
|
67
|
+
'jwtToken option, setToken(), or connect(jwt). See README "로그인".'
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
70
|
|
package/types/index.d.ts
CHANGED
|
@@ -725,8 +725,6 @@ export interface ChatMessage {
|
|
|
725
725
|
nickName?: string;
|
|
726
726
|
/** 발신자 프로필 이미지 URL. */
|
|
727
727
|
profileUrl?: string | null;
|
|
728
|
-
/** 발신자의 프로젝트 스코프 사용자 ID. */
|
|
729
|
-
projectUserId?: string;
|
|
730
728
|
content: string | null;
|
|
731
729
|
chatMessageType: ChatMessageTypeValue;
|
|
732
730
|
chatType: string;
|
|
@@ -968,7 +966,16 @@ export interface LoginMember {
|
|
|
968
966
|
email?: string | null;
|
|
969
967
|
nickname?: string | null;
|
|
970
968
|
profileUrl?: string | null;
|
|
969
|
+
/** 회사 역할 — 클라이언트가 파트너 화면 노출을 결정한다. 인가 근거는 아니다(서버가 재확인). */
|
|
970
|
+
companyRole: 'PARTNER' | 'MEMBER';
|
|
971
971
|
preferredLanguage?: string | null;
|
|
972
|
+
/**
|
|
973
|
+
* 초기 비밀번호를 아직 안 바꿨는가 — `true` 면 **비밀번호 변경 화면으로 보내라**.
|
|
974
|
+
*
|
|
975
|
+
* 회원은 파트너가 정한 임시 비밀번호로 처음 들어오고, 파트너가 초기화하면 다시 이 상태가 된다.
|
|
976
|
+
* 로그인은 **성공** 한 것이다 — 실패로 다루면 사용자가 「비밀번호가 틀렸다」로 오해한다.
|
|
977
|
+
*/
|
|
978
|
+
mustChangePassword: boolean;
|
|
972
979
|
}
|
|
973
980
|
|
|
974
981
|
/**
|
|
@@ -986,6 +993,39 @@ export interface LoginResult {
|
|
|
986
993
|
user: LoginMember;
|
|
987
994
|
}
|
|
988
995
|
|
|
996
|
+
/** 회사 조직도의 회원 — 초대 UI 에 필요한 것만. */
|
|
997
|
+
export interface DirectoryMember {
|
|
998
|
+
userId: string;
|
|
999
|
+
name?: string | null;
|
|
1000
|
+
profileUrl?: string | null;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/** 조직도 노드. `parentId` 가 null 이면 최상위. */
|
|
1004
|
+
export interface DirectoryNode {
|
|
1005
|
+
orgNodeId: string;
|
|
1006
|
+
name: string;
|
|
1007
|
+
parentId?: string | null;
|
|
1008
|
+
sortOrder: number;
|
|
1009
|
+
/** 자손 포함 누적 인원 — 화면의 「개발팀 (2)」가 이 값. */
|
|
1010
|
+
memberCount: number;
|
|
1011
|
+
/** 이 노드 직속 인원 수. */
|
|
1012
|
+
directMemberCount: number;
|
|
1013
|
+
/** 이 노드 **직속** 회원. 누적 목록은 `children` 을 따라 모은다. */
|
|
1014
|
+
members: DirectoryMember[];
|
|
1015
|
+
children: DirectoryNode[];
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/** `getCompanyDirectory()` 응답. */
|
|
1019
|
+
export interface CompanyDirectory {
|
|
1020
|
+
tree: DirectoryNode[];
|
|
1021
|
+
/** 부서가 없는 회원 — 빠지면 초대 화면에서 그 사람을 찾을 수 없다. */
|
|
1022
|
+
unassignedMembers: DirectoryMember[];
|
|
1023
|
+
/** 활성 회원 총수(트리 + 미지정). */
|
|
1024
|
+
totalMemberCount: number;
|
|
1025
|
+
/** true 면 인원 **수** 는 정확하지만 **목록** 이 비어 있다 — 검색 기반 선택으로 넘어가야 한다. */
|
|
1026
|
+
membersOmitted: boolean;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
989
1029
|
/** 내 정보. 수정 응답에는 `accessToken` 이 함께 온다(name/profileUrl 이 토큰 claim). */
|
|
990
1030
|
export interface MyProfile {
|
|
991
1031
|
userId: string;
|
|
@@ -1000,6 +1040,13 @@ export interface MyProfile {
|
|
|
1000
1040
|
preferredLanguage?: string | null;
|
|
1001
1041
|
projectId: string;
|
|
1002
1042
|
joinedAt?: string | null;
|
|
1043
|
+
/**
|
|
1044
|
+
* 초기 비밀번호를 아직 안 바꿨는가 — `true` 면 비밀번호 변경을 요구해야 한다.
|
|
1045
|
+
*
|
|
1046
|
+
* 로그인 응답에도 같은 값이 있다. 여기에도 있는 이유: 새로고침·딥링크로 로그인 응답을 거치지
|
|
1047
|
+
* 않고 들어오는 화면에서 그 요구가 조용히 사라지지 않게 하려는 것이다.
|
|
1048
|
+
*/
|
|
1049
|
+
mustChangePassword: boolean;
|
|
1003
1050
|
accessToken?: string;
|
|
1004
1051
|
expiresIn?: number;
|
|
1005
1052
|
}
|
|
@@ -1009,7 +1056,6 @@ export interface ParticipantResponse {
|
|
|
1009
1056
|
userId: string;
|
|
1010
1057
|
nickName?: string;
|
|
1011
1058
|
profileUrl?: string | null;
|
|
1012
|
-
projectUserId?: string;
|
|
1013
1059
|
/**
|
|
1014
1060
|
* 참가자 역할 — 서버 {@code ParticipantRole} enum 과 일치.
|
|
1015
1061
|
* <ul>
|