@vibexnpm/talkx 5.6.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +69 -4
- 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 +73 -134
- 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 +69 -4
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>: 로그인 키가 전역 유일이라 이메일 하나로 회원과 회사가
|
|
@@ -302,7 +287,16 @@ class TalkFlowClient extends EventEmitter {
|
|
|
302
287
|
/**
|
|
303
288
|
* 내 정보 조회.
|
|
304
289
|
*
|
|
305
|
-
*
|
|
290
|
+
* `metadata` 를 볼 수 있는 **유일한** 곳이다. 고치는 문은 {@link #updateMyProfile} 하나뿐이고
|
|
291
|
+
* 그쪽은 **전체 교체**라, 부분 수정하려면 여기서 먼저 읽어 병합한 뒤 통째로 보내야 한다:
|
|
292
|
+
*
|
|
293
|
+
* ```js
|
|
294
|
+
* const me = await client.getMyProfile();
|
|
295
|
+
* await client.updateMyProfile({ metadata: { ...me.metadata, statusMessage: '회의 중' } });
|
|
296
|
+
* ```
|
|
297
|
+
*
|
|
298
|
+
* @returns {Promise<Object>} `{userId, email, name, phone, profileUrl, companyRole,
|
|
299
|
+
* orgNodeId, orgName, preferredLanguage, metadata, mustChangePassword, ...}`
|
|
306
300
|
*/
|
|
307
301
|
async getMyProfile() {
|
|
308
302
|
this._checkToken();
|
|
@@ -319,10 +313,24 @@ class TalkFlowClient extends EventEmitter {
|
|
|
319
313
|
* <p>이메일·조직·역할은 바꿀 수 없다 — 이메일은 로그인 키, 조직은 파트너가 정하고,
|
|
320
314
|
* 역할은 본인이 승급할 수 없어야 한다.</p>
|
|
321
315
|
*
|
|
322
|
-
* @
|
|
323
|
-
* @
|
|
324
|
-
*
|
|
325
|
-
* @
|
|
316
|
+
* <p><b>내 정보를 고치는 유일한 메서드다.</b> {@code updateMyInfo} 와
|
|
317
|
+
* {@code PUT /api/v1/users/update} 는 v7.0.0 에서 걷어냈다 — 같은 칸(이름·프로필 이미지)을
|
|
318
|
+
* 고치는 문이 둘이라 한 화면에서 순서 없이 두 번 부르면 나중 호출이 앞을 덮었고, 어느 쪽도
|
|
319
|
+
* 혼자서는 부족했다(그쪽엔 {@code phone} 이, 이쪽엔 나머지 셋이 없었다).</p>
|
|
320
|
+
*
|
|
321
|
+
* <p>서버는 갱신된 프로필이 담긴 <b>새 JWT</b> 를 함께 준다. 채택하지 않으면 옛 토큰의
|
|
322
|
+
* name/profileUrl 클레임이 최대 7일(토큰 TTL) 동안 계속 나간다. 같은 사용자의 토큰 교체라
|
|
323
|
+
* {@code setToken} 은 재연결을 일으키지 않는다 — STOMP connectHeaders 만 갱신된다.</p>
|
|
324
|
+
*
|
|
325
|
+
* @param {Object} profile — 넘기지 않은 필드는 «변경 안 함»
|
|
326
|
+
* @param {string} [profile.name] - 이름 (max 100자)
|
|
327
|
+
* @param {string} [profile.phone] - 전화번호 (max 30자)
|
|
328
|
+
* @param {string} [profile.profileUrl] - 프로필 이미지 URL (max 500자)
|
|
329
|
+
* @param {Object} [profile.metadata] - 메타데이터 (**전체 교체** — 부분 수정하려면
|
|
330
|
+
* {@link #getMyProfile} 로 현재 값을 읽어 병합한 뒤 통째로 보내라. 서버는 병합하지 않는다)
|
|
331
|
+
* @param {string|null} [profile.preferredLanguage] - 기본 선호 언어 (BCP-47 / 'OFF'(원문) / null)
|
|
332
|
+
* @param {string} [profile.timeZone] - IANA zone id. 어시가 대화 시각을 이 기준으로 렌더한다.
|
|
333
|
+
* `Intl.DateTimeFormat().resolvedOptions().timeZone` 값을 그대로 보내면 된다.
|
|
326
334
|
* @returns {Promise<Object>} 갱신된 내 정보 (accessToken 포함)
|
|
327
335
|
*/
|
|
328
336
|
async updateMyProfile(profile) {
|
|
@@ -357,106 +365,13 @@ class TalkFlowClient extends EventEmitter {
|
|
|
357
365
|
return this.apiClient.put('/api/v1/me/password', { currentPassword, newPassword });
|
|
358
366
|
}
|
|
359
367
|
|
|
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
|
-
/**
|
|
419
|
-
* 사용자 정보 수정.
|
|
420
|
-
*
|
|
421
|
-
* 서버는 갱신된 프로필이 담긴 **새 JWT** 를 응답에 함께 실어 준다(`/auth` 와 같은 계약).
|
|
422
|
-
* 이걸 채택하지 않으면 옛 토큰의 name/profileUrl 클레임이 계속 나가, 서버가 그 클레임을 읽는
|
|
423
|
-
* 경로에서 낡은 값이 최대 7일(토큰 TTL) 동안 보인다.
|
|
424
|
-
*
|
|
425
|
-
* 같은 사용자의 토큰 교체라 `setToken` 은 재연결을 일으키지 않는다 — userId 가 바뀔 때만
|
|
426
|
-
* 재초기화하고, 여기서는 STOMP connectHeaders 만 갱신된다.
|
|
427
|
-
*
|
|
428
|
-
* @param {Object} userData
|
|
429
|
-
* @param {string} [userData.nickname] - 닉네임 (max 100자)
|
|
430
|
-
* @param {string} [userData.profileImageUrl] - 프로필 이미지 URL (max 500자)
|
|
431
|
-
* @param {Object} [userData.metadata] - 메타데이터 (전체 교체)
|
|
432
|
-
* @param {string|null} [userData.preferredLanguage] - 기본 선호 언어 (BCP-47 / 'OFF'(원문) / null). 다국어.
|
|
433
|
-
* @returns {Promise<Object>}
|
|
434
|
-
*/
|
|
435
|
-
async updateMyInfo(userData) {
|
|
436
|
-
this._checkToken();
|
|
437
|
-
const response = await this.apiClient.put('/api/v1/users/update', userData);
|
|
438
|
-
|
|
439
|
-
if (response?.data?.accessToken) {
|
|
440
|
-
// 토큰 교체 실패로 수정 자체를 실패시키지 않는다 — 서버 반영은 이미 끝났고,
|
|
441
|
-
// 옛 토큰도 유효(낡았을 뿐)하므로 여기서 throw 하면 호출자가 이미 적용된 수정을
|
|
442
|
-
// 다시 시도하게 된다. authenticate() 는 토큰이 없으면 아무것도 못 하므로 계약이 다르다.
|
|
443
|
-
try {
|
|
444
|
-
await this.setToken(response.data.accessToken);
|
|
445
|
-
} catch (error) {
|
|
446
|
-
this.logger.warn('Profile updated but token refresh failed; keeping the old token', error);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
return response;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
368
|
/**
|
|
454
369
|
* 내 기본 선호 언어 설정 (전 방 공통, 다국어). 명시 변경 — 방별 오버라이드는 {@link #setMyRoomLanguage}.
|
|
455
370
|
* @param {string|null} language - BCP-47 코드(예 'ko','en') / 'OFF'(원문) / null·''(미설정)
|
|
456
371
|
* @returns {Promise<Object>}
|
|
457
372
|
*/
|
|
458
373
|
async setPreferredLanguage(language) {
|
|
459
|
-
return this.
|
|
374
|
+
return this.updateMyProfile({ preferredLanguage: language });
|
|
460
375
|
}
|
|
461
376
|
|
|
462
377
|
/**
|
|
@@ -468,7 +383,10 @@ class TalkFlowClient extends EventEmitter {
|
|
|
468
383
|
* 지역 변형을 그대로 보내면 명시 요청({@code lang: 'en'})과 키가 분열되어 같은 언어 이중 생성·조회 miss 가
|
|
469
384
|
* 발생한다. 서버 {@code LanguageCode} 도 동일 규칙으로 축약하므로 어느 쪽으로 들어와도 같은 값이 된다.</p>
|
|
470
385
|
*
|
|
471
|
-
* <p
|
|
386
|
+
* <p>예전에는 {@code registerUser} 가 이 값을 자동으로 실어 보냈는데 그 경로가 사라졌다.
|
|
387
|
+
* 지금은 호출자가 직접 감지해 {@link #setPreferredLanguage} (또는 {@link #updateMyProfile} 의
|
|
388
|
+
* {@code preferredLanguage}) 로 넣어야 한다 — 넣지 않으면 사용자 기본 언어가 미설정으로 남아
|
|
389
|
+
* 자동 번역이 서버 기본값을 쓴다.</p>
|
|
472
390
|
* @returns {string|null}
|
|
473
391
|
*/
|
|
474
392
|
static detectBrowserLanguage() {
|
|
@@ -522,16 +440,37 @@ class TalkFlowClient extends EventEmitter {
|
|
|
522
440
|
}
|
|
523
441
|
|
|
524
442
|
/**
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
443
|
+
* 회사 조직도 + 인원 — <b>방 만들 때 «조직으로 초대»</b> 가 쓴다.
|
|
444
|
+
*
|
|
445
|
+
* <p>조직을 고르면 <b>하위 조직 인원까지</b> 초대된다. 그래서 각 노드의 {@code memberCount} 는
|
|
446
|
+
* 자손을 포함한 누적이고, {@code members} 는 그 노드 <b>직속</b>만이다 — 누적 목록이 필요하면
|
|
447
|
+
* {@code children} 을 따라 내려가며 모은다(상위마다 중복해 실으면 응답이 제곱으로 커진다).</p>
|
|
448
|
+
*
|
|
449
|
+
* <p>부서가 없는 회원은 {@code unassignedMembers} 에 있다. 퇴사·차단 회원은 어디에도 없다.</p>
|
|
450
|
+
*
|
|
451
|
+
* @returns {Promise<Object>} `{tree, unassignedMembers, totalMemberCount, membersOmitted}`
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* const dir = await client.getCompanyDirectory();
|
|
455
|
+
* // 조직 하나를 고르면 그 자손까지 전부 모은다
|
|
456
|
+
* const collect = (node) => [...node.members, ...node.children.flatMap(collect)];
|
|
528
457
|
*/
|
|
529
|
-
async
|
|
530
|
-
|
|
458
|
+
async getCompanyDirectory() {
|
|
459
|
+
this._checkToken();
|
|
460
|
+
return this.apiClient.get('/api/v1/company/directory');
|
|
531
461
|
}
|
|
532
462
|
|
|
533
463
|
/**
|
|
534
464
|
* 사용자 목록 조회
|
|
465
|
+
*
|
|
466
|
+
* <p>응답 행에 {@code email}/{@code phone}/{@code orgNodeId}/{@code orgName} 이 포함된다 —
|
|
467
|
+
* 부서명은 저장값이 아니라 조회 시점에 조직 마스터에서 해소한 값이다.</p>
|
|
468
|
+
*
|
|
469
|
+
* <p>{@code email} 은 <b>연락용</b>이다. 로그인 키인 {@code loginEmail} 은 어떤 목록에도
|
|
470
|
+
* 실리지 않는다 — 둘은 다른 값이고, 퇴사 시 {@code loginEmail} 만 비워진다.</p>
|
|
471
|
+
*
|
|
472
|
+
* <p>퇴사·차단된 회원은 목록·검색 어디에도 나오지 않는다.</p>
|
|
473
|
+
*
|
|
535
474
|
* @param {Object} [params]
|
|
536
475
|
* @param {number} [params.size=50] - 페이지 크기 (1-100)
|
|
537
476
|
* @param {string} [params.lastId] - 마지막 사용자 ID
|
|
@@ -565,9 +504,9 @@ class TalkFlowClient extends EventEmitter {
|
|
|
565
504
|
_checkToken() {
|
|
566
505
|
if (!this.options.jwtToken) {
|
|
567
506
|
throw new Error(
|
|
568
|
-
'JWT token is required for this operation.
|
|
569
|
-
'(
|
|
570
|
-
'See README "
|
|
507
|
+
'JWT token is required for this operation. Sign in with ' +
|
|
508
|
+
'TalkFlowClient.loginUser({ serverUrl, email, password }) and pass the ' +
|
|
509
|
+
'returned accessToken via setToken(). See README "로그인".'
|
|
571
510
|
);
|
|
572
511
|
}
|
|
573
512
|
}
|
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;
|
|
@@ -998,8 +1038,24 @@ export interface MyProfile {
|
|
|
998
1038
|
orgNodeId?: string | null;
|
|
999
1039
|
orgName?: string | null;
|
|
1000
1040
|
preferredLanguage?: string | null;
|
|
1041
|
+
/**
|
|
1042
|
+
* 확장 메타데이터 — 내 값을 볼 수 있는 **유일한** 곳이다.
|
|
1043
|
+
*
|
|
1044
|
+
* 고치는 문은 `updateMyProfile({ metadata })` 하나뿐이고 **전체 교체**다 —
|
|
1045
|
+
* 서버는 병합하지 않는다. 부분 수정하려면 이 값을 먼저 읽어 병합한 뒤 통째로 보내라.
|
|
1046
|
+
*/
|
|
1047
|
+
metadata?: Record<string, unknown> | null;
|
|
1048
|
+
/** IANA zone id — 어시가 대화 시각을 이 기준으로 렌더한다. 읽을 수 없으면 설정 화면이 현재 값을 못 보여준다. */
|
|
1049
|
+
timeZone?: string | null;
|
|
1001
1050
|
projectId: string;
|
|
1002
1051
|
joinedAt?: string | null;
|
|
1052
|
+
/**
|
|
1053
|
+
* 초기 비밀번호를 아직 안 바꿨는가 — `true` 면 비밀번호 변경을 요구해야 한다.
|
|
1054
|
+
*
|
|
1055
|
+
* 로그인 응답에도 같은 값이 있다. 여기에도 있는 이유: 새로고침·딥링크로 로그인 응답을 거치지
|
|
1056
|
+
* 않고 들어오는 화면에서 그 요구가 조용히 사라지지 않게 하려는 것이다.
|
|
1057
|
+
*/
|
|
1058
|
+
mustChangePassword: boolean;
|
|
1003
1059
|
accessToken?: string;
|
|
1004
1060
|
expiresIn?: number;
|
|
1005
1061
|
}
|
|
@@ -1009,7 +1065,6 @@ export interface ParticipantResponse {
|
|
|
1009
1065
|
userId: string;
|
|
1010
1066
|
nickName?: string;
|
|
1011
1067
|
profileUrl?: string | null;
|
|
1012
|
-
projectUserId?: string;
|
|
1013
1068
|
/**
|
|
1014
1069
|
* 참가자 역할 — 서버 {@code ParticipantRole} enum 과 일치.
|
|
1015
1070
|
* <ul>
|
|
@@ -2312,7 +2367,17 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
|
|
|
2312
2367
|
getMyProfile(): Promise<MyProfile>;
|
|
2313
2368
|
|
|
2314
2369
|
/** 내 정보 수정 — 응답의 새 토큰을 자동 채택한다(name/profileUrl 이 토큰 claim). */
|
|
2315
|
-
updateMyProfile(profile: {
|
|
2370
|
+
updateMyProfile(profile: {
|
|
2371
|
+
name?: string;
|
|
2372
|
+
phone?: string;
|
|
2373
|
+
profileUrl?: string;
|
|
2374
|
+
/** **전체 교체** — 부분 수정은 `getMyProfile()` 로 읽어 병합한 뒤 통째로 보낸다. */
|
|
2375
|
+
metadata?: Record<string, unknown> | null;
|
|
2376
|
+
/** BCP-47 코드 / 'OFF'(원문) / null */
|
|
2377
|
+
preferredLanguage?: string | null;
|
|
2378
|
+
/** IANA zone id — `Intl.DateTimeFormat().resolvedOptions().timeZone` */
|
|
2379
|
+
timeZone?: string;
|
|
2380
|
+
}): Promise<MyProfile>;
|
|
2316
2381
|
|
|
2317
2382
|
/** 비밀번호 변경 — 현재 비밀번호 재확인. 토큰은 그대로 유효해 재로그인이 필요 없다. */
|
|
2318
2383
|
changeMyPassword(params: { currentPassword: string; newPassword: string }): Promise<unknown>;
|