connectbase-client 3.24.0 → 3.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,149 +1,24 @@
1
- interface ApiErrorDetail {
2
- code: string;
3
- message: string;
4
- details?: unknown;
5
- }
6
1
  declare class ApiError extends Error {
7
2
  statusCode: number;
8
- code: string | undefined;
9
- details: unknown | undefined;
10
- constructor(statusCode: number, message: string, code?: string, details?: unknown);
3
+ constructor(statusCode: number, message: string);
11
4
  }
12
5
  declare class AuthError extends Error {
13
6
  constructor(message: string);
14
7
  }
15
- /**
16
- * GameError 는 game-server 가 surface 한 `error` 메시지 (createRoom/joinRoom/sendAction
17
- * 응답 또는 broadcastScriptError) 를 client 측에서 일관된 형태로 다루기 위한 예외 클래스.
18
- *
19
- * 기존엔 모든 reject 가 `new Error(message)` 였어서 `code`/`phase`/`feature` 같은
20
- * 분류 메타가 손실되어 SDK 사용자가 onError UI 분기를 만들 수 없었다 (NJB 사례,
21
- * platform-issue 019e21dd / 2026-05-13).
22
- *
23
- * 사용 패턴:
24
- * ```ts
25
- * try {
26
- * await room.createRoom({ scriptName: 'my-script' })
27
- * } catch (e) {
28
- * if (e instanceof GameError && e.code === 'SCRIPT_NOT_FOUND') {
29
- * console.error('script missing — available:', e.available)
30
- * }
31
- * }
32
- * ```
33
- *
34
- * onError 콜백도 동일하게 GameError 인스턴스로 surface 된다.
35
- */
36
- declare class GameError extends Error {
37
- /** server 가 분류한 에러 코드. literal 비교용은 GameErrorCode 참조. */
38
- code: string;
39
- /** Lua hook 단계 — 'onJoin'|'onLeave'|'onTick'|'onAction'. 비-lua 에러는 undefined. */
40
- phase?: string;
41
- /** FEATURE_DISABLED 시 비활성 feature 이름. */
42
- feature?: string;
43
- /** 영향받은 room id. */
44
- roomId?: string;
45
- /** `<appID>:<scriptName>` 형식의 attached script id. */
46
- scriptId?: string;
47
- /** broadcast 형태의 onAction 에러일 때, 액션을 일으킨 client (본인 아님 확인용). */
48
- originClientId?: string;
49
- /** SCRIPT_NOT_FOUND 응답 시 요청했던 scriptName. */
50
- requested?: string;
51
- /** SCRIPT_NOT_FOUND 응답 시 active 상태인 script 이름 목록. */
52
- available?: string[];
53
- constructor(init: {
54
- code?: string;
55
- message?: string;
56
- phase?: string;
57
- feature?: string;
58
- roomId?: string;
59
- scriptId?: string;
60
- originClientId?: string;
61
- requested?: string;
62
- available?: string[];
63
- });
64
- }
65
-
66
- interface AbortOptions {
67
- timeout?: number;
68
- signal?: AbortSignal;
69
- }
70
8
 
71
- /**
72
- * Recent API calls breadcrumb buffer — SDK 디버깅 / platform issue 발행 시 자동 첨부.
73
- *
74
- * **저장 정책 (PII 보호):**
75
- * - method, path (query string strip), status, duration_ms, timestamp 만 저장
76
- * - body / response body / 인증 토큰 미저장
77
- * - URL 내 secret-like 패턴 (key=, token=, password=) 자동 redact
78
- * - `/v1/auth/*`, `/v1/oauth/token` 등 민감 endpoint 는 path 만 (쿼리 strip)
79
- */
80
- interface RecentApiCall {
81
- method: string;
82
- path: string;
83
- status: number;
84
- duration_ms: number;
85
- timestamp: string;
86
- }
87
-
88
- type TokenPersistence = 'localStorage' | 'sessionStorage' | 'none';
89
9
  interface HttpClientConfig {
90
10
  baseUrl: string;
91
- /**
92
- * Public Key (cb_pk_* 형식). 콘솔 → 설정 → API 에서 발급.
93
- * 브라우저/클라이언트에서 사용 (Row Level Security 적용).
94
- */
95
- publicKey?: string;
96
- /**
97
- * Secret Key (cb_sk_* 형식). 사용자 프로필에서 발급.
98
- * 서버 환경에서만 사용 (전체 권한, 절대 노출 금지).
99
- */
100
- secretKey?: string;
11
+ apiKey?: string;
101
12
  accessToken?: string;
102
13
  refreshToken?: string;
103
- /**
104
- * 토큰 저장 방식. **기본값은 'none' (메모리 저장)**.
105
- * XSS 취약점이 하나라도 있을 경우 영구 저장은 즉시 전 세션 탈취로 이어지므로,
106
- * 영구 저장 옵션은 위험을 이해하고 명시적으로 선택한 경우에만 사용한다.
107
- *
108
- * - 'none' (권장·기본값): access token 만 메모리 저장. refresh token 은 서버가 발급한
109
- * HttpOnly cookie 로만 보관되어 JS 가 접근할 수 없다 (XSS 시 탈취 불가). 새로고침 후에는
110
- * `autoRestoreSession`(기본 true) 으로 cookie 만으로 자동 복구된다.
111
- * - 'sessionStorage': 탭 종료 시 삭제. JS 접근 가능 → XSS 로 탭 세션 탈취 가능
112
- * - 'localStorage': 브라우저 종료 후에도 유지. JS 접근 가능 → XSS 로 영구 탈취 가능
113
- */
114
- persistence?: TokenPersistence;
115
- /**
116
- * 새로고침/탭 재개 시 HttpOnly cookie 로부터 access token 을 자동 복구할지 여부.
117
- * 기본값은 브라우저 환경에서 true. 비-브라우저(Node.js, RN) 에서는 무시된다.
118
- *
119
- * cookie 가 없는 경우(미로그인) 조용히 실패하며 콘솔 에러를 발생시키지 않는다.
120
- */
121
- autoRestoreSession?: boolean;
122
- /**
123
- * 요청별 기본 타임아웃(ms). 개별 호출의 `timeout` 이 우선.
124
- * 기본값 30000ms. 0 또는 음수 지정 시 타임아웃 비활성화.
125
- */
126
- requestTimeoutMs?: number;
127
- /**
128
- * 전역 에러 관찰자. 모든 ApiError/AuthError 발생 시 호출된다.
129
- * 운영 관측성(Sentry/Datadog/자체 엔드포인트)과 연결하기 위한 훅.
130
- */
131
- onError?: (error: ApiError | AuthError) => void;
132
14
  onTokenRefresh?: (tokens: {
133
15
  accessToken: string;
134
16
  refreshToken: string;
135
17
  }) => void;
136
18
  onAuthError?: (error: AuthError) => void;
137
19
  onTokenExpired?: () => void;
138
- /**
139
- * `/v1/auth/re-issue` 의 일시적 실패(5xx, 네트워크 오류, abort) 발생 시 호출.
140
- * 이 경우 토큰은 폐기되지 않으며 `onTokenExpired` 도 호출되지 않는다 — 다음 호출에서
141
- * backoff 만료 후 자동 재시도. 사용자에게 "연결이 잠시 불안정합니다" 같은 비파괴적 알림을
142
- * 표시할 때 사용. `onAuthError` 도 함께 호출되므로 둘을 동시에 wiring 하면 중복 처리에 주의.
143
- */
144
- onTransientRefreshFailure?: (error: AuthError) => void;
145
20
  }
146
- interface RequestConfig extends AbortOptions {
21
+ interface RequestConfig {
147
22
  skipAuth?: boolean;
148
23
  headers?: Record<string, string>;
149
24
  }
@@ -151,574 +26,35 @@ declare class HttpClient {
151
26
  private config;
152
27
  private isRefreshing;
153
28
  private refreshPromise;
154
- private storageKey;
155
- private refreshFailureCount;
156
- private refreshLockedUntil;
157
- /**
158
- * 최근 API 호출 breadcrumb (PII strip 후 저장). platform_issue 발행 시 자동 첨부 가능.
159
- * `client.support.getRecentCalls()` 로 외부 노출.
160
- */
161
- private recentCalls;
162
- /**
163
- * 부팅 시 fire-and-forget 으로 시작한 cookie 기반 복구 promise. `prepareHeaders` 가
164
- * 인증 호출을 보내기 직전에 이 promise 를 await 해, 페이지 진입 직후 첫 API 호출이
165
- * 메모리 토큰 빈 상태로 401 받는 race 를 막는다 (platform-issue 019e638d, 2026-05-26).
166
- * 한 번 settle 되면 그 결과(메모리 적재 또는 미로그인)가 항상 반영되어 있다.
167
- */
168
- private bootRestorePromise;
169
29
  constructor(config: HttpClientConfig);
170
- /**
171
- * 페이지 진입 시 fire-and-forget 으로 시작된 cookie 복구 promise 를 SDK 가 등록한다.
172
- * 같은 promise 가 `prepareHeaders` 에서 await 되어, 첫 인증 호출이 cookie 복구
173
- * 완료 후 발화한다.
174
- */
175
- setBootRestorePromise(p: Promise<boolean>): void;
176
- /** 최근 호출 ring buffer 스냅샷 (시간순). */
177
- getRecentCalls(): RecentApiCall[];
178
- /** 최근 호출 buffer clear (테스트/프라이버시 처리). */
179
- clearRecentCalls(): void;
180
- private warnIfUnsafePersistence;
181
30
  updateConfig(config: Partial<HttpClientConfig>): void;
182
31
  setTokens(accessToken: string, refreshToken: string): void;
183
32
  clearTokens(): void;
184
33
  /**
185
- * OAuth redirect callback 직후 호출되어 HttpOnly cookie 를 부트스트랩한다.
186
- *
187
- * 배경: 콜백 흐름에서 서버가 redirect 응답에 Set-Cookie 를 함께 발급하지만, 일부 deployment /
188
- * 브라우저 정책 / 사용자 설정 (3rd-party cookie 차단 등) 환경에서 이 Set-Cookie 가 브라우저에
189
- * 저장되지 않는다. `persistence='none'` 모드에서는 토큰이 메모리에만 있고 cookie 가 없는 상태로
190
- * 페이지 새로고침이 발생하면 `/v1/auth/re-issue` 가 401 으로 떨어져 강제 로그아웃되는 회귀가
191
- * 있다 (platform-issue 019e3960, 2026-05-18).
192
- *
193
- * 이 메서드는 메모리의 refresh token 으로 한 번 `/v1/auth/re-issue` 를 호출해 서버가 cookie 를
194
- * 명시적으로 발급하도록 유도한다. 백엔드는 Bearer + `X-Public-Key` 조합을 SDK 호출 신호로
195
- * 인식해 응답에 `cb_member_refresh_token` cookie 를 함께 내려준다.
196
- *
197
- * persistence='localStorage' / 'sessionStorage' 모드는 새로고침 후에도 메모리 복구가 가능하므로
198
- * 호출하지 않는다 (rotation 만 일어나 비용만 증가). 비-브라우저 환경에서도 cookie 자체가 없으므로
199
- * 호출하지 않는다.
200
- */
201
- bootstrapRefreshCookie(): Promise<void>;
202
- private get persistence();
203
- private getStorage;
204
- /**
205
- * AuthAPI 등 내부 사용자가 "현재 persistence 설정된 스토리지"를 확인할 수 있도록 노출.
206
- * persistence='none' 이면 null. XSS 완화 기본값을 공유하기 위함.
207
- */
208
- getPersistenceStorage(): Storage | null;
209
- private buildStorageKey;
210
- private persistTokens;
211
- private restoreTokens;
212
- private removePersistedTokens;
213
- /**
214
- * Public Key 가 설정되어 있는지 확인
215
- */
216
- hasPublicKey(): boolean;
217
- /**
218
- * Public Key 반환
34
+ * API Key가 설정되어 있는지 확인
219
35
  */
220
- getPublicKey(): string | undefined;
36
+ hasApiKey(): boolean;
221
37
  /**
222
- * Secret Key 가 설정되어 있는지 확인
38
+ * API Key 반환
223
39
  */
224
- hasSecretKey(): boolean;
225
- /**
226
- * Secret Key 반환
227
- */
228
- getSecretKey(): string | undefined;
229
- /**
230
- * 현재 설정된 자격증명 반환.
231
- *
232
- * publicKey 가 있으면 그걸 반환한다 (X-Public-Key 헤더 = 앱 식별용 = cb_pk_*).
233
- * publicKey 가 없고 secretKey 만 있는 경우는 secretKey 로 폴백 — 단, 서버는
234
- * cb_sk_ 가 X-Public-Key 에 실리면 거부하므로 의도된 401 이 나간다(앱 식별 불가).
235
- *
236
- * secretKey 의 admin 권한은 별도로 Authorization: Bearer 로 보낸다. 그 처리는
237
- * prepareHeaders 안에서 publicKey + secretKey 가 함께 있을 때 수행한다.
238
- */
239
- getCredential(): string | undefined;
40
+ getApiKey(): string | undefined;
240
41
  /**
241
42
  * Access Token 반환
242
43
  */
243
44
  getAccessToken(): string | undefined;
244
- /**
245
- * JWT(Access Token) 가 설정되어 있는지 확인
246
- */
247
- hasJWT(): boolean;
248
45
  /**
249
46
  * Base URL 반환
250
47
  */
251
48
  getBaseUrl(): string;
252
49
  private refreshAccessToken;
253
- /**
254
- * 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 복구한다.
255
- *
256
- * 동작:
257
- * - 메모리에 access token 이 이미 있으면 그대로 반환 (true).
258
- * - 없으면 `/v1/auth/re-issue` 를 cookie 만으로 호출. cookie 가 있으면 access token 회복.
259
- * - cookie 가 없거나(미로그인) 만료된 경우 조용히 false 반환 (콘솔 에러 없음).
260
- *
261
- * 비-브라우저 환경에서는 cookie 흐름이 없으므로 즉시 false 반환.
262
- */
263
- tryRestoreSessionFromCookie(): Promise<boolean>;
264
- private emitError;
265
50
  private isTokenExpired;
266
51
  private prepareHeaders;
267
52
  private handleResponse;
268
- /**
269
- * AbortController 를 관리하며 fetch 호출을 실행. 타임아웃/외부 signal 병합.
270
- *
271
- * 401 자동 복구: 인증 호출이 401 을 받으면 cookie 기반 복구를 *한 번* 시도하고 retry 한다.
272
- * 메모리 토큰이 만료/누락 + cookie 는 살아 있는 경우를 자동으로 회복시켜,
273
- * 페이지 진입 직후 race 로 401 을 받은 첫 호출이 사용자 흐름을 차단하지 않게 한다
274
- * (platform-issue 019e638d, 2026-05-26). retry 는 1회 한정 — 무한 루프 차단.
275
- */
276
- private doFetch;
277
- private tryFetchOnce;
278
53
  get<T>(url: string, config?: RequestConfig): Promise<T>;
279
54
  post<T>(url: string, data?: unknown, config?: RequestConfig): Promise<T>;
280
55
  put<T>(url: string, data: unknown, config?: RequestConfig): Promise<T>;
281
56
  patch<T>(url: string, data: unknown, config?: RequestConfig): Promise<T>;
282
57
  delete<T>(url: string, config?: RequestConfig): Promise<T>;
283
- /**
284
- * Raw fetch 요청 (SSE 스트리밍 등에 사용)
285
- * 인증 헤더가 자동으로 추가됩니다. timeout 은 호출자가 직접 signal 로 관리해야 합니다
286
- * (스트리밍 특성상 전역 timeout 을 강제하지 않음).
287
- */
288
- fetchRaw(url: string, init?: RequestInit): Promise<Response>;
289
- }
290
-
291
- interface AnalyticsConfig {
292
- /** 자동 페이지뷰 추적 @default true */
293
- trackPageViews?: boolean;
294
- /** 커스텀 이벤트 활성화 @default true */
295
- trackEvents?: boolean;
296
- /** 세션 + heartbeat @default true */
297
- trackSessions?: boolean;
298
- /** 히트맵 (opt-in) @default false */
299
- heatmap?: boolean;
300
- /** 세션 녹화 (opt-in) @default false */
301
- recording?: boolean;
302
- /** 배치 크기 @default 10 */
303
- batchSize?: number;
304
- /** 배치 전송 간격 (ms) @default 5000 */
305
- flushInterval?: number;
306
- /** DNT 헤더 존중 @default true */
307
- respectDoNotTrack?: boolean;
308
- /** 디버그 모드 @default false */
309
- debug?: boolean;
310
- }
311
- interface ConsentOptions {
312
- analytics?: boolean;
313
- heatmap?: boolean;
314
- recording?: boolean;
315
- }
316
- interface AnalyticsEvent {
317
- name: string;
318
- properties?: Record<string, unknown>;
319
- timestamp?: number;
320
- }
321
- /**
322
- * 인기 페이지 목록 응답.
323
- *
324
- * 백엔드 `dto.PopularPagesResponse` 와 1:1 매핑.
325
- */
326
- interface PopularPagesResponse {
327
- pages: Array<{
328
- page_path: string;
329
- page_views: number;
330
- }>;
331
- start_date: number;
332
- end_date: number;
333
- }
334
- /**
335
- * 방문자 목록 응답.
336
- *
337
- * 백엔드 `dto.VisitorListResponse` 와 1:1 매핑. `app_member_id` 는 게스트 방문자에는
338
- * 없을 수 있다.
339
- */
340
- interface VisitorListResponse {
341
- visitors: Array<{
342
- id: string;
343
- visitor_uid: string;
344
- app_member_id?: string;
345
- total_visits: number;
346
- total_page_views: number;
347
- referrer?: string;
348
- last_ip?: string;
349
- country?: string;
350
- is_bot: boolean;
351
- first_visit_at: string;
352
- last_visit_at: string;
353
- }>;
354
- total: number;
355
- limit: number;
356
- offset: number;
357
- has_more: boolean;
358
- }
359
- /**
360
- * 페이지 전환 플로우(Sankey) 응답.
361
- *
362
- * 백엔드 `dto.NavigationFlowResponse` 와 1:1 매핑. `nodes` 는 페이지, `links` 는 전환.
363
- */
364
- interface NavigationFlowResponse {
365
- nodes: Array<{
366
- id: string;
367
- label: string;
368
- value: number;
369
- }>;
370
- links: Array<{
371
- source: string;
372
- target: string;
373
- value: number;
374
- }>;
375
- }
376
- /**
377
- * 조회 메서드 공통 옵션.
378
- *
379
- * `start_date` / `end_date` 는 백엔드가 정수형 epoch-day 또는 yyyymmdd 로 다루는 값
380
- * (서비스 메서드 시그니처 기준 int). 0 또는 미지정 시 기본 기간을 사용.
381
- */
382
- interface AnalyticsRangeOptions {
383
- start_date?: number;
384
- end_date?: number;
385
- limit?: number;
386
- }
387
- interface VisitorListOptions {
388
- limit?: number;
389
- offset?: number;
390
- /**
391
- * 백엔드가 인식하는 정렬 키 — 외 값은 silent 로 default(`last_visit`) 처리됩니다.
392
- *
393
- * 1.12.0 에서 백엔드 실제 동작에 맞춰 enum 정정 (1.10/1.11 의 `total_visits`/
394
- * `total_page_views` 는 백엔드에서 인식되지 않아 항상 default 분기였음).
395
- */
396
- sort_by?: 'last_visit' | 'visits' | 'page_views' | 'first_visit';
397
- }
398
- /**
399
- * 멤버별 합산 방문자 그룹 항목.
400
- *
401
- * 한 명의 회원이 여러 디바이스/브라우저로 접속했을 때 visitor row 들을 합쳐 단일 row.
402
- * 익명 visitor 는 `app_member_id == undefined` 로 단일 row 그대로 노출되며 `visitor_count == 1`.
403
- *
404
- * `visitor_count` 는 "디바이스 수" 가 아닌 **"추적 브라우저 인스턴스 수"** 를 의미합니다.
405
- * 같은 디바이스에서 시크릿모드 + 일반모드는 visitor 2 = visitor_count 2 로 카운트됩니다.
406
- */
407
- interface VisitorGroupItem {
408
- app_member_id?: string;
409
- /** 익명 visitor row 의 경우 단일 visitor_uid. 회원 그룹은 visitor_uids 참조. */
410
- visitor_uid?: string;
411
- /** 회원 그룹에 속한 visitor_uid 목록. 익명 row 는 undefined. */
412
- visitor_uids?: string[];
413
- visitor_count: number;
414
- total_visits: number;
415
- total_page_views: number;
416
- first_visit_at: string;
417
- last_visit_at: string;
418
- country?: string;
419
- is_bot: boolean;
420
- }
421
- interface VisitorGroupListResponse {
422
- groups: VisitorGroupItem[];
423
- total: number;
424
- limit: number;
425
- offset: number;
426
- has_more: boolean;
427
- }
428
- interface VisitorByMemberResponse {
429
- app_member_id: string;
430
- visitor_uids: string[];
431
- visitor_count: number;
432
- total_visits: number;
433
- total_page_views: number;
434
- first_visit_at: string;
435
- last_visit_at: string;
436
- country?: string;
437
- is_bot: boolean;
438
- }
439
- interface MergeVisitorsRequest {
440
- source_visitor_uid: string;
441
- /** target_visitor_uid 또는 target_member_id 중 하나는 필수. */
442
- target_visitor_uid?: string;
443
- target_member_id?: string;
444
- }
445
- interface MergeVisitorsResponse {
446
- success: boolean;
447
- target_visitor_id: string;
448
- moved_records: number;
449
- message?: string;
450
- }
451
- declare class SessionManager {
452
- private _sessionId;
453
- private _visitorUid;
454
- private _lastActivity;
455
- private _isNewSession;
456
- get sessionId(): string;
457
- get visitorUid(): string;
458
- get isNewSession(): boolean;
459
- /** 활동 기록 — 세션 타임아웃 리셋 */
460
- touch(): void;
461
- /** 세션 강제 리셋 */
462
- reset(): void;
463
- /**
464
- * visitor_uid 를 새로 발급하고 세션도 함께 초기화.
465
- *
466
- * 사용 시점:
467
- * - 사용자 로그아웃 (이전 사용자 활동이 다음 사용자에 attribution 되는 것 방지)
468
- * - 다른 사용자 로그인 감지 (link-member 가 `VISITOR_LINKED_TO_OTHER_MEMBER` 응답)
469
- *
470
- * localStorage 의 `__cb_visitor_uid` 가 새 UUID 로 교체되며 sessionStorage 의
471
- * 세션 키도 같이 비워진다. 이후의 모든 batch 는 새 visitor 로 기록되어 멤버 간
472
- * 데이터 오염이 차단된다.
473
- */
474
- regenerateVisitorUid(): string;
475
- private ensureSession;
476
- private loadOrCreateVisitorUid;
477
- }
478
- declare class AnalyticsAPI {
479
- private http;
480
- private config;
481
- private consent;
482
- private session;
483
- private storageWebId;
484
- private memberId;
485
- private eventQueue;
486
- private batchTimer;
487
- private isInitialized;
488
- private heartbeatTimer;
489
- private visibilityHandler;
490
- private unloadHeartbeatHandler;
491
- private popstateHandler;
492
- private beforeUnloadHandler;
493
- private origPushState;
494
- private origReplaceState;
495
- private heatmapClickHandler;
496
- private heatmapScrollHandler;
497
- private utm;
498
- constructor(http: HttpClient);
499
- /**
500
- * Analytics 초기화
501
- * @param storageWebId 웹 스토리지 ID
502
- * @param config 설정 (선택)
503
- */
504
- init(storageWebId: string, config?: AnalyticsConfig): void;
505
- /** Analytics 정리 */
506
- destroy(): void;
507
- /**
508
- * 동의 설정 변경
509
- */
510
- setConsent(consent: ConsentOptions): void;
511
- /** 현재 동의 상태 조회 */
512
- getConsent(): ConsentOptions;
513
- /**
514
- * 페이지뷰 수동 추적
515
- */
516
- trackPageView(path?: string): void;
517
- /**
518
- * 커스텀 이벤트 추적
519
- */
520
- trackEvent(name: string, properties?: Record<string, unknown>): void;
521
- /**
522
- * 사용자 식별 (로그인 직후 호출).
523
- *
524
- * 이후 모든 방문 배치에 `app_member_id` 가 첨부되어 새 활동은 회원으로 기록됩니다.
525
- * 추가로 **현재 visitor_uid 의 기존 익명 활동을 즉시 회원에게 backfill** 하기 위해
526
- * 백엔드 `link-member` 엔드포인트를 한 번 호출합니다 (1.11.0+). 호출 실패는
527
- * silent — 다음 batch 가 닿을 때 백엔드 자동 매핑이 동일하게 처리하므로 자가 복구.
528
- *
529
- * **사용자 전환 자동 처리 (1.13.0+)**: 동일 브라우저에서 다른 사용자가 로그인해
530
- * 백엔드가 `VISITOR_LINKED_TO_OTHER_MEMBER` 응답을 보내면, 큐를 비우고
531
- * `visitor_uid` 를 새로 발급한 뒤 link-member 를 한 번 더 호출해 새 visitor 가
532
- * 즉시 회원으로 기록되도록 자가 복구한다.
533
- *
534
- * 산업 표준(GA4 User-ID, Mixpanel/PostHog `identify`) 과 동작 정합.
535
- *
536
- * @example
537
- * ```ts
538
- * // 로그인 성공 직후
539
- * const member = await cb.auth.signInMember({ login_id, password })
540
- * cb.analytics.identify(member.member_id)
541
- * ```
542
- */
543
- identify(memberId: string): void;
544
- /**
545
- * 로그아웃 / 사용자 전환 시 호출. 익명 상태로 복귀하면서 visitor_uid 를 새로
546
- * 발급해 다음 사용자의 활동이 이전 사용자에게 attribution 되는 데이터 오염을 차단.
547
- *
548
- * 동작:
549
- * 1. memberId 를 null 로 설정 (이후 batch 는 익명으로 기록)
550
- * 2. 큐에 쌓인 미전송 이벤트 폐기 (이전 visitor 로 가는 것을 막기 위함)
551
- * 3. localStorage `__cb_visitor_uid` 새 UUID 로 교체 + sessionStorage 세션 키 정리
552
- * 4. heatmap 큐도 함께 비움
553
- *
554
- * @example
555
- * ```ts
556
- * // 로그아웃 핸들러
557
- * await cb.auth.signOut()
558
- * cb.analytics.reset()
559
- * ```
560
- */
561
- reset(): void;
562
- /**
563
- * 방문자 트래커에 현재 회원 ID 설정 (로그인/게스트 가입 시 호출).
564
- *
565
- * `identify()` 와 달리 즉시 backfill 호출은 하지 않습니다 — 단순히 이후 batch 의
566
- * `app_member_id` 값만 갱신. null 을 넘기면 익명 상태로 복귀 (로그아웃 시에는
567
- * 데이터 오염 방지를 위해 `reset()` 를 권장).
568
- */
569
- setMemberId(memberId: string | null): void;
570
- /**
571
- * 백엔드 link-member 엔드포인트 한 번 호출 — 즉시 backfill 트리거.
572
- *
573
- * - 첫 페이지뷰가 아직 백엔드에 닿기 전이면 visitor 가 없어 404. 무시 — 다음 batch 가
574
- * 가면 백엔드 BatchRecordVisit 가 자동 LinkMember 를 호출.
575
- * - 다른 멤버에 이미 연결된 visitor 인 경우 (`VISITOR_LINKED_TO_OTHER_MEMBER`)
576
- * visitor_uid 를 자동 재발급하고 link-member 를 한 번 더 호출 — 한 단계의 자가
577
- * 복구로 끝나며 무한 재귀를 막기 위해 두 번째 시도는 응답을 더 보지 않는다.
578
- * - storage_web_id 가 init 안 됐거나 모든 종류의 네트워크 오류 — silent fail.
579
- */
580
- private linkMemberSilent;
581
- /** 현재 설정된 회원 ID 조회 (미설정 시 null) */
582
- getMemberId(): string | null;
583
- /**
584
- * 히트맵 수집 활성화 (opt-in)
585
- */
586
- enableHeatmap(options?: {
587
- click?: boolean;
588
- scroll?: boolean;
589
- }): void;
590
- /**
591
- * 세션 heartbeat 자동 전송 시작 (30초 간격)
592
- */
593
- enableHeartbeat(): void;
594
- /**
595
- * 큐에 있는 이벤트 즉시 전송.
596
- *
597
- * 기본적으로 이벤트는 배치(10개) 또는 주기(5초)로 flush 되지만, 페이지 이탈 직전이나
598
- * 결정적 이벤트(결제 완료 등)는 수동으로 `flush()` 를 호출해 전송 지연을 줄일 수 있다.
599
- *
600
- * @returns 서버 응답 완료 시까지 대기하는 Promise
601
- *
602
- * @example
603
- * ```ts
604
- * // 결제 완료 직후 이탈에 대비해 즉시 flush
605
- * await cb.analytics.track('purchase_completed', { order_id: '123' })
606
- * await cb.analytics.flush()
607
- * window.location.href = '/thank-you'
608
- * ```
609
- */
610
- flush(): Promise<void>;
611
- /**
612
- * 인기 페이지 조회 (콘솔 JWT 또는 User Secret Key `cb_sk_` 인증 필요).
613
- *
614
- * 브라우저 환경에서 Public Key(`cb_pk_`) 만 가진 SDK 인스턴스로는 호출이 차단된다.
615
- * Functions / 어드민 앱 등 cb_sk_ 환경에서 사용하라.
616
- *
617
- * @param storageWebId 조회할 웹스토리지 ID. 미지정 시 `init()` 에 전달된 ID 사용.
618
- * @param options 기간/limit 옵션
619
- * @example
620
- * ```ts
621
- * const { pages } = await cb.analytics.getPopularPages('019d8...', { limit: 20 })
622
- * ```
623
- */
624
- getPopularPages(storageWebId?: string, options?: AnalyticsRangeOptions): Promise<PopularPagesResponse>;
625
- /**
626
- * 페이지 전환 플로우(Sankey) 조회 — JWT/cb_sk_ 인증 필요.
627
- */
628
- getNavigationFlow(storageWebId?: string, options?: AnalyticsRangeOptions): Promise<NavigationFlowResponse>;
629
- /**
630
- * 방문자 목록 조회 — JWT/cb_sk_ 인증 필요.
631
- */
632
- getVisitors(storageWebId?: string, options?: VisitorListOptions): Promise<VisitorListResponse>;
633
- /**
634
- * 멤버별 합산 방문자 그룹 조회 — JWT/cb_sk_ 인증 필요. (1.11+)
635
- *
636
- * `getVisitors` 와 달리 visitor row 들을 `app_member_id` 로 합쳐 단일 row 로 반환.
637
- * 같은 사람이 PC + 모바일 + 태블릿으로 접속한 경우 visitor 3개 → 그룹 1개.
638
- * 익명 visitor 는 단일 row 로 그대로 포함되어 페이지네이션이 일관됨.
639
- *
640
- * @example
641
- * ```ts
642
- * const { groups } = await cb.analytics.getVisitorGroups('019d8...', { limit: 50 })
643
- * for (const g of groups) {
644
- * if (g.app_member_id) console.log(`${g.app_member_id}: ${g.visitor_count} 디바이스`)
645
- * }
646
- * ```
647
- */
648
- getVisitorGroups(storageWebId?: string, options?: VisitorListOptions): Promise<VisitorGroupListResponse>;
649
- /**
650
- * 단건 멤버 합산 방문자 조회 — JWT/cb_sk_ 인증 필요. (1.11+)
651
- *
652
- * 어드민 회원 상세 페이지처럼 **한 명만 필요**할 때. 페이지네이션 풀 다운 없이
653
- * 한 번의 호출로 합산 결과 반환.
654
- *
655
- * @example
656
- * ```ts
657
- * const v = await cb.analytics.getVisitorByMember('019d8...', memberId)
658
- * console.log(`${v.visitor_count} 디바이스, 총 ${v.total_page_views} pv`)
659
- * ```
660
- */
661
- getVisitorByMember(storageWebId: string | undefined, memberId: string): Promise<VisitorByMemberResponse>;
662
- /**
663
- * 두 visitor 를 한 사람으로 통합하는 admin 작업 — JWT/cb_sk_ 인증 필요. (1.11+)
664
- *
665
- * 외부 인증 시스템에서 두 visitor 가 동일인임을 알았을 때 사용. source 의 자식 레코드
666
- * (page_views, daily, custom_events, experiment_assignments, heatmap_events,
667
- * session_recordings) 를 target 으로 옮기고 source visitor 는 삭제됨.
668
- *
669
- * @param request 둘 중 하나 필수: `target_visitor_uid` 또는 `target_member_id`.
670
- * @example
671
- * ```ts
672
- * await cb.analytics.mergeVisitors('019d8...', {
673
- * source_visitor_uid: 'old-uid',
674
- * target_member_id: '01a...',
675
- * })
676
- * ```
677
- */
678
- mergeVisitors(storageWebId: string | undefined, request: MergeVisitorsRequest): Promise<MergeVisitorsResponse>;
679
- /**
680
- * 조회 메서드 공통 가드 — Public Key 인증 SDK 인스턴스에서는 명확한 에러를 던진다.
681
- * 백엔드 라우트는 cb_pk_ 를 거부하므로 호출 자체를 막는 것이 디버깅에 유리.
682
- */
683
- private requireServerSideStorageId;
684
- /**
685
- * 세션 매니저 접근 (고급 사용자용).
686
- *
687
- * 외부에서 세션 ID 를 읽어 자체 로깅에 합치거나 강제 세션 종료/재시작이 필요한 경우
688
- * 사용. 일반적으로는 AnalyticsAPI 가 내부적으로 세션을 관리하므로 호출할 필요가 없다.
689
- *
690
- * @returns 내부 SessionManager 인스턴스
691
- */
692
- getSession(): SessionManager;
693
- private canTrack;
694
- private isDNT;
695
- private createBaseEvent;
696
- private enqueue;
697
- private flushQueue;
698
- /**
699
- * sendBeacon 으로 동기 flush (beforeunload 용).
700
- *
701
- * sendBeacon 은 일반 fetch 와 달리 User-Agent 헤더가 신뢰할 수 있지만, 백엔드 봇
702
- * 탐지가 body 의 `user_agent` 필드 첫 번째 값만 보므로 여기서도 동일하게 채워
703
- * 첫 방문이 unload 타이밍에 도달했을 때 봇으로 오판되는 것을 방지한다.
704
- */
705
- private flushSync;
706
- private trackSessionStart;
707
- private startBatchTimer;
708
- private stopBatchTimer;
709
- private startHeartbeat;
710
- private stopHeartbeat;
711
- /** 하트비트를 큐에 넣어 배치 전송 */
712
- private sendHeartbeat;
713
- /** sendBeacon으로 하트비트 전송 (unload/visibility hidden용) */
714
- private sendHeartbeatBeacon;
715
- private setupAutoPageView;
716
- private removeAutoPageView;
717
- private removeHeatmapListeners;
718
- private handleHeatmapClick;
719
- private recordHeatmapEvent;
720
- private heatmapQueue;
721
- private log;
722
58
  }
723
59
 
724
60
  /** 앱 멤버 게스트 가입 응답 */
@@ -757,20 +93,16 @@ interface MemberInfoResponse {
757
93
  member_id: string;
758
94
  nickname: string;
759
95
  is_active: boolean;
760
- custom_data: Record<string, unknown>;
761
- /** 멤버 이메일 (EMAIL identity 에서 추출, 없으면 빈 문자열) */
762
- email?: string;
763
- /** 멤버 역할 — RLS 규칙에서 `auth.role` 로 참조 (예: "admin", "moderator") */
764
- role?: string;
96
+ custom_data: Record<string, any>;
765
97
  }
766
98
  /** 앱 멤버 custom_data 수정 요청 */
767
99
  interface UpdateCustomDataRequest {
768
- custom_data: Record<string, unknown>;
100
+ custom_data: Record<string, any>;
769
101
  }
770
102
  /** 앱 멤버 custom_data 수정 응답 */
771
103
  interface UpdateCustomDataResponse {
772
104
  member_id: string;
773
- custom_data: Record<string, unknown>;
105
+ custom_data: Record<string, any>;
774
106
  }
775
107
  /** 앱 인증 설정 응답 */
776
108
  interface AuthSettingsResponse {
@@ -786,14 +118,7 @@ declare class AuthAPI {
786
118
  private http;
787
119
  private guestMemberLoginPromise;
788
120
  private cachedGuestMemberTokenKey;
789
- private analytics;
790
121
  constructor(http: HttpClient);
791
- /**
792
- * AnalyticsAPI 주입 — 로그인/가입/로그아웃 시 방문자 트래커에 member_id 를 전달하여
793
- * 게스트 방문자를 회원과 연결한다. ConnectBase 컨스트럭터에서 내부적으로 호출된다.
794
- */
795
- _attachAnalytics(analytics: AnalyticsAPI): void;
796
- private notifyVisitorTracker;
797
122
  /**
798
123
  * 앱의 인증 설정 조회
799
124
  * 어떤 로그인 방식이 허용되는지 확인합니다.
@@ -883,44 +208,6 @@ declare class AuthAPI {
883
208
  * ```
884
209
  */
885
210
  updateCustomData(data: UpdateCustomDataRequest): Promise<UpdateCustomDataResponse>;
886
- /**
887
- * Admin: 다른 멤버의 정보를 수정합니다 (role / nickname / custom_data).
888
- *
889
- * 이 메서드는 RLS 의 `auth.role` 평가에 사용되는 `member.role` 필드를 set 할 수 있는
890
- * 유일한 SDK 경로입니다. 보안상 AppMember 자신의 self-update 는 허용되지 않습니다
891
- * (권한 상승 우회 차단) — 이 메서드는 cb_sk_ admin secret key 권한 컨텍스트에서만
892
- * 호출 가능합니다.
893
- *
894
- * 사전 조건:
895
- * - ConnectBase 인스턴스가 `publicKey` 와 `secretKey` 를 모두 가지고 있어야 함
896
- * - secretKey 의 user 가 해당 앱에 권한을 가지고 있어야 함 (서버에서 검증)
897
- *
898
- * @example
899
- * ```typescript
900
- * const cb = new ConnectBase({ publicKey: 'cb_pk_...', secretKey: 'cb_sk_...' })
901
- * await cb.auth.adminUpdateMember('member-uuid', { role: 'admin' })
902
- *
903
- * // role 해제
904
- * await cb.auth.adminUpdateMember('member-uuid', { role: '' })
905
- *
906
- * // 다중 필드
907
- * await cb.auth.adminUpdateMember('member-uuid', {
908
- * nickname: 'Alice',
909
- * role: 'editor',
910
- * custom_data: { level: 5 }
911
- * })
912
- * ```
913
- */
914
- adminUpdateMember(memberID: string, fields: {
915
- nickname?: string;
916
- role?: string;
917
- custom_data?: Record<string, unknown>;
918
- }): Promise<{
919
- member_id: string;
920
- nickname: string;
921
- custom_data: Record<string, unknown>;
922
- role: string;
923
- }>;
924
211
  /**
925
212
  * 로그아웃
926
213
  */
@@ -936,51 +223,26 @@ declare class AuthAPI {
936
223
  private storeGuestMemberTokens;
937
224
  }
938
225
 
939
- /**
940
- * 서버(data-server `ent/schema/table.go` — `ValidSchemaTypes`) 가 허용하는 컬럼 타입 목록.
941
- * - `string` / `number` / `int` : 문자열 / 숫자(float) / 정수
942
- * - `bool` : 참/거짓 (JS 의 `boolean` 이 아니라 `bool` 입니다)
943
- * - `uuid` : UUID 문자열
944
- * - `date` : ISO-8601 날짜/시간
945
- * - `object` / `array` : 중첩 JSON
946
- */
947
- type DataType = 'string' | 'int' | 'number' | 'bool' | 'uuid' | 'date' | 'object' | 'array';
948
- /**
949
- * 컬럼 정보 (Table.schema 맵에서 합성된 객체).
950
- *
951
- * 서버는 컬럼을 별도 레코드로 저장하지 않으므로 `id` 는 컬럼 이름과 동일하며,
952
- * `order` 는 SDK 가 맵 순서대로 부여한 인덱스입니다. `created_at` 은 테이블의
953
- * `created_at` 으로 대체됩니다.
954
- */
226
+ type DataType = 'string' | 'number' | 'boolean' | 'date' | 'object' | 'array';
955
227
  interface ColumnSchema {
956
- /** 컬럼 이름과 동일 (서버 별도 ID 없음) */
957
228
  id: string;
958
229
  name: string;
959
230
  data_type: DataType;
960
231
  is_required: boolean;
961
- default_value?: unknown;
962
- description?: string;
963
- encrypted?: boolean;
964
- /** SDK 가 맵 순서대로 부여한 인덱스 */
232
+ default_value?: string;
233
+ validation_rule?: string;
965
234
  order: number;
966
- /** 테이블의 `created_at` (컬럼 개별 타임스탬프는 없음) */
235
+ description?: string;
967
236
  created_at: string;
968
- /**
969
- * @deprecated 서버가 지원하지 않습니다.
970
- */
971
- validation_rule?: string;
972
- /**
973
- * @deprecated 컬럼 개별 타임스탬프가 없습니다.
974
- */
975
237
  updated_at?: string;
976
238
  }
977
239
  interface CreateColumnRequest {
978
240
  name: string;
979
241
  data_type: DataType;
980
- /** 필수 필드 여부 (기본 `false`) */
981
- is_required?: boolean;
982
- /** 기본값 (서버는 임의 타입을 허용하므로 `unknown`) */
983
- default_value?: unknown;
242
+ is_required: boolean;
243
+ default_value?: string;
244
+ validation_rule?: string;
245
+ order: number;
984
246
  description?: string;
985
247
  /**
986
248
  * 필드 레벨 암호화 활성화 여부.
@@ -989,172 +251,26 @@ interface CreateColumnRequest {
989
251
  * 서버에 SECRET_ENCRYPTION_KEY가 설정되어 있어야 합니다.
990
252
  */
991
253
  encrypted?: boolean;
992
- /**
993
- * @deprecated 서버가 무시합니다. 컬럼 순서는 서버가 자동 관리합니다.
994
- */
995
- order?: number;
996
- /**
997
- * @deprecated 서버가 해당 필드를 저장하지 않습니다.
998
- */
999
- validation_rule?: string;
1000
254
  }
1001
255
  interface UpdateColumnRequest {
256
+ name?: string;
1002
257
  data_type?: DataType;
1003
258
  is_required?: boolean;
1004
- default_value?: unknown;
1005
- description?: string;
1006
- encrypted?: boolean;
1007
- /**
1008
- * @deprecated 서버가 컬럼 이름 변경을 지원하지 않습니다. 변경하려면
1009
- * 삭제 후 재생성하거나 data-server 의 rename 엔드포인트를 사용하세요.
1010
- */
1011
- name?: string;
1012
- /**
1013
- * @deprecated 서버가 무시합니다.
1014
- */
1015
- order?: number;
1016
- /**
1017
- * @deprecated 서버가 해당 필드를 저장하지 않습니다.
1018
- */
259
+ default_value?: string;
1019
260
  validation_rule?: string;
261
+ order?: number;
262
+ description?: string;
1020
263
  }
1021
- /**
1022
- * 서버(data-server `ent/schema/table.go` 의 `Table`) 가 반환하는 테이블 레코드.
1023
- * 필드 이름은 Go ent 모델과 일치합니다.
1024
- */
1025
264
  interface TableSchema {
1026
265
  id: string;
1027
- app_id: string;
1028
- /** 테이블 이름 (ent 필드: `title`) */
1029
- title: string;
1030
- /** 평면 스키마 맵. 컬럼 객체 배열로 변환하려면 `getColumns()` 사용. */
1031
- schema: TableSchemaDefinition;
1032
- /** 검증 스키마 (table-level). 설정되어 있으면 데이터 insert/update 시 평가됨. */
1033
- validation_schema?: ValidationSchema;
1034
- access_level: TableAccessLevel;
1035
- is_active: boolean;
1036
- created_at: string;
1037
- updated_at: string;
1038
- }
1039
- /**
1040
- * 컬럼 정의. 서버는 두 가지 형식 중 하나로 저장합니다:
1041
- * - 플랫: 타입 문자열만 (`'string'`, `'int'` 등)
1042
- * - 중첩: 타입 + 추가 속성 객체 (`{type, required, default, description, encrypted}`)
1043
- *
1044
- * 클라이언트에서 `createTable` 시에는 플랫 형태가 권장되며, 추가 속성은
1045
- * `createColumn` 을 사용하거나 중첩 객체로 직접 지정할 수 있습니다.
1046
- */
1047
- type TableColumnDef = DataType | {
1048
- type: DataType;
1049
- required?: boolean;
1050
- default?: unknown;
266
+ name: string;
1051
267
  description?: string;
1052
- encrypted?: boolean;
1053
- };
1054
- /**
1055
- * 테이블 스키마 정의.
1056
- * 키는 컬럼 이름, 값은 타입 문자열 또는 중첩 컬럼 객체입니다.
1057
- * `$required` 는 특수 키로, 필수 컬럼 이름의 배열을 받습니다.
1058
- *
1059
- * @example
1060
- * ```ts
1061
- * // 플랫 형태
1062
- * { email: 'string', age: 'int', $required: ['email'] }
1063
- *
1064
- * // 중첩 형태 (필드별 옵션)
1065
- * { email: { type: 'string', required: true, encrypted: true } }
1066
- * ```
1067
- */
1068
- interface TableSchemaDefinition {
1069
- /** 필수 컬럼 이름 목록 (서버 hook 이 `$required` 키로 해석) */
1070
- $required?: string[];
1071
- /** 컬럼명 → 컬럼 정의. `$required` 키는 예외적으로 `string[]` */
1072
- [columnName: string]: TableColumnDef | string[] | undefined;
1073
- }
1074
- /** 테이블 접근 수준 */
1075
- type TableAccessLevel = 'Creator' | 'Public' | 'AppMember';
1076
- /** 단일 필드의 검증 규칙 */
1077
- interface ValidationSchemaField {
1078
- /** 데이터 타입 */
1079
- type: DataType;
1080
- /** 필수 여부 */
1081
- required?: boolean;
1082
- /** 기본값 */
1083
- default?: unknown;
1084
- /** 숫자 최소값 */
1085
- min?: number;
1086
- /** 숫자 최대값 */
1087
- max?: number;
1088
- /** 문자열 최소 길이 */
1089
- minLength?: number;
1090
- /** 문자열 최대 길이 */
1091
- maxLength?: number;
1092
- /** 정규식 패턴 (Go regexp 호환) */
1093
- pattern?: string;
1094
- /** 허용 값 목록 */
1095
- enum?: unknown[];
1096
- /** 배열 아이템 스키마 */
1097
- items?: ValidationSchemaField;
1098
- /** object 의 속성 스키마 */
1099
- properties?: Record<string, ValidationSchemaField>;
1100
- /** 수정 불가 (immutable) */
1101
- immutable?: boolean;
1102
- /** 유니크 제약 */
1103
- unique?: boolean;
1104
- /** 새니타이징 ('html', 'xss', 'trim') */
1105
- sanitize?: string;
1106
- /** 자동 계산 표현식 */
1107
- computed?: string;
1108
- /** 상태 전이 규칙: 현재상태 → 허용되는 다음 상태들 */
1109
- transitions?: Record<string, string[]>;
1110
- }
1111
- /** 상태 전이 규칙 (객체 단위 — 필드 + 전이 맵) */
1112
- interface ValidationStateTransitions {
1113
- field: string;
1114
- transitions: Record<string, string[]>;
1115
- }
1116
- /**
1117
- * 테이블 검증 스키마 (table-level).
1118
- * 데이터 insert/update 시 자동으로 평가됩니다.
1119
- *
1120
- * @example
1121
- * ```ts
1122
- * {
1123
- * fields: {
1124
- * email: { type: 'string', pattern: '^.+@.+$', required: true },
1125
- * age: { type: 'int', min: 0, max: 150 }
1126
- * },
1127
- * required: ['email'],
1128
- * immutable: ['email']
1129
- * }
1130
- * ```
1131
- */
1132
- interface ValidationSchema {
1133
- fields: Record<string, ValidationSchemaField>;
1134
- required?: string[];
1135
- unique?: string[];
1136
- immutable?: string[];
1137
- computed?: Record<string, string>;
1138
- transitions?: Record<string, ValidationStateTransitions>;
268
+ columns: ColumnSchema[];
269
+ created_at: string;
270
+ updated_at?: string;
1139
271
  }
1140
272
  interface CreateTableRequest {
1141
- /** 테이블 이름 (서버는 내부적으로 `title` 로 저장) */
1142
273
  name: string;
1143
- /**
1144
- * 초기 컬럼 스키마 (선택).
1145
- * 생략하면 컬럼이 없는 빈 테이블로 생성되며, 이후 `addColumn()` 으로 추가할 수 있습니다.
1146
- */
1147
- schema?: TableSchemaDefinition;
1148
- /**
1149
- * 접근 수준 (선택, 기본값 `'Creator'`).
1150
- * - `Creator`: 테이블 생성자만 전체 권한
1151
- * - `Public`: 누구나 읽기/쓰기
1152
- * - `AppMember`: 앱 멤버만 읽기/쓰기
1153
- */
1154
- accessLevel?: TableAccessLevel;
1155
- /**
1156
- * @deprecated 서버에 저장되지 않습니다. 필드는 backward-compat 을 위해 남아있으며 SDK 가 서버로 전송하지 않습니다.
1157
- */
1158
274
  description?: string;
1159
275
  }
1160
276
  interface DataItem {
@@ -1169,21 +285,9 @@ interface CreateDataRequest {
1169
285
  interface UpdateDataRequest {
1170
286
  data: Record<string, unknown>;
1171
287
  }
1172
- /**
1173
- * 테이블 데이터 조회 응답.
1174
- *
1175
- * 서버(data-server `internal_data_controller.go` 의 `FetchDataByTableGET`,
1176
- * `QueryDataByTable`) 가 반환하는 JSON 과 1:1 매핑합니다.
1177
- *
1178
- * 참고: `1.3.0` 이전 버전(`0.16.1` 기준)에서는 `datas` / `total_size` 로
1179
- * 선언돼 있었으나, 실제 서버 wire 포맷은 `data` / `total_count` 이므로 런타임에
1180
- * 속성 접근이 `undefined` 를 반환하는 결함이 있었습니다. `1.4.0` 에서 바로잡습니다.
1181
- */
1182
288
  interface FetchDataResponse {
1183
- /** 조회된 문서 배열 */
1184
- data: DataItem[];
1185
- /** 총 매칭 문서 수 */
1186
- total_count: number;
289
+ datas: DataItem[];
290
+ total_size: number;
1187
291
  }
1188
292
  interface QueryOptions {
1189
293
  limit?: number;
@@ -1330,7 +434,7 @@ interface PopulateOption {
1330
434
  populate?: PopulateOption[];
1331
435
  }
1332
436
  type TriggerEvent = 'create' | 'update' | 'delete' | 'all';
1333
- type TriggerHandlerType = 'function' | 'workflow' | 'webhook' | 'inline';
437
+ type TriggerHandlerType = 'function' | 'workflow' | 'webhook';
1334
438
  interface Trigger {
1335
439
  id: string;
1336
440
  app_id: string;
@@ -1341,7 +445,6 @@ interface Trigger {
1341
445
  handler_type: TriggerHandlerType;
1342
446
  handler_id?: string;
1343
447
  handler_url?: string;
1344
- handler_action?: Record<string, unknown>;
1345
448
  is_active: boolean;
1346
449
  order: number;
1347
450
  created_at: string;
@@ -1354,7 +457,6 @@ interface CreateTriggerRequest {
1354
457
  handler_type: TriggerHandlerType;
1355
458
  handler_id?: string;
1356
459
  handler_url?: string;
1357
- handler_action?: Record<string, unknown>;
1358
460
  condition?: Record<string, unknown>;
1359
461
  is_active?: boolean;
1360
462
  order?: number;
@@ -1365,7 +467,6 @@ interface UpdateTriggerRequest {
1365
467
  handler_type?: TriggerHandlerType;
1366
468
  handler_id?: string;
1367
469
  handler_url?: string;
1368
- handler_action?: Record<string, unknown>;
1369
470
  condition?: Record<string, unknown>;
1370
471
  is_active?: boolean;
1371
472
  order?: number;
@@ -1487,31 +588,6 @@ interface TransactionWrite {
1487
588
  exists?: boolean;
1488
589
  };
1489
590
  }
1490
- interface BatchOperationResult {
1491
- index: number;
1492
- success: boolean;
1493
- doc_id?: string;
1494
- error?: string;
1495
- }
1496
- interface BatchWriteResult {
1497
- success: boolean;
1498
- results: BatchOperationResult[];
1499
- total_count?: number;
1500
- success_count?: number;
1501
- failed_count?: number;
1502
- }
1503
- interface TransactionWriteResult {
1504
- index: number;
1505
- doc_id?: string;
1506
- success: boolean;
1507
- }
1508
- interface TransactionResult {
1509
- success: boolean;
1510
- transaction_id?: string;
1511
- reads?: Record<string, unknown>;
1512
- results?: TransactionWriteResult[];
1513
- error?: string;
1514
- }
1515
591
  interface TTLConfig {
1516
592
  table_name: string;
1517
593
  field: string;
@@ -1603,11 +679,7 @@ interface DatabaseRealtimeSubscription {
1603
679
  }
1604
680
  /** 데이터베이스 실시간 연결 옵션 */
1605
681
  interface DatabaseRealtimeConnectOptions {
1606
- /**
1607
- * 액세스 토큰 (필수). 두 종류를 모두 지원:
1608
- * - 앱 멤버(AppMember) JWT — 자기 앱의 테이블 구독
1609
- * - cross-app OAuth access token — Provider 앱의 테이블 구독 (`database:read` scope 필요)
1610
- */
682
+ /** JWT 액세스 토큰 (필수) */
1611
683
  accessToken: string;
1612
684
  /** data-server URL (기본: baseUrl) */
1613
685
  dataServerUrl?: string;
@@ -1618,6 +690,14 @@ interface DatabaseRealtimeConnectOptions {
1618
690
  /** 디버그 로깅 (기본: false) */
1619
691
  debug?: boolean;
1620
692
  }
693
+ /** 데이터베이스 실시간 프레즌스 상태 */
694
+ interface DatabasePresenceState {
695
+ user_id: string;
696
+ status: 'online' | 'away' | 'offline';
697
+ last_seen: string;
698
+ device?: string;
699
+ metadata?: Record<string, unknown>;
700
+ }
1621
701
  interface CreateBackupRequest {
1622
702
  /** 백업 이름 */
1623
703
  name: string;
@@ -1672,17 +752,6 @@ interface ExportDataResponse {
1672
752
  data?: Record<string, unknown>;
1673
753
  schema?: Record<string, unknown>;
1674
754
  }
1675
- /**
1676
- * 데이터 가져오기 요청
1677
- * @example
1678
- * {
1679
- * data: {
1680
- * users: { rows: [{ name: 'Alice', age: 30 }] },
1681
- * orders: { rows: [{ user: 'Alice', amount: 1000 }] }
1682
- * },
1683
- * mode: 'merge'
1684
- * }
1685
- */
1686
755
  interface ImportDataRequest {
1687
756
  /** 가져올 데이터: { "테이블이름": { "rows": [{...}, ...] }, ... } */
1688
757
  data: Record<string, {
@@ -1803,109 +872,37 @@ declare class DatabaseAPI {
1803
872
  private activeSubscriptions;
1804
873
  constructor(http: HttpClient);
1805
874
  /**
1806
- * Database 공개 API 접두사.
1807
- *
1808
- * 서버의 테이블/컬럼/데이터 CRUD 는 모두 `/v1/public/*` 경로에서만 제공되며
1809
- * (core-server → data-server 프록시), JWT 기반 `/v1/*` 경로는 존재하지 않습니다.
1810
- * 따라서 항상 `/v1/public` 을 사용합니다. 인증은 `X-Public-Key` 헤더에
1811
- * `publicKey` (`cb_pk_*`) 를 실어 서버가 검증합니다.
875
+ * API Key 인증 시 /v1/public 접두사 반환
1812
876
  */
1813
877
  private getPublicPrefix;
1814
878
  /**
1815
879
  * 테이블 목록 조회
1816
880
  */
1817
- getTables(): Promise<TableSchema[]>;
1818
- /**
1819
- * 테이블 상세 조회
1820
- */
1821
- getTable(tableId: string): Promise<TableSchema>;
881
+ getTables(databaseId: string): Promise<TableSchema[]>;
1822
882
  /**
1823
- * 테이블 생성.
1824
- *
1825
- * 서버 DTO (`{title, schema, access_level}`) 로 변환하여 전송합니다.
1826
- * `schema` 가 비어있으면 요청 본문에서 키 자체를 생략하여 빈 테이블을
1827
- * 생성합니다. (서버는 explicit 빈 맵을 거부)
1828
- *
1829
- * 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
1830
- * 생성된 테이블 메타데이터가 필요하면 이어서 `getTables()` 로 조회하세요.
883
+ * 테이블 생성
1831
884
  */
1832
- createTable(data: CreateTableRequest): Promise<void>;
1833
- /**
1834
- * 테이블 수정.
1835
- *
1836
- * `name` 은 서버의 `title` 로, `accessLevel` 은 `access_level` 로 매핑됩니다.
1837
- */
1838
- updateTable(tableId: string, data: Partial<CreateTableRequest>): Promise<void>;
885
+ createTable(databaseId: string, data: CreateTableRequest): Promise<TableSchema>;
1839
886
  /**
1840
887
  * 테이블 삭제
1841
888
  */
1842
- deleteTable(tableId: string): Promise<void>;
1843
- /**
1844
- * 테이블 검증 스키마 조회.
1845
- *
1846
- * 검증 스키마가 설정되어 있지 않으면 `null` 을 반환합니다.
1847
- * Public Key 인증으로 호출 가능 (조회 권한).
1848
- */
1849
- getValidationSchema(tableId: string): Promise<ValidationSchema | null>;
1850
- /**
1851
- * 테이블 검증 스키마 설정/교체.
1852
- *
1853
- * **권한**: 콘솔 (앱 소유자 JWT) 전용. Public Key 로는 설정 불가.
1854
- * SDK 에서는 `accessToken` 이 설정되어 있어야 하며, 콘솔에서 사용하는 패턴입니다.
1855
- *
1856
- * 빈 fields 는 거부됩니다. 검증 스키마를 제거하려면 `deleteValidationSchema` 사용.
1857
- *
1858
- * @example
1859
- * ```ts
1860
- * await cb.database.setValidationSchema(tableId, {
1861
- * fields: {
1862
- * email: { type: 'string', pattern: '^.+@.+$', required: true },
1863
- * age: { type: 'int', min: 0, max: 150 }
1864
- * },
1865
- * required: ['email'],
1866
- * immutable: ['email']
1867
- * })
1868
- * ```
1869
- */
1870
- setValidationSchema(tableId: string, schema: ValidationSchema): Promise<void>;
889
+ deleteTable(databaseId: string, tableId: string): Promise<void>;
1871
890
  /**
1872
- * 테이블 검증 스키마 제거 (schemaless 모드로 복귀).
1873
- *
1874
- * **권한**: 콘솔 (앱 소유자 JWT) 전용.
1875
- */
1876
- deleteValidationSchema(tableId: string): Promise<void>;
1877
- /**
1878
- * appId 가 설정되어 있어야 콘솔 경로를 호출할 수 있습니다 (validation_schema set/delete).
1879
- */
1880
- private requireAppId;
1881
- /**
1882
- * 컬럼 목록 조회 (`Table.schema` 맵을 컬럼 객체 배열로 변환).
1883
- *
1884
- * 서버는 컬럼을 다음 두 가지 중 하나로 저장합니다:
1885
- * - 플랫: `"email": "string"` (추가 속성이 없는 경우)
1886
- * - 중첩: `"email": { "type": "string", "required": true, ... }` (추가 속성이 있는 경우)
1887
- *
1888
- * `$required` 키는 별도 배열로 필수 컬럼을 지정할 수 있으며, 중첩 객체의
1889
- * `required: true` 와 병합되어 판정됩니다.
891
+ * 컬럼 목록 조회
1890
892
  */
1891
893
  getColumns(tableId: string): Promise<ColumnSchema[]>;
1892
894
  /**
1893
- * 컬럼 생성.
1894
- *
1895
- * 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
1896
- * 생성 결과 컬럼을 얻으려면 이어서 `getColumns(tableId)` 로 조회하세요.
895
+ * 컬럼 생성
1897
896
  */
1898
- createColumn(tableId: string, data: CreateColumnRequest): Promise<void>;
897
+ createColumn(tableId: string, data: CreateColumnRequest): Promise<ColumnSchema>;
1899
898
  /**
1900
- * 컬럼 수정.
1901
- *
1902
- * 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
899
+ * 컬럼 수정
1903
900
  */
1904
- updateColumn(tableId: string, columnName: string, data: UpdateColumnRequest): Promise<void>;
901
+ updateColumn(tableId: string, columnId: string, data: UpdateColumnRequest): Promise<ColumnSchema>;
1905
902
  /**
1906
903
  * 컬럼 삭제
1907
904
  */
1908
- deleteColumn(tableId: string, columnName: string): Promise<void>;
905
+ deleteColumn(tableId: string, columnId: string): Promise<void>;
1909
906
  /**
1910
907
  * 데이터 조회 (페이지네이션)
1911
908
  */
@@ -1919,19 +916,9 @@ declare class DatabaseAPI {
1919
916
  */
1920
917
  getDataById(tableId: string, dataId: string): Promise<DataItem>;
1921
918
  /**
1922
- * 데이터 생성.
1923
- *
1924
- * `tableIdOrName` 은 테이블 UUID 또는 이름. UUID 형식이면 기존 경로
1925
- * (`/tables/:tableId/data`), 그렇지 않으면 이름 기반 경로
1926
- * (`/tables/name/:tableName/data`) 를 사용합니다.
1927
- *
1928
- * `options.autoCreate: true` 시 테이블이 없으면 빈 schemaless 테이블을
1929
- * 자동으로 생성한 뒤 insert. opt-in 이며 프로토타입/AI 자동화 워크플로에
1930
- * 권장합니다. production 앱은 명시적 `createTable` 호출 권장.
919
+ * 데이터 생성
1931
920
  */
1932
- createData(tableIdOrName: string, data: CreateDataRequest, options?: {
1933
- autoCreate?: boolean;
1934
- }): Promise<DataItem>;
921
+ createData(tableId: string, data: CreateDataRequest): Promise<DataItem>;
1935
922
  /**
1936
923
  * 데이터 수정 (부분 업데이트)
1937
924
  * 제공된 필드만 수정되고, 기존 필드는 유지됩니다.
@@ -1972,18 +959,16 @@ declare class DatabaseAPI {
1972
959
  }): Promise<GeoResponse>;
1973
960
  /**
1974
961
  * 배치 쓰기 (여러 테이블 다중 문서 원자적 처리)
1975
- *
1976
- * 서버는 HTTP 200 + `success:false` + 개별 op `error` 로 부분 실패를 표현한다.
1977
- * SDK 는 호출자가 silent success 로 오해하지 않도록 첫 실패 op 의 메시지로 throw 한다.
1978
962
  */
1979
- batch(operations: BatchOperation[]): Promise<BatchWriteResult>;
963
+ batch(operations: BatchOperation[]): Promise<{
964
+ results: Record<string, unknown>[];
965
+ }>;
1980
966
  /**
1981
967
  * 트랜잭션 실행 (읽기 → 쓰기 ACID)
1982
- *
1983
- * 서버는 부분 실패가 없는 ACID 트랜잭션을 보장하지만, 검증/RLS/충돌은
1984
- * `success:false` + `error` 로 알린다. SDK 는 silent success 회귀 방지 차원에서 throw.
1985
968
  */
1986
- transaction(reads: TransactionRead[], writes: TransactionWrite[]): Promise<TransactionResult>;
969
+ transaction(reads: TransactionRead[], writes: TransactionWrite[]): Promise<{
970
+ results: Record<string, unknown>;
971
+ }>;
1987
972
  /**
1988
973
  * 데이터 조회 시 릴레이션 로딩 (JOIN)
1989
974
  */
@@ -2029,23 +1014,17 @@ declare class DatabaseAPI {
2029
1014
  createGeoIndex(appId: string, tableId: string, data: CreateGeoIndexRequest): Promise<GeoIndex>;
2030
1015
  deleteGeoIndex(appId: string, tableId: string, indexId: string): Promise<void>;
2031
1016
  /**
2032
- * 테이블 릴레이션 목록 조회. `sourceTable` 생략 시 앱 전체의 릴레이션을 반환.
2033
- *
2034
- * 서버 경로: `GET /v1/apps/:appID/databases/relations[?source_table=...]`
1017
+ * 테이블 릴레이션 목록 조회
2035
1018
  */
2036
- listRelations(appId: string, sourceTable?: string): Promise<TableRelation[]>;
1019
+ listRelations(appId: string, tableId: string): Promise<TableRelation[]>;
2037
1020
  /**
2038
- * 릴레이션 생성.
2039
- *
2040
- * 서버 경로: `POST /v1/apps/:appID/databases/relations` — body 내 `source_table`/`target_table` 로 테이블 지정.
1021
+ * 릴레이션 생성
2041
1022
  */
2042
- createRelation(appId: string, data: CreateRelationRequest): Promise<TableRelation>;
1023
+ createRelation(appId: string, tableId: string, data: CreateRelationRequest): Promise<TableRelation>;
2043
1024
  /**
2044
- * 릴레이션 삭제. `relationId` 는 서버에서 발급한 UUID (listRelations 로 조회해 얻음).
2045
- *
2046
- * 서버 경로: `DELETE /v1/apps/:appID/databases/relations/:relationID`
1025
+ * 릴레이션 삭제
2047
1026
  */
2048
- deleteRelation(appId: string, relationId: string): Promise<void>;
1027
+ deleteRelation(appId: string, tableId: string, relationName: string): Promise<void>;
2049
1028
  /**
2050
1029
  * 트리거 목록 조회
2051
1030
  */
@@ -2108,26 +1087,14 @@ declare class DatabaseAPI {
2108
1087
  * 데이터베이스 실시간 연결
2109
1088
  * data-server의 WebSocket에 연결하여 데이터 변경을 실시간으로 수신합니다.
2110
1089
  *
2111
- * `accessToken` 은 두 종류를 모두 받습니다:
2112
- * - 앱 멤버(AppMember) JWT — 자기 앱의 테이블을 구독
2113
- * - cross-app OAuth access token — Provider 앱이 `database:read` scope 를 노출했을 때,
2114
- * Consumer 가 Provider 앱의 테이블을 구독 (구독 대상 앱은 토큰 `aud` 로 결정되므로
2115
- * Provider 의 publicKey 는 불필요). RLS 는 토큰 `end_user_id` 를 subject 로 평가합니다.
2116
- *
2117
1090
  * @example
2118
1091
  * ```typescript
2119
- * // 자기 앱 테이블 구독 (AppMember JWT)
1092
+ * // 연결
2120
1093
  * cb.database.connectRealtime({
2121
1094
  * accessToken: 'your-jwt-token',
2122
1095
  * dataServerUrl: 'https://data.connectbase.world'
2123
1096
  * })
2124
1097
  *
2125
- * // cross-app: Provider 앱의 테이블 구독 (OAuth access token)
2126
- * cb.database.connectRealtime({
2127
- * accessToken: oauthAccessToken,
2128
- * dataServerUrl: 'https://data.connectbase.world'
2129
- * })
2130
- *
2131
1098
  * // 테이블 구독
2132
1099
  * const sub = cb.database.subscribe('users', {
2133
1100
  * onSnapshot: (docs, info) => {
@@ -2162,6 +1129,14 @@ declare class DatabaseAPI {
2162
1129
  * @returns 구독 해제 가능한 객체
2163
1130
  */
2164
1131
  subscribe(tableId: string, handlers: DatabaseRealtimeHandlers, options?: DatabaseSubscribeOptions): DatabaseRealtimeSubscription;
1132
+ /**
1133
+ * 프레즌스 상태 설정
1134
+ */
1135
+ setPresence(status: string, device?: string, metadata?: Record<string, unknown>): void;
1136
+ /**
1137
+ * 프레즌스 구독 (다른 사용자의 온라인 상태 감시)
1138
+ */
1139
+ subscribePresence(userIds: string[], onPresence: (states: Record<string, DatabasePresenceState>) => void): void;
2165
1140
  /**
2166
1141
  * 실시간 연결 상태 확인
2167
1142
  */
@@ -2391,28 +1366,10 @@ declare class StorageAPI {
2391
1366
  * API Key 인증 시 /v1/public 접두사 반환
2392
1367
  */
2393
1368
  private getPublicPrefix;
2394
- /**
2395
- * Presigned URL 로 직접 업로드. 호출 전에 URL 스킴(https)을 검증해
2396
- * 서버 응답을 그대로 신뢰하는 SSRF/오용 경로를 차단.
2397
- * 타임아웃과 외부 signal 을 모두 지원한다.
2398
- */
2399
- private uploadToPresigned;
2400
1369
  /**
2401
1370
  * 파일 목록 조회
2402
- *
2403
- * @param storageId - 파일 스토리지 ID
2404
- * @param parentId - 부모 폴더 ID (미지정 시 루트 폴더)
2405
- *
2406
- * @example
2407
- * ```ts
2408
- * // 루트 폴더의 파일 목록
2409
- * const files = await cb.storage.getFiles('storage-id')
2410
- *
2411
- * // 특정 폴더의 하위 파일 목록
2412
- * const subFiles = await cb.storage.getFiles('storage-id', 'folder-id')
2413
- * ```
2414
1371
  */
2415
- getFiles(storageId: string, parentId?: string): Promise<FileItem[]>;
1372
+ getFiles(storageId: string): Promise<FileItem[]>;
2416
1373
  /**
2417
1374
  * 파일 업로드 (Presigned URL 방식)
2418
1375
  *
@@ -2440,14 +1397,10 @@ declare class StorageAPI {
2440
1397
  deleteFile(storageId: string, fileId: string): Promise<void>;
2441
1398
  /**
2442
1399
  * 파일/폴더 이동
2443
- *
2444
- * Public Key 인증만으로는 이 엔드포인트가 노출되지 않습니다. JWT(콘솔 토큰)가 필요합니다.
2445
1400
  */
2446
1401
  moveFile(storageId: string, fileId: string, data: MoveFileRequest): Promise<void>;
2447
1402
  /**
2448
1403
  * 파일/폴더 이름 변경
2449
- *
2450
- * Public Key 인증만으로는 이 엔드포인트가 노출되지 않습니다. JWT(콘솔 토큰)가 필요합니다.
2451
1404
  */
2452
1405
  renameFile(storageId: string, fileId: string, data: RenameFileRequest): Promise<RenameFileResponse>;
2453
1406
  /**
@@ -2571,15 +1524,15 @@ declare class StorageAPI {
2571
1524
  }
2572
1525
 
2573
1526
  /**
2574
- * Public Key 관련 타입 정의
1527
+ * API Key 관련 타입 정의
2575
1528
  */
2576
1529
  /**
2577
- * Public Key 아이템
1530
+ * API Key 아이템
2578
1531
  */
2579
- interface PublicKeyItem {
2580
- /** Public Key ID */
1532
+ interface ApiKeyItem {
1533
+ /** API Key ID */
2581
1534
  id: string;
2582
- /** Public Key 이름 */
1535
+ /** API Key 이름 */
2583
1536
  name: string;
2584
1537
  /** 키 앞 12자리 (표시용) */
2585
1538
  key_prefix: string;
@@ -2593,21 +1546,21 @@ interface PublicKeyItem {
2593
1546
  created_at: string;
2594
1547
  }
2595
1548
  /**
2596
- * Public Key 생성 요청
1549
+ * API Key 생성 요청
2597
1550
  */
2598
- interface CreatePublicKeyRequest {
2599
- /** Public Key 이름 (예: Production, Development) */
1551
+ interface CreateApiKeyRequest {
1552
+ /** API Key 이름 (예: Production, Development) */
2600
1553
  name: string;
2601
1554
  /** 만료일 (옵션) */
2602
1555
  expires_at?: string;
2603
1556
  }
2604
1557
  /**
2605
- * Public Key 생성 응답 (전체 키는 이때만 반환됨)
1558
+ * API Key 생성 응답 (전체 키는 이때만 반환됨)
2606
1559
  */
2607
- interface CreatePublicKeyResponse {
2608
- /** Public Key ID */
1560
+ interface CreateApiKeyResponse {
1561
+ /** API Key ID */
2609
1562
  id: string;
2610
- /** Public Key 이름 */
1563
+ /** API Key 이름 */
2611
1564
  name: string;
2612
1565
  /** 전체 키 값 (생성 시에만 반환, 안전하게 보관 필요) */
2613
1566
  key: string;
@@ -2621,24 +1574,24 @@ interface CreatePublicKeyResponse {
2621
1574
  created_at: string;
2622
1575
  }
2623
1576
  /**
2624
- * Public Key 목록 조회 응답
1577
+ * API Key 목록 조회 응답
2625
1578
  */
2626
- interface FetchPublicKeysResponse {
2627
- public_keys: PublicKeyItem[];
1579
+ interface FetchApiKeysResponse {
1580
+ api_keys: ApiKeyItem[];
2628
1581
  }
2629
1582
  /**
2630
- * Public Key 수정 요청
1583
+ * API Key 수정 요청
2631
1584
  */
2632
- interface UpdatePublicKeyRequest {
2633
- /** Public Key 이름 변경 */
1585
+ interface UpdateApiKeyRequest {
1586
+ /** API Key 이름 변경 */
2634
1587
  name?: string;
2635
1588
  /** 활성화/비활성화 */
2636
1589
  is_active?: boolean;
2637
1590
  }
2638
1591
  /**
2639
- * Public Key 수정 응답
1592
+ * API Key 수정 응답
2640
1593
  */
2641
- interface UpdatePublicKeyResponse {
1594
+ interface UpdateApiKeyResponse {
2642
1595
  id: string;
2643
1596
  name: string;
2644
1597
  key_prefix: string;
@@ -2648,55 +1601,55 @@ interface UpdatePublicKeyResponse {
2648
1601
  }
2649
1602
 
2650
1603
  /**
2651
- * Public Key 관리 API
1604
+ * API Key 관리 API
2652
1605
  *
2653
1606
  * @example
2654
1607
  * ```typescript
2655
- * // Public Key 목록 조회
2656
- * const keys = await cb.publicKey.getPublicKeys('app-id')
1608
+ * // API Key 목록 조회
1609
+ * const keys = await cb.apiKey.getApiKeys('app-id')
2657
1610
  *
2658
- * // 새 Public Key 생성
2659
- * const newKey = await cb.publicKey.createPublicKey('app-id', { name: 'Production' })
1611
+ * // 새 API Key 생성
1612
+ * const newKey = await cb.apiKey.createApiKey('app-id', { name: 'Production' })
2660
1613
  * console.log(newKey.key) // 전체 키 (이때만 볼 수 있음!)
2661
1614
  *
2662
- * // Public Key 비활성화
2663
- * await cb.publicKey.updatePublicKey('app-id', 'key-id', { is_active: false })
1615
+ * // API Key 비활성화
1616
+ * await cb.apiKey.updateApiKey('app-id', 'key-id', { is_active: false })
2664
1617
  *
2665
- * // Public Key 삭제
2666
- * await cb.publicKey.deletePublicKey('app-id', 'key-id')
1618
+ * // API Key 삭제
1619
+ * await cb.apiKey.deleteApiKey('app-id', 'key-id')
2667
1620
  * ```
2668
1621
  */
2669
- declare class PublicKeyAPI {
1622
+ declare class ApiKeyAPI {
2670
1623
  private http;
2671
1624
  constructor(http: HttpClient);
2672
1625
  /**
2673
- * 앱의 Public Key 목록을 조회합니다
1626
+ * 앱의 API Key 목록을 조회합니다
2674
1627
  * @param appId 앱 ID
2675
1628
  */
2676
- getPublicKeys(appId: string): Promise<FetchPublicKeysResponse>;
1629
+ getApiKeys(appId: string): Promise<FetchApiKeysResponse>;
2677
1630
  /**
2678
- * 새 Public Key 를 생성합니다
1631
+ * 새 API Key를 생성합니다
2679
1632
  *
2680
1633
  * **중요**: 반환되는 `key` 값은 이 응답에서만 볼 수 있습니다.
2681
1634
  * 안전한 곳에 저장하세요.
2682
1635
  *
2683
1636
  * @param appId 앱 ID
2684
- * @param data 생성할 Public Key 정보
1637
+ * @param data 생성할 API Key 정보
2685
1638
  */
2686
- createPublicKey(appId: string, data: CreatePublicKeyRequest): Promise<CreatePublicKeyResponse>;
1639
+ createApiKey(appId: string, data: CreateApiKeyRequest): Promise<CreateApiKeyResponse>;
2687
1640
  /**
2688
- * Public Key 를 수정합니다 (이름 변경, 활성화/비활성화)
1641
+ * API Key를 수정합니다 (이름 변경, 활성화/비활성화)
2689
1642
  * @param appId 앱 ID
2690
- * @param keyId Public Key ID
1643
+ * @param keyId API Key ID
2691
1644
  * @param data 수정할 정보
2692
1645
  */
2693
- updatePublicKey(appId: string, keyId: string, data: UpdatePublicKeyRequest): Promise<UpdatePublicKeyResponse>;
1646
+ updateApiKey(appId: string, keyId: string, data: UpdateApiKeyRequest): Promise<UpdateApiKeyResponse>;
2694
1647
  /**
2695
- * Public Key 를 삭제합니다
1648
+ * API Key를 삭제합니다
2696
1649
  * @param appId 앱 ID
2697
- * @param keyId Public Key ID
1650
+ * @param keyId API Key ID
2698
1651
  */
2699
- deletePublicKey(appId: string, keyId: string): Promise<void>;
1652
+ deleteApiKey(appId: string, keyId: string): Promise<void>;
2700
1653
  }
2701
1654
 
2702
1655
  /**
@@ -2757,36 +1710,6 @@ declare class FunctionsAPI {
2757
1710
  * ```
2758
1711
  */
2759
1712
  invoke(functionId: string, payload?: Record<string, unknown>, timeout?: number): Promise<InvokeFunctionResponse>;
2760
- /**
2761
- * cross-app OAuth 액세스 토큰으로 다른 앱(Provider)의 함수를 실행합니다.
2762
- *
2763
- * Provider 앱이 OAuth Provider 로 `function:invoke` scope 를 노출하고, Consumer 앱이
2764
- * cross-app OAuth 로 발급받은 access token 으로 Provider 의 함수를 호출하는 경로입니다.
2765
- * 호출자 신원은 서버가 토큰에서 추출해 함수 런타임의 `ctx.memberId`(end-user) /
2766
- * `ctx.callerAppId`(Consumer 앱) 로 전달하므로, 함수 핸들러가 호출자별 로직을 구현할 수 있습니다.
2767
- *
2768
- * 일반 `invoke()` 와 달리 SDK 클라이언트의 publicKey/멤버 토큰이 아니라 전달된
2769
- * `accessToken` 만 `Authorization: Bearer` 로 사용합니다.
2770
- *
2771
- * @param providerAppId - 함수를 소유한 Provider 앱 ID (토큰 `aud` 의 `app:<id>` 와 일치해야 함)
2772
- * @param functionId - 실행할 함수 ID
2773
- * @param accessToken - cross-app OAuth access token
2774
- * @param payload - 함수에 전달할 데이터 (선택)
2775
- * @param timeout - 실행 타임아웃 (초, 선택)
2776
- * @returns 함수 실행 결과
2777
- *
2778
- * @example
2779
- * ```typescript
2780
- * // Consumer 앱이 Provider(ai-tool)의 함수를 호출
2781
- * const result = await cb.functions.invokeCrossApp(
2782
- * 'provider-app-id',
2783
- * 'models-generate-image',
2784
- * oauthAccessToken,
2785
- * { prompt: 'a cat' }
2786
- * )
2787
- * ```
2788
- */
2789
- invokeCrossApp(providerAppId: string, functionId: string, accessToken: string, payload?: Record<string, unknown>, timeout?: number): Promise<InvokeFunctionResponse>;
2790
1713
  /**
2791
1714
  * 서버리스 함수 실행 (비동기 래퍼)
2792
1715
  * 결과만 반환하고 메타데이터는 제외
@@ -2802,25 +1725,6 @@ declare class FunctionsAPI {
2802
1725
  * ```
2803
1726
  */
2804
1727
  call<T = unknown>(functionId: string, payload?: Record<string, unknown>): Promise<T>;
2805
- /**
2806
- * 함수의 raw HTTP webhook 수신용 공개 URL 을 반환합니다.
2807
- *
2808
- * 함수가 `http_trigger_enabled=true` 로 생성/수정되어야 동작합니다.
2809
- * 이 URL 을 외부 SaaS (Discord Interactions, Stripe Webhooks, GitHub Webhooks,
2810
- * Slack Events 등) 에 등록하면 raw HTTP 요청이 함수 핸들러에 그대로 전달됩니다.
2811
- *
2812
- * - body 는 wrap 없이 raw bytes 그대로 (Ed25519/HMAC 서명 검증 호환)
2813
- * - 모든 헤더 forward (X-Signature-*, Stripe-Signature 등)
2814
- * - 함수가 `{statusCode, headers, body}` 를 반환하면 그대로 HTTP 응답에 매핑
2815
- *
2816
- * @example
2817
- * ```typescript
2818
- * // Discord 봇 endpoint 등록용 URL
2819
- * const url = cb.functions.getWebhookURL('function-id')
2820
- * // → https://api.connectbase.world/v1/public/functions/<id>/webhook
2821
- * ```
2822
- */
2823
- getWebhookURL(functionId: string): string;
2824
1728
  }
2825
1729
 
2826
1730
  /**
@@ -2901,47 +1805,22 @@ interface ClientMessage {
2901
1805
  /** 서버 -> 클라이언트 메시지 */
2902
1806
  interface ServerMessage<T = unknown> {
2903
1807
  category?: string;
2904
- event: 'connected' | 'subscribed' | 'unsubscribed' | 'message' | 'sent' | 'result' | 'error' | 'pong' | 'history' | 'stream_token' | 'stream_done' | 'stream_error' | 'stream_tool_call' | 'stream_tool_result' | 'read_receipt' | 'presence' | 'presence_status' | 'typing';
1808
+ event: 'connected' | 'subscribed' | 'unsubscribed' | 'message' | 'sent' | 'result' | 'error' | 'pong' | 'history' | 'stream_token' | 'stream_done' | 'stream_error' | 'read_receipt' | 'presence' | 'presence_status' | 'typing';
2905
1809
  data?: T;
2906
1810
  request_id?: string;
2907
1811
  error?: string;
2908
1812
  timestamp: number;
2909
1813
  }
2910
- /** AI 스트리밍 메시지의 단일 텍스트 파트 (멀티모달 content array 항목). OpenAI Vision spec. */
2911
- interface StreamTextPart {
2912
- type: 'text';
2913
- text: string;
2914
- }
2915
- /** AI 스트리밍 메시지의 이미지 파트. URL 은 https://... 또는 data:image/...;base64,... */
2916
- interface StreamImageURLPart {
2917
- type: 'image_url';
2918
- image_url: {
2919
- url: string;
2920
- /** OpenAI 전용 힌트 ('auto' | 'low' | 'high'). 다른 provider 는 무시. */
2921
- detail?: 'auto' | 'low' | 'high';
2922
- };
2923
- }
2924
- /** AI 스트리밍 메시지의 단일 content 파트. */
2925
- type StreamContentPart = StreamTextPart | StreamImageURLPart;
2926
- /**
2927
- * AI 스트리밍 메시지.
2928
- *
2929
- * content 는 string (단일 텍스트) 또는 StreamContentPart[] (멀티모달) 를 받습니다.
2930
- * 멀티모달 형식은 OpenAI Vision spec (https://platform.openai.com/docs/guides/vision)
2931
- * 을 따르며, 서버가 각 provider 의 wire format 으로 변환:
2932
- * - openai / openai_compatible (vLLM 등): content array 그대로 passthrough
2933
- * - claude: data URI → base64 source, https URL → url source 의 image content block
2934
- * - gemini: image URL fetch → base64 inline_data (Gemini API 가 외부 URL fetch 미지원)
2935
- */
1814
+ /** AI 스트리밍 메시지 */
2936
1815
  interface StreamMessage {
2937
1816
  role: 'user' | 'assistant' | 'system';
2938
- content: string | StreamContentPart[];
1817
+ content: string;
2939
1818
  }
2940
1819
  /** AI 스트리밍 요청 옵션 */
2941
1820
  interface StreamOptions {
2942
- /** AI 프로바이더 ( 설정의 프로바이더 사용. override 시 지정) */
2943
- provider?: 'gemini' | 'openai' | 'claude' | 'ollama' | 'lm_studio' | 'openai_compatible';
2944
- /** 모델명 ( 설정의 모델을 override (선택적)) */
1821
+ /** AI 프로바이더 (기본: gemini) */
1822
+ provider?: 'gemini' | 'openai' | 'claude';
1823
+ /** 모델명 (기본: gemini-2.5-flash) */
2945
1824
  model?: string;
2946
1825
  /** 시스템 프롬프트 */
2947
1826
  system?: string;
@@ -2953,8 +1832,6 @@ interface StreamOptions {
2953
1832
  sessionId?: string;
2954
1833
  /** 사용자 정의 메타데이터 */
2955
1834
  metadata?: Record<string, unknown>;
2956
- /** MCP 그룹 슬러그 (AI Agent 모드 — 등록된 MCP 서버의 도구를 AI에 제공) */
2957
- mcpGroup?: string;
2958
1835
  }
2959
1836
  /** AI 스트리밍 토큰 콜백 */
2960
1837
  type StreamTokenCallback = (token: string, index: number) => void;
@@ -2970,19 +1847,11 @@ interface StreamDoneData {
2970
1847
  type StreamDoneCallback = (result: StreamDoneData) => void;
2971
1848
  /** AI 스트리밍 에러 콜백 */
2972
1849
  type StreamErrorCallback = (error: Error) => void;
2973
- /** AI 도구 호출 콜백 */
2974
- type StreamToolCallCallback = (toolName: string, args: Record<string, unknown>, index: number) => void;
2975
- /** AI 도구 실행 완료 콜백 */
2976
- type StreamToolResultCallback = (toolName: string, success: boolean, durationMs: number, index: number) => void;
2977
1850
  /** AI 스트리밍 핸들러 */
2978
1851
  interface StreamHandlers {
2979
1852
  onToken?: StreamTokenCallback;
2980
1853
  onDone?: StreamDoneCallback;
2981
1854
  onError?: StreamErrorCallback;
2982
- /** MCP 도구 호출 시 콜백 (AI Agent 모드) */
2983
- onToolCall?: StreamToolCallCallback;
2984
- /** MCP 도구 실행 완료 시 콜백 (AI Agent 모드) */
2985
- onToolResult?: StreamToolResultCallback;
2986
1855
  }
2987
1856
  /** AI 스트리밍 세션 */
2988
1857
  interface StreamSession {
@@ -3342,12 +2211,6 @@ declare class RealtimeAPI {
3342
2211
  onReadReceipt(category: string, handler: ReadReceiptHandler): () => void;
3343
2212
  private doConnect;
3344
2213
  private log;
3345
- /**
3346
- * 에러 로그. `options.debug` 가 true 일 때만 console 에 출력.
3347
- * 운영 환경에서는 소비자 애플리케이션의 `ConnectBase({ onError })` 또는
3348
- * `cb.errorTracker` 로 전달하는 것을 권장하므로 SDK 자체 console 출력은 opt-in.
3349
- */
3350
- private logError;
3351
2214
  private handleServerMessage;
3352
2215
  private handleDisconnect;
3353
2216
  /**
@@ -3708,7 +2571,7 @@ declare class ErrorTrackerAPI {
3708
2571
  /**
3709
2572
  * OAuth 프로바이더 타입
3710
2573
  */
3711
- type OAuthProvider = 'google' | 'kakao' | 'naver' | 'apple' | 'github' | 'discord';
2574
+ type OAuthProvider = 'google' | 'kakao' | 'naver' | 'github' | 'discord' | 'apple';
3712
2575
  /**
3713
2576
  * 활성화된 OAuth 프로바이더 정보
3714
2577
  */
@@ -3730,19 +2593,29 @@ interface GetAuthorizationURLResponse {
3730
2593
  }
3731
2594
  /**
3732
2595
  * OAuth 콜백 응답
3733
- *
3734
- * `email` 은 1.10 부터 추가된 선택적 필드. Apple private relay 또는 이메일 권한 미동의
3735
- * 시 빈 문자열 / undefined 일 수 있다. 값이 있으면 첫 로그인 시점에 이메일을 확보할 수 있어
3736
- * 별도 `getMember()` 호출이 불필요하다.
3737
2596
  */
3738
2597
  interface OAuthCallbackResponse {
3739
2598
  member_id: string;
3740
2599
  access_token: string;
3741
2600
  refresh_token: string;
3742
2601
  is_new_member: boolean;
3743
- email?: string;
3744
2602
  }
3745
2603
 
2604
+ /**
2605
+ * 3rd-party 인앱 브라우저(카카오톡, 라인, 인스타그램, 페이스북 등) 감지
2606
+ * 구글은 이런 환경에서 OAuth를 차단(disallowed_useragent)하므로 별도 처리 필요
2607
+ */
2608
+ declare function detectInAppBrowser(): {
2609
+ type: 'kakaotalk' | 'line' | 'instagram' | 'facebook' | 'naverapp' | 'wechat' | 'other';
2610
+ ua: string;
2611
+ } | null;
2612
+ /**
2613
+ * 3rd-party 인앱 브라우저에서 현재 URL을 외부(시스템) 브라우저로 강제로 여는 방법 제공
2614
+ * - 카카오톡: kakaotalk://web/openExternal?url=...
2615
+ * - 라인: line://nv/openURL?url=...
2616
+ * - 그 외: 사용자에게 안내 메시지 표시 권장
2617
+ */
2618
+ declare function escapeToExternalBrowser(currentUrl?: string): boolean;
3746
2619
  /**
3747
2620
  * OAuth API
3748
2621
  * 소셜 로그인 (Google, Naver, GitHub, Discord)
@@ -3763,10 +2636,9 @@ declare class OAuthAPI {
3763
2636
  getEnabledProviders(): Promise<EnabledProvidersResponse>;
3764
2637
  /**
3765
2638
  * 소셜 로그인 (리다이렉트 방식) - 권장
3766
- *
3767
- * 기존 회원만 로그인 (`intent=signin` 명시). 앱에서 처음인 사용자는 콜백에서
3768
- * `error=account_not_found` 받아 가입 안내 UX 로 분기해야 한다. 신규 가입까지 한 번에
3769
- * 처리하려면 [signUp] 을 사용한다.
2639
+ * Google Cloud Console에 별도로 redirect_uri를 등록할 필요가 없습니다.
2640
+ * OAuth 완료 지정한 콜백 URL로 토큰과 함께 리다이렉트됩니다.
2641
+ * 모든 배포 환경에서 안정적으로 동작합니다.
3770
2642
  *
3771
2643
  * @param provider - OAuth 프로바이더 (google, naver, github, discord)
3772
2644
  * @param callbackUrl - OAuth 완료 후 리다이렉트될 앱의 URL
@@ -3774,30 +2646,27 @@ declare class OAuthAPI {
3774
2646
  *
3775
2647
  * @example
3776
2648
  * ```typescript
3777
- * // "로그인" 버튼 클릭 시
2649
+ * // 로그인 버튼 클릭 시
3778
2650
  * await cb.oauth.signIn('google', 'https://myapp.com/oauth/callback')
2651
+ * // Google 로그인 후 https://myapp.com/oauth/callback?access_token=...&refresh_token=... 로 리다이렉트됨
3779
2652
  * ```
3780
2653
  *
3781
2654
  * @example
3782
2655
  * ```typescript
3783
2656
  * // callback 페이지에서
3784
- * const result = await cb.oauth.getCallbackResult()
3785
- * if (result?.error === 'account_not_found') {
3786
- * // 가입되지 않은 사용자 — 회원가입 화면으로 안내
3787
- * window.location.href = '/signup'
2657
+ * const result = cb.oauth.getCallbackResult()
2658
+ * if (result) {
2659
+ * if (result.error) {
2660
+ * console.error('로그인 실패:', result.error)
2661
+ * } else {
2662
+ * console.log('로그인 성공:', result.member_id)
2663
+ * // 메인 페이지로 이동
2664
+ * window.location.href = '/'
2665
+ * }
3788
2666
  * }
3789
2667
  * ```
3790
2668
  */
3791
2669
  signIn(provider: OAuthProvider, callbackUrl: string, state?: string): Promise<void>;
3792
- /**
3793
- * 소셜 회원가입 (리다이렉트 방식) — 사용자가 명시적으로 가입 의사를 표시한 경우에만 호출.
3794
- *
3795
- * 기존 회원이면 그대로 로그인 (idempotent). 없으면 신규 AppMember 생성.
3796
- * "로그인" 버튼에는 [signIn] 을 쓰고, "회원가입" 버튼에서만 이 메서드를 호출해야 silent
3797
- * auto-signup 회귀를 방지할 수 있다.
3798
- */
3799
- signUp(provider: OAuthProvider, callbackUrl: string, state?: string): Promise<void>;
3800
- private startCentralOAuth;
3801
2670
  /**
3802
2671
  * 소셜 로그인 (팝업 방식)
3803
2672
  * 팝업 창에서 소셜 로그인을 처리하고 결과를 Promise로 반환합니다.
@@ -3822,72 +2691,36 @@ declare class OAuthAPI {
3822
2691
  * const result = await cb.oauth.signInWithPopup('google', 'https://myapp.com/oauth/callback')
3823
2692
  * ```
3824
2693
  */
3825
- signInWithPopup(provider: OAuthProvider, callbackUrl?: string, options?: {
3826
- intent?: 'signin' | 'signup';
3827
- }): Promise<OAuthCallbackResponse>;
2694
+ signInWithPopup(provider: OAuthProvider, callbackUrl?: string): Promise<OAuthCallbackResponse>;
3828
2695
  /**
3829
2696
  * 콜백 URL에서 OAuth 결과 추출
3830
2697
  * 중앙 콜백 방식에서 리다이렉트 후 URL 파라미터에서 결과를 추출합니다.
3831
2698
  * 토큰이 있으면 자동으로 저장됩니다.
3832
2699
  *
3833
- * 본 메서드는 `Promise` 를 반환한다. 토큰 저장 직후 `cb_member_refresh_token`
3834
- * cookie 부트스트랩(`/v1/auth/re-issue` 1회)을 *await* 하기 위함이다. 표준 예제
3835
- * 코드는 결과를 await 한 다음 `window.location.href='/'` 로 이동하므로, cookie 가
3836
- * 브라우저에 안전하게 저장된 뒤 페이지가 전환된다.
3837
- *
3838
- * 이전(3.22.x) 의 fire-and-forget 부트스트랩은 모바일/느린 네트워크에서 navigation
3839
- * 이 fetch 보다 먼저 발화해 cookie 가 발급되지 않고, 새 페이지 진입 시 cookie
3840
- * 없는 상태로 `getMe()` 가 401 으로 떨어지는 회귀가 있었다 (platform-issue
3841
- * 019e638d, 2026-05-26).
3842
- *
3843
2700
  * @returns OAuth 결과 (토큰, member_id 등) 또는 null
3844
2701
  *
3845
2702
  * @example
3846
2703
  * ```typescript
3847
2704
  * // callback 페이지에서
3848
- * const result = await cb.oauth.getCallbackResult()
2705
+ * const result = cb.oauth.getCallbackResult()
3849
2706
  * if (result) {
3850
2707
  * if (result.error) {
3851
2708
  * alert('로그인 실패: ' + result.error)
3852
2709
  * } else {
2710
+ * console.log('로그인 성공:', result.member_id)
3853
2711
  * window.location.href = '/'
3854
2712
  * }
3855
2713
  * }
3856
2714
  * ```
3857
2715
  */
3858
- getCallbackResult(): Promise<{
2716
+ getCallbackResult(): {
3859
2717
  access_token?: string;
3860
2718
  refresh_token?: string;
3861
2719
  member_id?: string;
3862
2720
  is_new_member?: boolean;
3863
- email?: string;
3864
2721
  state?: string;
3865
2722
  error?: string;
3866
- } | null>;
3867
- /**
3868
- * 콜백 URL 에서 1회용 `code` 를 토큰으로 교환합니다 (`OAUTH_CODE_ONLY` 서버 모드용).
3869
- *
3870
- * 서버가 `OAUTH_CODE_ONLY=true` 모드면 리다이렉트 URL 에 토큰 대신 `code` 만 포함됩니다.
3871
- * 이 메서드는 code 를 `POST /v1/auth/oauth/exchange` 로 교환하고 토큰을 자동 저장합니다.
3872
- *
3873
- * 권장 호출 순서:
3874
- * 1) `cb.oauth.exchangeCodeFromCallback()` 먼저 — code-only 모드면 성공
3875
- * 2) null 반환 시 `cb.oauth.getCallbackResult()` 폴백 (legacy 토큰-in-URL 모드)
3876
- *
3877
- * @example
3878
- * ```typescript
3879
- * const result = await cb.oauth.exchangeCodeFromCallback()
3880
- * ?? cb.oauth.getCallbackResult()
3881
- * ```
3882
- */
3883
- exchangeCodeFromCallback(): Promise<{
3884
- access_token: string;
3885
- refresh_token: string;
3886
- member_id: string;
3887
- is_new_member: boolean;
3888
- email?: string;
3889
- state?: string;
3890
- } | null>;
2723
+ } | null;
3891
2724
  }
3892
2725
 
3893
2726
  type PaymentProvider = 'toss' | 'stripe';
@@ -3914,25 +2747,6 @@ interface PreparePaymentResponse {
3914
2747
  stripe_client_secret?: string;
3915
2748
  stripe_publishable_key?: string;
3916
2749
  }
3917
- interface CreateCheckoutSessionRequest {
3918
- amount: number;
3919
- currency: string;
3920
- product_name: string;
3921
- success_url: string;
3922
- cancel_url: string;
3923
- order_id?: string;
3924
- customer_email?: string;
3925
- customer_name?: string;
3926
- metadata?: Record<string, unknown>;
3927
- }
3928
- interface CreateCheckoutSessionResponse {
3929
- payment_id: string;
3930
- order_id: string;
3931
- amount: number;
3932
- currency: string;
3933
- session_id: string;
3934
- session_url: string;
3935
- }
3936
2750
  interface ConfirmPaymentRequest {
3937
2751
  payment_key: string;
3938
2752
  order_id: string;
@@ -3992,69 +2806,43 @@ declare class PaymentAPI {
3992
2806
  */
3993
2807
  private getPublicPrefix;
3994
2808
  /**
3995
- * 결제 준비
3996
- * 토스 결제 위젯에 필요한 정보를 반환합니다.
3997
- *
3998
- * @param data - 결제 준비 정보 (금액, 상품명, 고객 정보 등)
3999
- * @returns 토스 클라이언트 키, 고객 키, 성공/실패 URL 등
4000
- *
4001
- * @example
4002
- * ```typescript
4003
- * const result = await cb.payment.prepare({
4004
- * amount: 10000,
4005
- * order_name: '프리미엄 1개월',
4006
- * customer_email: 'test@example.com',
4007
- * customer_name: '홍길동'
4008
- * })
4009
- *
4010
- * // Toss V2 결제 플로우
4011
- * if (result.payment_provider === 'toss') {
4012
- * const tossPayments = TossPayments(result.toss_client_key)
4013
- * const payment = tossPayments.payment({ customerKey: result.customer_key })
4014
- * await payment.requestPayment({
4015
- * method: 'CARD',
4016
- * amount: { currency: 'KRW', value: result.amount },
4017
- * orderId: result.order_id,
4018
- * orderName: result.order_name,
4019
- * successUrl: result.success_url,
4020
- * failUrl: result.fail_url,
4021
- * })
4022
- * }
4023
- *
4024
- * // Stripe 결제 플로우
4025
- * if (result.payment_provider === 'stripe') {
4026
- * const stripe = await loadStripe(result.stripe_publishable_key)
4027
- * const elements = stripe.elements({ clientSecret: result.stripe_client_secret })
4028
- * // Mount PaymentElement, then:
4029
- * // stripe.confirmPayment({ elements, redirect: 'if_required' })
4030
- * }
4031
- * ```
4032
- */
4033
- prepare(data: PreparePaymentRequest): Promise<PreparePaymentResponse>;
4034
- /**
4035
- * Stripe-hosted 결제 페이지 세션 생성 (client_secret 미노출 플로우).
4036
- *
4037
- * Elements 플로우(`prepare`) 대신 Stripe-hosted Checkout 페이지로 리다이렉트하고 싶을 때 사용.
4038
- * 응답에 `session_url` 이 포함되며, 브라우저를 해당 URL 로 이동시키면 Stripe 가 카드 입력·결제를 처리한 뒤
4039
- * `success_url` / `cancel_url` 로 복귀한다. `client_secret` 은 서버·클라이언트 어디에도 노출되지 않는다.
2809
+ * 결제 준비
2810
+ * 토스 결제 위젯에 필요한 정보를 반환합니다.
4040
2811
  *
4041
- * @param data - 결제 세션 정보 (금액, 통화, 상품명, success/cancel URL 등)
4042
- * @returns session_id, session_url (redirect 대상)
2812
+ * @param data - 결제 준비 정보 (금액, 상품명, 고객 정보 등)
2813
+ * @returns 토스 클라이언트 키, 고객 키, 성공/실패 URL 등
4043
2814
  *
4044
2815
  * @example
4045
2816
  * ```typescript
4046
- * const sess = await client.payment.createCheckoutSession({
4047
- * amount: 1000,
4048
- * currency: 'USD',
4049
- * product_name: 'Premium Subscription',
4050
- * success_url: 'https://app.example.com/success?session_id={CHECKOUT_SESSION_ID}',
4051
- * cancel_url: 'https://app.example.com/cancel',
4052
- * customer_email: 'user@example.com',
2817
+ * const prepareResult = await client.payment.prepare({
2818
+ * amount: 10000,
2819
+ * order_name: '테스트 상품',
2820
+ * customer_email: 'test@example.com',
2821
+ * customer_name: '홍길동'
2822
+ * })
2823
+ *
2824
+ * // 토스 결제 위젯 초기화
2825
+ * const tossPayments = TossPayments(prepareResult.toss_client_key)
2826
+ * await tossPayments.requestPayment('카드', {
2827
+ * amount: prepareResult.amount,
2828
+ * orderId: prepareResult.order_id,
2829
+ * orderName: prepareResult.order_name,
2830
+ * customerKey: prepareResult.customer_key,
2831
+ * successUrl: prepareResult.success_url,
2832
+ * failUrl: prepareResult.fail_url
4053
2833
  * })
4054
- * window.location.href = sess.session_url
2834
+ *
2835
+ * // Stripe 결제 플로우:
2836
+ * // const result = await client.payment.prepare({ amount: 1000, order_name: 'Product' })
2837
+ * // if (result.payment_provider === 'stripe') {
2838
+ * // const stripe = await loadStripe(result.stripe_publishable_key)
2839
+ * // const elements = stripe.elements({ clientSecret: result.stripe_client_secret })
2840
+ * // // Mount PaymentElement, then:
2841
+ * // // stripe.confirmPayment({ elements, redirect: 'if_required' })
2842
+ * // }
4055
2843
  * ```
4056
2844
  */
4057
- createCheckoutSession(data: CreateCheckoutSessionRequest): Promise<CreateCheckoutSessionResponse>;
2845
+ prepare(data: PreparePaymentRequest): Promise<PreparePaymentResponse>;
4058
2846
  /**
4059
2847
  * 결제 승인
4060
2848
  * 토스에서 결제 완료 후 콜백으로 받은 정보로 결제를 최종 승인합니다.
@@ -4609,7 +3397,7 @@ declare class SubscriptionAPI {
4609
3397
  * order_name: '추가 결제'
4610
3398
  * })
4611
3399
  *
4612
- * if (result.status === 'done') {
3400
+ * if (result.status === 'DONE') {
4613
3401
  * console.log('결제 완료!')
4614
3402
  * }
4615
3403
  * ```
@@ -4673,35 +3461,6 @@ interface WebPushSubscription {
4673
3461
  };
4674
3462
  }
4675
3463
 
4676
- /**
4677
- * `cb.push.sendToMembers` 페이로드. 백엔드 `dto.SendPushRequest` 의 멤버 발송 케이스
4678
- * 에 매핑되며, target_type / target_member_ids 는 SDK 가 자동 채운다.
4679
- */
4680
- interface SendToMembersPayload {
4681
- title: string;
4682
- body: string;
4683
- imageUrl?: string;
4684
- data?: Record<string, unknown>;
4685
- /** 'ios' | 'android' | 'web' 중 선택. 빈 배열/미지정 시 전체 플랫폼 발송. */
4686
- platforms?: Array<'ios' | 'android' | 'web'>;
4687
- /** TTL(초). 기본 86400 (24h). */
4688
- ttlSeconds?: number;
4689
- priority?: 'normal' | 'high';
4690
- clickAction?: string;
4691
- /** ISO8601 형식 예약 발송 시각. 미지정 시 즉시 발송. */
4692
- scheduledAt?: string;
4693
- }
4694
- /**
4695
- * 발송 결과. 백엔드 `dto.SendResultResponse` 와 1:1 매핑.
4696
- */
4697
- interface SendPushResult {
4698
- message_id: string;
4699
- total_target: number;
4700
- sent_count: number;
4701
- failed_count: number;
4702
- status: string;
4703
- error_message?: string;
4704
- }
4705
3464
  /**
4706
3465
  * 푸시 알림 API
4707
3466
  *
@@ -4714,8 +3473,8 @@ interface SendPushResult {
4714
3473
  * device_name: 'Galaxy S24',
4715
3474
  * })
4716
3475
  *
4717
- * // 토픽 구독 (device_token 필수)
4718
- * await cb.push.subscribeTopic(device.device_token, 'announcements')
3476
+ * // 토픽 구독
3477
+ * await cb.push.subscribeTopic('announcements')
4719
3478
  *
4720
3479
  * // Web Push 등록 (브라우저)
4721
3480
  * const vapidKey = await cb.push.getVAPIDPublicKey()
@@ -4763,38 +3522,52 @@ declare class PushAPI {
4763
3522
  /**
4764
3523
  * 디바이스 등록 해제
4765
3524
  *
4766
- * @param deviceToken 디바이스 토큰
3525
+ * @param deviceId 디바이스 ID
4767
3526
  *
4768
3527
  * @example
4769
3528
  * ```typescript
4770
- * await cb.push.unregisterDevice('fcm-token-here')
3529
+ * await cb.push.unregisterDevice('device-id-here')
4771
3530
  * ```
4772
3531
  */
4773
- unregisterDevice(deviceToken: string): Promise<void>;
3532
+ unregisterDevice(deviceId: string): Promise<void>;
3533
+ /**
3534
+ * 현재 등록된 디바이스 목록 조회
3535
+ *
3536
+ * @returns 디바이스 목록
3537
+ */
3538
+ getDevices(): Promise<DeviceInfo[]>;
4774
3539
  /**
4775
3540
  * 토픽 구독
4776
3541
  *
4777
- * @param deviceToken 디바이스 토큰
4778
3542
  * @param topicName 토픽 이름
4779
3543
  *
4780
3544
  * @example
4781
3545
  * ```typescript
4782
- * await cb.push.subscribeTopic('fcm-token-here', 'announcements')
3546
+ * // 공지사항 토픽 구독
3547
+ * await cb.push.subscribeTopic('announcements')
3548
+ *
3549
+ * // 마케팅 토픽 구독
3550
+ * await cb.push.subscribeTopic('marketing')
4783
3551
  * ```
4784
3552
  */
4785
- subscribeTopic(deviceToken: string, topicName: string): Promise<void>;
3553
+ subscribeTopic(topicName: string): Promise<void>;
4786
3554
  /**
4787
3555
  * 토픽 구독 해제
4788
3556
  *
4789
- * @param deviceToken 디바이스 토큰
4790
3557
  * @param topicName 토픽 이름
4791
3558
  *
4792
3559
  * @example
4793
3560
  * ```typescript
4794
- * await cb.push.unsubscribeTopic('fcm-token-here', 'marketing')
3561
+ * await cb.push.unsubscribeTopic('marketing')
4795
3562
  * ```
4796
3563
  */
4797
- unsubscribeTopic(deviceToken: string, topicName: string): Promise<void>;
3564
+ unsubscribeTopic(topicName: string): Promise<void>;
3565
+ /**
3566
+ * 구독 중인 토픽 목록 조회
3567
+ *
3568
+ * @returns 구독 중인 토픽 이름 목록
3569
+ */
3570
+ getSubscribedTopics(): Promise<string[]>;
4798
3571
  /**
4799
3572
  * VAPID Public Key 조회 (Web Push용)
4800
3573
  *
@@ -4836,33 +3609,8 @@ declare class PushAPI {
4836
3609
  registerWebPush(subscription: PushSubscription | WebPushSubscription): Promise<DeviceInfo>;
4837
3610
  /**
4838
3611
  * Web Push 구독 해제
4839
- *
4840
- * @param deviceToken Web Push endpoint URL (등록 시 사용한 endpoint)
4841
3612
  */
4842
- unregisterWebPush(deviceToken: string): Promise<void>;
4843
- /**
4844
- * 회원 ID 목록으로 푸시 발송 — Functions / cb_sk_ 인증 환경 전용.
4845
- *
4846
- * 백엔드 라우트 `POST /v1/apps/:appID/push/send` 는 콘솔 JWT 또는 User Secret Key
4847
- * (`cb_sk_`) 만 받는다. 브라우저 SDK 의 Public Key(`cb_pk_`) 인스턴스에서는 호출
4848
- * 자체가 차단되며, 권한 누설을 막기 위해 명확한 에러를 던진다.
4849
- *
4850
- * @param appId 발송 대상 앱 ID (cb_sk_ 환경은 user 단위 권한이므로 명시 전달).
4851
- * @param memberIds 받는 회원 ID 배열 (UUID).
4852
- * @param payload 푸시 내용/옵션.
4853
- *
4854
- * @example
4855
- * ```ts
4856
- * // ConnectBase Function (Node.js, secrets.CB_SECRET_KEY 주입)
4857
- * const result = await cb.push.sendToMembers(APP_ID, [memberId], {
4858
- * title: '새 알림',
4859
- * body: '확인해보세요',
4860
- * data: { route: '/notifications' },
4861
- * })
4862
- * console.log(result.message_id, result.sent_count)
4863
- * ```
4864
- */
4865
- sendToMembers(appId: string, memberIds: string[], payload: SendToMembersPayload): Promise<SendPushResult>;
3613
+ unregisterWebPush(): Promise<void>;
4866
3614
  /**
4867
3615
  * 브라우저 고유 ID 생성 (localStorage에 저장)
4868
3616
  */
@@ -5099,62 +3847,6 @@ interface ChannelMembership {
5099
3847
  started_at: string;
5100
3848
  expires_at?: string;
5101
3849
  }
5102
- interface VideoStorage {
5103
- id: string;
5104
- app_id: string;
5105
- name: string;
5106
- description: string;
5107
- current_size: number;
5108
- video_count: number;
5109
- is_public: boolean;
5110
- allow_server_to_server: boolean;
5111
- allowed_origins: string[];
5112
- strict_referer_check: boolean;
5113
- cdn_provider: string;
5114
- cdn_config: Record<string, unknown>;
5115
- default_qualities: string[];
5116
- hls_encryption_enabled: boolean;
5117
- token_expiry_hours: number;
5118
- created_at: string;
5119
- updated_at: string;
5120
- }
5121
- interface CreateVideoStorageRequest {
5122
- name: string;
5123
- description?: string;
5124
- is_public?: boolean;
5125
- allow_server_to_server?: boolean;
5126
- allowed_origins?: string[];
5127
- strict_referer_check?: boolean;
5128
- cdn_provider?: string;
5129
- cdn_config?: Record<string, unknown>;
5130
- default_qualities?: string[];
5131
- hls_encryption_enabled?: boolean;
5132
- token_expiry_hours?: number;
5133
- }
5134
- interface UpdateVideoStorageRequest {
5135
- name?: string;
5136
- description?: string;
5137
- is_public?: boolean;
5138
- allow_server_to_server?: boolean;
5139
- allowed_origins?: string[];
5140
- strict_referer_check?: boolean;
5141
- cdn_provider?: string;
5142
- cdn_config?: Record<string, unknown>;
5143
- default_qualities?: string[];
5144
- hls_encryption_enabled?: boolean;
5145
- token_expiry_hours?: number;
5146
- }
5147
- interface VideoStorageListResponse {
5148
- storage_videos: VideoStorage[];
5149
- total_count: number;
5150
- }
5151
- interface StorageUploadOptions {
5152
- title: string;
5153
- description?: string;
5154
- visibility?: VideoVisibility;
5155
- tags?: string[];
5156
- onProgress?: (progress: UploadProgress) => void;
5157
- }
5158
3850
  interface SuperChat {
5159
3851
  id: string;
5160
3852
  video_id: string;
@@ -5196,29 +3888,7 @@ declare class VideoAPI {
5196
3888
  */
5197
3889
  waitForReady(videoId: string, options?: WaitOptions): Promise<Video>;
5198
3890
  /**
5199
- * 영상 목록 조회.
5200
- *
5201
- * 필터링/페이지네이션 옵션을 조합해 조건에 맞는 영상들을 반환한다.
5202
- * API Key(publicKey) 또는 JWT 인증이 모두 지원되며, publicKey 로는 공개 영상만 노출된다.
5203
- *
5204
- * @param options - 필터/페이지네이션 옵션
5205
- * @param options.status - `uploading` | `processing` | `ready` | `failed`
5206
- * @param options.visibility - `public` | `unlisted` | `private` | `members`
5207
- * @param options.search - 제목 부분 일치 검색
5208
- * @param options.channel_id - 특정 채널로 한정
5209
- * @param options.page - 1 부터 시작
5210
- * @param options.limit - 기본 20, 최대 100
5211
- * @returns `videos` 배열과 `total` 카운트를 포함하는 응답
5212
- *
5213
- * @example
5214
- * ```ts
5215
- * // 내 채널의 최근 공개 영상 10개
5216
- * const { videos, total } = await cb.video.list({
5217
- * channel_id: 'ch_abc',
5218
- * visibility: 'public',
5219
- * limit: 10,
5220
- * })
5221
- * ```
3891
+ * List videos
5222
3892
  */
5223
3893
  list(options?: VideoListOptions): Promise<VideoListResponse>;
5224
3894
  /**
@@ -5234,20 +3904,7 @@ declare class VideoAPI {
5234
3904
  */
5235
3905
  delete(videoId: string): Promise<void>;
5236
3906
  /**
5237
- * 영상 스트리밍 URL 획득 (HLS manifest 권장).
5238
- *
5239
- * 반환된 URL 은 `hls.js` 또는 Safari 네이티브 HLS 플레이어에 그대로 전달 가능하다.
5240
- * 서명된 URL 이므로 만료 시간이 존재하며, 갱신이 필요한 경우 다시 호출한다.
5241
- *
5242
- * @param videoId - 대상 영상 ID
5243
- * @param quality - `auto` (기본) | `1080p` | `720p` | `480p` | `360p` 등 트랜스코딩된 품질 레벨
5244
- * @returns HLS manifest URL 과 만료 정보
5245
- *
5246
- * @example
5247
- * ```ts
5248
- * const { url, expires_at } = await cb.video.getStreamUrl(videoId, '720p')
5249
- * videoElement.src = url
5250
- * ```
3907
+ * Get streaming URL for a video
5251
3908
  */
5252
3909
  getStreamUrl(videoId: string, quality?: string): Promise<StreamURLResponse>;
5253
3910
  /**
@@ -5403,131 +4060,11 @@ declare class VideoAPI {
5403
4060
  * Submit recommendation feedback
5404
4061
  */
5405
4062
  submitFeedback(videoId: string, feedback: 'interested' | 'not_interested'): Promise<void>;
5406
- /**
5407
- * Storage sub-namespace for video storage container operations
5408
- */
5409
- readonly storage: {
5410
- /**
5411
- * Create a video storage container
5412
- */
5413
- create: (data: CreateVideoStorageRequest) => Promise<VideoStorage>;
5414
- /**
5415
- * List video storage containers
5416
- */
5417
- list: () => Promise<VideoStorageListResponse>;
5418
- /**
5419
- * Get a video storage container
5420
- */
5421
- get: (storageId: string) => Promise<VideoStorage>;
5422
- /**
5423
- * Update a video storage container
5424
- */
5425
- update: (storageId: string, data: UpdateVideoStorageRequest) => Promise<VideoStorage>;
5426
- /**
5427
- * Delete a video storage container
5428
- */
5429
- delete: (storageId: string) => Promise<void>;
5430
- /**
5431
- * Upload a video to a specific storage via core-server proxy (chunked)
5432
- */
5433
- upload: (storageId: string, file: File, options: StorageUploadOptions) => Promise<Video>;
5434
- /**
5435
- * List videos in a specific storage
5436
- */
5437
- listVideos: (storageId: string, options?: VideoListOptions) => Promise<VideoListResponse>;
5438
- /**
5439
- * Get a video in a specific storage
5440
- */
5441
- getVideo: (storageId: string, videoId: string) => Promise<Video>;
5442
- /**
5443
- * Delete a video from a specific storage
5444
- */
5445
- deleteVideo: (storageId: string, videoId: string) => Promise<void>;
5446
- /**
5447
- * Get streaming URL for a video in a specific storage
5448
- */
5449
- getStreamUrl: (storageId: string, videoId: string) => Promise<StreamURLResponse>;
5450
- /**
5451
- * Get transcode status for a video in a specific storage
5452
- */
5453
- getTranscodeStatus: (storageId: string, videoId: string) => Promise<TranscodeStatus>;
5454
- };
5455
- }
5456
-
5457
- /**
5458
- * 게임 서버 기능 opt-in 토글 API.
5459
- *
5460
- * v3.1 (2026-04-30+) 부터 도입. 7개 게임 기능 (matchqueue / leaderboard / entity /
5461
- * scripts / voice / replay / spectator) 은 모두 앱 단위로 명시적 opt-in 해야 사용 가능.
5462
- *
5463
- * 정책:
5464
- * - 신규 앱: 모든 토글 false (App 생성 시 row 자동 삽입)
5465
- * - 기존 앱: row 가 없으면 레거시 호환으로 모두 ON 으로 응답 (서비스 단절 방지)
5466
- * - PATCH 후 game-server 캐시는 NATS publish 로 즉시 무효화 (또는 30s TTL)
5467
- *
5468
- * @example
5469
- * ```ts
5470
- * const cfg = await cb.game.config.get(appId)
5471
- * if (!cfg.matchqueue_enabled) {
5472
- * await cb.game.config.set(appId, { matchqueue_enabled: true })
5473
- * }
5474
- * ```
5475
- *
5476
- * @see docs/game-server/OPT_IN.md
5477
- */
5478
-
5479
- /** 7개 토글 + 메타. */
5480
- interface GameConfig {
5481
- matchqueue_enabled: boolean;
5482
- leaderboard_enabled: boolean;
5483
- entity_enabled: boolean;
5484
- scripts_enabled: boolean;
5485
- voice_enabled: boolean;
5486
- replay_enabled: boolean;
5487
- spectator_enabled: boolean;
5488
- }
5489
- /** PATCH body — 변경할 필드만 명시 (partial update). */
5490
- type GameConfigPatch = Partial<GameConfig>;
5491
- /**
5492
- * 게임 기능 토글 클라이언트.
5493
- *
5494
- * 직접 인스턴스화하지 않고 `cb.game.config` 로 접근.
5495
- */
5496
- declare class GameConfigAPI {
5497
- private http;
5498
- private appId?;
5499
- constructor(http: HttpClient, appId?: string);
5500
- /**
5501
- * 현재 토글 상태 조회.
5502
- *
5503
- * @param appId 앱 ID (생성자에서 주입한 기본값 사용 가능)
5504
- */
5505
- get(appId?: string): Promise<GameConfig>;
5506
- /**
5507
- * 토글 변경. 보낸 필드만 갱신, 나머지는 보존. PATCH 직후 game-server 캐시 즉시 drop
5508
- * (NATS publish) — TTL 기다릴 필요 없음.
5509
- *
5510
- * @example
5511
- * await cb.game.config.set(appId, { matchqueue_enabled: true, leaderboard_enabled: true })
5512
- */
5513
- set(appId: string | GameConfigPatch, patch?: GameConfigPatch): Promise<GameConfig>;
5514
- /**
5515
- * 단일 기능 활성화 — set() 의 편의 wrapper.
5516
- *
5517
- * @example await cb.game.config.enable(appId, "matchqueue")
5518
- */
5519
- enable(appId: string, feature: keyof GameConfig): Promise<GameConfig>;
5520
- /**
5521
- * 단일 기능 비활성화.
5522
- */
5523
- disable(appId: string, feature: keyof GameConfig): Promise<GameConfig>;
5524
- private resolveAppId;
5525
4063
  }
5526
4064
 
5527
4065
  /**
5528
4066
  * Game Server Types
5529
4067
  */
5530
-
5531
4068
  /**
5532
4069
  * 게임 룸 설정
5533
4070
  */
@@ -5540,16 +4077,6 @@ interface GameRoomConfig {
5540
4077
  tickRate?: number;
5541
4078
  /** 최대 플레이어 수 (기본 100) */
5542
4079
  maxPlayers?: number;
5543
- /**
5544
- * 룸에 attach 할 Lua 스크립트 이름. 콘솔 또는 `POST /v1/game/:appID/scripts` 로 업로드+활성화한
5545
- * 스크립트만 사용 가능. 미지정 시 server tick + delta 만 흐르고 onTick / onJoin / onAction 등
5546
- * 사용자 hook 은 호출되지 않는다.
5547
- *
5548
- * platform-issue 019e123a (2026-05-10) 해결로 추가된 필드. 이전 버전에서 createRoom config 에
5549
- * `script_name` 을 직접 넣어 호출하던 워크어라운드는 SDK 가 와이어 페이로드에서 자동으로
5550
- * `script_name` 으로 매핑해 호환된다.
5551
- */
5552
- scriptName?: string;
5553
4080
  /** 커스텀 메타데이터 */
5554
4081
  metadata?: Record<string, string>;
5555
4082
  }
@@ -5617,7 +4144,7 @@ interface GameRoomInfo {
5617
4144
  /**
5618
4145
  * 게임 서버 메시지 타입
5619
4146
  */
5620
- type GameServerMessageType = 'room_created' | 'room_joined' | 'room_left' | 'state' | 'delta' | 'player_event' | 'chat' | 'pong' | 'room_list' | 'error' | 'room_stale' | 'sync_lost';
4147
+ type GameServerMessageType = 'room_created' | 'room_joined' | 'room_left' | 'state' | 'delta' | 'player_event' | 'chat' | 'pong' | 'room_list' | 'error';
5621
4148
  /**
5622
4149
  * 게임 서버 메시지
5623
4150
  * 서버는 모든 필드를 최상위에 보냄 (data 래퍼 없음)
@@ -5654,30 +4181,6 @@ interface GameServerMessage {
5654
4181
  timestamp?: number;
5655
4182
  client_timestamp?: number;
5656
4183
  server_timestamp?: number;
5657
- phase?: string;
5658
- feature?: string;
5659
- script_id?: string;
5660
- /**
5661
- * broadcastScriptError 가 onAction 에러 broadcast 시 origin client 식별을 위해
5662
- * 함께 보내는 필드. sender 본인은 점대점 error 응답으로 받으므로 broadcast 에서
5663
- * 제외되며, 다른 player 가 누가 액션을 일으켰는지 가시화하는 용도.
5664
- */
5665
- origin_client_id?: string;
5666
- /**
5667
- * `code === 'SCRIPT_NOT_FOUND'` 응답에 포함 — createRoom 시 client 가 요청했던
5668
- * scriptName. 오타/대소문자/스킴 mismatch 디버깅 단서.
5669
- */
5670
- requested?: string;
5671
- /**
5672
- * `code === 'SCRIPT_NOT_FOUND'` 응답에 포함 — 현재 app 에 활성(active+version>0)
5673
- * 상태인 script 이름 목록. 사용자가 후보 중 골라 재시도하기 좋도록 노출.
5674
- */
5675
- available?: string[];
5676
- reason?: string;
5677
- script_version?: number;
5678
- /** room_created 응답 — 서버가 attach 한 lua script 이름 (검증된 값). */
5679
- script_name?: string;
5680
- hint?: string;
5681
4184
  }
5682
4185
  /**
5683
4186
  * 플레이어 이벤트
@@ -5699,74 +4202,11 @@ interface ChatMessage {
5699
4202
  serverTime: number;
5700
4203
  }
5701
4204
  /**
5702
- * GameErrorCode — game-server 가 surface 하는 에러 코드 literal union.
5703
- *
5704
- * - `SCRIPT_NOT_FOUND` : createRoom 의 scriptName 이 미존재/비활성. 응답에 requested + available 동봉.
5705
- * - `NO_ACTION_HANDLER` : action 의 type 에 대응하는 lua handler 미정의 (`onAction` 또는 `handlers[type]`).
5706
- * - `FEATURE_DISABLED` : entity/matchqueue/leaderboard 등 opt-in feature 가 OFF. feature 필드로 분기.
5707
- * - `SCRIPT_ERROR` : lua 실행 중 일반 에러 (분류되지 않은 throw / runtime error).
5708
- * - `SCRIPT_TIMEOUT` : 사용자 hook code 의 context deadline 초과 (기본 100ms). hook 본문이 느림.
5709
- * - `SCRIPT_SETUP_OVERRUN` : hook 호출 *전* 플랫폼 측 setup phase (스크립트 로드 + state 직렬화 + API 주입) 가
5710
- * setupBudget(기본 100ms / onInit 1s) 초과. 일반적으로 룸 state 가 너무 큼.
5711
- * 해결: state 의 큰 데이터를 entity primitive 로 이전, state.dbg.* 누적 중단.
5712
- * issue 019e2c29 후 도입 (2026-05-16).
5713
- * - `RATE_LIMITED` : per-app script rate limit 초과 (`SCRIPT_RATE_PER_SEC`).
5714
- * - `QUOTA_EXCEEDED` : plan 한도(`script_executions/month`) 초과.
5715
- * - `TIMEOUT` : SDK 측 sendWithHandler timeout (서버 응답 없음).
5716
- * - `UNKNOWN` : 분류되지 않은 server 에러 (server 에 신규 코드가 생겼는데 SDK 미반영).
5717
- *
5718
- * literal autocomplete 를 유지하면서 미래에 server 가 신규 코드를 추가해도 타입 에러가
5719
- * 안 나도록 `string & {}` 도 union 에 포함 (TypeScript 'string literal type widening' 우회 패턴).
5720
- */
5721
- type GameErrorCode = 'SCRIPT_NOT_FOUND' | 'NO_ACTION_HANDLER' | 'FEATURE_DISABLED' | 'SCRIPT_ERROR' | 'SCRIPT_TIMEOUT' | 'SCRIPT_SETUP_OVERRUN' | 'RATE_LIMITED' | 'QUOTA_EXCEEDED' | 'TIMEOUT' | 'UNKNOWN' | (string & {});
5722
- /**
5723
- * 에러 메시지 — server `error` 메시지의 wire format. SDK 사용자에게는 GameError
5724
- * 인스턴스(class)로 surface 되지만, 본 인터페이스는 raw payload 매핑 + 타입 추론용.
4205
+ * 에러 메시지
5725
4206
  */
5726
4207
  interface ErrorMessage {
5727
- code: GameErrorCode;
4208
+ code: string;
5728
4209
  message: string;
5729
- /**
5730
- * 서버측 Lua hook 단계 식별자 — 값이 있으면 lua 실행 에러.
5731
- * "onJoin" | "onLeave" | "onTick" | "onAction".
5732
- * 일반 transport / protocol 에러는 phase 없음.
5733
- */
5734
- phase?: 'onJoin' | 'onLeave' | 'onTick' | 'onAction' | (string & {});
5735
- /**
5736
- * code === "FEATURE_DISABLED" 일 때 비활성 feature 이름.
5737
- * "entity" | "matchqueue" | "leaderboard" 등 — `toggle_game_features` 와 1:1 매핑.
5738
- */
5739
- feature?: 'entity' | 'matchqueue' | 'leaderboard' | (string & {});
5740
- roomId?: string;
5741
- scriptId?: string;
5742
- /**
5743
- * broadcast 형태의 onAction 에러일 때 액션을 일으킨 client. 본인이 아닌 다른
5744
- * player 에서 발생한 에러를 SDK 사용자가 식별/필터링하기 위한 필드.
5745
- */
5746
- originClientId?: string;
5747
- /** code === 'SCRIPT_NOT_FOUND' 시 client 가 요청했던 scriptName. */
5748
- requested?: string;
5749
- /** code === 'SCRIPT_NOT_FOUND' 시 후보로 노출되는 active script 이름 목록. */
5750
- available?: string[];
5751
- }
5752
- /**
5753
- * CreateRoomResult — `createRoomDetailed` 의 반환 타입. createRoom 의 호환성 보존을
5754
- * 위해 별도 메서드로 노출 (createRoom 은 기존대로 `Promise<GameState>` 유지).
5755
- *
5756
- * server 의 `room_created` 응답에는 이전엔 room_id + initial_state 만 들어왔으나
5757
- * platform-issue 019e21dd (NJB regression, 2026-05-13) 의 회귀 가드로 attach 된 lua
5758
- * script 의 이름/버전이 함께 echo 된다 — client 가 attach 검증 후 mismatch 시
5759
- * 즉시 throw 할 수 있도록.
5760
- */
5761
- interface CreateRoomResult {
5762
- /** 서버 생성 룸 UUID (config.roomId 와 항상 같지는 않음 — 서버가 fresh UUID 생성 가능). */
5763
- roomId: string;
5764
- /** 초기 상태 스냅샷. 기존 `createRoom` 의 resolve 값과 동일 shape. */
5765
- state: GameState;
5766
- /** Attached lua script 이름. config.scriptName 미지정 시 undefined. */
5767
- scriptName?: string;
5768
- /** Attached lua script 의 active version (Manager.GetMeta.ActiveVersion). */
5769
- scriptVersion?: number;
5770
4210
  }
5771
4211
  /**
5772
4212
  * Ping/Pong 응답
@@ -5775,38 +4215,13 @@ interface PongMessage {
5775
4215
  clientTimestamp: number;
5776
4216
  serverTimestamp: number;
5777
4217
  }
5778
- /**
5779
- * room_stale 이벤트 페이로드.
5780
- * 서버 측 hot reload(active_version bump) 등으로 현재 room 의 lua 핸들러가
5781
- * 갱신됐지만 기존 state 가 보존되어 사용자 lua 의 `state.initialized` 같은
5782
- * 가드로 `handlers.init` 가 재실행되지 않을 수 있을 때 발화된다.
5783
- *
5784
- * 권장 처리: leaveRoom → joinRoom 또는 새 roomId 로 createRoom.
5785
- * 자동 disconnect 는 SDK 가 하지 않는다 — 정책은 사용자 게임이 결정.
5786
- */
5787
- interface RoomStaleMessage {
5788
- /** "script_reloaded" | "schema_changed" | "manual" 등 — 자유 문자열 */
5789
- reason: string;
5790
- roomId: string;
5791
- /** appID:scriptName 형태. 없을 수 있음. */
5792
- scriptId?: string;
5793
- /** 새 active_version. 없을 수 있음. */
5794
- scriptVersion?: number;
5795
- serverTime: number;
5796
- }
5797
4218
  /**
5798
4219
  * 게임 클라이언트 이벤트 핸들러
5799
4220
  */
5800
4221
  interface GameEventHandlers {
5801
4222
  onConnect?: () => void;
5802
4223
  onDisconnect?: (event: CloseEvent) => void;
5803
- /**
5804
- * - `Event` : WebSocket transport-level 에러 (브라우저 WS API 의 onerror).
5805
- * - `GameError` : server 가 surface 한 `error` 메시지를 wrap 한 인스턴스 — `.code`,
5806
- * `.phase`, `.feature`, `.originClientId`, `.requested`, `.available` 모두 노출.
5807
- * `instanceof GameError` 로 분기하여 UI 표시 차별화 가능.
5808
- */
5809
- onError?: (error: Event | GameError) => void;
4224
+ onError?: (error: Event | ErrorMessage) => void;
5810
4225
  onStateUpdate?: (state: GameState) => void;
5811
4226
  onDelta?: (delta: GameDelta) => void;
5812
4227
  onAction?: (action: {
@@ -5819,22 +4234,6 @@ interface GameEventHandlers {
5819
4234
  onPlayerLeft?: (player: GamePlayer) => void;
5820
4235
  onChat?: (message: ChatMessage) => void;
5821
4236
  onPong?: (pong: PongMessage) => void;
5822
- /**
5823
- * 커스텀 broadcast 메시지 핸들러 — 서버 Lua 의 `room.broadcast(data)` / `room.send_to(clientId, data)`
5824
- * 로 보낸, SDK 가 모르는 `type` 의 메시지가 여기로 흘러온다. `delta`/`state`/`chat`/`player_event`/`error`
5825
- * 같은 표준 타입은 전용 핸들러로 가고 여기엔 오지 않는다.
5826
- *
5827
- * 게임별 커스텀 프로토콜(예: `{ type: "chunk", ... }`, `{ type: "turn_played", ... }`)은
5828
- * 이 핸들러에서 `msg.type` 으로 분기한다.
5829
- */
5830
- onMessage?: (msg: Record<string, unknown> & {
5831
- type: string;
5832
- }) => void;
5833
- /**
5834
- * 서버가 room 을 stale 로 표시했을 때 발화. 자세한 사양은 `RoomStaleMessage` 참고.
5835
- * 미설정 시 SDK 는 console.warn 으로 가시화만 하고 자동 동작은 하지 않는다.
5836
- */
5837
- onRoomStale?: (msg: RoomStaleMessage) => void;
5838
4237
  }
5839
4238
  /**
5840
4239
  * 게임 클라이언트 설정
@@ -5844,8 +4243,8 @@ interface GameClientConfig {
5844
4243
  gameServerUrl?: string;
5845
4244
  /** 앱 ID */
5846
4245
  appId?: string;
5847
- /** Public Key */
5848
- publicKey?: string;
4246
+ /** API Key */
4247
+ apiKey?: string;
5849
4248
  /** 액세스 토큰 */
5850
4249
  accessToken?: string;
5851
4250
  /** 클라이언트 ID */
@@ -5975,202 +4374,7 @@ interface LobbyInvite {
5975
4374
  createdAt: string;
5976
4375
  expiresAt: string;
5977
4376
  }
5978
- interface PartyInfo {
5979
- id: string;
5980
- app_id: string;
5981
- leader_id: string;
5982
- members: PartyMember[];
5983
- max_size: number;
5984
- settings?: Record<string, string>;
5985
- state: string;
5986
- created_at: string;
5987
- updated_at: string;
5988
- }
5989
- interface PartyMember {
5990
- player_id: string;
5991
- display_name?: string;
5992
- ready: boolean;
5993
- role: string;
5994
- joined_at: string;
5995
- metadata?: Record<string, string>;
5996
- }
5997
- interface PartyInvite {
5998
- invite_id: string;
5999
- party_id: string;
6000
- inviter_id: string;
6001
- status: string;
6002
- expires_at: string;
6003
- }
6004
- interface SpectatorInfo {
6005
- id: string;
6006
- user_id?: string;
6007
- room_id: string;
6008
- mode: "free" | "follow" | "director";
6009
- target_id?: string;
6010
- joined_at: string;
6011
- delay_seconds?: number;
6012
- }
6013
- interface SpectatorState {
6014
- room_id: string;
6015
- tick: number;
6016
- timestamp: number;
6017
- players: SpectatorPlayerState[];
6018
- game_state?: Record<string, unknown>;
6019
- events?: Record<string, unknown>[];
6020
- }
6021
- interface SpectatorPlayerState {
6022
- id: string;
6023
- display_name: string;
6024
- position: {
6025
- x: number;
6026
- y: number;
6027
- z: number;
6028
- };
6029
- health?: number;
6030
- score?: number;
6031
- is_alive: boolean;
6032
- }
6033
- interface LeaderboardEntry {
6034
- rank: number;
6035
- player_id: string;
6036
- display_name: string;
6037
- rating: number;
6038
- wins: number;
6039
- losses: number;
6040
- games_played: number;
6041
- win_rate: number;
6042
- tier: string;
6043
- division: number;
6044
- updated_at: string;
6045
- }
6046
- interface PlayerStats {
6047
- player_id: string;
6048
- rating: number;
6049
- wins: number;
6050
- losses: number;
6051
- games_played: number;
6052
- win_rate: number;
6053
- tier: string;
6054
- division: number;
6055
- match_history: MatchResult[];
6056
- }
6057
- interface MatchResult {
6058
- match_id: string;
6059
- result: "win" | "loss" | "draw";
6060
- rating_change: number;
6061
- played_at: string;
6062
- }
6063
- interface VoiceChannel {
6064
- id: string;
6065
- room_id: string;
6066
- name: string;
6067
- type: "global" | "team" | "proximity" | "private" | "spectator";
6068
- team_id?: string;
6069
- max_members: number;
6070
- members: Record<string, VoiceMember>;
6071
- webrtc_room_id: string;
6072
- created_at: string;
6073
- }
6074
- interface VoiceMember {
6075
- player_id: string;
6076
- display_name: string;
6077
- joined_at: string;
6078
- webrtc_url?: string;
6079
- }
6080
- interface ReplayInfo {
6081
- id: string;
6082
- room_id: string;
6083
- game_type: string;
6084
- map_name?: string;
6085
- duration: number;
6086
- tick_rate: number;
6087
- start_tick: number;
6088
- end_tick: number;
6089
- players: ReplayPlayerInfo[];
6090
- file_size: number;
6091
- created_at: string;
6092
- schema_version?: string;
6093
- }
6094
- interface ReplayPlayerInfo {
6095
- player_id: string;
6096
- display_name: string;
6097
- team_id?: string;
6098
- }
6099
- interface ReplayHighlight {
6100
- tick: number;
6101
- type: string;
6102
- actor_id: string;
6103
- score: number;
6104
- }
6105
- /** Matchqueue ticket — attributes 는 free-form (rating/region 등 자유). */
6106
- interface MatchqueueTicket {
6107
- ticket_id: string;
6108
- queue_key: string;
6109
- attributes?: Record<string, unknown>;
6110
- enqueued_at: string;
6111
- ttl_at?: string;
6112
- }
6113
- interface MatchqueueListResponse {
6114
- tickets: MatchqueueTicket[];
6115
- count: number;
6116
- }
6117
- /**
6118
- * v3.0 Leaderboard score entry — score 는 사용자 Lua 의 ELO/MMR 공식 결과.
6119
- * rank 는 GetTop/GetRank 시점에만 채워진다 (저장은 안 됨).
6120
- *
6121
- * v2.x 의 LeaderboardEntry (player_id/rating/tier/wins/losses 등) 와 다른 시그니처라
6122
- * 새 이름 사용. v2 LeaderboardEntry 는 v3.0 에서 deprecated.
6123
- */
6124
- interface LeaderboardScoreEntry {
6125
- member: string;
6126
- score: number;
6127
- updated_at: string;
6128
- rank?: number;
6129
- }
6130
- interface LeaderboardListResponse {
6131
- entries: LeaderboardScoreEntry[];
6132
- count: number;
6133
- }
6134
- /** Lua script — 메타 + 단일 버전. */
6135
- interface ScriptMeta {
6136
- app_id: string;
6137
- name: string;
6138
- active_version: number;
6139
- latest_version: number;
6140
- status: "active" | "staging" | "disabled";
6141
- updated_at: string;
6142
- }
6143
- interface ScriptVersion {
6144
- app_id: string;
6145
- name: string;
6146
- version: number;
6147
- hash: string;
6148
- code: string;
6149
- uploaded_at: string;
6150
- uploaded_by?: string;
6151
- }
6152
- interface ScriptListResponse {
6153
- scripts: ScriptMeta[];
6154
- count: number;
6155
- }
6156
- interface ScriptVersionListResponse {
6157
- versions: ScriptVersion[];
6158
- count: number;
6159
- }
6160
- interface ScriptDetailResponse {
6161
- meta: ScriptMeta;
6162
- active?: ScriptVersion;
6163
- }
6164
4377
 
6165
- /**
6166
- * 게임 서버 `create_room` 메시지 페이로드는 snake_case 필드를 기대한다 (Go 핸들러 JSON 태그).
6167
- * SDK 의 GameRoomConfig 는 camelCase 이므로, scriptName / tickRate / maxPlayers / roomId /
6168
- * categoryId 를 와이어 형식으로 매핑한다. 추가 필드(metadata)는 그대로 통과.
6169
- *
6170
- * platform-issue 019e123a (2026-05-10) 의 회귀 가드 — scriptName 누락 시 RoomConfig.ScriptID
6171
- * 가 비어 onTick / onPlayerJoin 등이 silent skip 되던 문제 차단.
6172
- */
6173
- declare function toCreateRoomWire(config: GameRoomConfig): Record<string, unknown>;
6174
4378
  /**
6175
4379
  * 게임 룸 클라이언트
6176
4380
  * WebSocket 연결을 관리하고 게임 상태를 동기화합니다.
@@ -6185,8 +4389,6 @@ declare class GameRoom {
6185
4389
  private actionSequence;
6186
4390
  private _roomId;
6187
4391
  private _state;
6188
- private _scriptName;
6189
- private _scriptVersion;
6190
4392
  private _isConnected;
6191
4393
  constructor(config: GameClientConfig);
6192
4394
  /**
@@ -6214,29 +4416,9 @@ declare class GameRoom {
6214
4416
  */
6215
4417
  disconnect(): void;
6216
4418
  /**
6217
- * 룸 생성 (호환 시그니처) — 초기 상태만 반환.
6218
- *
6219
- * Attached script 의 검증(scriptName/scriptVersion echo 검사) 이 필요하면
6220
- * 인스턴스 getter (`room.scriptName`, `room.scriptVersion`) 를 함께 사용하거나,
6221
- * 메타까지 한 번에 받고 싶다면 {@link createRoomDetailed} 를 사용한다.
6222
- *
6223
- * 미존재/비활성 scriptName 은 서버가 `SCRIPT_NOT_FOUND` 로 즉시 reject 한다.
6224
- * reject 값은 `GameError` 인스턴스이며 `.code`/`.requested`/`.available` 로 분기 가능.
4419
+ * 룸 생성
6225
4420
  */
6226
4421
  createRoom(config?: GameRoomConfig): Promise<GameState>;
6227
- /**
6228
- * 룸 생성 + 메타 — server 가 attach 한 lua script 이름/버전을 함께 반환.
6229
- *
6230
- * @example
6231
- * const { state, scriptName, scriptVersion } = await room.createRoomDetailed({ scriptName: 'njb-main' })
6232
- * if (scriptName !== 'njb-main') throw new Error('script mismatch')
6233
- * console.log('attached', scriptName, 'v', scriptVersion)
6234
- */
6235
- createRoomDetailed(config?: GameRoomConfig): Promise<CreateRoomResult>;
6236
- /** Attached lua script 이름 — createRoom 이후 server 가 echo 한 값. */
6237
- get scriptName(): string | null;
6238
- /** Attached lua script 의 active version. */
6239
- get scriptVersion(): number | null;
6240
4422
  /**
6241
4423
  * 룸 참가
6242
4424
  */
@@ -6285,18 +4467,9 @@ declare class GameAPI {
6285
4467
  private http;
6286
4468
  private gameServerUrl;
6287
4469
  private appId?;
6288
- /**
6289
- * 게임 기능 opt-in 토글 (v3.1+). `cb.game.config.get/set` 으로 호출.
6290
- * 자세한 내용은 [GameConfigAPI] 참고.
6291
- */
6292
- readonly config: GameConfigAPI;
6293
4470
  constructor(http: HttpClient, gameServerUrl?: string, appId?: string);
6294
4471
  /**
6295
- * 게임 룸 클라이언트 생성.
6296
- *
6297
- * `appId` 는 `createClient({ appId })` 로 호출별 지정하거나, 생략 시 `new ConnectBase({ appId })`
6298
- * 의 전역 값을 사용한다. 둘 다 없으면 `connect()` 가 명확한 에러로 reject 한다 — 빈 appId 로
6299
- * `/v1/game//ws` 에 붙어 server 측이 SCRIPT_NOT_FOUND 로 깨지던 silent 회귀 차단 (NJB 019e2210).
4472
+ * 게임 룸 클라이언트 생성
6300
4473
  */
6301
4474
  createClient(config: Omit<GameClientConfig, 'gameServerUrl'>): GameRoom;
6302
4475
  /**
@@ -6309,138 +4482,98 @@ declare class GameAPI {
6309
4482
  getRoom(roomId: string): Promise<GameRoomInfo>;
6310
4483
  /**
6311
4484
  * 룸 생성 (HTTP, gRPC 대안)
6312
- *
6313
- * @deprecated 현재 SDK public 경로로 노출되지 않습니다. 콘솔(admin) 에서 진행하거나 백엔드 public 경로 오픈을 요청하세요.
6314
4485
  */
6315
- createRoom(_appId: string, _config?: GameRoomConfig): Promise<GameRoomInfo>;
4486
+ createRoom(appId: string, config?: GameRoomConfig): Promise<GameRoomInfo>;
6316
4487
  /**
6317
4488
  * 룸 삭제 (HTTP)
6318
- *
6319
- * @deprecated 현재 SDK public 경로로 노출되지 않습니다. 콘솔(admin) 에서 진행하거나 백엔드 public 경로 오픈을 요청하세요.
6320
- */
6321
- deleteRoom(_roomId: string): Promise<void>;
6322
- joinSpectator(roomId: string, playerId: string): Promise<SpectatorInfo>;
6323
- leaveSpectator(roomId: string, spectatorId: string): Promise<void>;
6324
- getSpectators(roomId: string): Promise<SpectatorInfo[]>;
6325
- joinVoiceChannel(roomId: string, playerId: string): Promise<VoiceChannel>;
6326
- leaveVoiceChannel(roomId: string, playerId: string): Promise<void>;
6327
- setMute(playerId: string, muted: boolean): Promise<void>;
6328
- listReplays(roomId?: string): Promise<ReplayInfo[]>;
6329
- getReplay(replayId: string): Promise<ReplayInfo>;
6330
- downloadReplay(replayId: string): Promise<ArrayBuffer>;
6331
- getReplayHighlights(replayId: string): Promise<ReplayHighlight[]>;
6332
- private getHeaders;
6333
- private gameFetch;
4489
+ */
4490
+ deleteRoom(roomId: string): Promise<void>;
6334
4491
  /**
6335
- * matchqueue ticket 등록.
6336
- *
6337
- * @example
6338
- * await cb.game.enqueueMatch(appId, "ranked", userId, {
6339
- * rating: 1500, region: "asia", team_size: 5,
6340
- * }, 60)
6341
- *
6342
- * @constraints appId/queueKey/ticketId 는 [a-zA-Z0-9_-] 만 허용. ttlSec=0 이면 만료 없음.
4492
+ * 매치메이킹 큐에 참가
6343
4493
  */
6344
- enqueueMatch(appId: string, queueKey: string, ticketId: string, attributes?: Record<string, unknown>, ttlSec?: number): Promise<MatchqueueTicket>;
4494
+ joinQueue(req: JoinQueueRequest): Promise<MatchmakingTicket>;
6345
4495
  /**
6346
- * matchqueue 모든 ticket 목록 반환. 사용자 Lua 가 매칭 후보 선별에 사용.
6347
- *
6348
- * @example const tickets = await cb.game.listMatchqueue(appId, "ranked")
4496
+ * 매치메이킹 큐에서 탈퇴
6349
4497
  */
6350
- listMatchqueue(appId: string, queueKey: string): Promise<MatchqueueListResponse>;
4498
+ leaveQueue(ticketId: string): Promise<void>;
6351
4499
  /**
6352
- * 매칭 큐에서 ticket 제거 (예: 사용자가 매칭 취소).
4500
+ * 매치메이킹 상태 조회
6353
4501
  */
6354
- cancelMatch(appId: string, queueKey: string, ticketId: string): Promise<void>;
4502
+ getMatchStatus(params: {
4503
+ ticketId?: string;
4504
+ playerId?: string;
4505
+ }): Promise<MatchmakingTicket>;
6355
4506
  /**
6356
- * leaderboard 점수 기록. mode="set" (기본, overwrite) 또는 "incr" (증감).
6357
- *
6358
- * @example
6359
- * await cb.game.submitScore(appId, "global_elo", userId, 32, "incr")
4507
+ * 로비 목록 조회
6360
4508
  */
6361
- submitScore(appId: string, leaderboardKey: string, member: string, score: number, mode?: "set" | "incr"): Promise<LeaderboardScoreEntry>;
4509
+ listLobbies(): Promise<LobbyInfo[]>;
6362
4510
  /**
6363
- * 상위 n 명 조회 (기본 10).
4511
+ * 로비 생성
6364
4512
  */
6365
- getTopScores(appId: string, leaderboardKey: string, n?: number): Promise<LeaderboardListResponse>;
4513
+ createLobby(req: CreateLobbyRequest): Promise<LobbyInfo>;
6366
4514
  /**
6367
- * 단일 member 의 rank + score.
4515
+ * 로비 상세 조회
6368
4516
  */
6369
- getMemberRank(appId: string, leaderboardKey: string, member: string): Promise<LeaderboardScoreEntry>;
4517
+ getLobby(lobbyId: string): Promise<LobbyInfo>;
6370
4518
  /**
6371
- * member 주변 (위 above 명 + 본인 + 아래 below 명) 조회.
4519
+ * 로비 참가
6372
4520
  */
6373
- getRankAround(appId: string, leaderboardKey: string, member: string, above?: number, below?: number): Promise<LeaderboardListResponse>;
4521
+ joinLobby(lobbyId: string, playerId: string, displayName?: string, password?: string): Promise<{
4522
+ lobbyId: string;
4523
+ }>;
6374
4524
  /**
6375
- * leaderboard 전체 reset (시즌 종료 시).
4525
+ * 로비 퇴장
6376
4526
  */
6377
- resetLeaderboard(appId: string, leaderboardKey: string): Promise<void>;
4527
+ leaveLobby(lobbyId: string, playerId: string): Promise<void>;
6378
4528
  /**
6379
- * 특정 member 만 제거 (계정 삭제 등).
4529
+ * 레디 상태 토글
6380
4530
  */
6381
- removeFromLeaderboard(appId: string, leaderboardKey: string, member: string): Promise<void>;
4531
+ toggleReady(lobbyId: string, playerId: string, ready: boolean): Promise<void>;
6382
4532
  /**
6383
- * 스크립트 버전 업로드 (latest+1). idempotent — 동일 hash 면 새 버전 만들지 않음.
6384
- *
6385
- * @example
6386
- * await cb.game.uploadScript(appId, "ranked_br", fs.readFileSync("./ranked.lua", "utf-8"))
4533
+ * 게임 시작 (호스트만)
6387
4534
  */
6388
- uploadScript(appId: string, name: string, code: string): Promise<ScriptVersion>;
4535
+ startGame(lobbyId: string, hostId: string): Promise<{
4536
+ roomId: string;
4537
+ }>;
6389
4538
  /**
6390
- * app 모든 스크립트 메타데이터 목록.
4539
+ * 플레이어 추방 (호스트만)
6391
4540
  */
6392
- listScripts(appId: string): Promise<ScriptListResponse>;
4541
+ kickPlayer(lobbyId: string, hostId: string, targetPlayerId: string): Promise<void>;
6393
4542
  /**
6394
- * 단일 스크립트 메타 + active version code.
4543
+ * 로비 설정 업데이트 (호스트만)
6395
4544
  */
6396
- getScript(appId: string, name: string): Promise<ScriptDetailResponse>;
4545
+ updateLobby(lobbyId: string, req: UpdateLobbyRequest): Promise<LobbyInfo>;
6397
4546
  /**
6398
- * 단일 스크립트의 모든 버전 이력.
4547
+ * 로비 채팅 전송
6399
4548
  */
6400
- listScriptVersions(appId: string, name: string): Promise<ScriptVersionListResponse>;
4549
+ sendLobbyChat(lobbyId: string, playerId: string, message: string): Promise<void>;
6401
4550
  /**
6402
- * 특정 버전 활성화 (hot reload 자동). version=0 또는 미지정 → latest 활성화.
4551
+ * 플레이어 초대
6403
4552
  */
6404
- activateScript(appId: string, name: string, version?: number): Promise<ScriptMeta>;
4553
+ invitePlayer(lobbyId: string, inviterId: string, inviteeId: string): Promise<LobbyInvite>;
6405
4554
  /**
6406
- * 직전 active 버전으로 롤백 (hot reload 자동).
4555
+ * 초대 수락
6407
4556
  */
6408
- rollbackScript(appId: string, name: string): Promise<ScriptMeta>;
4557
+ acceptInvite(inviteId: string, playerId: string, displayName?: string): Promise<{
4558
+ lobbyId: string;
4559
+ }>;
6409
4560
  /**
6410
- * 스크립트 비활성화 (status=disabled, ActiveVersion=0). 코드/모든 버전은 보존되며
6411
- * 이후 {@link activateScript} 로 재활성화할 수 있다.
6412
- *
6413
- * v3.14.0 BREAKING — 이전엔 `disableScript` 가 `DELETE /scripts/:name` 을 호출해
6414
- * Disable 매핑이었으나, server 측 `DELETE` 가 hard-delete 로 분리됨에 따라
6415
- * `POST /scripts/:name/deactivate` 로 endpoint 이동. 메서드명도 의도 명확화 위해 rename.
4561
+ * 초대 거절
6416
4562
  */
6417
- deactivateScript(appId: string, name: string): Promise<void>;
4563
+ declineInvite(inviteId: string, playerId: string): Promise<void>;
6418
4564
  /**
6419
- * 스크립트 영구 삭제 (hard-delete) — 메타 + 모든 버전 영구 제거. 복구 불가.
6420
- *
6421
- * 잘못 업로드된 스크립트나 더 이상 사용하지 않는 슬롯을 정리할 때 사용. 단순 비활성화는
6422
- * {@link deactivateScript} 를 사용한다 (코드 보존, 재활성화 가능).
6423
- *
6424
- * v3.14.0 신규 — server 측 `DELETE /scripts/:name` 의 의미가 Disable → hard-delete 로
6425
- * 변경된 것에 대응.
4565
+ * 플레이어의 받은 초대 목록
6426
4566
  */
6427
- deleteScript(appId: string, name: string): Promise<void>;
4567
+ getPlayerInvites(playerId: string): Promise<LobbyInvite[]>;
4568
+ private getHeaders;
6428
4569
  }
6429
4570
 
6430
- interface AdsenseConnectionInfo {
6431
- is_connected: boolean;
6432
- email?: string;
6433
- account_id?: string;
6434
- }
6435
- interface AdmobConnectionInfo {
4571
+ interface GoogleConnectionStatus {
6436
4572
  is_connected: boolean;
6437
4573
  email?: string;
6438
- account_id?: string;
6439
- publisher_id?: string;
6440
- }
6441
- interface GoogleConnectionStatus {
6442
- adsense: AdsenseConnectionInfo;
6443
- admob: AdmobConnectionInfo;
4574
+ adsense_account_id?: string;
4575
+ admob_account_id?: string;
4576
+ admob_publisher_id?: string;
6444
4577
  }
6445
4578
  interface AdReportSummary {
6446
4579
  total_earnings: number;
@@ -6492,18 +4625,15 @@ declare class AdsAPI {
6492
4625
  */
6493
4626
  private getPublicPrefix;
6494
4627
  /**
6495
- * AdSense / AdMob 연결 상태 확인 (중첩 구조)
4628
+ * AdSense 연결 상태 확인
6496
4629
  *
6497
- * @returns `{ adsense, admob }` 각각 연결 상태·이메일·계정 ID
4630
+ * @returns 연결 상태, 이메일, AdSense 계정 ID
6498
4631
  *
6499
4632
  * @example
6500
4633
  * ```typescript
6501
4634
  * const status = await cb.ads.getConnectionStatus()
6502
- * if (status.adsense.is_connected) {
6503
- * console.log('AdSense 연결됨:', status.adsense.email, status.adsense.account_id)
6504
- * }
6505
- * if (status.admob.is_connected) {
6506
- * console.log('AdMob 연결됨:', status.admob.account_id, status.admob.publisher_id)
4635
+ * if (status.is_connected) {
4636
+ * console.log('연결됨:', status.email)
6507
4637
  * }
6508
4638
  * ```
6509
4639
  */
@@ -6578,7 +4708,7 @@ declare class AdsAPI {
6578
4708
  * ```typescript
6579
4709
  * import ConnectBase from 'connectbase-client'
6580
4710
  *
6581
- * const cb = new ConnectBase({ publicKey: 'your-public-key' })
4711
+ * const cb = new ConnectBase({ apiKey: 'your-api-key' })
6582
4712
  *
6583
4713
  * // 플랫폼 감지
6584
4714
  * const platform = cb.native.getPlatform() // 'web' | 'mobile' | 'desktop'
@@ -7036,47 +5166,7 @@ interface CreateDocumentRequest {
7036
5166
  source_type?: 'file' | 'text' | 'url';
7037
5167
  content?: string;
7038
5168
  source_url?: string;
7039
- /**
7040
- * source_type='file' 일 때 사용. 바이너리를 base64 로 인코딩해서 보낸다.
7041
- * 서버가 PDF / DOCX / text 류를 자동으로 텍스트 추출 → 청킹·인덱싱 한다.
7042
- * 파일 50MB 상한, 이미지 OCR / 스캔 PDF 는 미지원.
7043
- * 일반적으로는 [`KnowledgeAPI.addDocumentFromFile`](../api/knowledge.ts) 헬퍼를 사용하면
7044
- * Blob / File 로 직접 넘길 수 있다.
7045
- */
7046
- file_content?: string;
7047
- /**
7048
- * file_content 의 MIME (예: 'application/pdf',
7049
- * 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'text/markdown').
7050
- * 비어있으면 서버가 매직 바이트로 자동 추정 — 정확도를 위해 명시 권장.
7051
- */
7052
- mime_type?: string;
7053
- /**
7054
- * 클라이언트가 지정하는 멱등(upsert) 키. 같은 지식 베이스 안에서 유일하다.
7055
- * 같은 `external_id` 로 다시 `addDocument` 하면 새 문서를 만들지 않고 기존 문서를
7056
- * 교체(update)한다 — `listDocuments` 로 기존 문서를 찾아 지울 필요 없이
7057
- * idempotent 한 동기화가 가능하다. 문서 id 도 그대로 유지된다.
7058
- */
7059
- external_id?: string;
7060
- metadata?: Record<string, unknown>;
7061
- }
7062
- /**
7063
- * 문서 update/upsert 요청. 보낸 필드만 교체된다 (생략한 필드는 변경 없음).
7064
- *
7065
- * `content` / `file_content` / `metadata` 중 하나라도 보내면 서버가 전체 재색인을 수행한다
7066
- * (기존 청크 삭제 → 재청킹 → 재색인). RAG 특성상 콘텐츠가 바뀌면 청크 경계가 바뀌어
7067
- * 부분 수정이 불가능하며, 재색인 시 색인 토큰이 다시 과금된다.
7068
- * `name` / `external_id` 만 보내면 재색인 없이 라벨·멱등 키만 변경된다.
7069
- * 문서 id 는 항상 유지되므로 검색 결과의 `document_id` 참조가 깨지지 않는다.
7070
- */
7071
- interface UpdateDocumentRequest {
7072
- name?: string;
7073
- content?: string;
7074
- /** base64 인코딩 파일 (파일 재업로드, PDF/DOCX/text). content 보다 우선하며 source_type 무관하게 추출. */
7075
- file_content?: string;
7076
- /** file_content 의 MIME. 비어있으면 서버 자동 추정. */
7077
- mime_type?: string;
7078
5169
  metadata?: Record<string, unknown>;
7079
- external_id?: string;
7080
5170
  }
7081
5171
  interface DocumentResponse {
7082
5172
  id: string;
@@ -7084,7 +5174,6 @@ interface DocumentResponse {
7084
5174
  source_type: string;
7085
5175
  mime_type?: string;
7086
5176
  source_url?: string;
7087
- external_id?: string;
7088
5177
  file_size: number;
7089
5178
  chunk_count: number;
7090
5179
  status: 'pending' | 'processing' | 'ready' | 'failed';
@@ -7099,35 +5188,9 @@ interface ListDocumentsResponse {
7099
5188
  documents: DocumentResponse[];
7100
5189
  total_count: number;
7101
5190
  }
7102
- /**
7103
- * 사용자 격리용 magic 토큰. `where` 값에 이 문자열을 넣으면 서버가
7104
- * 인증된 AppMember ID 로 자동 치환한다 (클라이언트 변조 불가).
7105
- *
7106
- * AppMember JWT 가 함께 오지 않은 호출에서 사용하면 401.
7107
- */
7108
- declare const AUTH_MEMBER_ID_TOKEN: "$auth.member_id";
7109
- /**
7110
- * 검색 요청
7111
- * @example { query: "환불 방법", top_k: 5, agentic: true }
7112
- *
7113
- * 사용자별 격리 (AppMember JWT 가 Authorization 헤더로 함께 올 때):
7114
- * - 서버가 자동으로 본인 metadata.user_id 문서로 결과를 제한.
7115
- * - `where` 로 추가 필터를 걸 수 있음. magic 토큰 사용 예:
7116
- * `where: { 'metadata.tag': 'work' }`
7117
- */
7118
5191
  interface KnowledgeSearchRequest {
7119
- /** 검색 쿼리 (필수) */
7120
5192
  query: string;
7121
- /** 반환할 결과 수 (기본: KB 설정값) */
7122
5193
  top_k?: number;
7123
- /** Agentic Search 활성화 — AI 가 쿼리를 자동 생성하여 다중 검색 수행 */
7124
- agentic?: boolean;
7125
- /**
7126
- * metadata 기반 필터 (옵셔널). 키 형식 `metadata.<path>` 또는 raw key.
7127
- * 값에 `AUTH_MEMBER_ID_TOKEN` 사용 시 서버가 인증된 AppMember ID 로 치환.
7128
- * AppMember JWT 컨텍스트가 있으면 서버가 추가로 metadata.user_id 강제 필터를 AND.
7129
- */
7130
- where?: Record<string, unknown>;
7131
5194
  }
7132
5195
  interface KnowledgeSearchResult {
7133
5196
  chunk_id: string;
@@ -7143,634 +5206,124 @@ interface KnowledgeSearchResponse {
7143
5206
  query: string;
7144
5207
  results: KnowledgeSearchResult[];
7145
5208
  total: number;
7146
- /**
7147
- * 응답이 agentic 다중검색으로 생성됐는지 여부. `agentic: true` 를 보냈더라도
7148
- * AI provider 미설정·LLM 오류로 단일 키워드 검색에 폴백되면 `false` — 옵션이
7149
- * placebo 가 되지 않도록 실제 수행 여부를 신호한다.
7150
- */
7151
- agentic?: boolean;
7152
- /** 수행된 agentic 검색 라운드 수 (1~2). 0 또는 미존재는 폴백을 의미. */
7153
- agentic_rounds?: number;
5209
+ }
5210
+ interface ChatRequest {
5211
+ message: string;
5212
+ top_k?: number;
5213
+ model?: string;
5214
+ stream?: boolean;
5215
+ }
5216
+ interface ChatResponse {
5217
+ answer: string;
5218
+ sources: KnowledgeSearchResult[];
5219
+ model: string;
5220
+ token_used?: number;
7154
5221
  }
7155
5222
 
7156
- /**
7157
- * `addDocumentFromFile` 입력 형식.
7158
- *
7159
- * - `File` (DOM) — 브라우저 `<input type="file">` 결과를 그대로 사용. name / type 이 자동 추출됨.
7160
- * - `Blob` (DOM) — Blob.type 이 MIME 으로 사용됨 (없으면 서버 자동 추정).
7161
- * - `{ data, mimeType?, name? }` — Node.js Buffer / Uint8Array. mimeType 명시 권장.
7162
- */
7163
- type KnowledgeFileInput = File | Blob | {
7164
- data: Uint8Array | ArrayBuffer;
7165
- mimeType?: string;
7166
- name?: string;
7167
- };
7168
5223
  /**
7169
5224
  * Knowledge Base API
7170
5225
  *
7171
- * AI 데이터베이스를 위한 문서 저장 및 검색 API.
5226
+ * RAG(Retrieval-Augmented Generation)를 위한 문서 저장 및 검색 API.
7172
5227
  * 문서를 업로드하면 자동으로 청킹되어 키워드 기반 검색이 가능합니다.
7173
5228
  *
7174
- * ## 사용자별 격리 (다중 사용자 RAG 시나리오)
7175
- *
7176
- * `Authorization: Bearer <appmember-jwt>` 를 함께 보내면 서버가:
7177
- * - 검색 결과를 본인 문서 (metadata.user_id == member_id) 로 제한
7178
- * - addDocument 시 metadata.user_id 자동 태깅
7179
- * - listDocuments / deleteDocument 도 본인 자료만 노출
7180
- *
7181
5229
  * @example
7182
5230
  * ```typescript
7183
- * const cb = new ConnectBase({ publicKey: 'your-public-key' })
5231
+ * const cb = new ConnectBase({ apiKey: 'your-api-key' })
7184
5232
  *
7185
5233
  * // 텍스트 문서 추가
7186
5234
  * await cb.knowledge.addDocument('kb-id', {
7187
5235
  * name: '환불 정책',
7188
5236
  * source_type: 'text',
7189
- * content: '환불은 구매 후 7일 이내에 가능합니다...'
7190
- * })
7191
- *
7192
- * // 문서 검색
7193
- * const results = await cb.knowledge.search('kb-id', {
7194
- * query: '환불 정책',
7195
- * top_k: 5
7196
- * })
7197
- *
7198
- * // 사용자별 격리 + 추가 필터 (AppMember JWT 가 같이 와야 함)
7199
- * const results = await cb.knowledge.search('kb-id', {
7200
- * query: '내 메모',
7201
- * where: { 'metadata.tag': 'work' },
7202
- * })
7203
- * ```
7204
- */
7205
- declare class KnowledgeAPI {
7206
- private http;
7207
- constructor(http: HttpClient);
7208
- /**
7209
- * 문서 추가
7210
- *
7211
- * 지식 베이스에 새 문서를 추가합니다. 텍스트 소스인 경우 즉시 처리됩니다.
7212
- *
7213
- * @param kbID - 지식 베이스 ID
7214
- * @param data - 문서 생성 요청
7215
- * @returns 생성된 문서 정보
7216
- *
7217
- * @example
7218
- * ```typescript
7219
- * // 텍스트 문서
7220
- * const doc = await cb.knowledge.addDocument('kb-id', {
7221
- * name: 'FAQ',
7222
- * source_type: 'text',
7223
- * content: '자주 묻는 질문들...'
7224
- * })
7225
- *
7226
- * // URL에서 가져오기
7227
- * const doc2 = await cb.knowledge.addDocument('kb-id', {
7228
- * name: '도움말',
7229
- * source_type: 'url',
7230
- * source_url: 'https://example.com/help.html'
7231
- * })
7232
- *
7233
- * // external_id 로 idempotent 동기화 (빌드 스크립트 등)
7234
- * // 같은 external_id 로 다시 호출하면 기존 문서를 교체한다 (문서 id 유지).
7235
- * await cb.knowledge.addDocument('kb-id', {
7236
- * name: '블로그: 제목',
7237
- * source_type: 'text',
7238
- * content: '...',
7239
- * external_id: 'blog/my-post-slug',
7240
- * })
7241
- * ```
7242
- */
7243
- addDocument(kbID: string, data: CreateDocumentRequest): Promise<DocumentResponse>;
7244
- /**
7245
- * 문서 수정 (update/upsert)
7246
- *
7247
- * 기존 문서의 내용·이름·메타데이터를 교체합니다. **문서 id 는 그대로 유지**되므로
7248
- * 검색 결과의 `document_id` 를 외부에서 참조(인용·캐시)해도 편집 후 깨지지 않습니다.
7249
- * 보낸 필드만 교체되며, 내용/이름/메타데이터가 실제로 바뀌면 자동으로 재청킹·재색인됩니다.
7250
- *
7251
- * @param kbID - 지식 베이스 ID
7252
- * @param docID - 문서 ID
7253
- * @param data - 변경할 필드 (생략한 필드는 변경 없음)
7254
- * @returns 수정된 문서 정보
7255
- *
7256
- * @example
7257
- * ```typescript
7258
- * // 내용만 교체 — document_id 는 그대로
7259
- * const doc = await cb.knowledge.updateDocument('kb-id', 'doc-id', {
7260
- * content: '갱신된 환불 정책...',
7261
- * })
7262
- *
7263
- * // 이름 + 메타데이터 교체
7264
- * await cb.knowledge.updateDocument('kb-id', 'doc-id', {
7265
- * name: 'FAQ (2026)',
7266
- * metadata: { tag: 'faq', updated: '2026-05' },
7267
- * })
7268
- * ```
7269
- */
7270
- updateDocument(kbID: string, docID: string, data: UpdateDocumentRequest): Promise<DocumentResponse>;
7271
- /**
7272
- * 파일로 문서 수정 (PDF / DOCX / text 파일 재업로드).
7273
- *
7274
- * [`addDocumentFromFile`](#addDocumentFromFile) 의 update 버전. 문서 id 는 유지됩니다.
7275
- *
7276
- * @param kbID - Knowledge Base ID
7277
- * @param docID - 문서 ID
7278
- * @param file - 새 파일. `File` (DOM) / `Blob` / `{ data, mimeType, name }` (Node) 모두 가능
7279
- * @param options - name / metadata 오버라이드
7280
- * @returns 수정된 문서 정보
7281
- */
7282
- updateDocumentFromFile(kbID: string, docID: string, file: KnowledgeFileInput, options?: {
7283
- name?: string;
7284
- metadata?: Record<string, unknown>;
7285
- }): Promise<DocumentResponse>;
7286
- /**
7287
- * 파일을 KB 에 추가 (PDF / DOCX / text 파일).
7288
- *
7289
- * 브라우저에서 `<input type="file">` 로 받은 `File` 또는 `Blob` 을 그대로 넘기면 SDK 가
7290
- * base64 로 인코딩 + MIME 추정 후 서버에 보낸다. 서버는 PDF / DOCX / text 류를 자동
7291
- * 텍스트 추출하여 기존 청킹·인덱싱 파이프라인을 태운다.
7292
- *
7293
- * 지원 MIME (2026-05 기준):
7294
- * - `application/pdf` (텍스트 PDF — 스캔 이미지 PDF 는 미지원)
7295
- * - `application/vnd.openxmlformats-officedocument.wordprocessingml.document` (DOCX)
7296
- * - `text/*` (plain / markdown / csv / html)
7297
- * - `application/json`
7298
- *
7299
- * 비지원 MIME (이미지 OCR / 한글 HWP / XLSX 등) 은 향후 추가될 예정 — 현재는 명시적 에러.
7300
- *
7301
- * 파일 크기 상한: 50MB (원본 바이너리 기준).
7302
- *
7303
- * @param kbID - Knowledge Base ID
7304
- * @param file - 업로드할 파일. `File` (DOM) / `Blob` / `{ data, mimeType, name }` (Node) 모두 가능
7305
- * @param options - name 오버라이드 + metadata
7306
- * @returns 생성된 문서 정보 (서버에서 status='processing' → 청킹 완료 후 'ready')
7307
- *
7308
- * @example
7309
- * ```typescript
7310
- * // 브라우저: <input type="file"> 로 받은 File
7311
- * const file = event.target.files[0]
7312
- * const doc = await cb.knowledge.addDocumentFromFile('kb-id', file, {
7313
- * metadata: { tag: 'manual' }
7314
- * })
7315
- *
7316
- * // Node.js: fs.readFileSync 로 읽은 Buffer
7317
- * import { readFileSync } from 'node:fs'
7318
- * const data = readFileSync('./report.pdf')
7319
- * const doc = await cb.knowledge.addDocumentFromFile('kb-id', {
7320
- * data,
7321
- * mimeType: 'application/pdf',
7322
- * name: 'report.pdf',
7323
- * })
7324
- * ```
7325
- */
7326
- addDocumentFromFile(kbID: string, file: KnowledgeFileInput, options?: {
7327
- name?: string;
7328
- metadata?: Record<string, unknown>;
7329
- }): Promise<DocumentResponse>;
7330
- /**
7331
- * 문서 목록 조회
7332
- *
7333
- * @param kbID - 지식 베이스 ID
7334
- * @returns 문서 목록
7335
- */
7336
- listDocuments(kbID: string): Promise<ListDocumentsResponse>;
7337
- /**
7338
- * 문서 삭제
7339
- *
7340
- * 문서와 관련된 모든 청크도 함께 삭제됩니다.
7341
- *
7342
- * @param kbID - 지식 베이스 ID
7343
- * @param docID - 문서 ID
7344
- */
7345
- deleteDocument(kbID: string, docID: string): Promise<void>;
7346
- /**
7347
- * 문서 검색
7348
- *
7349
- * 키워드 기반으로 관련 문서 청크를 검색합니다.
7350
- * 제목, 내용, 키워드 필드에서 매칭되며 점수 기반으로 정렬됩니다.
7351
- *
7352
- * @param kbID - 지식 베이스 ID
7353
- * @param request - 검색 요청
7354
- * @returns 검색 결과
7355
- *
7356
- * @example
7357
- * ```typescript
7358
- * const results = await cb.knowledge.search('kb-id', {
7359
- * query: '환불 신청 방법',
7360
- * top_k: 5 // 상위 5개 결과
7361
- * })
7362
- *
7363
- * for (const result of results.results) {
7364
- * console.log(`[${result.score}] ${result.title}`)
7365
- * console.log(result.content)
7366
- * }
7367
- * ```
7368
- */
7369
- search(kbID: string, request: KnowledgeSearchRequest): Promise<KnowledgeSearchResponse>;
7370
- /**
7371
- * 문서 검색 (GET 방식)
7372
- *
7373
- * 쿼리 파라미터로 검색합니다.
7374
- *
7375
- * @param kbID - 지식 베이스 ID
7376
- * @param query - 검색 쿼리
7377
- * @param topK - 반환할 결과 수 (기본값: 지식 베이스 설정)
7378
- */
7379
- searchGet(kbID: string, query: string, topK?: number): Promise<KnowledgeSearchResponse>;
7380
- }
7381
-
7382
- interface AIMessage {
7383
- role: 'system' | 'user' | 'assistant' | 'tool';
7384
- content: string;
7385
- toolCalls?: AIToolCall[];
7386
- toolCallId?: string;
7387
- }
7388
- interface AIToolCall {
7389
- id: string;
7390
- name: string;
7391
- arguments: Record<string, unknown>;
7392
- }
7393
- interface AITool {
7394
- name: string;
7395
- description: string;
7396
- inputSchema: Record<string, unknown>;
7397
- }
7398
- interface AIChatRequest {
7399
- messages: AIMessage[];
7400
- maxTokens?: number;
7401
- temperature?: number;
7402
- topP?: number;
7403
- provider?: string;
7404
- model?: string;
7405
- tools?: AITool[];
7406
- appId?: string;
7407
- knowledgeBaseId?: string;
7408
- topK?: number;
7409
- agentic?: boolean;
7410
- toolGroupId?: string;
7411
- }
7412
- interface AIToolEvent {
7413
- type: 'tool_start' | 'tool_end' | 'heartbeat';
7414
- name?: string;
7415
- toolCallId?: string;
7416
- arguments?: Record<string, unknown>;
7417
- result?: string;
7418
- success?: boolean;
7419
- durationMs?: number;
7420
- }
7421
- interface AISource {
7422
- chunkId: string;
7423
- documentId: string;
7424
- documentName: string;
7425
- content: string;
7426
- score: number;
7427
- }
7428
- interface AIChatResponse {
7429
- content: string;
7430
- /**
7431
- * 추론 모델(Qwen3 reasoning, OpenAI o-series, DeepSeek-R1 등)의 사고 과정.
7432
- * 최종 답변(`content`)과 분리되어 제공됩니다. 추론 모델이 아니면 비어 있습니다.
7433
- */
7434
- reasoning?: string;
7435
- finishReason?: string;
7436
- usage?: {
7437
- promptTokens: number;
7438
- completionTokens: number;
7439
- totalTokens: number;
7440
- };
7441
- provider: string;
7442
- model: string;
7443
- toolCalls?: AIToolCall[];
7444
- sources?: AISource[];
7445
- }
7446
- /**
7447
- * Agentic 검색의 한 단계 진행 상황. `chatStream({ agentic: true })` 시
7448
- * `onSearching` 콜백으로 실시간 전달되어 "검색 중…" 진행 UI 를 구성할 수 있다.
7449
- */
7450
- interface AgenticSearchProgress {
7451
- /** 'query_generation' = 검색어 생성 중, 'searching' = 검색 실행 중, 'complete' = 검색 종료 */
7452
- phase: 'query_generation' | 'searching' | 'complete';
7453
- /** 검색 라운드 (1~2). agentic 은 결과가 부족하면 2라운드까지 수행. */
7454
- round?: number;
7455
- /** 이 라운드에서 생성된 검색 쿼리들 (phase='searching'). */
7456
- queries?: string[];
7457
- /** phase='complete': 누적 결과 청크 수. */
7458
- results?: number;
7459
- /** phase='complete': 총 검색 라운드 수. 0 은 폴백(단일 키워드 검색)을 의미. */
7460
- rounds?: number;
7461
- }
7462
- interface AIStreamChunk {
7463
- type?: 'sources' | 'token' | 'searching' | 'tool_start' | 'tool_end' | 'heartbeat';
7464
- content: string;
7465
- /**
7466
- * 추론 모델의 사고 과정 델타. 추론 구간의 청크는 `content`가 비어 있고
7467
- * `reasoning`만 채워져 전달됩니다.
7468
- */
7469
- reasoning?: string;
7470
- finishReason?: string;
7471
- done: boolean;
7472
- toolCalls?: AIToolCall[];
7473
- sources?: AISource[];
7474
- name?: string;
7475
- toolCallId?: string;
7476
- arguments?: Record<string, unknown>;
7477
- result?: string;
7478
- success?: boolean;
7479
- durationMs?: number;
7480
- searching?: AgenticSearchProgress;
7481
- }
7482
-
7483
- /**
7484
- * AI API
7485
- *
7486
- * AI 채팅 및 AI 데이터베이스 연동 API.
7487
- * AI 데이터베이스 ID를 지정하면 문서 검색 후 컨텍스트를 포함하여 응답합니다.
7488
- *
7489
- * @example
7490
- * ```typescript
7491
- * const cb = new ConnectBase({ publicKey: 'your-public-key' })
7492
- *
7493
- * // 일반 AI 채팅
7494
- * const response = await cb.ai.chat({
7495
- * messages: [{ role: 'user', content: '안녕하세요' }],
7496
- * provider: 'gemini'
7497
- * })
7498
- *
7499
- * // AI 데이터베이스 연동 채팅
7500
- * const ragResponse = await cb.ai.chat({
7501
- * messages: [{ role: 'user', content: '환불 정책이 어떻게 되나요?' }],
7502
- * knowledgeBaseId: 'kb-id',
7503
- * topK: 5
7504
- * })
7505
- * ```
7506
- */
7507
- declare class AIAPI {
7508
- private http;
7509
- constructor(http: HttpClient);
7510
- /**
7511
- * AI 채팅 (동기)
7512
- */
7513
- chat(request: AIChatRequest): Promise<AIChatResponse>;
7514
- /**
7515
- * AI 채팅 스트리밍 (SSE)
7516
- *
7517
- * @example
7518
- * ```typescript
7519
- * await cb.ai.chatStream({
7520
- * messages: [{ role: 'user', content: '안녕하세요' }],
7521
- * knowledgeBaseId: 'kb-id',
7522
- * }, {
7523
- * onSources: (sources) => console.log('참조:', sources),
7524
- * onReasoning: (reasoning) => process.stdout.write(reasoning),
7525
- * onToken: (content) => process.stdout.write(content),
7526
- * onDone: () => console.log('\\n완료'),
7527
- * onError: (error) => console.error('에러:', error)
7528
- * })
7529
- * ```
7530
- */
7531
- chatStream(request: AIChatRequest, callbacks: {
7532
- onSources?: (sources: AISource[]) => void;
7533
- /**
7534
- * 추론 모델의 사고 과정 델타. 추론 모델(Qwen3 reasoning, o-series 등)을
7535
- * 사용할 때만 호출되며, 최종 답변은 `onToken`으로 별도 전달됩니다.
7536
- */
7537
- onReasoning?: (reasoning: string) => void;
7538
- onToken?: (content: string) => void;
7539
- onToolEvent?: (event: AIToolEvent) => void;
7540
- /**
7541
- * Agentic 검색(`agentic: true`) 진행 상황. agentic 검색이 검색어를
7542
- * 생성하고 다중 라운드로 검색하는 각 단계마다 호출되어, "검색 중…"
7543
- * 진행 UI 를 구성할 수 있다. agentic 미사용 시 호출되지 않는다.
7544
- */
7545
- onSearching?: (progress: AgenticSearchProgress) => void;
7546
- onDone?: () => void;
7547
- onError?: (error: string) => void;
7548
- }): Promise<void>;
7549
- }
7550
-
7551
- /**
7552
- * EndpointAPI — 사용자 PC GPU 모델을 `cb_pk_*` 한 키로 호출하는 dumb pipe.
7553
- *
7554
- * 핵심 비전: ConnectBase 는 **모델·API·워크플로우를 알지 않는다**. 사용자가 자기 PC 에서
7555
- * 자기 모델을 띄우고 자기 API 를 정한다. SDK 는 라벨 → tunnel 매핑만 알고 페이로드
7556
- * 그대로 forward.
7557
- *
7558
- * @example ComfyUI 호출
7559
- * ```typescript
7560
- * const cb = new ConnectBase({ publicKey: "cb_pk_..." })
7561
- * const res = await cb.endpoint.call("comfyui-main", {
7562
- * method: "POST",
7563
- * path: "/prompt",
7564
- * body: JSON.stringify({
7565
- * prompt: {
7566
- * // ComfyUI 노드 그래프
7567
- * },
7568
- * }),
7569
- * headers: { "Content-Type": "application/json" },
7570
- * })
7571
- * const data = await res.json()
7572
- * ```
7573
- *
7574
- * @example 스트리밍 응답 (SSE / chunked)
7575
- * ```typescript
7576
- * const res = await cb.endpoint.call("vllm-local", {
7577
- * method: "POST",
7578
- * path: "/v1/chat/completions",
7579
- * body: JSON.stringify({
7580
- * stream: true,
7581
- * messages: [
7582
- * // { role, content }
7583
- * ],
7584
- * }),
7585
- * headers: { "Content-Type": "application/json" },
5237
+ * content: '환불은 구매 후 7일 이내에 가능합니다...'
7586
5238
  * })
7587
- * if (!res.body) throw new Error("no stream")
7588
- * const reader = res.body.getReader()
7589
- * while (true) {
7590
- * const { done, value } = await reader.read()
7591
- * if (done) break
7592
- * // value 는 Uint8Array — 디코드 후 처리
7593
- * }
7594
- * ```
7595
5239
  *
7596
- * @example AbortSignal 으로 취소
7597
- * ```typescript
7598
- * const ctrl = new AbortController()
7599
- * setTimeout(() => ctrl.abort(), 30_000) // 30초 후 취소
7600
- * const res = await cb.endpoint.call("hunyuan-laptop", {
7601
- * method: "POST",
7602
- * path: "/generate",
7603
- * signal: ctrl.signal,
7604
- * body: JSON.stringify({
7605
- * // 모델 입력
7606
- * }),
5240
+ * // 문서 검색
5241
+ * const results = await cb.knowledge.search('kb-id', {
5242
+ * query: '환불 정책',
5243
+ * top_k: 5
7607
5244
  * })
7608
5245
  * ```
7609
5246
  */
7610
- declare class EndpointAPI {
5247
+ declare class KnowledgeAPI {
7611
5248
  private http;
7612
5249
  constructor(http: HttpClient);
7613
5250
  /**
7614
- * 라벨 + path 로 사용자 PC 모델 호출. fetch() 시그니처 호환.
7615
- *
7616
- * 동작:
7617
- * - URL 조립: `${baseUrl}/v1/proxy/${label}${path}`
7618
- * - X-Public-Key 헤더 자동 주입 (호출자가 명시하면 그 값 우선)
7619
- * - body / method / 추가 헤더 / signal 그대로 전달
7620
- * - 응답 그대로 반환 (Response 객체) — 스트리밍은 res.body 로 read
5251
+ * 문서 추가
7621
5252
  *
7622
- * @param label - 콘솔에서 등록한 endpoint 라벨 (예: "comfyui-main")
7623
- * @param init - fetch() 의 RequestInit + path. path 는 사용자 모델 서버의 엔드포인트 경로 (예: "/prompt", "/v1/chat/completions").
7624
- */
7625
- call(label: string, init: EndpointCallInit): Promise<Response>;
7626
- /**
7627
- * 라벨 + path 의 최종 호출 URL `${baseUrl}/v1/proxy/${label}${path}` 을 조립해서
7628
- * 반환. URL 을 다른 시스템 (Service Worker, 백엔드 워커, 로깅) 에 넘기거나
7629
- * 디버깅 용도일 때 사용.
5253
+ * 지식 베이스에 문서를 추가합니다. 텍스트 소스인 경우 즉시 처리됩니다.
7630
5254
  *
7631
- * ⚠️ **`<img src>` / 네이티브 `WebSocket` / `<script src>` / `EventSource` 처럼
7632
- * 커스텀 헤더를 보내는 브라우저 API 에 직접 넘기면 401 입니다.** ConnectBase
7633
- * 프록시는 모든 요청에 `X-Public-Key` 헤더를 요구하고, 쿼리 파라미터 폴백은
7634
- * 제공하지 않습니다. 그런 경우엔 `call()` 로 받아서 Blob URL 로 변환하세요.
5255
+ * @param kbID - 지식 베이스 ID
5256
+ * @param data - 문서 생성 요청
5257
+ * @returns 생성된 문서 정보
7635
5258
  *
7636
- * @example URL 을 워커로 넘겨 호출 (✅)
5259
+ * @example
7637
5260
  * ```typescript
7638
- * sw.postMessage({
7639
- * url: cb.endpoint.url("comfyui-main", "/prompt"),
7640
- * key: publicKey, // 워커가 X-Public-Key 헤더로 부착
5261
+ * // 텍스트 문서
5262
+ * const doc = await cb.knowledge.addDocument('kb-id', {
5263
+ * name: 'FAQ',
5264
+ * source_type: 'text',
5265
+ * content: '자주 묻는 질문들...'
7641
5266
  * })
7642
- * ```
7643
5267
  *
7644
- * @example 이미지 렌더링은 call() + Blob URL 패턴으로 (✅)
7645
- * ```typescript
7646
- * const res = await cb.endpoint.call("comfyui-main", {
7647
- * path: `/view?filename=${encodeURIComponent(name)}`,
5268
+ * // URL에서 가져오기
5269
+ * const doc2 = await cb.knowledge.addDocument('kb-id', {
5270
+ * name: '도움말',
5271
+ * source_type: 'url',
5272
+ * source_url: 'https://example.com/help.html'
7648
5273
  * })
7649
- * img.src = URL.createObjectURL(await res.blob())
7650
- * // 나중에 URL.revokeObjectURL(img.src)
7651
5274
  * ```
7652
5275
  */
7653
- url(label: string, path: string): string;
5276
+ addDocument(kbID: string, data: CreateDocumentRequest): Promise<DocumentResponse>;
7654
5277
  /**
7655
- * 같은 endpoint 호출을 `predicate` 가 값을 반환할 때까지 주기적으로 반복.
7656
- * ComfyUI `/history/{id}`, A1111 `/sdapi/v1/progress`, 자체 큐 API 처럼
7657
- * "작업 제출 → 폴링" 패턴을 한 줄로 처리하기 위한 헬퍼.
5278
+ * 문서 목록 조회
7658
5279
  *
7659
- * 동작:
7660
- * - `call(label, init)` → predicate(response) 호출
7661
- * - predicate 가 `undefined` 면 `intervalMs` 만큼 대기 후 재시도
7662
- * - predicate 가 값을 반환하면 그 값을 즉시 resolve
7663
- * - `timeoutMs` 초과 또는 `signal` abort 시 reject
7664
- * - HTTP 5xx/네트워크 오류는 재시도, 4xx 는 즉시 reject (작업 자체가 잘못된 경우)
5280
+ * @param kbID - 지식 베이스 ID
5281
+ * @returns 문서 목록
5282
+ */
5283
+ listDocuments(kbID: string): Promise<ListDocumentsResponse>;
5284
+ /**
5285
+ * 문서 삭제
7665
5286
  *
7666
- * predicate 같은 Response 한 번만 읽을 수 있으므로, 헬퍼 내부에서
7667
- * `res.clone().json()` 형태로 안전하게 파싱한 뒤 호출자에게 전달.
5287
+ * 문서와 관련된 모든 청크도 함께 삭제됩니다.
7668
5288
  *
7669
- * @example ComfyUI 결과 폴링
7670
- * ```typescript
7671
- * type Hist = Record<string, { outputs: Record<string, { images?: { filename: string }[] }> }>
7672
- *
7673
- * const filename = await cb.endpoint.pollUntil<string>(
7674
- * "comfyui-main",
7675
- * { path: `/history/${promptId}` },
7676
- * (data: Hist) => {
7677
- * const entry = data[promptId]
7678
- * if (!entry) return undefined // 아직 큐에 있음
7679
- * for (const out of Object.values(entry.outputs)) {
7680
- * const img = out.images?.[0]
7681
- * if (img) return img.filename
7682
- * }
7683
- * return undefined
7684
- * },
7685
- * { intervalMs: 1000, timeoutMs: 5 * 60_000 },
7686
- * )
7687
- * ```
5289
+ * @param kbID - 지식 베이스 ID
5290
+ * @param docID - 문서 ID
7688
5291
  */
5292
+ deleteDocument(kbID: string, docID: string): Promise<void>;
7689
5293
  /**
7690
- * Open a native browser `WebSocket` to a user model server (e.g. ComfyUI's
7691
- * `/ws` event channel) through the ConnectBase proxy.
5294
+ * 문서 검색
7692
5295
  *
7693
- * Background: native browser `WebSocket` cannot send custom headers, so
7694
- * `X-Public-Key` cannot be supplied on the Upgrade request. This helper
7695
- * solves that by:
7696
- * 1. POSTing to `/v1/proxy/<label>/ws-ticket` (with `X-Public-Key`)
7697
- * to obtain a single-use 30s token,
7698
- * 2. Opening `wss://<base>/v1/proxy/<label><path>?ticket=<token>&...`,
7699
- * 3. Returning the established `WebSocket` to the caller.
5296
+ * 키워드 기반으로 관련 문서 청크를 검색합니다.
5297
+ * 제목, 내용, 키워드 필드에서 매칭되며 점수 기반으로 정렬됩니다.
7700
5298
  *
7701
- * Both the client→upstream and upstream→client byte streams flow
7702
- * transparently the proxy server adds no framing of its own.
5299
+ * @param kbID - 지식 베이스 ID
5300
+ * @param request - 검색 요청
5301
+ * @returns 검색 결과
7703
5302
  *
7704
- * @example ComfyUI native event channel
7705
- * ```ts
7706
- * const ws = await cb.endpoint.connectWebSocket("comfyui-main", {
7707
- * path: "/ws",
7708
- * query: { clientId: crypto.randomUUID() },
5303
+ * @example
5304
+ * ```typescript
5305
+ * const results = await cb.knowledge.search('kb-id', {
5306
+ * query: '환불 신청 방법',
5307
+ * top_k: 5 // 상위 5개 결과
7709
5308
  * })
7710
- * ws.onmessage = (e) => {
7711
- * const msg = JSON.parse(typeof e.data === "string" ? e.data : "")
7712
- * if (msg.type === "progress") {
7713
- * // progress.value / progress.max — exact node-level progress
7714
- * }
5309
+ *
5310
+ * for (const result of results.results) {
5311
+ * console.log(`[${result.score}] ${result.title}`)
5312
+ * console.log(result.content)
7715
5313
  * }
7716
5314
  * ```
7717
5315
  */
7718
- connectWebSocket(label: string, opts?: ConnectWebSocketOptions): Promise<WebSocket>;
7719
- pollUntil<T>(label: string, init: EndpointCallInit, predicate: (body: any, res: Response) => T | undefined | Promise<T | undefined>, opts?: PollUntilOptions): Promise<T>;
7720
- }
7721
- /**
7722
- * EndpointAPI.call 의 init 인자.
7723
- *
7724
- * 표준 RequestInit 와 거의 동일하지만 `path` 가 필수 (URL 은 SDK 가 조립).
7725
- */
7726
- interface EndpointCallInit {
7727
- /**
7728
- * 사용자 모델 서버의 엔드포인트 경로. 반드시 `/` 로 시작.
7729
- * 예: `"/prompt"`, `"/v1/chat/completions"`, `"/sdapi/v1/txt2img"`.
7730
- */
7731
- path: string;
7732
- /** HTTP 메서드 (기본: GET). */
7733
- method?: string;
7734
- /** 추가 요청 헤더. Content-Type / Authorization 등 사용자 모델 서버가 요구하는 것 그대로. */
7735
- headers?: HeadersInit;
7736
- /** 요청 본문. Blob / ArrayBuffer / FormData / string / ReadableStream 모두 지원. */
7737
- body?: BodyInit | null;
7738
- /** 취소 신호. */
7739
- signal?: AbortSignal;
7740
- }
7741
- /**
7742
- * EndpointAPI.connectWebSocket 옵션.
7743
- */
7744
- interface ConnectWebSocketOptions {
7745
- /**
7746
- * 사용자 모델 서버의 WebSocket 엔드포인트 경로. `/` 로 시작 (기본 `/`).
7747
- * 예: `/ws`, `/socket`.
7748
- */
7749
- path?: string;
7750
- /** 추가 query 파라미터 (ticket 은 자동 주입). */
7751
- query?: Record<string, string>;
7752
- /** WebSocket subprotocols (Sec-WebSocket-Protocol 헤더). */
7753
- protocols?: string | string[];
7754
- /** Abort 시 ws.close(1000) 호출. */
7755
- signal?: AbortSignal;
7756
- }
7757
- /**
7758
- * EndpointAPI.pollUntil 옵션.
7759
- */
7760
- interface PollUntilOptions {
7761
- /** 폴링 간격 (ms). 기본 1500. */
7762
- intervalMs?: number;
7763
- /** 전체 timeout (ms). 기본 300000 (5분). */
7764
- timeoutMs?: number;
5316
+ search(kbID: string, request: KnowledgeSearchRequest): Promise<KnowledgeSearchResponse>;
7765
5317
  /**
7766
- * 응답 본문 파싱 방식.
7767
- * - `"json"` (기본) — `res.json()`. 파싱 실패 시 predicate 에 `undefined` 전달.
7768
- * - `"text"` 문자열 그대로.
7769
- * - `"none"` — 본문 사용 안 함 (예: 헤더 / status 만 보고 판단).
5318
+ * 문서 검색 (GET 방식)
5319
+ *
5320
+ * 쿼리 파라미터로 검색합니다.
5321
+ *
5322
+ * @param kbID - 지식 베이스 ID
5323
+ * @param query - 검색 쿼리
5324
+ * @param topK - 반환할 결과 수 (기본값: 지식 베이스 설정)
7770
5325
  */
7771
- parse?: "json" | "text" | "none";
7772
- /** 외부 취소 신호. abort 시 즉시 reject. */
7773
- signal?: AbortSignal;
5326
+ searchGet(kbID: string, query: string, topK?: number): Promise<KnowledgeSearchResponse>;
7774
5327
  }
7775
5328
 
7776
5329
  interface PublishMessageRequest {
@@ -7836,7 +5389,7 @@ interface QueueInfoResponse {
7836
5389
  *
7837
5390
  * @example
7838
5391
  * ```typescript
7839
- * const cb = new ConnectBase({ publicKey: 'your-public-key' })
5392
+ * const cb = new ConnectBase({ apiKey: 'your-api-key' })
7840
5393
  *
7841
5394
  * // 메시지 발행
7842
5395
  * await cb.queue.publish('queue-id', {
@@ -7885,334 +5438,6 @@ declare class QueueAPI {
7885
5438
  getInfo(queueID: string): Promise<QueueInfoResponse>;
7886
5439
  }
7887
5440
 
7888
- /**
7889
- * 사용자 제보 (end-user issue) — 카테고리.
7890
- *
7891
- * - `bug` 버그 리포트
7892
- * - `question` 질문/문의
7893
- * - `feature_request` 기능 요청
7894
- * - `incident` 긴급 장애
7895
- * - `other` 그 외
7896
- */
7897
- type SupportIssueCategory = 'bug' | 'question' | 'feature_request' | 'incident' | 'other';
7898
- /**
7899
- * 제보 발행 요청 본문.
7900
- */
7901
- interface ReportIssueRequest {
7902
- /** 제목 (최대 200자) */
7903
- title: string;
7904
- /** 본문 (최대 16KB, 마크다운) */
7905
- body: string;
7906
- /** 카테고리. 기본값 `other`. AI 가 자동 보정해 줌. */
7907
- category?: SupportIssueCategory;
7908
- /** 자유 컨텍스트 (페이지 URL, 앱 버전, trace_id 등) */
7909
- metadata?: Record<string, unknown>;
7910
- /**
7911
- * 익명 발행 시 회신 받을 이메일 (선택).
7912
- * 로그인된 사용자면 무시되고 AppMember 정보로 대체.
7913
- */
7914
- anonymousEmail?: string;
7915
- /**
7916
- * Google reCAPTCHA v3 토큰. 익명 발행 + 운영자가 reCAPTCHA 활성화한 앱은 권장.
7917
- * 미주입 시 score=0 으로 처리 — 거부될 수 있음.
7918
- */
7919
- recaptchaToken?: string;
7920
- }
7921
- /**
7922
- * 제보 발행 응답. 보안상 최소 정보만 (id + status + created_at).
7923
- */
7924
- interface ReportIssueResponse {
7925
- id: string;
7926
- status: 'open';
7927
- created_at: string;
7928
- }
7929
- /**
7930
- * Cross-app 위임 user 발행 요청 본문.
7931
- *
7932
- * 일반 `reportIssue` 와 다른 점:
7933
- * - target_app 이 호출자(source_app) 와 다름 (다른 앱에 보내는 위임 제보)
7934
- * - SDK 인증 컨텍스트(publicKey/secretKey) 가 아닌 cross-app OAuth Bearer access_token 사용
7935
- * - reporter_member_id 는 access_token 의 end_user_id claim 에서 자동 추출 (body 로 전달 불가)
7936
- */
7937
- interface ReportIssueOnBehalfOfUserRequest {
7938
- /** 수신 앱(target) 의 ID. OAuth token 의 audience(`app:<uuid>`) 와 일치해야 함. */
7939
- targetAppId: string;
7940
- /**
7941
- * Cross-app OAuth access_token (`authorization_code` grant — user binding 필수).
7942
- * scope 에 `issue:report:on-behalf-of-user` 가 포함돼야 한다.
7943
- * `client_credentials` grant 토큰은 401 `delegated_user_required` 로 거부됨.
7944
- */
7945
- accessToken: string;
7946
- /** 제목 (최대 200자) */
7947
- title: string;
7948
- /** 본문 (최대 16KB, 마크다운) */
7949
- body: string;
7950
- /** 카테고리. 기본값 `other`. AI triage 가 보정. */
7951
- category?: SupportIssueCategory;
7952
- /** 자유 컨텍스트 (페이지 URL, request_id 등). PII 직접 포함 금지. */
7953
- metadata?: Record<string, unknown>;
7954
- }
7955
- /**
7956
- * Support API — 사용자 제보 (end-user issue) 발행.
7957
- *
7958
- * 사용자가 앱 운영자에게 직접 버그/질문/요청을 보내는 채널. AppMember JWT 가 있으면
7959
- * 강 신뢰로 회신 가능, 없으면 익명 발행 (reCAPTCHA 권장).
7960
- *
7961
- * 발행된 이슈는 운영자 콘솔의 inbox 에 들어가며, AI 가 자동으로 요약/긴급도/카테고리를
7962
- * 분류해줘 운영자/AI 의 처리 효율을 높여준다.
7963
- *
7964
- * @example 로그인 사용자가 버그 리포트
7965
- * ```typescript
7966
- * // (cb.auth.signIn 으로 로그인 완료된 상태)
7967
- * await cb.support.reportIssue({
7968
- * title: "결제 화면이 멈춰요",
7969
- * body: "결제 버튼 클릭 후 로딩이 끝나지 않습니다.",
7970
- * category: "bug",
7971
- * metadata: { pageUrl: window.location.href, browser: navigator.userAgent },
7972
- * })
7973
- * ```
7974
- *
7975
- * @example 익명 + reCAPTCHA v3
7976
- * ```typescript
7977
- * // window.grecaptcha 는 reCAPTCHA v3 site script 로드 후 전역으로 노출됨
7978
- * const grecaptcha = (globalThis as any).grecaptcha
7979
- * const SITE_KEY = "your-recaptcha-site-key"
7980
- * const token = await grecaptcha.execute(SITE_KEY, { action: 'report_issue' })
7981
- * await cb.support.reportIssue({
7982
- * title: "버그 제보",
7983
- * body: "익명으로 제보합니다",
7984
- * category: "question",
7985
- * anonymousEmail: "user@example.com",
7986
- * recaptchaToken: token,
7987
- * })
7988
- * ```
7989
- */
7990
- declare class SupportAPI {
7991
- private http;
7992
- constructor(http: HttpClient);
7993
- /**
7994
- * 앱 운영자에게 이슈/문의/요청을 발행한다.
7995
- *
7996
- * 로그인된 사용자(AppMember)면 신뢰 등급이 높고 reporter_member_id 가 자동으로 채워진다.
7997
- * 익명 발행도 가능하나 운영자가 reCAPTCHA 를 활성화한 경우 `recaptchaToken` 이 권장된다.
7998
- *
7999
- * @returns 발행된 이슈 id + 초기 status (`open`) + 생성 시각.
8000
- * @throws ApiError — 본문 길이 초과 / 쿼터 초과(429) / reCAPTCHA 거부(403) 등.
8001
- */
8002
- reportIssue(req: ReportIssueRequest): Promise<ReportIssueResponse>;
8003
- /**
8004
- * 다른 앱(target) 에 자기 사용자(end-user) 명의로 cross-app 위임 이슈를 발행한다.
8005
- *
8006
- * **사용 시나리오**: source_app(예: Makers) 의 backend 가 OAuth `authorization_code` grant 로
8007
- * 발급받은 사용자 access_token 을 사용해, target_app(예: ai-tool) 에게 "Makers user A 가 보낸"
8008
- * 이슈로 제보. target 측 콘솔 inbox 에는 `reporter_kind=user` + `reporter_app_id=Makers` 로 표시.
8009
- *
8010
- * 본 메서드는 SDK 의 publicKey/secretKey 인증 컨텍스트를 사용하지 않고, 호출자가 명시적으로 전달한
8011
- * cross-app OAuth Bearer access_token 만 사용 (server-to-server 위임 흐름).
8012
- *
8013
- * @param req - 발행 본문
8014
- * @param req.targetAppId - 수신 앱 ID (= OAuth token 의 audience `app:<uuid>` 와 일치해야 함)
8015
- * @param req.accessToken - source_app 이 발급받은 cross-app OAuth access_token (authorization_code grant)
8016
- *
8017
- * @throws ApiError 401 — `delegated_user_required`: access_token 이 client_credentials grant
8018
- * @throws ApiError 403 — `trust_chain_violation`: source_app 이 target_app 의 cross-app provider 로 등록되지 않음
8019
- * @throws ApiError 403 — `insufficient_scope`: token 에 `issue:report:on-behalf-of-user` 미포함
8020
- *
8021
- * @example Makers backend → ai-tool 에 사용자 명의 제보
8022
- * ```typescript
8023
- * await cb.support.reportIssueOnBehalfOfUser({
8024
- * targetAppId: 'ai-tool-uuid',
8025
- * accessToken: makersUserAccessToken, // authorization_code grant 토큰
8026
- * title: '생성 실패',
8027
- * body: 'prompt X 가 5분째 응답 없음',
8028
- * category: 'bug',
8029
- * metadata: { prompt_id: 'p_42' },
8030
- * })
8031
- * ```
8032
- */
8033
- reportIssueOnBehalfOfUser(req: ReportIssueOnBehalfOfUserRequest): Promise<ReportIssueResponse>;
8034
- /**
8035
- * ConnectBase 플랫폼 자체 버그/요청/문의를 발행한다.
8036
- *
8037
- * `reportIssue` 와 다른 점: target 이 앱 운영자가 아닌 **ConnectBase 운영팀**.
8038
- * SDK 가 자체 버그를 던지거나, 결제/문서/플랫폼 동작이 이상할 때 사용.
8039
- *
8040
- * 자동 첨부 (opt-out 가능):
8041
- * - SDK 버전 + 플랫폼 (web/node)
8042
- * - 마지막 N(20)개 API 호출 breadcrumb (PII strip 후)
8043
- * - error 객체의 stack trace (sanitize)
8044
- *
8045
- * @example SDK 가 throw 한 에러를 자동 첨부해서 발행
8046
- * ```typescript
8047
- * try {
8048
- * await cb.functions.invoke('foo', {})
8049
- * } catch (err) {
8050
- * await cb.support.reportPlatformBug({
8051
- * title: 'functions.invoke 가 504 만 반환',
8052
- * body: '같은 인자로 5분 째 504. 콘솔에선 정상.',
8053
- * category: 'sdk',
8054
- * severity: 'high',
8055
- * error: err as Error,
8056
- * })
8057
- * }
8058
- * ```
8059
- */
8060
- reportPlatformBug(req: ReportPlatformBugRequest): Promise<ReportPlatformBugResponse>;
8061
- /** 디버깅용: 마지막 N개 API 호출 breadcrumb. PII 제거 후 저장돼있다. */
8062
- getRecentApiCalls(): RecentApiCall[];
8063
- /** breadcrumb buffer 비우기 (사용자 명시적 요청 / 프라이버시). */
8064
- clearRecentApiCalls(): void;
8065
- /**
8066
- * Platform issue 의 reply thread 조회.
8067
- *
8068
- * 본인이 발행한 issue 만 조회 가능 (server-side ownership guard). admin 의 internal 메모는 응답 제외.
8069
- *
8070
- * @param issueId - `reportPlatformBug` 가 반환한 id
8071
- * @throws ApiError 404 — 본인 issue 가 아니거나 존재하지 않음
8072
- */
8073
- listPlatformIssueReplies(issueId: string): Promise<PlatformIssueReply[]>;
8074
- /**
8075
- * Platform issue 에 follow-up reply 작성.
8076
- *
8077
- * 단말 상태(resolved/wontfix/duplicate) issue 는 reply 거부 — 새 issue 발행 권장.
8078
- */
8079
- replyToPlatformIssue(issueId: string, body: string): Promise<PlatformIssueReply>;
8080
- /**
8081
- * 본인이 발행한 platform issue 의 처리 진행 상황을 단건 조회.
8082
- *
8083
- * AI 가 "내가 발행한 이슈 처리됐어?" 를 폴링하는 표준 경로. status / resolution_note /
8084
- * external_links 로 ConnectBase 운영팀의 처리 상태를 확인.
8085
- *
8086
- * @throws ApiError 404 — 본인 issue 가 아니거나 존재하지 않음
8087
- */
8088
- getPlatformIssue(issueId: string): Promise<PlatformIssueDetail>;
8089
- /**
8090
- * 본인 app 으로 발행한 platform issue 목록 (cursor 페이지네이션).
8091
- *
8092
- * status/severity/category 필터 + `since_updated_at` 으로 미해결만 폴링하는 사용 패턴 권장:
8093
- *
8094
- * ```typescript
8095
- * const { issues } = await cb.support.listMyPlatformIssues({ status: ['open', 'triaged', 'in_progress'] })
8096
- * ```
8097
- */
8098
- listMyPlatformIssues(opts?: ListMyPlatformIssuesOptions): Promise<PlatformIssueListPage>;
8099
- }
8100
- /**
8101
- * ConnectBase 플랫폼 이슈 카테고리.
8102
- */
8103
- type PlatformIssueCategory = 'bug' | 'feature_request' | 'sdk' | 'billing' | 'security' | 'performance' | 'docs' | 'other';
8104
- type PlatformIssueSeverity = 'low' | 'medium' | 'high' | 'critical';
8105
- interface ReportPlatformBugRequest {
8106
- /** 제목 (≤200) */
8107
- title: string;
8108
- /** 본문 (≤16KB, markdown) */
8109
- body: string;
8110
- /** 카테고리. 기본 `other`. 운영자가 admin 콘솔에서 조정 가능. */
8111
- category?: PlatformIssueCategory;
8112
- /** 긴급도. 기본 `medium`. 운영자가 admin 콘솔에서 조정 가능. */
8113
- severity?: PlatformIssueSeverity;
8114
- /** AppMember 가 없는 경우 회신용 이메일. */
8115
- reporterEmail?: string;
8116
- /** 익명 발행 시 reCAPTCHA v3 토큰 (운영팀이 RECAPTCHA_SECRET 설정한 경우). */
8117
- recaptchaToken?: string;
8118
- /** 자유 컨텍스트 (페이지 URL, request_id 등). PII 직접 포함 금지 — 호출자 책임. */
8119
- metadata?: Record<string, unknown>;
8120
- /**
8121
- * 자동 컨텍스트 첨부 on/off (기본 true).
8122
- * false 로 설정하면 SDK 버전/플랫폼/breadcrumb/stack 모두 미첨부.
8123
- */
8124
- attachAutomaticContext?: boolean;
8125
- /**
8126
- * 에러 객체. .stack 을 sanitize 후 자동 첨부.
8127
- * stackTrace 와 둘 중 하나만 — error 가 우선.
8128
- */
8129
- error?: Error;
8130
- /** 직접 작성한 stack trace (≤32KB, SDK 가 sanitize). */
8131
- stackTrace?: string;
8132
- /** 호출자가 명시적으로 buffer 를 override (테스트/특수 케이스). */
8133
- recentApiCalls?: RecentApiCall[];
8134
- /** SDK 버전 override (자동 감지 실패 시). */
8135
- sdkVersion?: string;
8136
- /** SDK 플랫폼 override. */
8137
- sdkPlatform?: 'web' | 'node' | 'unity' | 'godot' | 'unreal' | 'cli' | 'mcp' | 'other';
8138
- /** 환경 (production/staging/development). 기본 unknown. */
8139
- environment?: 'production' | 'staging' | 'development' | 'unknown';
8140
- }
8141
- interface ReportPlatformBugResponse {
8142
- id: string;
8143
- status: 'open';
8144
- created_at: string;
8145
- }
8146
- /**
8147
- * Platform issue thread 의 단일 reply.
8148
- *
8149
- * - `author_kind=admin`: ConnectBase 운영팀 응답
8150
- * - `author_kind=user`: 본인이 작성
8151
- * - `author_kind=system`: 자동 메시지 (상태 변경 알림 등)
8152
- */
8153
- /**
8154
- * Platform issue 의 처리 진행 상황 (reporter 시점).
8155
- *
8156
- * admin-only 필드(`assignee_user_id` 등) 는 server 가 redact.
8157
- *
8158
- * 주요 필드:
8159
- * - `status` : `open` → `triaged` → `in_progress` → `resolved` | `wontfix` | `duplicate`
8160
- * - `resolution_note` : 단말 상태 진입 시 ConnectBase 운영팀이 작성한 처리 결과 (markdown)
8161
- * - `external_links` : 운영팀이 연결한 GitHub PR / Linear ticket 등
8162
- * - `resolved_at` : `resolved`/`wontfix` 진입 시각 (해결됐는지 빠른 검사용)
8163
- */
8164
- interface PlatformIssueDetail {
8165
- id: string;
8166
- reporter_kind: 'user' | 'app' | 'system';
8167
- title: string;
8168
- body: string;
8169
- category: PlatformIssueCategory;
8170
- severity: PlatformIssueSeverity;
8171
- status: 'open' | 'triaged' | 'in_progress' | 'resolved' | 'wontfix' | 'duplicate';
8172
- resolution_note?: string;
8173
- sdk_version?: string;
8174
- sdk_platform?: 'web' | 'node' | 'unity' | 'godot' | 'unreal' | 'cli' | 'mcp' | 'other';
8175
- environment?: 'production' | 'staging' | 'development' | 'unknown';
8176
- external_links?: Array<{
8177
- kind: string;
8178
- url: string;
8179
- label?: string;
8180
- }>;
8181
- duplicate_of_id?: string;
8182
- created_at: string;
8183
- updated_at: string;
8184
- resolved_at?: string;
8185
- }
8186
- /** `listMyPlatformIssues` 응답 페이지. */
8187
- interface PlatformIssueListPage {
8188
- issues: PlatformIssueDetail[];
8189
- /** 다음 페이지 cursor. 빈 문자열/undefined 면 끝. */
8190
- next_cursor?: string;
8191
- }
8192
- /** `listMyPlatformIssues` 옵션. */
8193
- interface ListMyPlatformIssuesOptions {
8194
- /** 다중 status 필터. 미지정 시 전체. 처리중만 보려면 `['open','triaged','in_progress']`. */
8195
- status?: PlatformIssueDetail['status'][];
8196
- severity?: PlatformIssueSeverity[];
8197
- category?: PlatformIssueCategory[];
8198
- /** RFC3339 timestamp — 이후 update 된 issue 만. polling 시 마지막 fetch 시각 전달. */
8199
- sinceUpdatedAt?: string;
8200
- /** 이전 페이지 응답의 `next_cursor`. */
8201
- cursor?: string;
8202
- /** 페이지 크기. 기본 50, 최대 200. */
8203
- limit?: number;
8204
- }
8205
- interface PlatformIssueReply {
8206
- id: string;
8207
- issue_id: string;
8208
- author_kind: 'admin' | 'user' | 'system';
8209
- author_user_id?: string;
8210
- author_member_id?: string;
8211
- body: string;
8212
- is_internal: boolean;
8213
- created_at: string;
8214
- }
8215
-
8216
5441
  /**
8217
5442
  * WebTransport-based Game Client
8218
5443
  *
@@ -8259,8 +5484,6 @@ declare class GameRoomTransport {
8259
5484
  private actionSequence;
8260
5485
  private _roomId;
8261
5486
  private _state;
8262
- private _scriptName;
8263
- private _scriptVersion;
8264
5487
  private _isConnected;
8265
5488
  private _connectionStatus;
8266
5489
  private _lastError;
@@ -8287,10 +5510,6 @@ declare class GameRoomTransport {
8287
5510
  * Connection status
8288
5511
  */
8289
5512
  get isConnected(): boolean;
8290
- /** Attached lua script 이름 — createRoom 이후 server 가 echo 한 값. */
8291
- get scriptName(): string | null;
8292
- /** Attached lua script 의 active version. */
8293
- get scriptVersion(): number | null;
8294
5513
  /**
8295
5514
  * Connection state information
8296
5515
  */
@@ -8312,16 +5531,9 @@ declare class GameRoomTransport {
8312
5531
  */
8313
5532
  disconnect(): void;
8314
5533
  /**
8315
- * Create a new room (호환 시그니처) — 초기 상태만 반환.
8316
- *
8317
- * Attached script 메타가 필요하면 {@link createRoomDetailed} 또는 인스턴스 getter
8318
- * (`transport.scriptName`, `transport.scriptVersion`) 를 사용.
5534
+ * Create a new room
8319
5535
  */
8320
5536
  createRoom(config?: GameRoomConfig): Promise<GameState>;
8321
- /**
8322
- * Create a new room — server 가 attach 한 lua script 메타 함께 반환.
8323
- */
8324
- createRoomDetailed(config?: GameRoomConfig): Promise<CreateRoomResult>;
8325
5537
  /**
8326
5538
  * Join an existing room
8327
5539
  */
@@ -8394,21 +5606,9 @@ interface ConnectBaseConfig {
8394
5606
  */
8395
5607
  appId?: string;
8396
5608
  /**
8397
- * Public Key (cb_pk_* 형식, 콘솔 → 설정 → API 에서 발급)
8398
- * 브라우저/클라이언트에서 사용. RLS(Row Level Security) 적용.
8399
- */
8400
- publicKey?: string;
8401
- /**
8402
- * User Secret Key (`cb_sk_*` 형식, 사용자 프로필에서 발급).
8403
- *
8404
- * **SDK 의 앱 레벨 API (database, storage, function 등) 용 자격증명이 아닙니다.**
8405
- * 앱 API 호출에는 `publicKey` 를 사용하세요. 앱 API 경로(`/v1/public/*`)는
8406
- * Public Key(`cb_pk_*`) 만 허용하며, Secret Key 로 호출하면 서버가 401 을 반환합니다.
8407
- *
8408
- * 이 필드는 CLI (`connectbase tunnel`, `connectbase mcp`) 및 사용자 레벨 API 인증에
8409
- * 사용됩니다. 서버 환경에서만 사용하고 절대 브라우저에 노출하지 마세요.
5609
+ * API Key (콘솔에서 발급)
8410
5610
  */
8411
- secretKey?: string;
5611
+ apiKey?: string;
8412
5612
  /**
8413
5613
  * 토큰이 갱신될 때 호출되는 콜백
8414
5614
  */
@@ -8426,7 +5626,7 @@ interface ConnectBaseConfig {
8426
5626
  * @example
8427
5627
  * ```typescript
8428
5628
  * const cb = new ConnectBase({
8429
- * publicKey: 'your-public-key',
5629
+ * apiKey: 'your-api-key',
8430
5630
  * onTokenExpired: () => {
8431
5631
  * // 로그인 페이지로 리다이렉트
8432
5632
  * window.location.href = '/login'
@@ -8435,64 +5635,10 @@ interface ConnectBaseConfig {
8435
5635
  * ```
8436
5636
  */
8437
5637
  onTokenExpired?: () => void;
8438
- /**
8439
- * `/v1/auth/re-issue` 의 일시적 실패 (5xx, 네트워크 오류, abort, 손상된 200 응답) 시 호출.
8440
- *
8441
- * 이 경우 토큰은 폐기되지 않으며 `onTokenExpired` 도 호출되지 않습니다 — 다음 호출에서
8442
- * backoff 만료 후 자동 재시도. 사용자에게 "연결이 잠시 불안정합니다" 같은 비파괴적
8443
- * 알림을 띄울 때 사용. permanent (refresh token 자체 무효) 와 분리하기 위함.
8444
- *
8445
- * @example
8446
- * ```typescript
8447
- * const cb = new ConnectBase({
8448
- * publicKey: '...',
8449
- * onTransientRefreshFailure: (err) => toast.warn('연결이 불안정합니다. 잠시 후 자동 복구됩니다.'),
8450
- * onTokenExpired: () => { window.location.href = '/login' },
8451
- * })
8452
- * ```
8453
- */
8454
- onTransientRefreshFailure?: (error: Error) => void;
8455
- /**
8456
- * 토큰 저장 방식.
8457
- * - 'none' (기본·권장): access token 만 메모리. refresh token 은 서버 HttpOnly cookie 로
8458
- * 보관되어 JS 가 접근할 수 없음 (XSS 시 탈취 불가). 새로고침 후에는 `autoRestoreSession`
8459
- * (기본 true) 으로 cookie 만으로 자동 복구.
8460
- * - 'sessionStorage': 탭 종료 시 삭제 (XSS 시 탭 세션 탈취 가능 — 콘솔 경고 출력)
8461
- * - 'localStorage': 브라우저 종료 후에도 유지 (XSS 시 영구 탈취 가능 — 콘솔 경고 출력)
8462
- * @default 'none'
8463
- */
8464
- persistence?: TokenPersistence;
8465
- /**
8466
- * 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 자동 복구할지 여부.
8467
- * 브라우저 환경에서 기본 true. cookie 가 없거나 만료되었으면 조용히 실패.
8468
- *
8469
- * cookie 가 없는 미로그인 상태에서 콘솔에 401 노이즈가 찍히지 않도록 silent 처리한다.
8470
- *
8471
- * @default true (브라우저), false (Node.js / RN)
8472
- */
8473
- autoRestoreSession?: boolean;
8474
5638
  /**
8475
5639
  * 에러 트래커 설정
8476
5640
  */
8477
5641
  errorTracker?: ErrorTrackerConfig;
8478
- /**
8479
- * 기본 요청 타임아웃(ms). 개별 호출의 `timeout` 이 지정되면 해당 값이 우선.
8480
- * 기본값 30000ms. 0 또는 음수 지정 시 타임아웃 비활성화.
8481
- */
8482
- requestTimeoutMs?: number;
8483
- /**
8484
- * 전역 에러 관찰자. 모든 `ApiError` / `AuthError` 발생 시 호출됩니다.
8485
- * Sentry/Datadog 등 관측성 파이프라인과 연결하기 위한 훅.
8486
- *
8487
- * @example
8488
- * ```ts
8489
- * const cb = new ConnectBase({
8490
- * publicKey: '...',
8491
- * onError: (err) => Sentry.captureException(err),
8492
- * })
8493
- * ```
8494
- */
8495
- onError?: (error: ApiError | AuthError) => void;
8496
5642
  }
8497
5643
  /**
8498
5644
  * Connect Base SDK
@@ -8503,13 +5649,13 @@ interface ConnectBaseConfig {
8503
5649
  *
8504
5650
  * // 간단 초기화 (API Key만 필요)
8505
5651
  * const cb = new ConnectBase({
8506
- * publicKey: 'your-public-key'
5652
+ * apiKey: 'your-api-key'
8507
5653
  * })
8508
5654
  *
8509
5655
  * // 커스텀 서버 URL 사용 시
8510
5656
  * const cb = new ConnectBase({
8511
5657
  * baseUrl: 'https://your-server.com',
8512
- * publicKey: 'your-public-key'
5658
+ * apiKey: 'your-api-key'
8513
5659
  * })
8514
5660
  *
8515
5661
  * // 데이터 조회
@@ -8543,10 +5689,9 @@ declare class ConnectBase {
8543
5689
  */
8544
5690
  readonly storage: StorageAPI;
8545
5691
  /**
8546
- * Public Key 관리 API
5692
+ * API Key 관리 API
8547
5693
  */
8548
- readonly publicKey: PublicKeyAPI;
8549
- /**
5694
+ readonly apiKey: ApiKeyAPI;
8550
5695
  /**
8551
5696
  * 서버리스 함수 API
8552
5697
  */
@@ -8597,54 +5742,16 @@ declare class ConnectBase {
8597
5742
  */
8598
5743
  readonly native: NativeAPI;
8599
5744
  /**
8600
- * AI 데이터베이스 API (문서 검색)
5745
+ * Knowledge Base API (RAG 문서 검색)
8601
5746
  * 문서를 업로드하고 키워드 기반으로 검색하여 AI 챗봇을 구축할 수 있습니다.
8602
5747
  */
8603
5748
  readonly knowledge: KnowledgeAPI;
8604
- /**
8605
- * AI API (채팅 + AI 데이터베이스 연동)
8606
- * AI 채팅, AI 데이터베이스 문서 참조 응답, 스트리밍 지원.
8607
- */
8608
- readonly ai: AIAPI;
8609
5749
  /**
8610
5750
  * Queue API (메시지 큐)
8611
5751
  * NATS JetStream 기반 고신뢰 메시지 큐. 발행, 소비, Ack, Nack 지원.
8612
5752
  */
8613
5753
  readonly queue: QueueAPI;
8614
- /**
8615
- * Analytics API (웹 분석)
8616
- * 페이지뷰, 커스텀 이벤트, 세션 추적. GA4 수준의 웹 분석 제공.
8617
- */
8618
- readonly analytics: AnalyticsAPI;
8619
- /**
8620
- * Endpoint API (로컬 모델 터널 dumb pipe)
8621
- * 사용자 PC GPU 모델 (ComfyUI / A1111 / Hunyuan3D / 자체 FastAPI 등) 을 `cb_pk_*`
8622
- * 한 키로 호출. 라벨로 라우팅, 페이로드/응답은 그대로 통과.
8623
- */
8624
- readonly endpoint: EndpointAPI;
8625
- /**
8626
- * Support API (사용자 제보 / 이슈 큐)
8627
- * 앱 사용자가 운영자에게 버그/질문/요청을 발행. AI 가 자동 분류/우선순위 처리.
8628
- */
8629
- readonly support: SupportAPI;
8630
5754
  constructor(config?: ConnectBaseConfig);
8631
- /**
8632
- * 현재 페이지가 OAuth 리다이렉트 콜백 페이지인지 판별.
8633
- *
8634
- * `?access_token=...&refresh_token=...` (legacy 토큰-in-URL 흐름) 또는
8635
- * `?code=...` (OAUTH_CODE_ONLY 흐름) 또는 OAuth 에러 응답 (`?error=...&state=...`)
8636
- * 패턴이면 true. 콜백 페이지에서 `autoRestoreSession` 을 건너뛰어 rotation race 를 피한다.
8637
- */
8638
- private isOAuthCallbackUrl;
8639
- /**
8640
- * 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
8641
- *
8642
- * 일반적으로는 ConnectBase 생성 시 `autoRestoreSession: true` (기본값) 으로 자동 호출되지만,
8643
- * 호출 결과를 await 해서 로그인 상태에 따라 다른 UI 를 그리고 싶다면 명시적으로 호출한다.
8644
- *
8645
- * @returns access token 복원 성공 시 true, 미로그인/cookie 만료 시 false
8646
- */
8647
- restoreSession(): Promise<boolean>;
8648
5755
  /**
8649
5756
  * 수동으로 토큰 설정 (기존 토큰으로 세션 복원 시)
8650
5757
  */
@@ -8659,4 +5766,4 @@ declare class ConnectBase {
8659
5766
  updateConfig(config: Partial<ConnectBaseConfig>): void;
8660
5767
  }
8661
5768
 
8662
- export { AIAPI, type AIChatRequest, type AIChatResponse, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, AUTH_MEMBER_ID_TOKEN, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AgenticSearchProgress, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchOperationResult, type BatchSetPageMetaRequest, type BatchWriteResult, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateRoomResult, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type DatabaseRealtimeConnectOptions, type DatabaseRealtimeFilter, type DatabaseRealtimeHandlers, type DatabaseRealtimeSubscription, type DatabaseSnapshot, type DatabaseSnapshotMessage, type DatabaseSubscribeOptions, type DeleteWhereResponse, type DeviceInfo, type DocumentResponse, type EnabledProviderInfo, type EnabledProvidersResponse, EndpointAPI, type EndpointCallInit, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type ExportDataRequest, type ExportDataResponse, type FetchDataResponse, type FetchFilesResponse, type FetchPublicKeysResponse, type FileItem, type FileStats, GameAPI, type GameAction, type GameClientConfig, type GameConfig, GameConfigAPI, type GameConfigPatch, type GameConnectionState, type GameConnectionStatus, type GameDelta, GameError, type GameErrorCode, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GenerateUploadURLByPathRequest, type GenerateUploadURLRequest, type GenerateUploadURLResponse, type GeoIndex, type GeoNear, type GeoPoint, type GeoPolygon, type GeoQuery, type GeoResponse, type GeoResult, type GeoWithin, type GetAuthorizationURLResponse, type GetFileByPathResponse, type GoogleConnectionStatus, type GuestMemberSignInResponse, type HistoryResponse, type ICEServer, type ICEServersResponse, type ImageResult, type ImportDataRequest, type ImportDataResponse, type IndexAnalysis, type IndexRecommendation, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinQueueRequest, type JoinRoomRequest, type JoinRoomResponse, type KnowledgeSearchRequest, type KnowledgeSearchResponse, type KnowledgeSearchResult, type LeaderboardEntry, type LeaderboardListResponse, type LeaderboardScoreEntry, type LifecyclePolicy, type ListBillingKeysResponse, type ListDocumentsResponse, type ListPageMetasOptions, type ListPageMetasResponse, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type LobbyInfo, type LobbyInvite, type LobbyMember, type LobbyVisibility, type MatchResult, type MatchmakingTicket, type MatchqueueListResponse, type MatchqueueTicket, type MemberInfoResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MemoryInfo, type MessageHandler, type MigrateDataRequest, type MigrateDataResponse, type MoveFileRequest, type NackMessageRequest, NativeAPI, type OAuthCallbackResponse, type OAuthProvider, type OpenDialogOptions, type OpenDialogResult, type PageMetaResponse, type PartyInfo, type PartyInvite, type PartyMember, type PauseSubscriptionRequest, type PaymentDetail, type PaymentProvider, type PaymentStatus, type PeerInfo, type Platform, type PlayerEvent, type PlayerStats, type Playlist, type PlaylistItem, type PollUntilOptions, type PongMessage, type PopulateOption, type Position, type PreparePaymentRequest, type PreparePaymentResponse, type PresenceChangeHandler, type PresenceInfo, type PresenceSetOptions, type PresenceStatus, type PresenceStatusResult, type PublicKeyItem, type PublishBatchRequest, type PublishBatchResponse, type PublishMessageRequest, type PublishMessageResponse, type PushPlatform, type QualityProgress, type QueryOptions, type QueueInfoResponse, type QueueMessage, type ReadReceiptHandler, type ReadReceiptInfo, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RelationType, type RenameFileRequest, type RenameFileResponse, type ReplayHighlight, type ReplayInfo, type ReplayPlayerInfo, type RestoreBackupRequest, type RestoreBackupResponse, type RetentionPolicy, type RoomInfo, type RoomStaleMessage, type RoomStats, type RoomsResponse, type SaveDialogOptions, type SaveDialogResult, type ScriptDetailResponse, type ScriptListResponse, type ScriptMeta, type ScriptVersion, type ScriptVersionListResponse, type SearchIndex, type SearchOptions, type SearchResponse, type SearchResult, type SecurityRule, type SendOptions, type ServerMessage, SessionManager, type SetPageMetaRequest, type Shorts, type ShortsListResponse, type SignalingMessage, type SignalingMessageType, type SlowQueryInfo, type SpectatorInfo, type SpectatorPlayerState, type SpectatorState, type StateChange, type StateChangeHandler, type StorageUploadOptions, type StreamContentPart, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamImageURLPart, type StreamMessage, type StreamOptions, type StreamSession, type StreamTextPart, type StreamTokenCallback, type StreamToolCallCallback, type StreamToolResultCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type SystemInfo, type TTLConfig, type TableAccessLevel, type TableColumnDef, type TableIndex, type TableRelation, type TableSchema, type TableSchemaDefinition, type TokenPersistence, type TransactionRead, type TransactionResult, type TransactionWrite, type TransactionWriteResult, type TranscodeStatus, type TransportType, type Trigger, type TriggerEvent, type TriggerHandlerType, type TypingChangeHandler, type TypingInfo, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateCustomDataRequest, type UpdateCustomDataResponse, type UpdateDataRequest, type UpdateDocumentRequest, type UpdateLobbyRequest, type UpdatePublicKeyRequest, type UpdatePublicKeyResponse, type UpdateSecurityRuleRequest, type UpdateSubscriptionRequest, type UpdateTriggerRequest, type UpdateVideoRequest, type UpdateVideoStorageRequest, type UploadByPathOptions, type UploadFileResponse, type UploadOptions, type UploadProgress, type VAPIDPublicKeyResponse, type ValidateResponse, type ValidationSchema, type ValidationSchemaField, type ValidationStateTransitions, type Video, type VideoComment, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoStorage, type VideoStorageListResponse, type VideoVisibility, type VoiceChannel, type VoiceMember, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, isWebTransportSupported, toCreateRoomWire };
5769
+ export { type AckMessagesRequest, type AdReportResponse, type AdReportSummary, AdsAPI, type AggregateResult, type AggregateStage, ApiError, type ApiKeyItem, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchSetPageMetaRequest, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ChatRequest, type ChatResponse, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreateRelationRequest, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type DatabasePresenceState, type DatabaseRealtimeConnectOptions, type DatabaseRealtimeFilter, type DatabaseRealtimeHandlers, type DatabaseRealtimeSubscription, type DatabaseSnapshot, type DatabaseSnapshotMessage, type DatabaseSubscribeOptions, type DeleteWhereResponse, type DeviceInfo, type DocumentResponse, type EnabledProviderInfo, type EnabledProvidersResponse, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type ExportDataRequest, type ExportDataResponse, type FetchApiKeysResponse, type FetchDataResponse, type FetchFilesResponse, type FileItem, type FileStats, GameAPI, type GameAction, type GameClientConfig, type GameConnectionState, type GameConnectionStatus, type GameDelta, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GenerateUploadURLByPathRequest, type GenerateUploadURLRequest, type GenerateUploadURLResponse, type GeoIndex, type GeoNear, type GeoPoint, type GeoPolygon, type GeoQuery, type GeoResponse, type GeoResult, type GeoWithin, type GetAuthorizationURLResponse, type GetFileByPathResponse, type GoogleConnectionStatus, type GuestMemberSignInResponse, type HistoryResponse, type ICEServer, type ICEServersResponse, type ImageResult, type ImportDataRequest, type ImportDataResponse, type IndexAnalysis, type IndexRecommendation, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinQueueRequest, type JoinRoomRequest, type JoinRoomResponse, type KnowledgeSearchRequest, type KnowledgeSearchResponse, type KnowledgeSearchResult, type LifecyclePolicy, type ListBillingKeysResponse, type ListDocumentsResponse, type ListPageMetasOptions, type ListPageMetasResponse, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type LobbyInfo, type LobbyInvite, type LobbyMember, type LobbyVisibility, type MatchmakingTicket, type MemberInfoResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MemoryInfo, type MessageHandler, type MigrateDataRequest, type MigrateDataResponse, type MoveFileRequest, type NackMessageRequest, NativeAPI, type OAuthCallbackResponse, type OAuthProvider, type OpenDialogOptions, type OpenDialogResult, type PageMetaResponse, type PauseSubscriptionRequest, type PaymentDetail, type PaymentProvider, type PaymentStatus, type PeerInfo, type Platform, type PlayerEvent, type Playlist, type PlaylistItem, type PongMessage, type PopulateOption, type Position, type PreparePaymentRequest, type PreparePaymentResponse, type PresenceChangeHandler, type PresenceInfo, type PresenceSetOptions, type PresenceStatus, type PresenceStatusResult, type PublishBatchRequest, type PublishBatchResponse, type PublishMessageRequest, type PublishMessageResponse, type PushPlatform, type QualityProgress, type QueryOptions, type QueueInfoResponse, type QueueMessage, type ReadReceiptHandler, type ReadReceiptInfo, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RelationType, type RenameFileRequest, type RenameFileResponse, type RestoreBackupRequest, type RestoreBackupResponse, type RetentionPolicy, type RoomInfo, type RoomStats, type RoomsResponse, type SaveDialogOptions, type SaveDialogResult, type SearchIndex, type SearchOptions, type SearchResponse, type SearchResult, type SecurityRule, type SendOptions, type ServerMessage, type SetPageMetaRequest, type Shorts, type ShortsListResponse, type SignalingMessage, type SignalingMessageType, type SlowQueryInfo, type StateChange, type StateChangeHandler, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamMessage, type StreamOptions, type StreamSession, type StreamTokenCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type SystemInfo, type TTLConfig, type TableIndex, type TableRelation, type TableSchema, type TransactionRead, type TransactionWrite, type TranscodeStatus, type TransportType, type Trigger, type TriggerEvent, type TriggerHandlerType, type TypingChangeHandler, type TypingInfo, type UpdateApiKeyRequest, type UpdateApiKeyResponse, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateCustomDataRequest, type UpdateCustomDataResponse, type UpdateDataRequest, type UpdateLobbyRequest, type UpdateSecurityRuleRequest, type UpdateSubscriptionRequest, type UpdateTriggerRequest, type UpdateVideoRequest, type UploadByPathOptions, type UploadFileResponse, type UploadOptions, type UploadProgress, type VAPIDPublicKeyResponse, type ValidateResponse, type Video, type VideoComment, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoVisibility, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, detectInAppBrowser, escapeToExternalBrowser, isWebTransportSupported };