connectbase-client 3.25.0 → 3.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1911 -0
- package/LICENSE +21 -0
- package/MIGRATION-v2.md +149 -0
- package/README.md +575 -52
- package/dist/cli.js +1760 -324
- package/dist/connect-base.umd.js +4 -2
- package/dist/index.d.mts +3243 -335
- package/dist/index.d.ts +3243 -335
- package/dist/index.js +4132 -949
- package/dist/index.mjs +4124 -948
- package/package.json +19 -7
package/dist/index.d.mts
CHANGED
|
@@ -1,24 +1,149 @@
|
|
|
1
|
+
interface ApiErrorDetail {
|
|
2
|
+
code: string;
|
|
3
|
+
message: string;
|
|
4
|
+
details?: unknown;
|
|
5
|
+
}
|
|
1
6
|
declare class ApiError extends Error {
|
|
2
7
|
statusCode: number;
|
|
3
|
-
|
|
8
|
+
code: string | undefined;
|
|
9
|
+
details: unknown | undefined;
|
|
10
|
+
constructor(statusCode: number, message: string, code?: string, details?: unknown);
|
|
4
11
|
}
|
|
5
12
|
declare class AuthError extends Error {
|
|
6
13
|
constructor(message: string);
|
|
7
14
|
}
|
|
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
|
+
|
|
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
|
+
}
|
|
8
87
|
|
|
88
|
+
type TokenPersistence = 'localStorage' | 'sessionStorage' | 'none';
|
|
9
89
|
interface HttpClientConfig {
|
|
10
90
|
baseUrl: string;
|
|
11
|
-
|
|
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;
|
|
12
101
|
accessToken?: string;
|
|
13
102
|
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;
|
|
14
132
|
onTokenRefresh?: (tokens: {
|
|
15
133
|
accessToken: string;
|
|
16
134
|
refreshToken: string;
|
|
17
135
|
}) => void;
|
|
18
136
|
onAuthError?: (error: AuthError) => void;
|
|
19
137
|
onTokenExpired?: () => void;
|
|
138
|
+
/**
|
|
139
|
+
* `/v1/auth/re-issue` 의 일시적 실패(5xx, 네트워크 오류, abort) 발생 시 호출.
|
|
140
|
+
* 이 경우 토큰은 폐기되지 않으며 `onTokenExpired` 도 호출되지 않는다 — 다음 호출에서
|
|
141
|
+
* backoff 만료 후 자동 재시도. 사용자에게 "연결이 잠시 불안정합니다" 같은 비파괴적 알림을
|
|
142
|
+
* 표시할 때 사용. `onAuthError` 도 함께 호출되므로 둘을 동시에 wiring 하면 중복 처리에 주의.
|
|
143
|
+
*/
|
|
144
|
+
onTransientRefreshFailure?: (error: AuthError) => void;
|
|
20
145
|
}
|
|
21
|
-
interface RequestConfig {
|
|
146
|
+
interface RequestConfig extends AbortOptions {
|
|
22
147
|
skipAuth?: boolean;
|
|
23
148
|
headers?: Record<string, string>;
|
|
24
149
|
}
|
|
@@ -26,35 +151,574 @@ declare class HttpClient {
|
|
|
26
151
|
private config;
|
|
27
152
|
private isRefreshing;
|
|
28
153
|
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;
|
|
29
169
|
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;
|
|
30
181
|
updateConfig(config: Partial<HttpClientConfig>): void;
|
|
31
182
|
setTokens(accessToken: string, refreshToken: string): void;
|
|
32
183
|
clearTokens(): void;
|
|
33
184
|
/**
|
|
34
|
-
*
|
|
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 가 설정되어 있는지 확인
|
|
35
215
|
*/
|
|
36
|
-
|
|
216
|
+
hasPublicKey(): boolean;
|
|
37
217
|
/**
|
|
38
|
-
*
|
|
218
|
+
* Public Key 반환
|
|
39
219
|
*/
|
|
40
|
-
|
|
220
|
+
getPublicKey(): string | undefined;
|
|
221
|
+
/**
|
|
222
|
+
* Secret Key 가 설정되어 있는지 확인
|
|
223
|
+
*/
|
|
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;
|
|
41
240
|
/**
|
|
42
241
|
* Access Token 반환
|
|
43
242
|
*/
|
|
44
243
|
getAccessToken(): string | undefined;
|
|
244
|
+
/**
|
|
245
|
+
* JWT(Access Token) 가 설정되어 있는지 확인
|
|
246
|
+
*/
|
|
247
|
+
hasJWT(): boolean;
|
|
45
248
|
/**
|
|
46
249
|
* Base URL 반환
|
|
47
250
|
*/
|
|
48
251
|
getBaseUrl(): string;
|
|
49
252
|
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;
|
|
50
265
|
private isTokenExpired;
|
|
51
266
|
private prepareHeaders;
|
|
52
267
|
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;
|
|
53
278
|
get<T>(url: string, config?: RequestConfig): Promise<T>;
|
|
54
279
|
post<T>(url: string, data?: unknown, config?: RequestConfig): Promise<T>;
|
|
55
280
|
put<T>(url: string, data: unknown, config?: RequestConfig): Promise<T>;
|
|
56
281
|
patch<T>(url: string, data: unknown, config?: RequestConfig): Promise<T>;
|
|
57
282
|
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;
|
|
58
722
|
}
|
|
59
723
|
|
|
60
724
|
/** 앱 멤버 게스트 가입 응답 */
|
|
@@ -93,16 +757,20 @@ interface MemberInfoResponse {
|
|
|
93
757
|
member_id: string;
|
|
94
758
|
nickname: string;
|
|
95
759
|
is_active: boolean;
|
|
96
|
-
custom_data: Record<string,
|
|
760
|
+
custom_data: Record<string, unknown>;
|
|
761
|
+
/** 멤버 이메일 (EMAIL identity 에서 추출, 없으면 빈 문자열) */
|
|
762
|
+
email?: string;
|
|
763
|
+
/** 멤버 역할 — RLS 규칙에서 `auth.role` 로 참조 (예: "admin", "moderator") */
|
|
764
|
+
role?: string;
|
|
97
765
|
}
|
|
98
766
|
/** 앱 멤버 custom_data 수정 요청 */
|
|
99
767
|
interface UpdateCustomDataRequest {
|
|
100
|
-
custom_data: Record<string,
|
|
768
|
+
custom_data: Record<string, unknown>;
|
|
101
769
|
}
|
|
102
770
|
/** 앱 멤버 custom_data 수정 응답 */
|
|
103
771
|
interface UpdateCustomDataResponse {
|
|
104
772
|
member_id: string;
|
|
105
|
-
custom_data: Record<string,
|
|
773
|
+
custom_data: Record<string, unknown>;
|
|
106
774
|
}
|
|
107
775
|
/** 앱 인증 설정 응답 */
|
|
108
776
|
interface AuthSettingsResponse {
|
|
@@ -118,7 +786,14 @@ declare class AuthAPI {
|
|
|
118
786
|
private http;
|
|
119
787
|
private guestMemberLoginPromise;
|
|
120
788
|
private cachedGuestMemberTokenKey;
|
|
789
|
+
private analytics;
|
|
121
790
|
constructor(http: HttpClient);
|
|
791
|
+
/**
|
|
792
|
+
* AnalyticsAPI 주입 — 로그인/가입/로그아웃 시 방문자 트래커에 member_id 를 전달하여
|
|
793
|
+
* 게스트 방문자를 회원과 연결한다. ConnectBase 컨스트럭터에서 내부적으로 호출된다.
|
|
794
|
+
*/
|
|
795
|
+
_attachAnalytics(analytics: AnalyticsAPI): void;
|
|
796
|
+
private notifyVisitorTracker;
|
|
122
797
|
/**
|
|
123
798
|
* 앱의 인증 설정 조회
|
|
124
799
|
* 어떤 로그인 방식이 허용되는지 확인합니다.
|
|
@@ -208,6 +883,44 @@ declare class AuthAPI {
|
|
|
208
883
|
* ```
|
|
209
884
|
*/
|
|
210
885
|
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
|
+
}>;
|
|
211
924
|
/**
|
|
212
925
|
* 로그아웃
|
|
213
926
|
*/
|
|
@@ -223,26 +936,51 @@ declare class AuthAPI {
|
|
|
223
936
|
private storeGuestMemberTokens;
|
|
224
937
|
}
|
|
225
938
|
|
|
226
|
-
|
|
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
|
+
*/
|
|
227
955
|
interface ColumnSchema {
|
|
956
|
+
/** 컬럼 이름과 동일 (서버 별도 ID 없음) */
|
|
228
957
|
id: string;
|
|
229
958
|
name: string;
|
|
230
959
|
data_type: DataType;
|
|
231
960
|
is_required: boolean;
|
|
232
|
-
default_value?:
|
|
233
|
-
validation_rule?: string;
|
|
234
|
-
order: number;
|
|
961
|
+
default_value?: unknown;
|
|
235
962
|
description?: string;
|
|
963
|
+
encrypted?: boolean;
|
|
964
|
+
/** SDK 가 맵 순서대로 부여한 인덱스 */
|
|
965
|
+
order: number;
|
|
966
|
+
/** 테이블의 `created_at` (컬럼 개별 타임스탬프는 없음) */
|
|
236
967
|
created_at: string;
|
|
968
|
+
/**
|
|
969
|
+
* @deprecated 서버가 지원하지 않습니다.
|
|
970
|
+
*/
|
|
971
|
+
validation_rule?: string;
|
|
972
|
+
/**
|
|
973
|
+
* @deprecated 컬럼 개별 타임스탬프가 없습니다.
|
|
974
|
+
*/
|
|
237
975
|
updated_at?: string;
|
|
238
976
|
}
|
|
239
977
|
interface CreateColumnRequest {
|
|
240
978
|
name: string;
|
|
241
979
|
data_type: DataType;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
980
|
+
/** 필수 필드 여부 (기본 `false`) */
|
|
981
|
+
is_required?: boolean;
|
|
982
|
+
/** 기본값 (서버는 임의 타입을 허용하므로 `unknown`) */
|
|
983
|
+
default_value?: unknown;
|
|
246
984
|
description?: string;
|
|
247
985
|
/**
|
|
248
986
|
* 필드 레벨 암호화 활성화 여부.
|
|
@@ -251,26 +989,172 @@ interface CreateColumnRequest {
|
|
|
251
989
|
* 서버에 SECRET_ENCRYPTION_KEY가 설정되어 있어야 합니다.
|
|
252
990
|
*/
|
|
253
991
|
encrypted?: boolean;
|
|
992
|
+
/**
|
|
993
|
+
* @deprecated 서버가 무시합니다. 컬럼 순서는 서버가 자동 관리합니다.
|
|
994
|
+
*/
|
|
995
|
+
order?: number;
|
|
996
|
+
/**
|
|
997
|
+
* @deprecated 서버가 해당 필드를 저장하지 않습니다.
|
|
998
|
+
*/
|
|
999
|
+
validation_rule?: string;
|
|
254
1000
|
}
|
|
255
1001
|
interface UpdateColumnRequest {
|
|
256
|
-
name?: string;
|
|
257
1002
|
data_type?: DataType;
|
|
258
1003
|
is_required?: boolean;
|
|
259
|
-
default_value?:
|
|
260
|
-
validation_rule?: string;
|
|
261
|
-
order?: number;
|
|
1004
|
+
default_value?: unknown;
|
|
262
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
|
+
*/
|
|
1019
|
+
validation_rule?: string;
|
|
263
1020
|
}
|
|
1021
|
+
/**
|
|
1022
|
+
* 서버(data-server `ent/schema/table.go` 의 `Table`) 가 반환하는 테이블 레코드.
|
|
1023
|
+
* 필드 이름은 Go ent 모델과 일치합니다.
|
|
1024
|
+
*/
|
|
264
1025
|
interface TableSchema {
|
|
265
1026
|
id: string;
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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;
|
|
269
1036
|
created_at: string;
|
|
270
|
-
updated_at
|
|
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;
|
|
1051
|
+
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>;
|
|
271
1139
|
}
|
|
272
1140
|
interface CreateTableRequest {
|
|
1141
|
+
/** 테이블 이름 (서버는 내부적으로 `title` 로 저장) */
|
|
273
1142
|
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
|
+
*/
|
|
274
1158
|
description?: string;
|
|
275
1159
|
}
|
|
276
1160
|
interface DataItem {
|
|
@@ -285,9 +1169,21 @@ interface CreateDataRequest {
|
|
|
285
1169
|
interface UpdateDataRequest {
|
|
286
1170
|
data: Record<string, unknown>;
|
|
287
1171
|
}
|
|
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
|
+
*/
|
|
288
1182
|
interface FetchDataResponse {
|
|
289
|
-
|
|
290
|
-
|
|
1183
|
+
/** 조회된 문서 배열 */
|
|
1184
|
+
data: DataItem[];
|
|
1185
|
+
/** 총 매칭 문서 수 */
|
|
1186
|
+
total_count: number;
|
|
291
1187
|
}
|
|
292
1188
|
interface QueryOptions {
|
|
293
1189
|
limit?: number;
|
|
@@ -434,7 +1330,7 @@ interface PopulateOption {
|
|
|
434
1330
|
populate?: PopulateOption[];
|
|
435
1331
|
}
|
|
436
1332
|
type TriggerEvent = 'create' | 'update' | 'delete' | 'all';
|
|
437
|
-
type TriggerHandlerType = 'function' | 'workflow' | 'webhook';
|
|
1333
|
+
type TriggerHandlerType = 'function' | 'workflow' | 'webhook' | 'inline';
|
|
438
1334
|
interface Trigger {
|
|
439
1335
|
id: string;
|
|
440
1336
|
app_id: string;
|
|
@@ -445,6 +1341,7 @@ interface Trigger {
|
|
|
445
1341
|
handler_type: TriggerHandlerType;
|
|
446
1342
|
handler_id?: string;
|
|
447
1343
|
handler_url?: string;
|
|
1344
|
+
handler_action?: Record<string, unknown>;
|
|
448
1345
|
is_active: boolean;
|
|
449
1346
|
order: number;
|
|
450
1347
|
created_at: string;
|
|
@@ -457,6 +1354,7 @@ interface CreateTriggerRequest {
|
|
|
457
1354
|
handler_type: TriggerHandlerType;
|
|
458
1355
|
handler_id?: string;
|
|
459
1356
|
handler_url?: string;
|
|
1357
|
+
handler_action?: Record<string, unknown>;
|
|
460
1358
|
condition?: Record<string, unknown>;
|
|
461
1359
|
is_active?: boolean;
|
|
462
1360
|
order?: number;
|
|
@@ -467,6 +1365,7 @@ interface UpdateTriggerRequest {
|
|
|
467
1365
|
handler_type?: TriggerHandlerType;
|
|
468
1366
|
handler_id?: string;
|
|
469
1367
|
handler_url?: string;
|
|
1368
|
+
handler_action?: Record<string, unknown>;
|
|
470
1369
|
condition?: Record<string, unknown>;
|
|
471
1370
|
is_active?: boolean;
|
|
472
1371
|
order?: number;
|
|
@@ -588,6 +1487,31 @@ interface TransactionWrite {
|
|
|
588
1487
|
exists?: boolean;
|
|
589
1488
|
};
|
|
590
1489
|
}
|
|
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
|
+
}
|
|
591
1515
|
interface TTLConfig {
|
|
592
1516
|
table_name: string;
|
|
593
1517
|
field: string;
|
|
@@ -679,7 +1603,11 @@ interface DatabaseRealtimeSubscription {
|
|
|
679
1603
|
}
|
|
680
1604
|
/** 데이터베이스 실시간 연결 옵션 */
|
|
681
1605
|
interface DatabaseRealtimeConnectOptions {
|
|
682
|
-
/**
|
|
1606
|
+
/**
|
|
1607
|
+
* 액세스 토큰 (필수). 두 종류를 모두 지원:
|
|
1608
|
+
* - 앱 멤버(AppMember) JWT — 자기 앱의 테이블 구독
|
|
1609
|
+
* - cross-app OAuth access token — Provider 앱의 테이블 구독 (`database:read` scope 필요)
|
|
1610
|
+
*/
|
|
683
1611
|
accessToken: string;
|
|
684
1612
|
/** data-server URL (기본: baseUrl) */
|
|
685
1613
|
dataServerUrl?: string;
|
|
@@ -690,14 +1618,6 @@ interface DatabaseRealtimeConnectOptions {
|
|
|
690
1618
|
/** 디버그 로깅 (기본: false) */
|
|
691
1619
|
debug?: boolean;
|
|
692
1620
|
}
|
|
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
|
-
}
|
|
701
1621
|
interface CreateBackupRequest {
|
|
702
1622
|
/** 백업 이름 */
|
|
703
1623
|
name: string;
|
|
@@ -752,6 +1672,17 @@ interface ExportDataResponse {
|
|
|
752
1672
|
data?: Record<string, unknown>;
|
|
753
1673
|
schema?: Record<string, unknown>;
|
|
754
1674
|
}
|
|
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
|
+
*/
|
|
755
1686
|
interface ImportDataRequest {
|
|
756
1687
|
/** 가져올 데이터: { "테이블이름": { "rows": [{...}, ...] }, ... } */
|
|
757
1688
|
data: Record<string, {
|
|
@@ -872,37 +1803,109 @@ declare class DatabaseAPI {
|
|
|
872
1803
|
private activeSubscriptions;
|
|
873
1804
|
constructor(http: HttpClient);
|
|
874
1805
|
/**
|
|
875
|
-
*
|
|
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_*`) 를 실어 서버가 검증합니다.
|
|
876
1812
|
*/
|
|
877
1813
|
private getPublicPrefix;
|
|
878
1814
|
/**
|
|
879
1815
|
* 테이블 목록 조회
|
|
880
1816
|
*/
|
|
881
|
-
getTables(
|
|
1817
|
+
getTables(): Promise<TableSchema[]>;
|
|
882
1818
|
/**
|
|
883
|
-
* 테이블
|
|
1819
|
+
* 테이블 상세 조회
|
|
1820
|
+
*/
|
|
1821
|
+
getTable(tableId: string): Promise<TableSchema>;
|
|
1822
|
+
/**
|
|
1823
|
+
* 테이블 생성.
|
|
1824
|
+
*
|
|
1825
|
+
* 서버 DTO (`{title, schema, access_level}`) 로 변환하여 전송합니다.
|
|
1826
|
+
* `schema` 가 비어있으면 요청 본문에서 키 자체를 생략하여 빈 테이블을
|
|
1827
|
+
* 생성합니다. (서버는 explicit 빈 맵을 거부)
|
|
1828
|
+
*
|
|
1829
|
+
* 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
|
|
1830
|
+
* 생성된 테이블 메타데이터가 필요하면 이어서 `getTables()` 로 조회하세요.
|
|
1831
|
+
*/
|
|
1832
|
+
createTable(data: CreateTableRequest): Promise<void>;
|
|
1833
|
+
/**
|
|
1834
|
+
* 테이블 수정.
|
|
1835
|
+
*
|
|
1836
|
+
* `name` 은 서버의 `title` 로, `accessLevel` 은 `access_level` 로 매핑됩니다.
|
|
884
1837
|
*/
|
|
885
|
-
|
|
1838
|
+
updateTable(tableId: string, data: Partial<CreateTableRequest>): Promise<void>;
|
|
886
1839
|
/**
|
|
887
1840
|
* 테이블 삭제
|
|
888
1841
|
*/
|
|
889
|
-
deleteTable(
|
|
1842
|
+
deleteTable(tableId: string): Promise<void>;
|
|
890
1843
|
/**
|
|
891
|
-
*
|
|
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>;
|
|
1871
|
+
/**
|
|
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` 와 병합되어 판정됩니다.
|
|
892
1890
|
*/
|
|
893
1891
|
getColumns(tableId: string): Promise<ColumnSchema[]>;
|
|
894
1892
|
/**
|
|
895
|
-
* 컬럼
|
|
1893
|
+
* 컬럼 생성.
|
|
1894
|
+
*
|
|
1895
|
+
* 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
|
|
1896
|
+
* 생성 결과 컬럼을 얻으려면 이어서 `getColumns(tableId)` 로 조회하세요.
|
|
896
1897
|
*/
|
|
897
|
-
createColumn(tableId: string, data: CreateColumnRequest): Promise<
|
|
1898
|
+
createColumn(tableId: string, data: CreateColumnRequest): Promise<void>;
|
|
898
1899
|
/**
|
|
899
|
-
* 컬럼
|
|
1900
|
+
* 컬럼 수정.
|
|
1901
|
+
*
|
|
1902
|
+
* 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
|
|
900
1903
|
*/
|
|
901
|
-
updateColumn(tableId: string,
|
|
1904
|
+
updateColumn(tableId: string, columnName: string, data: UpdateColumnRequest): Promise<void>;
|
|
902
1905
|
/**
|
|
903
1906
|
* 컬럼 삭제
|
|
904
1907
|
*/
|
|
905
|
-
deleteColumn(tableId: string,
|
|
1908
|
+
deleteColumn(tableId: string, columnName: string): Promise<void>;
|
|
906
1909
|
/**
|
|
907
1910
|
* 데이터 조회 (페이지네이션)
|
|
908
1911
|
*/
|
|
@@ -916,9 +1919,19 @@ declare class DatabaseAPI {
|
|
|
916
1919
|
*/
|
|
917
1920
|
getDataById(tableId: string, dataId: string): Promise<DataItem>;
|
|
918
1921
|
/**
|
|
919
|
-
* 데이터
|
|
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` 호출 권장.
|
|
920
1931
|
*/
|
|
921
|
-
createData(
|
|
1932
|
+
createData(tableIdOrName: string, data: CreateDataRequest, options?: {
|
|
1933
|
+
autoCreate?: boolean;
|
|
1934
|
+
}): Promise<DataItem>;
|
|
922
1935
|
/**
|
|
923
1936
|
* 데이터 수정 (부분 업데이트)
|
|
924
1937
|
* 제공된 필드만 수정되고, 기존 필드는 유지됩니다.
|
|
@@ -959,16 +1972,18 @@ declare class DatabaseAPI {
|
|
|
959
1972
|
}): Promise<GeoResponse>;
|
|
960
1973
|
/**
|
|
961
1974
|
* 배치 쓰기 (여러 테이블 다중 문서 원자적 처리)
|
|
1975
|
+
*
|
|
1976
|
+
* 서버는 HTTP 200 + `success:false` + 개별 op `error` 로 부분 실패를 표현한다.
|
|
1977
|
+
* SDK 는 호출자가 silent success 로 오해하지 않도록 첫 실패 op 의 메시지로 throw 한다.
|
|
962
1978
|
*/
|
|
963
|
-
batch(operations: BatchOperation[]): Promise<
|
|
964
|
-
results: Record<string, unknown>[];
|
|
965
|
-
}>;
|
|
1979
|
+
batch(operations: BatchOperation[]): Promise<BatchWriteResult>;
|
|
966
1980
|
/**
|
|
967
1981
|
* 트랜잭션 실행 (읽기 → 쓰기 ACID)
|
|
1982
|
+
*
|
|
1983
|
+
* 서버는 부분 실패가 없는 ACID 트랜잭션을 보장하지만, 검증/RLS/충돌은
|
|
1984
|
+
* `success:false` + `error` 로 알린다. SDK 는 silent success 회귀 방지 차원에서 throw.
|
|
968
1985
|
*/
|
|
969
|
-
transaction(reads: TransactionRead[], writes: TransactionWrite[]): Promise<
|
|
970
|
-
results: Record<string, unknown>;
|
|
971
|
-
}>;
|
|
1986
|
+
transaction(reads: TransactionRead[], writes: TransactionWrite[]): Promise<TransactionResult>;
|
|
972
1987
|
/**
|
|
973
1988
|
* 데이터 조회 시 릴레이션 로딩 (JOIN)
|
|
974
1989
|
*/
|
|
@@ -1014,17 +2029,23 @@ declare class DatabaseAPI {
|
|
|
1014
2029
|
createGeoIndex(appId: string, tableId: string, data: CreateGeoIndexRequest): Promise<GeoIndex>;
|
|
1015
2030
|
deleteGeoIndex(appId: string, tableId: string, indexId: string): Promise<void>;
|
|
1016
2031
|
/**
|
|
1017
|
-
* 테이블 릴레이션 목록
|
|
2032
|
+
* 테이블 릴레이션 목록 조회. `sourceTable` 생략 시 앱 전체의 릴레이션을 반환.
|
|
2033
|
+
*
|
|
2034
|
+
* 서버 경로: `GET /v1/apps/:appID/databases/relations[?source_table=...]`
|
|
1018
2035
|
*/
|
|
1019
|
-
listRelations(appId: string,
|
|
2036
|
+
listRelations(appId: string, sourceTable?: string): Promise<TableRelation[]>;
|
|
1020
2037
|
/**
|
|
1021
|
-
* 릴레이션
|
|
2038
|
+
* 릴레이션 생성.
|
|
2039
|
+
*
|
|
2040
|
+
* 서버 경로: `POST /v1/apps/:appID/databases/relations` — body 내 `source_table`/`target_table` 로 테이블 지정.
|
|
1022
2041
|
*/
|
|
1023
|
-
createRelation(appId: string,
|
|
2042
|
+
createRelation(appId: string, data: CreateRelationRequest): Promise<TableRelation>;
|
|
1024
2043
|
/**
|
|
1025
|
-
* 릴레이션
|
|
2044
|
+
* 릴레이션 삭제. `relationId` 는 서버에서 발급한 UUID (listRelations 로 조회해 얻음).
|
|
2045
|
+
*
|
|
2046
|
+
* 서버 경로: `DELETE /v1/apps/:appID/databases/relations/:relationID`
|
|
1026
2047
|
*/
|
|
1027
|
-
deleteRelation(appId: string,
|
|
2048
|
+
deleteRelation(appId: string, relationId: string): Promise<void>;
|
|
1028
2049
|
/**
|
|
1029
2050
|
* 트리거 목록 조회
|
|
1030
2051
|
*/
|
|
@@ -1087,14 +2108,26 @@ declare class DatabaseAPI {
|
|
|
1087
2108
|
* 데이터베이스 실시간 연결
|
|
1088
2109
|
* data-server의 WebSocket에 연결하여 데이터 변경을 실시간으로 수신합니다.
|
|
1089
2110
|
*
|
|
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
|
+
*
|
|
1090
2117
|
* @example
|
|
1091
2118
|
* ```typescript
|
|
1092
|
-
* //
|
|
2119
|
+
* // 자기 앱 테이블 구독 (AppMember JWT)
|
|
1093
2120
|
* cb.database.connectRealtime({
|
|
1094
2121
|
* accessToken: 'your-jwt-token',
|
|
1095
2122
|
* dataServerUrl: 'https://data.connectbase.world'
|
|
1096
2123
|
* })
|
|
1097
2124
|
*
|
|
2125
|
+
* // cross-app: Provider 앱의 테이블 구독 (OAuth access token)
|
|
2126
|
+
* cb.database.connectRealtime({
|
|
2127
|
+
* accessToken: oauthAccessToken,
|
|
2128
|
+
* dataServerUrl: 'https://data.connectbase.world'
|
|
2129
|
+
* })
|
|
2130
|
+
*
|
|
1098
2131
|
* // 테이블 구독
|
|
1099
2132
|
* const sub = cb.database.subscribe('users', {
|
|
1100
2133
|
* onSnapshot: (docs, info) => {
|
|
@@ -1129,14 +2162,6 @@ declare class DatabaseAPI {
|
|
|
1129
2162
|
* @returns 구독 해제 가능한 객체
|
|
1130
2163
|
*/
|
|
1131
2164
|
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;
|
|
1140
2165
|
/**
|
|
1141
2166
|
* 실시간 연결 상태 확인
|
|
1142
2167
|
*/
|
|
@@ -1366,10 +2391,28 @@ declare class StorageAPI {
|
|
|
1366
2391
|
* API Key 인증 시 /v1/public 접두사 반환
|
|
1367
2392
|
*/
|
|
1368
2393
|
private getPublicPrefix;
|
|
2394
|
+
/**
|
|
2395
|
+
* Presigned URL 로 직접 업로드. 호출 전에 URL 스킴(https)을 검증해
|
|
2396
|
+
* 서버 응답을 그대로 신뢰하는 SSRF/오용 경로를 차단.
|
|
2397
|
+
* 타임아웃과 외부 signal 을 모두 지원한다.
|
|
2398
|
+
*/
|
|
2399
|
+
private uploadToPresigned;
|
|
1369
2400
|
/**
|
|
1370
2401
|
* 파일 목록 조회
|
|
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
|
+
* ```
|
|
1371
2414
|
*/
|
|
1372
|
-
getFiles(storageId: string): Promise<FileItem[]>;
|
|
2415
|
+
getFiles(storageId: string, parentId?: string): Promise<FileItem[]>;
|
|
1373
2416
|
/**
|
|
1374
2417
|
* 파일 업로드 (Presigned URL 방식)
|
|
1375
2418
|
*
|
|
@@ -1397,10 +2440,14 @@ declare class StorageAPI {
|
|
|
1397
2440
|
deleteFile(storageId: string, fileId: string): Promise<void>;
|
|
1398
2441
|
/**
|
|
1399
2442
|
* 파일/폴더 이동
|
|
2443
|
+
*
|
|
2444
|
+
* Public Key 인증만으로는 이 엔드포인트가 노출되지 않습니다. JWT(콘솔 토큰)가 필요합니다.
|
|
1400
2445
|
*/
|
|
1401
2446
|
moveFile(storageId: string, fileId: string, data: MoveFileRequest): Promise<void>;
|
|
1402
2447
|
/**
|
|
1403
2448
|
* 파일/폴더 이름 변경
|
|
2449
|
+
*
|
|
2450
|
+
* Public Key 인증만으로는 이 엔드포인트가 노출되지 않습니다. JWT(콘솔 토큰)가 필요합니다.
|
|
1404
2451
|
*/
|
|
1405
2452
|
renameFile(storageId: string, fileId: string, data: RenameFileRequest): Promise<RenameFileResponse>;
|
|
1406
2453
|
/**
|
|
@@ -1524,15 +2571,15 @@ declare class StorageAPI {
|
|
|
1524
2571
|
}
|
|
1525
2572
|
|
|
1526
2573
|
/**
|
|
1527
|
-
*
|
|
2574
|
+
* Public Key 관련 타입 정의
|
|
1528
2575
|
*/
|
|
1529
2576
|
/**
|
|
1530
|
-
*
|
|
2577
|
+
* Public Key 아이템
|
|
1531
2578
|
*/
|
|
1532
|
-
interface
|
|
1533
|
-
/**
|
|
2579
|
+
interface PublicKeyItem {
|
|
2580
|
+
/** Public Key ID */
|
|
1534
2581
|
id: string;
|
|
1535
|
-
/**
|
|
2582
|
+
/** Public Key 이름 */
|
|
1536
2583
|
name: string;
|
|
1537
2584
|
/** 키 앞 12자리 (표시용) */
|
|
1538
2585
|
key_prefix: string;
|
|
@@ -1546,21 +2593,21 @@ interface ApiKeyItem {
|
|
|
1546
2593
|
created_at: string;
|
|
1547
2594
|
}
|
|
1548
2595
|
/**
|
|
1549
|
-
*
|
|
2596
|
+
* Public Key 생성 요청
|
|
1550
2597
|
*/
|
|
1551
|
-
interface
|
|
1552
|
-
/**
|
|
2598
|
+
interface CreatePublicKeyRequest {
|
|
2599
|
+
/** Public Key 이름 (예: Production, Development) */
|
|
1553
2600
|
name: string;
|
|
1554
2601
|
/** 만료일 (옵션) */
|
|
1555
2602
|
expires_at?: string;
|
|
1556
2603
|
}
|
|
1557
2604
|
/**
|
|
1558
|
-
*
|
|
2605
|
+
* Public Key 생성 응답 (전체 키는 이때만 반환됨)
|
|
1559
2606
|
*/
|
|
1560
|
-
interface
|
|
1561
|
-
/**
|
|
2607
|
+
interface CreatePublicKeyResponse {
|
|
2608
|
+
/** Public Key ID */
|
|
1562
2609
|
id: string;
|
|
1563
|
-
/**
|
|
2610
|
+
/** Public Key 이름 */
|
|
1564
2611
|
name: string;
|
|
1565
2612
|
/** 전체 키 값 (생성 시에만 반환, 안전하게 보관 필요) */
|
|
1566
2613
|
key: string;
|
|
@@ -1574,24 +2621,24 @@ interface CreateApiKeyResponse {
|
|
|
1574
2621
|
created_at: string;
|
|
1575
2622
|
}
|
|
1576
2623
|
/**
|
|
1577
|
-
*
|
|
2624
|
+
* Public Key 목록 조회 응답
|
|
1578
2625
|
*/
|
|
1579
|
-
interface
|
|
1580
|
-
|
|
2626
|
+
interface FetchPublicKeysResponse {
|
|
2627
|
+
public_keys: PublicKeyItem[];
|
|
1581
2628
|
}
|
|
1582
2629
|
/**
|
|
1583
|
-
*
|
|
2630
|
+
* Public Key 수정 요청
|
|
1584
2631
|
*/
|
|
1585
|
-
interface
|
|
1586
|
-
/**
|
|
2632
|
+
interface UpdatePublicKeyRequest {
|
|
2633
|
+
/** Public Key 이름 변경 */
|
|
1587
2634
|
name?: string;
|
|
1588
2635
|
/** 활성화/비활성화 */
|
|
1589
2636
|
is_active?: boolean;
|
|
1590
2637
|
}
|
|
1591
2638
|
/**
|
|
1592
|
-
*
|
|
2639
|
+
* Public Key 수정 응답
|
|
1593
2640
|
*/
|
|
1594
|
-
interface
|
|
2641
|
+
interface UpdatePublicKeyResponse {
|
|
1595
2642
|
id: string;
|
|
1596
2643
|
name: string;
|
|
1597
2644
|
key_prefix: string;
|
|
@@ -1601,55 +2648,55 @@ interface UpdateApiKeyResponse {
|
|
|
1601
2648
|
}
|
|
1602
2649
|
|
|
1603
2650
|
/**
|
|
1604
|
-
*
|
|
2651
|
+
* Public Key 관리 API
|
|
1605
2652
|
*
|
|
1606
2653
|
* @example
|
|
1607
2654
|
* ```typescript
|
|
1608
|
-
* //
|
|
1609
|
-
* const keys = await cb.
|
|
2655
|
+
* // Public Key 목록 조회
|
|
2656
|
+
* const keys = await cb.publicKey.getPublicKeys('app-id')
|
|
1610
2657
|
*
|
|
1611
|
-
* // 새
|
|
1612
|
-
* const newKey = await cb.
|
|
2658
|
+
* // 새 Public Key 생성
|
|
2659
|
+
* const newKey = await cb.publicKey.createPublicKey('app-id', { name: 'Production' })
|
|
1613
2660
|
* console.log(newKey.key) // 전체 키 (이때만 볼 수 있음!)
|
|
1614
2661
|
*
|
|
1615
|
-
* //
|
|
1616
|
-
* await cb.
|
|
2662
|
+
* // Public Key 비활성화
|
|
2663
|
+
* await cb.publicKey.updatePublicKey('app-id', 'key-id', { is_active: false })
|
|
1617
2664
|
*
|
|
1618
|
-
* //
|
|
1619
|
-
* await cb.
|
|
2665
|
+
* // Public Key 삭제
|
|
2666
|
+
* await cb.publicKey.deletePublicKey('app-id', 'key-id')
|
|
1620
2667
|
* ```
|
|
1621
2668
|
*/
|
|
1622
|
-
declare class
|
|
2669
|
+
declare class PublicKeyAPI {
|
|
1623
2670
|
private http;
|
|
1624
2671
|
constructor(http: HttpClient);
|
|
1625
2672
|
/**
|
|
1626
|
-
* 앱의
|
|
2673
|
+
* 앱의 Public Key 목록을 조회합니다
|
|
1627
2674
|
* @param appId 앱 ID
|
|
1628
2675
|
*/
|
|
1629
|
-
|
|
2676
|
+
getPublicKeys(appId: string): Promise<FetchPublicKeysResponse>;
|
|
1630
2677
|
/**
|
|
1631
|
-
* 새
|
|
2678
|
+
* 새 Public Key 를 생성합니다
|
|
1632
2679
|
*
|
|
1633
2680
|
* **중요**: 반환되는 `key` 값은 이 응답에서만 볼 수 있습니다.
|
|
1634
2681
|
* 안전한 곳에 저장하세요.
|
|
1635
2682
|
*
|
|
1636
2683
|
* @param appId 앱 ID
|
|
1637
|
-
* @param data 생성할
|
|
2684
|
+
* @param data 생성할 Public Key 정보
|
|
1638
2685
|
*/
|
|
1639
|
-
|
|
2686
|
+
createPublicKey(appId: string, data: CreatePublicKeyRequest): Promise<CreatePublicKeyResponse>;
|
|
1640
2687
|
/**
|
|
1641
|
-
*
|
|
2688
|
+
* Public Key 를 수정합니다 (이름 변경, 활성화/비활성화)
|
|
1642
2689
|
* @param appId 앱 ID
|
|
1643
|
-
* @param keyId
|
|
2690
|
+
* @param keyId Public Key ID
|
|
1644
2691
|
* @param data 수정할 정보
|
|
1645
2692
|
*/
|
|
1646
|
-
|
|
2693
|
+
updatePublicKey(appId: string, keyId: string, data: UpdatePublicKeyRequest): Promise<UpdatePublicKeyResponse>;
|
|
1647
2694
|
/**
|
|
1648
|
-
*
|
|
2695
|
+
* Public Key 를 삭제합니다
|
|
1649
2696
|
* @param appId 앱 ID
|
|
1650
|
-
* @param keyId
|
|
2697
|
+
* @param keyId Public Key ID
|
|
1651
2698
|
*/
|
|
1652
|
-
|
|
2699
|
+
deletePublicKey(appId: string, keyId: string): Promise<void>;
|
|
1653
2700
|
}
|
|
1654
2701
|
|
|
1655
2702
|
/**
|
|
@@ -1710,6 +2757,36 @@ declare class FunctionsAPI {
|
|
|
1710
2757
|
* ```
|
|
1711
2758
|
*/
|
|
1712
2759
|
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>;
|
|
1713
2790
|
/**
|
|
1714
2791
|
* 서버리스 함수 실행 (비동기 래퍼)
|
|
1715
2792
|
* 결과만 반환하고 메타데이터는 제외
|
|
@@ -1725,6 +2802,25 @@ declare class FunctionsAPI {
|
|
|
1725
2802
|
* ```
|
|
1726
2803
|
*/
|
|
1727
2804
|
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;
|
|
1728
2824
|
}
|
|
1729
2825
|
|
|
1730
2826
|
/**
|
|
@@ -1805,22 +2901,47 @@ interface ClientMessage {
|
|
|
1805
2901
|
/** 서버 -> 클라이언트 메시지 */
|
|
1806
2902
|
interface ServerMessage<T = unknown> {
|
|
1807
2903
|
category?: string;
|
|
1808
|
-
event: 'connected' | 'subscribed' | 'unsubscribed' | 'message' | 'sent' | 'result' | 'error' | 'pong' | 'history' | 'stream_token' | 'stream_done' | 'stream_error' | 'read_receipt' | 'presence' | 'presence_status' | 'typing';
|
|
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';
|
|
1809
2905
|
data?: T;
|
|
1810
2906
|
request_id?: string;
|
|
1811
2907
|
error?: string;
|
|
1812
2908
|
timestamp: number;
|
|
1813
2909
|
}
|
|
1814
|
-
/** AI 스트리밍
|
|
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
|
+
*/
|
|
1815
2936
|
interface StreamMessage {
|
|
1816
2937
|
role: 'user' | 'assistant' | 'system';
|
|
1817
|
-
content: string;
|
|
2938
|
+
content: string | StreamContentPart[];
|
|
1818
2939
|
}
|
|
1819
2940
|
/** AI 스트리밍 요청 옵션 */
|
|
1820
2941
|
interface StreamOptions {
|
|
1821
|
-
/** AI 프로바이더 (
|
|
1822
|
-
provider?: 'gemini' | 'openai' | 'claude';
|
|
1823
|
-
/** 모델명 (
|
|
2942
|
+
/** AI 프로바이더 (앱 설정의 프로바이더 사용. override 시 지정) */
|
|
2943
|
+
provider?: 'gemini' | 'openai' | 'claude' | 'ollama' | 'lm_studio' | 'openai_compatible';
|
|
2944
|
+
/** 모델명 (앱 설정의 모델을 override (선택적)) */
|
|
1824
2945
|
model?: string;
|
|
1825
2946
|
/** 시스템 프롬프트 */
|
|
1826
2947
|
system?: string;
|
|
@@ -1832,6 +2953,8 @@ interface StreamOptions {
|
|
|
1832
2953
|
sessionId?: string;
|
|
1833
2954
|
/** 사용자 정의 메타데이터 */
|
|
1834
2955
|
metadata?: Record<string, unknown>;
|
|
2956
|
+
/** MCP 그룹 슬러그 (AI Agent 모드 — 등록된 MCP 서버의 도구를 AI에 제공) */
|
|
2957
|
+
mcpGroup?: string;
|
|
1835
2958
|
}
|
|
1836
2959
|
/** AI 스트리밍 토큰 콜백 */
|
|
1837
2960
|
type StreamTokenCallback = (token: string, index: number) => void;
|
|
@@ -1847,11 +2970,19 @@ interface StreamDoneData {
|
|
|
1847
2970
|
type StreamDoneCallback = (result: StreamDoneData) => void;
|
|
1848
2971
|
/** AI 스트리밍 에러 콜백 */
|
|
1849
2972
|
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;
|
|
1850
2977
|
/** AI 스트리밍 핸들러 */
|
|
1851
2978
|
interface StreamHandlers {
|
|
1852
2979
|
onToken?: StreamTokenCallback;
|
|
1853
2980
|
onDone?: StreamDoneCallback;
|
|
1854
2981
|
onError?: StreamErrorCallback;
|
|
2982
|
+
/** MCP 도구 호출 시 콜백 (AI Agent 모드) */
|
|
2983
|
+
onToolCall?: StreamToolCallCallback;
|
|
2984
|
+
/** MCP 도구 실행 완료 시 콜백 (AI Agent 모드) */
|
|
2985
|
+
onToolResult?: StreamToolResultCallback;
|
|
1855
2986
|
}
|
|
1856
2987
|
/** AI 스트리밍 세션 */
|
|
1857
2988
|
interface StreamSession {
|
|
@@ -2211,6 +3342,12 @@ declare class RealtimeAPI {
|
|
|
2211
3342
|
onReadReceipt(category: string, handler: ReadReceiptHandler): () => void;
|
|
2212
3343
|
private doConnect;
|
|
2213
3344
|
private log;
|
|
3345
|
+
/**
|
|
3346
|
+
* 에러 로그. `options.debug` 가 true 일 때만 console 에 출력.
|
|
3347
|
+
* 운영 환경에서는 소비자 애플리케이션의 `ConnectBase({ onError })` 또는
|
|
3348
|
+
* `cb.errorTracker` 로 전달하는 것을 권장하므로 SDK 자체 console 출력은 opt-in.
|
|
3349
|
+
*/
|
|
3350
|
+
private logError;
|
|
2214
3351
|
private handleServerMessage;
|
|
2215
3352
|
private handleDisconnect;
|
|
2216
3353
|
/**
|
|
@@ -2571,7 +3708,7 @@ declare class ErrorTrackerAPI {
|
|
|
2571
3708
|
/**
|
|
2572
3709
|
* OAuth 프로바이더 타입
|
|
2573
3710
|
*/
|
|
2574
|
-
type OAuthProvider = 'google' | 'kakao' | 'naver' | '
|
|
3711
|
+
type OAuthProvider = 'google' | 'kakao' | 'naver' | 'apple' | 'github' | 'discord';
|
|
2575
3712
|
/**
|
|
2576
3713
|
* 활성화된 OAuth 프로바이더 정보
|
|
2577
3714
|
*/
|
|
@@ -2593,12 +3730,17 @@ interface GetAuthorizationURLResponse {
|
|
|
2593
3730
|
}
|
|
2594
3731
|
/**
|
|
2595
3732
|
* OAuth 콜백 응답
|
|
3733
|
+
*
|
|
3734
|
+
* `email` 은 1.10 부터 추가된 선택적 필드. Apple private relay 또는 이메일 권한 미동의
|
|
3735
|
+
* 시 빈 문자열 / undefined 일 수 있다. 값이 있으면 첫 로그인 시점에 이메일을 확보할 수 있어
|
|
3736
|
+
* 별도 `getMember()` 호출이 불필요하다.
|
|
2596
3737
|
*/
|
|
2597
3738
|
interface OAuthCallbackResponse {
|
|
2598
3739
|
member_id: string;
|
|
2599
3740
|
access_token: string;
|
|
2600
3741
|
refresh_token: string;
|
|
2601
3742
|
is_new_member: boolean;
|
|
3743
|
+
email?: string;
|
|
2602
3744
|
}
|
|
2603
3745
|
|
|
2604
3746
|
/**
|
|
@@ -2636,9 +3778,10 @@ declare class OAuthAPI {
|
|
|
2636
3778
|
getEnabledProviders(): Promise<EnabledProvidersResponse>;
|
|
2637
3779
|
/**
|
|
2638
3780
|
* 소셜 로그인 (리다이렉트 방식) - 권장
|
|
2639
|
-
*
|
|
2640
|
-
*
|
|
2641
|
-
*
|
|
3781
|
+
*
|
|
3782
|
+
* 기존 회원만 로그인 (`intent=signin` 명시). 이 앱에서 처음인 사용자는 콜백에서
|
|
3783
|
+
* `error=account_not_found` 를 받아 가입 안내 UX 로 분기해야 한다. 신규 가입까지 한 번에
|
|
3784
|
+
* 처리하려면 [signUp] 을 사용한다.
|
|
2642
3785
|
*
|
|
2643
3786
|
* @param provider - OAuth 프로바이더 (google, naver, github, discord)
|
|
2644
3787
|
* @param callbackUrl - OAuth 완료 후 리다이렉트될 앱의 URL
|
|
@@ -2646,27 +3789,30 @@ declare class OAuthAPI {
|
|
|
2646
3789
|
*
|
|
2647
3790
|
* @example
|
|
2648
3791
|
* ```typescript
|
|
2649
|
-
* // 로그인 버튼 클릭 시
|
|
3792
|
+
* // "로그인" 버튼 클릭 시
|
|
2650
3793
|
* await cb.oauth.signIn('google', 'https://myapp.com/oauth/callback')
|
|
2651
|
-
* // Google 로그인 후 https://myapp.com/oauth/callback?access_token=...&refresh_token=... 로 리다이렉트됨
|
|
2652
3794
|
* ```
|
|
2653
3795
|
*
|
|
2654
3796
|
* @example
|
|
2655
3797
|
* ```typescript
|
|
2656
3798
|
* // callback 페이지에서
|
|
2657
|
-
* const result = cb.oauth.getCallbackResult()
|
|
2658
|
-
* if (result) {
|
|
2659
|
-
*
|
|
2660
|
-
*
|
|
2661
|
-
* } else {
|
|
2662
|
-
* console.log('로그인 성공:', result.member_id)
|
|
2663
|
-
* // 메인 페이지로 이동
|
|
2664
|
-
* window.location.href = '/'
|
|
2665
|
-
* }
|
|
3799
|
+
* const result = await cb.oauth.getCallbackResult()
|
|
3800
|
+
* if (result?.error === 'account_not_found') {
|
|
3801
|
+
* // 가입되지 않은 사용자 — 회원가입 화면으로 안내
|
|
3802
|
+
* window.location.href = '/signup'
|
|
2666
3803
|
* }
|
|
2667
3804
|
* ```
|
|
2668
3805
|
*/
|
|
2669
3806
|
signIn(provider: OAuthProvider, callbackUrl: string, state?: string): Promise<void>;
|
|
3807
|
+
/**
|
|
3808
|
+
* 소셜 회원가입 (리다이렉트 방식) — 사용자가 명시적으로 가입 의사를 표시한 경우에만 호출.
|
|
3809
|
+
*
|
|
3810
|
+
* 기존 회원이면 그대로 로그인 (idempotent). 없으면 신규 AppMember 생성.
|
|
3811
|
+
* "로그인" 버튼에는 [signIn] 을 쓰고, "회원가입" 버튼에서만 이 메서드를 호출해야 silent
|
|
3812
|
+
* auto-signup 회귀를 방지할 수 있다.
|
|
3813
|
+
*/
|
|
3814
|
+
signUp(provider: OAuthProvider, callbackUrl: string, state?: string): Promise<void>;
|
|
3815
|
+
private startCentralOAuth;
|
|
2670
3816
|
/**
|
|
2671
3817
|
* 소셜 로그인 (팝업 방식)
|
|
2672
3818
|
* 팝업 창에서 소셜 로그인을 처리하고 결과를 Promise로 반환합니다.
|
|
@@ -2691,36 +3837,72 @@ declare class OAuthAPI {
|
|
|
2691
3837
|
* const result = await cb.oauth.signInWithPopup('google', 'https://myapp.com/oauth/callback')
|
|
2692
3838
|
* ```
|
|
2693
3839
|
*/
|
|
2694
|
-
signInWithPopup(provider: OAuthProvider, callbackUrl?: string
|
|
3840
|
+
signInWithPopup(provider: OAuthProvider, callbackUrl?: string, options?: {
|
|
3841
|
+
intent?: 'signin' | 'signup';
|
|
3842
|
+
}): Promise<OAuthCallbackResponse>;
|
|
2695
3843
|
/**
|
|
2696
3844
|
* 콜백 URL에서 OAuth 결과 추출
|
|
2697
3845
|
* 중앙 콜백 방식에서 리다이렉트 후 URL 파라미터에서 결과를 추출합니다.
|
|
2698
3846
|
* 토큰이 있으면 자동으로 저장됩니다.
|
|
2699
3847
|
*
|
|
3848
|
+
* 본 메서드는 `Promise` 를 반환한다. 토큰 저장 직후 `cb_member_refresh_token`
|
|
3849
|
+
* cookie 부트스트랩(`/v1/auth/re-issue` 1회)을 *await* 하기 위함이다. 표준 예제
|
|
3850
|
+
* 코드는 결과를 await 한 다음 `window.location.href='/'` 로 이동하므로, cookie 가
|
|
3851
|
+
* 브라우저에 안전하게 저장된 뒤 페이지가 전환된다.
|
|
3852
|
+
*
|
|
3853
|
+
* 이전(3.22.x) 의 fire-and-forget 부트스트랩은 모바일/느린 네트워크에서 navigation
|
|
3854
|
+
* 이 fetch 보다 먼저 발화해 cookie 가 발급되지 않고, 새 페이지 진입 시 cookie
|
|
3855
|
+
* 없는 상태로 `getMe()` 가 401 으로 떨어지는 회귀가 있었다 (platform-issue
|
|
3856
|
+
* 019e638d, 2026-05-26).
|
|
3857
|
+
*
|
|
2700
3858
|
* @returns OAuth 결과 (토큰, member_id 등) 또는 null
|
|
2701
3859
|
*
|
|
2702
3860
|
* @example
|
|
2703
3861
|
* ```typescript
|
|
2704
3862
|
* // callback 페이지에서
|
|
2705
|
-
* const result = cb.oauth.getCallbackResult()
|
|
3863
|
+
* const result = await cb.oauth.getCallbackResult()
|
|
2706
3864
|
* if (result) {
|
|
2707
3865
|
* if (result.error) {
|
|
2708
3866
|
* alert('로그인 실패: ' + result.error)
|
|
2709
3867
|
* } else {
|
|
2710
|
-
* console.log('로그인 성공:', result.member_id)
|
|
2711
3868
|
* window.location.href = '/'
|
|
2712
3869
|
* }
|
|
2713
3870
|
* }
|
|
2714
3871
|
* ```
|
|
2715
3872
|
*/
|
|
2716
|
-
getCallbackResult(): {
|
|
3873
|
+
getCallbackResult(): Promise<{
|
|
2717
3874
|
access_token?: string;
|
|
2718
3875
|
refresh_token?: string;
|
|
2719
3876
|
member_id?: string;
|
|
2720
3877
|
is_new_member?: boolean;
|
|
3878
|
+
email?: string;
|
|
2721
3879
|
state?: string;
|
|
2722
3880
|
error?: string;
|
|
2723
|
-
} | null
|
|
3881
|
+
} | null>;
|
|
3882
|
+
/**
|
|
3883
|
+
* 콜백 URL 에서 1회용 `code` 를 토큰으로 교환합니다 (`OAUTH_CODE_ONLY` 서버 모드용).
|
|
3884
|
+
*
|
|
3885
|
+
* 서버가 `OAUTH_CODE_ONLY=true` 모드면 리다이렉트 URL 에 토큰 대신 `code` 만 포함됩니다.
|
|
3886
|
+
* 이 메서드는 code 를 `POST /v1/auth/oauth/exchange` 로 교환하고 토큰을 자동 저장합니다.
|
|
3887
|
+
*
|
|
3888
|
+
* 권장 호출 순서:
|
|
3889
|
+
* 1) `cb.oauth.exchangeCodeFromCallback()` 먼저 — code-only 모드면 성공
|
|
3890
|
+
* 2) null 반환 시 `cb.oauth.getCallbackResult()` 폴백 (legacy 토큰-in-URL 모드)
|
|
3891
|
+
*
|
|
3892
|
+
* @example
|
|
3893
|
+
* ```typescript
|
|
3894
|
+
* const result = await cb.oauth.exchangeCodeFromCallback()
|
|
3895
|
+
* ?? cb.oauth.getCallbackResult()
|
|
3896
|
+
* ```
|
|
3897
|
+
*/
|
|
3898
|
+
exchangeCodeFromCallback(): Promise<{
|
|
3899
|
+
access_token: string;
|
|
3900
|
+
refresh_token: string;
|
|
3901
|
+
member_id: string;
|
|
3902
|
+
is_new_member: boolean;
|
|
3903
|
+
email?: string;
|
|
3904
|
+
state?: string;
|
|
3905
|
+
} | null>;
|
|
2724
3906
|
}
|
|
2725
3907
|
|
|
2726
3908
|
type PaymentProvider = 'toss' | 'stripe';
|
|
@@ -2747,6 +3929,25 @@ interface PreparePaymentResponse {
|
|
|
2747
3929
|
stripe_client_secret?: string;
|
|
2748
3930
|
stripe_publishable_key?: string;
|
|
2749
3931
|
}
|
|
3932
|
+
interface CreateCheckoutSessionRequest {
|
|
3933
|
+
amount: number;
|
|
3934
|
+
currency: string;
|
|
3935
|
+
product_name: string;
|
|
3936
|
+
success_url: string;
|
|
3937
|
+
cancel_url: string;
|
|
3938
|
+
order_id?: string;
|
|
3939
|
+
customer_email?: string;
|
|
3940
|
+
customer_name?: string;
|
|
3941
|
+
metadata?: Record<string, unknown>;
|
|
3942
|
+
}
|
|
3943
|
+
interface CreateCheckoutSessionResponse {
|
|
3944
|
+
payment_id: string;
|
|
3945
|
+
order_id: string;
|
|
3946
|
+
amount: number;
|
|
3947
|
+
currency: string;
|
|
3948
|
+
session_id: string;
|
|
3949
|
+
session_url: string;
|
|
3950
|
+
}
|
|
2750
3951
|
interface ConfirmPaymentRequest {
|
|
2751
3952
|
payment_key: string;
|
|
2752
3953
|
order_id: string;
|
|
@@ -2814,35 +4015,61 @@ declare class PaymentAPI {
|
|
|
2814
4015
|
*
|
|
2815
4016
|
* @example
|
|
2816
4017
|
* ```typescript
|
|
2817
|
-
* const
|
|
4018
|
+
* const result = await cb.payment.prepare({
|
|
2818
4019
|
* amount: 10000,
|
|
2819
|
-
* order_name: '
|
|
4020
|
+
* order_name: '프리미엄 1개월',
|
|
2820
4021
|
* customer_email: 'test@example.com',
|
|
2821
4022
|
* customer_name: '홍길동'
|
|
2822
4023
|
* })
|
|
2823
4024
|
*
|
|
2824
|
-
* //
|
|
2825
|
-
*
|
|
2826
|
-
*
|
|
2827
|
-
*
|
|
2828
|
-
*
|
|
2829
|
-
*
|
|
2830
|
-
*
|
|
2831
|
-
*
|
|
2832
|
-
*
|
|
2833
|
-
*
|
|
4025
|
+
* // Toss V2 결제 플로우
|
|
4026
|
+
* if (result.payment_provider === 'toss') {
|
|
4027
|
+
* const tossPayments = TossPayments(result.toss_client_key)
|
|
4028
|
+
* const payment = tossPayments.payment({ customerKey: result.customer_key })
|
|
4029
|
+
* await payment.requestPayment({
|
|
4030
|
+
* method: 'CARD',
|
|
4031
|
+
* amount: { currency: 'KRW', value: result.amount },
|
|
4032
|
+
* orderId: result.order_id,
|
|
4033
|
+
* orderName: result.order_name,
|
|
4034
|
+
* successUrl: result.success_url,
|
|
4035
|
+
* failUrl: result.fail_url,
|
|
4036
|
+
* })
|
|
4037
|
+
* }
|
|
2834
4038
|
*
|
|
2835
|
-
* // Stripe 결제
|
|
2836
|
-
*
|
|
2837
|
-
*
|
|
2838
|
-
*
|
|
2839
|
-
*
|
|
2840
|
-
*
|
|
2841
|
-
*
|
|
2842
|
-
* // }
|
|
4039
|
+
* // Stripe 결제 플로우
|
|
4040
|
+
* if (result.payment_provider === 'stripe') {
|
|
4041
|
+
* const stripe = await loadStripe(result.stripe_publishable_key)
|
|
4042
|
+
* const elements = stripe.elements({ clientSecret: result.stripe_client_secret })
|
|
4043
|
+
* // Mount PaymentElement, then:
|
|
4044
|
+
* // stripe.confirmPayment({ elements, redirect: 'if_required' })
|
|
4045
|
+
* }
|
|
2843
4046
|
* ```
|
|
2844
4047
|
*/
|
|
2845
4048
|
prepare(data: PreparePaymentRequest): Promise<PreparePaymentResponse>;
|
|
4049
|
+
/**
|
|
4050
|
+
* Stripe-hosted 결제 페이지 세션 생성 (client_secret 미노출 플로우).
|
|
4051
|
+
*
|
|
4052
|
+
* Elements 플로우(`prepare`) 대신 Stripe-hosted Checkout 페이지로 리다이렉트하고 싶을 때 사용.
|
|
4053
|
+
* 응답에 `session_url` 이 포함되며, 브라우저를 해당 URL 로 이동시키면 Stripe 가 카드 입력·결제를 처리한 뒤
|
|
4054
|
+
* `success_url` / `cancel_url` 로 복귀한다. `client_secret` 은 서버·클라이언트 어디에도 노출되지 않는다.
|
|
4055
|
+
*
|
|
4056
|
+
* @param data - 결제 세션 정보 (금액, 통화, 상품명, success/cancel URL 등)
|
|
4057
|
+
* @returns session_id, session_url (redirect 대상)
|
|
4058
|
+
*
|
|
4059
|
+
* @example
|
|
4060
|
+
* ```typescript
|
|
4061
|
+
* const sess = await client.payment.createCheckoutSession({
|
|
4062
|
+
* amount: 1000,
|
|
4063
|
+
* currency: 'USD',
|
|
4064
|
+
* product_name: 'Premium Subscription',
|
|
4065
|
+
* success_url: 'https://app.example.com/success?session_id={CHECKOUT_SESSION_ID}',
|
|
4066
|
+
* cancel_url: 'https://app.example.com/cancel',
|
|
4067
|
+
* customer_email: 'user@example.com',
|
|
4068
|
+
* })
|
|
4069
|
+
* window.location.href = sess.session_url
|
|
4070
|
+
* ```
|
|
4071
|
+
*/
|
|
4072
|
+
createCheckoutSession(data: CreateCheckoutSessionRequest): Promise<CreateCheckoutSessionResponse>;
|
|
2846
4073
|
/**
|
|
2847
4074
|
* 결제 승인
|
|
2848
4075
|
* 토스에서 결제 완료 후 콜백으로 받은 정보로 결제를 최종 승인합니다.
|
|
@@ -3397,7 +4624,7 @@ declare class SubscriptionAPI {
|
|
|
3397
4624
|
* order_name: '추가 결제'
|
|
3398
4625
|
* })
|
|
3399
4626
|
*
|
|
3400
|
-
* if (result.status === '
|
|
4627
|
+
* if (result.status === 'done') {
|
|
3401
4628
|
* console.log('결제 완료!')
|
|
3402
4629
|
* }
|
|
3403
4630
|
* ```
|
|
@@ -3461,6 +4688,35 @@ interface WebPushSubscription {
|
|
|
3461
4688
|
};
|
|
3462
4689
|
}
|
|
3463
4690
|
|
|
4691
|
+
/**
|
|
4692
|
+
* `cb.push.sendToMembers` 페이로드. 백엔드 `dto.SendPushRequest` 의 멤버 발송 케이스
|
|
4693
|
+
* 에 매핑되며, target_type / target_member_ids 는 SDK 가 자동 채운다.
|
|
4694
|
+
*/
|
|
4695
|
+
interface SendToMembersPayload {
|
|
4696
|
+
title: string;
|
|
4697
|
+
body: string;
|
|
4698
|
+
imageUrl?: string;
|
|
4699
|
+
data?: Record<string, unknown>;
|
|
4700
|
+
/** 'ios' | 'android' | 'web' 중 선택. 빈 배열/미지정 시 전체 플랫폼 발송. */
|
|
4701
|
+
platforms?: Array<'ios' | 'android' | 'web'>;
|
|
4702
|
+
/** TTL(초). 기본 86400 (24h). */
|
|
4703
|
+
ttlSeconds?: number;
|
|
4704
|
+
priority?: 'normal' | 'high';
|
|
4705
|
+
clickAction?: string;
|
|
4706
|
+
/** ISO8601 형식 예약 발송 시각. 미지정 시 즉시 발송. */
|
|
4707
|
+
scheduledAt?: string;
|
|
4708
|
+
}
|
|
4709
|
+
/**
|
|
4710
|
+
* 발송 결과. 백엔드 `dto.SendResultResponse` 와 1:1 매핑.
|
|
4711
|
+
*/
|
|
4712
|
+
interface SendPushResult {
|
|
4713
|
+
message_id: string;
|
|
4714
|
+
total_target: number;
|
|
4715
|
+
sent_count: number;
|
|
4716
|
+
failed_count: number;
|
|
4717
|
+
status: string;
|
|
4718
|
+
error_message?: string;
|
|
4719
|
+
}
|
|
3464
4720
|
/**
|
|
3465
4721
|
* 푸시 알림 API
|
|
3466
4722
|
*
|
|
@@ -3473,8 +4729,8 @@ interface WebPushSubscription {
|
|
|
3473
4729
|
* device_name: 'Galaxy S24',
|
|
3474
4730
|
* })
|
|
3475
4731
|
*
|
|
3476
|
-
* // 토픽 구독
|
|
3477
|
-
* await cb.push.subscribeTopic('announcements')
|
|
4732
|
+
* // 토픽 구독 (device_token 필수)
|
|
4733
|
+
* await cb.push.subscribeTopic(device.device_token, 'announcements')
|
|
3478
4734
|
*
|
|
3479
4735
|
* // Web Push 등록 (브라우저)
|
|
3480
4736
|
* const vapidKey = await cb.push.getVAPIDPublicKey()
|
|
@@ -3522,52 +4778,38 @@ declare class PushAPI {
|
|
|
3522
4778
|
/**
|
|
3523
4779
|
* 디바이스 등록 해제
|
|
3524
4780
|
*
|
|
3525
|
-
* @param
|
|
4781
|
+
* @param deviceToken 디바이스 토큰
|
|
3526
4782
|
*
|
|
3527
4783
|
* @example
|
|
3528
4784
|
* ```typescript
|
|
3529
|
-
* await cb.push.unregisterDevice('
|
|
4785
|
+
* await cb.push.unregisterDevice('fcm-token-here')
|
|
3530
4786
|
* ```
|
|
3531
4787
|
*/
|
|
3532
|
-
unregisterDevice(
|
|
3533
|
-
/**
|
|
3534
|
-
* 현재 등록된 디바이스 목록 조회
|
|
3535
|
-
*
|
|
3536
|
-
* @returns 디바이스 목록
|
|
3537
|
-
*/
|
|
3538
|
-
getDevices(): Promise<DeviceInfo[]>;
|
|
4788
|
+
unregisterDevice(deviceToken: string): Promise<void>;
|
|
3539
4789
|
/**
|
|
3540
4790
|
* 토픽 구독
|
|
3541
4791
|
*
|
|
4792
|
+
* @param deviceToken 디바이스 토큰
|
|
3542
4793
|
* @param topicName 토픽 이름
|
|
3543
4794
|
*
|
|
3544
4795
|
* @example
|
|
3545
4796
|
* ```typescript
|
|
3546
|
-
*
|
|
3547
|
-
* await cb.push.subscribeTopic('announcements')
|
|
3548
|
-
*
|
|
3549
|
-
* // 마케팅 토픽 구독
|
|
3550
|
-
* await cb.push.subscribeTopic('marketing')
|
|
4797
|
+
* await cb.push.subscribeTopic('fcm-token-here', 'announcements')
|
|
3551
4798
|
* ```
|
|
3552
4799
|
*/
|
|
3553
|
-
subscribeTopic(topicName: string): Promise<void>;
|
|
4800
|
+
subscribeTopic(deviceToken: string, topicName: string): Promise<void>;
|
|
3554
4801
|
/**
|
|
3555
4802
|
* 토픽 구독 해제
|
|
3556
4803
|
*
|
|
4804
|
+
* @param deviceToken 디바이스 토큰
|
|
3557
4805
|
* @param topicName 토픽 이름
|
|
3558
4806
|
*
|
|
3559
4807
|
* @example
|
|
3560
4808
|
* ```typescript
|
|
3561
|
-
* await cb.push.unsubscribeTopic('marketing')
|
|
4809
|
+
* await cb.push.unsubscribeTopic('fcm-token-here', 'marketing')
|
|
3562
4810
|
* ```
|
|
3563
4811
|
*/
|
|
3564
|
-
unsubscribeTopic(topicName: string): Promise<void>;
|
|
3565
|
-
/**
|
|
3566
|
-
* 구독 중인 토픽 목록 조회
|
|
3567
|
-
*
|
|
3568
|
-
* @returns 구독 중인 토픽 이름 목록
|
|
3569
|
-
*/
|
|
3570
|
-
getSubscribedTopics(): Promise<string[]>;
|
|
4812
|
+
unsubscribeTopic(deviceToken: string, topicName: string): Promise<void>;
|
|
3571
4813
|
/**
|
|
3572
4814
|
* VAPID Public Key 조회 (Web Push용)
|
|
3573
4815
|
*
|
|
@@ -3609,8 +4851,33 @@ declare class PushAPI {
|
|
|
3609
4851
|
registerWebPush(subscription: PushSubscription | WebPushSubscription): Promise<DeviceInfo>;
|
|
3610
4852
|
/**
|
|
3611
4853
|
* Web Push 구독 해제
|
|
4854
|
+
*
|
|
4855
|
+
* @param deviceToken Web Push endpoint URL (등록 시 사용한 endpoint)
|
|
4856
|
+
*/
|
|
4857
|
+
unregisterWebPush(deviceToken: string): Promise<void>;
|
|
4858
|
+
/**
|
|
4859
|
+
* 회원 ID 목록으로 푸시 발송 — Functions / cb_sk_ 인증 환경 전용.
|
|
4860
|
+
*
|
|
4861
|
+
* 백엔드 라우트 `POST /v1/apps/:appID/push/send` 는 콘솔 JWT 또는 User Secret Key
|
|
4862
|
+
* (`cb_sk_`) 만 받는다. 브라우저 SDK 의 Public Key(`cb_pk_`) 인스턴스에서는 호출
|
|
4863
|
+
* 자체가 차단되며, 권한 누설을 막기 위해 명확한 에러를 던진다.
|
|
4864
|
+
*
|
|
4865
|
+
* @param appId 발송 대상 앱 ID (cb_sk_ 환경은 user 단위 권한이므로 명시 전달).
|
|
4866
|
+
* @param memberIds 받는 회원 ID 배열 (UUID).
|
|
4867
|
+
* @param payload 푸시 내용/옵션.
|
|
4868
|
+
*
|
|
4869
|
+
* @example
|
|
4870
|
+
* ```ts
|
|
4871
|
+
* // ConnectBase Function (Node.js, secrets.CB_SECRET_KEY 주입)
|
|
4872
|
+
* const result = await cb.push.sendToMembers(APP_ID, [memberId], {
|
|
4873
|
+
* title: '새 알림',
|
|
4874
|
+
* body: '확인해보세요',
|
|
4875
|
+
* data: { route: '/notifications' },
|
|
4876
|
+
* })
|
|
4877
|
+
* console.log(result.message_id, result.sent_count)
|
|
4878
|
+
* ```
|
|
3612
4879
|
*/
|
|
3613
|
-
|
|
4880
|
+
sendToMembers(appId: string, memberIds: string[], payload: SendToMembersPayload): Promise<SendPushResult>;
|
|
3614
4881
|
/**
|
|
3615
4882
|
* 브라우저 고유 ID 생성 (localStorage에 저장)
|
|
3616
4883
|
*/
|
|
@@ -3847,6 +5114,62 @@ interface ChannelMembership {
|
|
|
3847
5114
|
started_at: string;
|
|
3848
5115
|
expires_at?: string;
|
|
3849
5116
|
}
|
|
5117
|
+
interface VideoStorage {
|
|
5118
|
+
id: string;
|
|
5119
|
+
app_id: string;
|
|
5120
|
+
name: string;
|
|
5121
|
+
description: string;
|
|
5122
|
+
current_size: number;
|
|
5123
|
+
video_count: number;
|
|
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
|
+
created_at: string;
|
|
5134
|
+
updated_at: string;
|
|
5135
|
+
}
|
|
5136
|
+
interface CreateVideoStorageRequest {
|
|
5137
|
+
name: string;
|
|
5138
|
+
description?: string;
|
|
5139
|
+
is_public?: boolean;
|
|
5140
|
+
allow_server_to_server?: boolean;
|
|
5141
|
+
allowed_origins?: string[];
|
|
5142
|
+
strict_referer_check?: boolean;
|
|
5143
|
+
cdn_provider?: string;
|
|
5144
|
+
cdn_config?: Record<string, unknown>;
|
|
5145
|
+
default_qualities?: string[];
|
|
5146
|
+
hls_encryption_enabled?: boolean;
|
|
5147
|
+
token_expiry_hours?: number;
|
|
5148
|
+
}
|
|
5149
|
+
interface UpdateVideoStorageRequest {
|
|
5150
|
+
name?: string;
|
|
5151
|
+
description?: string;
|
|
5152
|
+
is_public?: boolean;
|
|
5153
|
+
allow_server_to_server?: boolean;
|
|
5154
|
+
allowed_origins?: string[];
|
|
5155
|
+
strict_referer_check?: boolean;
|
|
5156
|
+
cdn_provider?: string;
|
|
5157
|
+
cdn_config?: Record<string, unknown>;
|
|
5158
|
+
default_qualities?: string[];
|
|
5159
|
+
hls_encryption_enabled?: boolean;
|
|
5160
|
+
token_expiry_hours?: number;
|
|
5161
|
+
}
|
|
5162
|
+
interface VideoStorageListResponse {
|
|
5163
|
+
storage_videos: VideoStorage[];
|
|
5164
|
+
total_count: number;
|
|
5165
|
+
}
|
|
5166
|
+
interface StorageUploadOptions {
|
|
5167
|
+
title: string;
|
|
5168
|
+
description?: string;
|
|
5169
|
+
visibility?: VideoVisibility;
|
|
5170
|
+
tags?: string[];
|
|
5171
|
+
onProgress?: (progress: UploadProgress) => void;
|
|
5172
|
+
}
|
|
3850
5173
|
interface SuperChat {
|
|
3851
5174
|
id: string;
|
|
3852
5175
|
video_id: string;
|
|
@@ -3888,7 +5211,29 @@ declare class VideoAPI {
|
|
|
3888
5211
|
*/
|
|
3889
5212
|
waitForReady(videoId: string, options?: WaitOptions): Promise<Video>;
|
|
3890
5213
|
/**
|
|
3891
|
-
*
|
|
5214
|
+
* 영상 목록 조회.
|
|
5215
|
+
*
|
|
5216
|
+
* 필터링/페이지네이션 옵션을 조합해 조건에 맞는 영상들을 반환한다.
|
|
5217
|
+
* API Key(publicKey) 또는 JWT 인증이 모두 지원되며, publicKey 로는 공개 영상만 노출된다.
|
|
5218
|
+
*
|
|
5219
|
+
* @param options - 필터/페이지네이션 옵션
|
|
5220
|
+
* @param options.status - `uploading` | `processing` | `ready` | `failed`
|
|
5221
|
+
* @param options.visibility - `public` | `unlisted` | `private` | `members`
|
|
5222
|
+
* @param options.search - 제목 부분 일치 검색
|
|
5223
|
+
* @param options.channel_id - 특정 채널로 한정
|
|
5224
|
+
* @param options.page - 1 부터 시작
|
|
5225
|
+
* @param options.limit - 기본 20, 최대 100
|
|
5226
|
+
* @returns `videos` 배열과 `total` 카운트를 포함하는 응답
|
|
5227
|
+
*
|
|
5228
|
+
* @example
|
|
5229
|
+
* ```ts
|
|
5230
|
+
* // 내 채널의 최근 공개 영상 10개
|
|
5231
|
+
* const { videos, total } = await cb.video.list({
|
|
5232
|
+
* channel_id: 'ch_abc',
|
|
5233
|
+
* visibility: 'public',
|
|
5234
|
+
* limit: 10,
|
|
5235
|
+
* })
|
|
5236
|
+
* ```
|
|
3892
5237
|
*/
|
|
3893
5238
|
list(options?: VideoListOptions): Promise<VideoListResponse>;
|
|
3894
5239
|
/**
|
|
@@ -3904,7 +5249,20 @@ declare class VideoAPI {
|
|
|
3904
5249
|
*/
|
|
3905
5250
|
delete(videoId: string): Promise<void>;
|
|
3906
5251
|
/**
|
|
3907
|
-
*
|
|
5252
|
+
* 영상 스트리밍 URL 획득 (HLS manifest 권장).
|
|
5253
|
+
*
|
|
5254
|
+
* 반환된 URL 은 `hls.js` 또는 Safari 네이티브 HLS 플레이어에 그대로 전달 가능하다.
|
|
5255
|
+
* 서명된 URL 이므로 만료 시간이 존재하며, 갱신이 필요한 경우 다시 호출한다.
|
|
5256
|
+
*
|
|
5257
|
+
* @param videoId - 대상 영상 ID
|
|
5258
|
+
* @param quality - `auto` (기본) | `1080p` | `720p` | `480p` | `360p` 등 트랜스코딩된 품질 레벨
|
|
5259
|
+
* @returns HLS manifest URL 과 만료 정보
|
|
5260
|
+
*
|
|
5261
|
+
* @example
|
|
5262
|
+
* ```ts
|
|
5263
|
+
* const { url, expires_at } = await cb.video.getStreamUrl(videoId, '720p')
|
|
5264
|
+
* videoElement.src = url
|
|
5265
|
+
* ```
|
|
3908
5266
|
*/
|
|
3909
5267
|
getStreamUrl(videoId: string, quality?: string): Promise<StreamURLResponse>;
|
|
3910
5268
|
/**
|
|
@@ -4060,11 +5418,131 @@ declare class VideoAPI {
|
|
|
4060
5418
|
* Submit recommendation feedback
|
|
4061
5419
|
*/
|
|
4062
5420
|
submitFeedback(videoId: string, feedback: 'interested' | 'not_interested'): Promise<void>;
|
|
5421
|
+
/**
|
|
5422
|
+
* Storage sub-namespace for video storage container operations
|
|
5423
|
+
*/
|
|
5424
|
+
readonly storage: {
|
|
5425
|
+
/**
|
|
5426
|
+
* Create a video storage container
|
|
5427
|
+
*/
|
|
5428
|
+
create: (data: CreateVideoStorageRequest) => Promise<VideoStorage>;
|
|
5429
|
+
/**
|
|
5430
|
+
* List video storage containers
|
|
5431
|
+
*/
|
|
5432
|
+
list: () => Promise<VideoStorageListResponse>;
|
|
5433
|
+
/**
|
|
5434
|
+
* Get a video storage container
|
|
5435
|
+
*/
|
|
5436
|
+
get: (storageId: string) => Promise<VideoStorage>;
|
|
5437
|
+
/**
|
|
5438
|
+
* Update a video storage container
|
|
5439
|
+
*/
|
|
5440
|
+
update: (storageId: string, data: UpdateVideoStorageRequest) => Promise<VideoStorage>;
|
|
5441
|
+
/**
|
|
5442
|
+
* Delete a video storage container
|
|
5443
|
+
*/
|
|
5444
|
+
delete: (storageId: string) => Promise<void>;
|
|
5445
|
+
/**
|
|
5446
|
+
* Upload a video to a specific storage via core-server proxy (chunked)
|
|
5447
|
+
*/
|
|
5448
|
+
upload: (storageId: string, file: File, options: StorageUploadOptions) => Promise<Video>;
|
|
5449
|
+
/**
|
|
5450
|
+
* List videos in a specific storage
|
|
5451
|
+
*/
|
|
5452
|
+
listVideos: (storageId: string, options?: VideoListOptions) => Promise<VideoListResponse>;
|
|
5453
|
+
/**
|
|
5454
|
+
* Get a video in a specific storage
|
|
5455
|
+
*/
|
|
5456
|
+
getVideo: (storageId: string, videoId: string) => Promise<Video>;
|
|
5457
|
+
/**
|
|
5458
|
+
* Delete a video from a specific storage
|
|
5459
|
+
*/
|
|
5460
|
+
deleteVideo: (storageId: string, videoId: string) => Promise<void>;
|
|
5461
|
+
/**
|
|
5462
|
+
* Get streaming URL for a video in a specific storage
|
|
5463
|
+
*/
|
|
5464
|
+
getStreamUrl: (storageId: string, videoId: string) => Promise<StreamURLResponse>;
|
|
5465
|
+
/**
|
|
5466
|
+
* Get transcode status for a video in a specific storage
|
|
5467
|
+
*/
|
|
5468
|
+
getTranscodeStatus: (storageId: string, videoId: string) => Promise<TranscodeStatus>;
|
|
5469
|
+
};
|
|
5470
|
+
}
|
|
5471
|
+
|
|
5472
|
+
/**
|
|
5473
|
+
* 게임 서버 기능 opt-in 토글 API.
|
|
5474
|
+
*
|
|
5475
|
+
* v3.1 (2026-04-30+) 부터 도입. 7개 게임 기능 (matchqueue / leaderboard / entity /
|
|
5476
|
+
* scripts / voice / replay / spectator) 은 모두 앱 단위로 명시적 opt-in 해야 사용 가능.
|
|
5477
|
+
*
|
|
5478
|
+
* 정책:
|
|
5479
|
+
* - 신규 앱: 모든 토글 false (App 생성 시 row 자동 삽입)
|
|
5480
|
+
* - 기존 앱: row 가 없으면 레거시 호환으로 모두 ON 으로 응답 (서비스 단절 방지)
|
|
5481
|
+
* - PATCH 후 game-server 캐시는 NATS publish 로 즉시 무효화 (또는 30s TTL)
|
|
5482
|
+
*
|
|
5483
|
+
* @example
|
|
5484
|
+
* ```ts
|
|
5485
|
+
* const cfg = await cb.game.config.get(appId)
|
|
5486
|
+
* if (!cfg.matchqueue_enabled) {
|
|
5487
|
+
* await cb.game.config.set(appId, { matchqueue_enabled: true })
|
|
5488
|
+
* }
|
|
5489
|
+
* ```
|
|
5490
|
+
*
|
|
5491
|
+
* @see docs/game-server/OPT_IN.md
|
|
5492
|
+
*/
|
|
5493
|
+
|
|
5494
|
+
/** 7개 토글 + 메타. */
|
|
5495
|
+
interface GameConfig {
|
|
5496
|
+
matchqueue_enabled: boolean;
|
|
5497
|
+
leaderboard_enabled: boolean;
|
|
5498
|
+
entity_enabled: boolean;
|
|
5499
|
+
scripts_enabled: boolean;
|
|
5500
|
+
voice_enabled: boolean;
|
|
5501
|
+
replay_enabled: boolean;
|
|
5502
|
+
spectator_enabled: boolean;
|
|
5503
|
+
}
|
|
5504
|
+
/** PATCH body — 변경할 필드만 명시 (partial update). */
|
|
5505
|
+
type GameConfigPatch = Partial<GameConfig>;
|
|
5506
|
+
/**
|
|
5507
|
+
* 게임 기능 토글 클라이언트.
|
|
5508
|
+
*
|
|
5509
|
+
* 직접 인스턴스화하지 않고 `cb.game.config` 로 접근.
|
|
5510
|
+
*/
|
|
5511
|
+
declare class GameConfigAPI {
|
|
5512
|
+
private http;
|
|
5513
|
+
private appId?;
|
|
5514
|
+
constructor(http: HttpClient, appId?: string);
|
|
5515
|
+
/**
|
|
5516
|
+
* 현재 토글 상태 조회.
|
|
5517
|
+
*
|
|
5518
|
+
* @param appId 앱 ID (생성자에서 주입한 기본값 사용 가능)
|
|
5519
|
+
*/
|
|
5520
|
+
get(appId?: string): Promise<GameConfig>;
|
|
5521
|
+
/**
|
|
5522
|
+
* 토글 변경. 보낸 필드만 갱신, 나머지는 보존. PATCH 직후 game-server 캐시 즉시 drop
|
|
5523
|
+
* (NATS publish) — TTL 기다릴 필요 없음.
|
|
5524
|
+
*
|
|
5525
|
+
* @example
|
|
5526
|
+
* await cb.game.config.set(appId, { matchqueue_enabled: true, leaderboard_enabled: true })
|
|
5527
|
+
*/
|
|
5528
|
+
set(appId: string | GameConfigPatch, patch?: GameConfigPatch): Promise<GameConfig>;
|
|
5529
|
+
/**
|
|
5530
|
+
* 단일 기능 활성화 — set() 의 편의 wrapper.
|
|
5531
|
+
*
|
|
5532
|
+
* @example await cb.game.config.enable(appId, "matchqueue")
|
|
5533
|
+
*/
|
|
5534
|
+
enable(appId: string, feature: keyof GameConfig): Promise<GameConfig>;
|
|
5535
|
+
/**
|
|
5536
|
+
* 단일 기능 비활성화.
|
|
5537
|
+
*/
|
|
5538
|
+
disable(appId: string, feature: keyof GameConfig): Promise<GameConfig>;
|
|
5539
|
+
private resolveAppId;
|
|
4063
5540
|
}
|
|
4064
5541
|
|
|
4065
5542
|
/**
|
|
4066
5543
|
* Game Server Types
|
|
4067
5544
|
*/
|
|
5545
|
+
|
|
4068
5546
|
/**
|
|
4069
5547
|
* 게임 룸 설정
|
|
4070
5548
|
*/
|
|
@@ -4077,6 +5555,16 @@ interface GameRoomConfig {
|
|
|
4077
5555
|
tickRate?: number;
|
|
4078
5556
|
/** 최대 플레이어 수 (기본 100) */
|
|
4079
5557
|
maxPlayers?: number;
|
|
5558
|
+
/**
|
|
5559
|
+
* 룸에 attach 할 Lua 스크립트 이름. 콘솔 또는 `POST /v1/game/:appID/scripts` 로 업로드+활성화한
|
|
5560
|
+
* 스크립트만 사용 가능. 미지정 시 server tick + delta 만 흐르고 onTick / onJoin / onAction 등
|
|
5561
|
+
* 사용자 hook 은 호출되지 않는다.
|
|
5562
|
+
*
|
|
5563
|
+
* platform-issue 019e123a (2026-05-10) 해결로 추가된 필드. 이전 버전에서 createRoom config 에
|
|
5564
|
+
* `script_name` 을 직접 넣어 호출하던 워크어라운드는 SDK 가 와이어 페이로드에서 자동으로
|
|
5565
|
+
* `script_name` 으로 매핑해 호환된다.
|
|
5566
|
+
*/
|
|
5567
|
+
scriptName?: string;
|
|
4080
5568
|
/** 커스텀 메타데이터 */
|
|
4081
5569
|
metadata?: Record<string, string>;
|
|
4082
5570
|
}
|
|
@@ -4144,7 +5632,7 @@ interface GameRoomInfo {
|
|
|
4144
5632
|
/**
|
|
4145
5633
|
* 게임 서버 메시지 타입
|
|
4146
5634
|
*/
|
|
4147
|
-
type GameServerMessageType = 'room_created' | 'room_joined' | 'room_left' | 'state' | 'delta' | 'player_event' | 'chat' | 'pong' | 'room_list' | 'error';
|
|
5635
|
+
type GameServerMessageType = 'room_created' | 'room_joined' | 'room_left' | 'state' | 'delta' | 'player_event' | 'chat' | 'pong' | 'room_list' | 'error' | 'room_stale' | 'sync_lost';
|
|
4148
5636
|
/**
|
|
4149
5637
|
* 게임 서버 메시지
|
|
4150
5638
|
* 서버는 모든 필드를 최상위에 보냄 (data 래퍼 없음)
|
|
@@ -4181,6 +5669,30 @@ interface GameServerMessage {
|
|
|
4181
5669
|
timestamp?: number;
|
|
4182
5670
|
client_timestamp?: number;
|
|
4183
5671
|
server_timestamp?: number;
|
|
5672
|
+
phase?: string;
|
|
5673
|
+
feature?: string;
|
|
5674
|
+
script_id?: string;
|
|
5675
|
+
/**
|
|
5676
|
+
* broadcastScriptError 가 onAction 에러 broadcast 시 origin client 식별을 위해
|
|
5677
|
+
* 함께 보내는 필드. sender 본인은 점대점 error 응답으로 받으므로 broadcast 에서
|
|
5678
|
+
* 제외되며, 다른 player 가 누가 액션을 일으켰는지 가시화하는 용도.
|
|
5679
|
+
*/
|
|
5680
|
+
origin_client_id?: string;
|
|
5681
|
+
/**
|
|
5682
|
+
* `code === 'SCRIPT_NOT_FOUND'` 응답에 포함 — createRoom 시 client 가 요청했던
|
|
5683
|
+
* scriptName. 오타/대소문자/스킴 mismatch 디버깅 단서.
|
|
5684
|
+
*/
|
|
5685
|
+
requested?: string;
|
|
5686
|
+
/**
|
|
5687
|
+
* `code === 'SCRIPT_NOT_FOUND'` 응답에 포함 — 현재 app 에 활성(active+version>0)
|
|
5688
|
+
* 상태인 script 이름 목록. 사용자가 후보 중 골라 재시도하기 좋도록 노출.
|
|
5689
|
+
*/
|
|
5690
|
+
available?: string[];
|
|
5691
|
+
reason?: string;
|
|
5692
|
+
script_version?: number;
|
|
5693
|
+
/** room_created 응답 — 서버가 attach 한 lua script 이름 (검증된 값). */
|
|
5694
|
+
script_name?: string;
|
|
5695
|
+
hint?: string;
|
|
4184
5696
|
}
|
|
4185
5697
|
/**
|
|
4186
5698
|
* 플레이어 이벤트
|
|
@@ -4202,11 +5714,74 @@ interface ChatMessage {
|
|
|
4202
5714
|
serverTime: number;
|
|
4203
5715
|
}
|
|
4204
5716
|
/**
|
|
4205
|
-
* 에러
|
|
5717
|
+
* GameErrorCode — game-server 가 surface 하는 에러 코드 literal union.
|
|
5718
|
+
*
|
|
5719
|
+
* - `SCRIPT_NOT_FOUND` : createRoom 의 scriptName 이 미존재/비활성. 응답에 requested + available 동봉.
|
|
5720
|
+
* - `NO_ACTION_HANDLER` : action 의 type 에 대응하는 lua handler 미정의 (`onAction` 또는 `handlers[type]`).
|
|
5721
|
+
* - `FEATURE_DISABLED` : entity/matchqueue/leaderboard 등 opt-in feature 가 OFF. feature 필드로 분기.
|
|
5722
|
+
* - `SCRIPT_ERROR` : lua 실행 중 일반 에러 (분류되지 않은 throw / runtime error).
|
|
5723
|
+
* - `SCRIPT_TIMEOUT` : 사용자 hook code 의 context deadline 초과 (기본 100ms). hook 본문이 느림.
|
|
5724
|
+
* - `SCRIPT_SETUP_OVERRUN` : hook 호출 *전* 플랫폼 측 setup phase (스크립트 로드 + state 직렬화 + API 주입) 가
|
|
5725
|
+
* setupBudget(기본 100ms / onInit 1s) 초과. 일반적으로 룸 state 가 너무 큼.
|
|
5726
|
+
* 해결: state 의 큰 데이터를 entity primitive 로 이전, state.dbg.* 누적 중단.
|
|
5727
|
+
* issue 019e2c29 후 도입 (2026-05-16).
|
|
5728
|
+
* - `RATE_LIMITED` : per-app script rate limit 초과 (`SCRIPT_RATE_PER_SEC`).
|
|
5729
|
+
* - `QUOTA_EXCEEDED` : plan 한도(`script_executions/month`) 초과.
|
|
5730
|
+
* - `TIMEOUT` : SDK 측 sendWithHandler timeout (서버 응답 없음).
|
|
5731
|
+
* - `UNKNOWN` : 분류되지 않은 server 에러 (server 에 신규 코드가 생겼는데 SDK 미반영).
|
|
5732
|
+
*
|
|
5733
|
+
* literal autocomplete 를 유지하면서 미래에 server 가 신규 코드를 추가해도 타입 에러가
|
|
5734
|
+
* 안 나도록 `string & {}` 도 union 에 포함 (TypeScript 'string literal type widening' 우회 패턴).
|
|
5735
|
+
*/
|
|
5736
|
+
type GameErrorCode = 'SCRIPT_NOT_FOUND' | 'NO_ACTION_HANDLER' | 'FEATURE_DISABLED' | 'SCRIPT_ERROR' | 'SCRIPT_TIMEOUT' | 'SCRIPT_SETUP_OVERRUN' | 'RATE_LIMITED' | 'QUOTA_EXCEEDED' | 'TIMEOUT' | 'UNKNOWN' | (string & {});
|
|
5737
|
+
/**
|
|
5738
|
+
* 에러 메시지 — server `error` 메시지의 wire format. SDK 사용자에게는 GameError
|
|
5739
|
+
* 인스턴스(class)로 surface 되지만, 본 인터페이스는 raw payload 매핑 + 타입 추론용.
|
|
4206
5740
|
*/
|
|
4207
5741
|
interface ErrorMessage {
|
|
4208
|
-
code:
|
|
5742
|
+
code: GameErrorCode;
|
|
4209
5743
|
message: string;
|
|
5744
|
+
/**
|
|
5745
|
+
* 서버측 Lua hook 단계 식별자 — 값이 있으면 lua 실행 에러.
|
|
5746
|
+
* "onJoin" | "onLeave" | "onTick" | "onAction".
|
|
5747
|
+
* 일반 transport / protocol 에러는 phase 없음.
|
|
5748
|
+
*/
|
|
5749
|
+
phase?: 'onJoin' | 'onLeave' | 'onTick' | 'onAction' | (string & {});
|
|
5750
|
+
/**
|
|
5751
|
+
* code === "FEATURE_DISABLED" 일 때 비활성 feature 이름.
|
|
5752
|
+
* "entity" | "matchqueue" | "leaderboard" 등 — `toggle_game_features` 와 1:1 매핑.
|
|
5753
|
+
*/
|
|
5754
|
+
feature?: 'entity' | 'matchqueue' | 'leaderboard' | (string & {});
|
|
5755
|
+
roomId?: string;
|
|
5756
|
+
scriptId?: string;
|
|
5757
|
+
/**
|
|
5758
|
+
* broadcast 형태의 onAction 에러일 때 액션을 일으킨 client. 본인이 아닌 다른
|
|
5759
|
+
* player 에서 발생한 에러를 SDK 사용자가 식별/필터링하기 위한 필드.
|
|
5760
|
+
*/
|
|
5761
|
+
originClientId?: string;
|
|
5762
|
+
/** code === 'SCRIPT_NOT_FOUND' 시 client 가 요청했던 scriptName. */
|
|
5763
|
+
requested?: string;
|
|
5764
|
+
/** code === 'SCRIPT_NOT_FOUND' 시 후보로 노출되는 active script 이름 목록. */
|
|
5765
|
+
available?: string[];
|
|
5766
|
+
}
|
|
5767
|
+
/**
|
|
5768
|
+
* CreateRoomResult — `createRoomDetailed` 의 반환 타입. createRoom 의 호환성 보존을
|
|
5769
|
+
* 위해 별도 메서드로 노출 (createRoom 은 기존대로 `Promise<GameState>` 유지).
|
|
5770
|
+
*
|
|
5771
|
+
* server 의 `room_created` 응답에는 이전엔 room_id + initial_state 만 들어왔으나
|
|
5772
|
+
* platform-issue 019e21dd (NJB regression, 2026-05-13) 의 회귀 가드로 attach 된 lua
|
|
5773
|
+
* script 의 이름/버전이 함께 echo 된다 — client 가 attach 검증 후 mismatch 시
|
|
5774
|
+
* 즉시 throw 할 수 있도록.
|
|
5775
|
+
*/
|
|
5776
|
+
interface CreateRoomResult {
|
|
5777
|
+
/** 서버 생성 룸 UUID (config.roomId 와 항상 같지는 않음 — 서버가 fresh UUID 생성 가능). */
|
|
5778
|
+
roomId: string;
|
|
5779
|
+
/** 초기 상태 스냅샷. 기존 `createRoom` 의 resolve 값과 동일 shape. */
|
|
5780
|
+
state: GameState;
|
|
5781
|
+
/** Attached lua script 이름. config.scriptName 미지정 시 undefined. */
|
|
5782
|
+
scriptName?: string;
|
|
5783
|
+
/** Attached lua script 의 active version (Manager.GetMeta.ActiveVersion). */
|
|
5784
|
+
scriptVersion?: number;
|
|
4210
5785
|
}
|
|
4211
5786
|
/**
|
|
4212
5787
|
* Ping/Pong 응답
|
|
@@ -4215,13 +5790,38 @@ interface PongMessage {
|
|
|
4215
5790
|
clientTimestamp: number;
|
|
4216
5791
|
serverTimestamp: number;
|
|
4217
5792
|
}
|
|
5793
|
+
/**
|
|
5794
|
+
* room_stale 이벤트 페이로드.
|
|
5795
|
+
* 서버 측 hot reload(active_version bump) 등으로 현재 room 의 lua 핸들러가
|
|
5796
|
+
* 갱신됐지만 기존 state 가 보존되어 사용자 lua 의 `state.initialized` 같은
|
|
5797
|
+
* 가드로 `handlers.init` 가 재실행되지 않을 수 있을 때 발화된다.
|
|
5798
|
+
*
|
|
5799
|
+
* 권장 처리: leaveRoom → joinRoom 또는 새 roomId 로 createRoom.
|
|
5800
|
+
* 자동 disconnect 는 SDK 가 하지 않는다 — 정책은 사용자 게임이 결정.
|
|
5801
|
+
*/
|
|
5802
|
+
interface RoomStaleMessage {
|
|
5803
|
+
/** "script_reloaded" | "schema_changed" | "manual" 등 — 자유 문자열 */
|
|
5804
|
+
reason: string;
|
|
5805
|
+
roomId: string;
|
|
5806
|
+
/** appID:scriptName 형태. 없을 수 있음. */
|
|
5807
|
+
scriptId?: string;
|
|
5808
|
+
/** 새 active_version. 없을 수 있음. */
|
|
5809
|
+
scriptVersion?: number;
|
|
5810
|
+
serverTime: number;
|
|
5811
|
+
}
|
|
4218
5812
|
/**
|
|
4219
5813
|
* 게임 클라이언트 이벤트 핸들러
|
|
4220
5814
|
*/
|
|
4221
5815
|
interface GameEventHandlers {
|
|
4222
5816
|
onConnect?: () => void;
|
|
4223
5817
|
onDisconnect?: (event: CloseEvent) => void;
|
|
4224
|
-
|
|
5818
|
+
/**
|
|
5819
|
+
* - `Event` : WebSocket transport-level 에러 (브라우저 WS API 의 onerror).
|
|
5820
|
+
* - `GameError` : server 가 surface 한 `error` 메시지를 wrap 한 인스턴스 — `.code`,
|
|
5821
|
+
* `.phase`, `.feature`, `.originClientId`, `.requested`, `.available` 모두 노출.
|
|
5822
|
+
* `instanceof GameError` 로 분기하여 UI 표시 차별화 가능.
|
|
5823
|
+
*/
|
|
5824
|
+
onError?: (error: Event | GameError) => void;
|
|
4225
5825
|
onStateUpdate?: (state: GameState) => void;
|
|
4226
5826
|
onDelta?: (delta: GameDelta) => void;
|
|
4227
5827
|
onAction?: (action: {
|
|
@@ -4234,6 +5834,22 @@ interface GameEventHandlers {
|
|
|
4234
5834
|
onPlayerLeft?: (player: GamePlayer) => void;
|
|
4235
5835
|
onChat?: (message: ChatMessage) => void;
|
|
4236
5836
|
onPong?: (pong: PongMessage) => void;
|
|
5837
|
+
/**
|
|
5838
|
+
* 커스텀 broadcast 메시지 핸들러 — 서버 Lua 의 `room.broadcast(data)` / `room.send_to(clientId, data)`
|
|
5839
|
+
* 로 보낸, SDK 가 모르는 `type` 의 메시지가 여기로 흘러온다. `delta`/`state`/`chat`/`player_event`/`error`
|
|
5840
|
+
* 같은 표준 타입은 전용 핸들러로 가고 여기엔 오지 않는다.
|
|
5841
|
+
*
|
|
5842
|
+
* 게임별 커스텀 프로토콜(예: `{ type: "chunk", ... }`, `{ type: "turn_played", ... }`)은
|
|
5843
|
+
* 이 핸들러에서 `msg.type` 으로 분기한다.
|
|
5844
|
+
*/
|
|
5845
|
+
onMessage?: (msg: Record<string, unknown> & {
|
|
5846
|
+
type: string;
|
|
5847
|
+
}) => void;
|
|
5848
|
+
/**
|
|
5849
|
+
* 서버가 room 을 stale 로 표시했을 때 발화. 자세한 사양은 `RoomStaleMessage` 참고.
|
|
5850
|
+
* 미설정 시 SDK 는 console.warn 으로 가시화만 하고 자동 동작은 하지 않는다.
|
|
5851
|
+
*/
|
|
5852
|
+
onRoomStale?: (msg: RoomStaleMessage) => void;
|
|
4237
5853
|
}
|
|
4238
5854
|
/**
|
|
4239
5855
|
* 게임 클라이언트 설정
|
|
@@ -4243,8 +5859,8 @@ interface GameClientConfig {
|
|
|
4243
5859
|
gameServerUrl?: string;
|
|
4244
5860
|
/** 앱 ID */
|
|
4245
5861
|
appId?: string;
|
|
4246
|
-
/**
|
|
4247
|
-
|
|
5862
|
+
/** Public Key */
|
|
5863
|
+
publicKey?: string;
|
|
4248
5864
|
/** 액세스 토큰 */
|
|
4249
5865
|
accessToken?: string;
|
|
4250
5866
|
/** 클라이언트 ID */
|
|
@@ -4374,7 +5990,202 @@ interface LobbyInvite {
|
|
|
4374
5990
|
createdAt: string;
|
|
4375
5991
|
expiresAt: string;
|
|
4376
5992
|
}
|
|
5993
|
+
interface PartyInfo {
|
|
5994
|
+
id: string;
|
|
5995
|
+
app_id: string;
|
|
5996
|
+
leader_id: string;
|
|
5997
|
+
members: PartyMember[];
|
|
5998
|
+
max_size: number;
|
|
5999
|
+
settings?: Record<string, string>;
|
|
6000
|
+
state: string;
|
|
6001
|
+
created_at: string;
|
|
6002
|
+
updated_at: string;
|
|
6003
|
+
}
|
|
6004
|
+
interface PartyMember {
|
|
6005
|
+
player_id: string;
|
|
6006
|
+
display_name?: string;
|
|
6007
|
+
ready: boolean;
|
|
6008
|
+
role: string;
|
|
6009
|
+
joined_at: string;
|
|
6010
|
+
metadata?: Record<string, string>;
|
|
6011
|
+
}
|
|
6012
|
+
interface PartyInvite {
|
|
6013
|
+
invite_id: string;
|
|
6014
|
+
party_id: string;
|
|
6015
|
+
inviter_id: string;
|
|
6016
|
+
status: string;
|
|
6017
|
+
expires_at: string;
|
|
6018
|
+
}
|
|
6019
|
+
interface SpectatorInfo {
|
|
6020
|
+
id: string;
|
|
6021
|
+
user_id?: string;
|
|
6022
|
+
room_id: string;
|
|
6023
|
+
mode: "free" | "follow" | "director";
|
|
6024
|
+
target_id?: string;
|
|
6025
|
+
joined_at: string;
|
|
6026
|
+
delay_seconds?: number;
|
|
6027
|
+
}
|
|
6028
|
+
interface SpectatorState {
|
|
6029
|
+
room_id: string;
|
|
6030
|
+
tick: number;
|
|
6031
|
+
timestamp: number;
|
|
6032
|
+
players: SpectatorPlayerState[];
|
|
6033
|
+
game_state?: Record<string, unknown>;
|
|
6034
|
+
events?: Record<string, unknown>[];
|
|
6035
|
+
}
|
|
6036
|
+
interface SpectatorPlayerState {
|
|
6037
|
+
id: string;
|
|
6038
|
+
display_name: string;
|
|
6039
|
+
position: {
|
|
6040
|
+
x: number;
|
|
6041
|
+
y: number;
|
|
6042
|
+
z: number;
|
|
6043
|
+
};
|
|
6044
|
+
health?: number;
|
|
6045
|
+
score?: number;
|
|
6046
|
+
is_alive: boolean;
|
|
6047
|
+
}
|
|
6048
|
+
interface LeaderboardEntry {
|
|
6049
|
+
rank: number;
|
|
6050
|
+
player_id: string;
|
|
6051
|
+
display_name: string;
|
|
6052
|
+
rating: number;
|
|
6053
|
+
wins: number;
|
|
6054
|
+
losses: number;
|
|
6055
|
+
games_played: number;
|
|
6056
|
+
win_rate: number;
|
|
6057
|
+
tier: string;
|
|
6058
|
+
division: number;
|
|
6059
|
+
updated_at: string;
|
|
6060
|
+
}
|
|
6061
|
+
interface PlayerStats {
|
|
6062
|
+
player_id: string;
|
|
6063
|
+
rating: number;
|
|
6064
|
+
wins: number;
|
|
6065
|
+
losses: number;
|
|
6066
|
+
games_played: number;
|
|
6067
|
+
win_rate: number;
|
|
6068
|
+
tier: string;
|
|
6069
|
+
division: number;
|
|
6070
|
+
match_history: MatchResult[];
|
|
6071
|
+
}
|
|
6072
|
+
interface MatchResult {
|
|
6073
|
+
match_id: string;
|
|
6074
|
+
result: "win" | "loss" | "draw";
|
|
6075
|
+
rating_change: number;
|
|
6076
|
+
played_at: string;
|
|
6077
|
+
}
|
|
6078
|
+
interface VoiceChannel {
|
|
6079
|
+
id: string;
|
|
6080
|
+
room_id: string;
|
|
6081
|
+
name: string;
|
|
6082
|
+
type: "global" | "team" | "proximity" | "private" | "spectator";
|
|
6083
|
+
team_id?: string;
|
|
6084
|
+
max_members: number;
|
|
6085
|
+
members: Record<string, VoiceMember>;
|
|
6086
|
+
webrtc_room_id: string;
|
|
6087
|
+
created_at: string;
|
|
6088
|
+
}
|
|
6089
|
+
interface VoiceMember {
|
|
6090
|
+
player_id: string;
|
|
6091
|
+
display_name: string;
|
|
6092
|
+
joined_at: string;
|
|
6093
|
+
webrtc_url?: string;
|
|
6094
|
+
}
|
|
6095
|
+
interface ReplayInfo {
|
|
6096
|
+
id: string;
|
|
6097
|
+
room_id: string;
|
|
6098
|
+
game_type: string;
|
|
6099
|
+
map_name?: string;
|
|
6100
|
+
duration: number;
|
|
6101
|
+
tick_rate: number;
|
|
6102
|
+
start_tick: number;
|
|
6103
|
+
end_tick: number;
|
|
6104
|
+
players: ReplayPlayerInfo[];
|
|
6105
|
+
file_size: number;
|
|
6106
|
+
created_at: string;
|
|
6107
|
+
schema_version?: string;
|
|
6108
|
+
}
|
|
6109
|
+
interface ReplayPlayerInfo {
|
|
6110
|
+
player_id: string;
|
|
6111
|
+
display_name: string;
|
|
6112
|
+
team_id?: string;
|
|
6113
|
+
}
|
|
6114
|
+
interface ReplayHighlight {
|
|
6115
|
+
tick: number;
|
|
6116
|
+
type: string;
|
|
6117
|
+
actor_id: string;
|
|
6118
|
+
score: number;
|
|
6119
|
+
}
|
|
6120
|
+
/** Matchqueue ticket — attributes 는 free-form (rating/region 등 자유). */
|
|
6121
|
+
interface MatchqueueTicket {
|
|
6122
|
+
ticket_id: string;
|
|
6123
|
+
queue_key: string;
|
|
6124
|
+
attributes?: Record<string, unknown>;
|
|
6125
|
+
enqueued_at: string;
|
|
6126
|
+
ttl_at?: string;
|
|
6127
|
+
}
|
|
6128
|
+
interface MatchqueueListResponse {
|
|
6129
|
+
tickets: MatchqueueTicket[];
|
|
6130
|
+
count: number;
|
|
6131
|
+
}
|
|
6132
|
+
/**
|
|
6133
|
+
* v3.0 Leaderboard score entry — score 는 사용자 Lua 의 ELO/MMR 공식 결과.
|
|
6134
|
+
* rank 는 GetTop/GetRank 시점에만 채워진다 (저장은 안 됨).
|
|
6135
|
+
*
|
|
6136
|
+
* v2.x 의 LeaderboardEntry (player_id/rating/tier/wins/losses 등) 와 다른 시그니처라
|
|
6137
|
+
* 새 이름 사용. v2 LeaderboardEntry 는 v3.0 에서 deprecated.
|
|
6138
|
+
*/
|
|
6139
|
+
interface LeaderboardScoreEntry {
|
|
6140
|
+
member: string;
|
|
6141
|
+
score: number;
|
|
6142
|
+
updated_at: string;
|
|
6143
|
+
rank?: number;
|
|
6144
|
+
}
|
|
6145
|
+
interface LeaderboardListResponse {
|
|
6146
|
+
entries: LeaderboardScoreEntry[];
|
|
6147
|
+
count: number;
|
|
6148
|
+
}
|
|
6149
|
+
/** Lua script — 메타 + 단일 버전. */
|
|
6150
|
+
interface ScriptMeta {
|
|
6151
|
+
app_id: string;
|
|
6152
|
+
name: string;
|
|
6153
|
+
active_version: number;
|
|
6154
|
+
latest_version: number;
|
|
6155
|
+
status: "active" | "staging" | "disabled";
|
|
6156
|
+
updated_at: string;
|
|
6157
|
+
}
|
|
6158
|
+
interface ScriptVersion {
|
|
6159
|
+
app_id: string;
|
|
6160
|
+
name: string;
|
|
6161
|
+
version: number;
|
|
6162
|
+
hash: string;
|
|
6163
|
+
code: string;
|
|
6164
|
+
uploaded_at: string;
|
|
6165
|
+
uploaded_by?: string;
|
|
6166
|
+
}
|
|
6167
|
+
interface ScriptListResponse {
|
|
6168
|
+
scripts: ScriptMeta[];
|
|
6169
|
+
count: number;
|
|
6170
|
+
}
|
|
6171
|
+
interface ScriptVersionListResponse {
|
|
6172
|
+
versions: ScriptVersion[];
|
|
6173
|
+
count: number;
|
|
6174
|
+
}
|
|
6175
|
+
interface ScriptDetailResponse {
|
|
6176
|
+
meta: ScriptMeta;
|
|
6177
|
+
active?: ScriptVersion;
|
|
6178
|
+
}
|
|
4377
6179
|
|
|
6180
|
+
/**
|
|
6181
|
+
* 게임 서버 `create_room` 메시지 페이로드는 snake_case 필드를 기대한다 (Go 핸들러 JSON 태그).
|
|
6182
|
+
* SDK 의 GameRoomConfig 는 camelCase 이므로, scriptName / tickRate / maxPlayers / roomId /
|
|
6183
|
+
* categoryId 를 와이어 형식으로 매핑한다. 추가 필드(metadata)는 그대로 통과.
|
|
6184
|
+
*
|
|
6185
|
+
* platform-issue 019e123a (2026-05-10) 의 회귀 가드 — scriptName 누락 시 RoomConfig.ScriptID
|
|
6186
|
+
* 가 비어 onTick / onPlayerJoin 등이 silent skip 되던 문제 차단.
|
|
6187
|
+
*/
|
|
6188
|
+
declare function toCreateRoomWire(config: GameRoomConfig): Record<string, unknown>;
|
|
4378
6189
|
/**
|
|
4379
6190
|
* 게임 룸 클라이언트
|
|
4380
6191
|
* WebSocket 연결을 관리하고 게임 상태를 동기화합니다.
|
|
@@ -4389,6 +6200,8 @@ declare class GameRoom {
|
|
|
4389
6200
|
private actionSequence;
|
|
4390
6201
|
private _roomId;
|
|
4391
6202
|
private _state;
|
|
6203
|
+
private _scriptName;
|
|
6204
|
+
private _scriptVersion;
|
|
4392
6205
|
private _isConnected;
|
|
4393
6206
|
constructor(config: GameClientConfig);
|
|
4394
6207
|
/**
|
|
@@ -4416,9 +6229,29 @@ declare class GameRoom {
|
|
|
4416
6229
|
*/
|
|
4417
6230
|
disconnect(): void;
|
|
4418
6231
|
/**
|
|
4419
|
-
* 룸 생성
|
|
6232
|
+
* 룸 생성 (호환 시그니처) — 초기 상태만 반환.
|
|
6233
|
+
*
|
|
6234
|
+
* Attached script 의 검증(scriptName/scriptVersion echo 검사) 이 필요하면
|
|
6235
|
+
* 인스턴스 getter (`room.scriptName`, `room.scriptVersion`) 를 함께 사용하거나,
|
|
6236
|
+
* 메타까지 한 번에 받고 싶다면 {@link createRoomDetailed} 를 사용한다.
|
|
6237
|
+
*
|
|
6238
|
+
* 미존재/비활성 scriptName 은 서버가 `SCRIPT_NOT_FOUND` 로 즉시 reject 한다.
|
|
6239
|
+
* reject 값은 `GameError` 인스턴스이며 `.code`/`.requested`/`.available` 로 분기 가능.
|
|
4420
6240
|
*/
|
|
4421
6241
|
createRoom(config?: GameRoomConfig): Promise<GameState>;
|
|
6242
|
+
/**
|
|
6243
|
+
* 룸 생성 + 메타 — server 가 attach 한 lua script 이름/버전을 함께 반환.
|
|
6244
|
+
*
|
|
6245
|
+
* @example
|
|
6246
|
+
* const { state, scriptName, scriptVersion } = await room.createRoomDetailed({ scriptName: 'njb-main' })
|
|
6247
|
+
* if (scriptName !== 'njb-main') throw new Error('script mismatch')
|
|
6248
|
+
* console.log('attached', scriptName, 'v', scriptVersion)
|
|
6249
|
+
*/
|
|
6250
|
+
createRoomDetailed(config?: GameRoomConfig): Promise<CreateRoomResult>;
|
|
6251
|
+
/** Attached lua script 이름 — createRoom 이후 server 가 echo 한 값. */
|
|
6252
|
+
get scriptName(): string | null;
|
|
6253
|
+
/** Attached lua script 의 active version. */
|
|
6254
|
+
get scriptVersion(): number | null;
|
|
4422
6255
|
/**
|
|
4423
6256
|
* 룸 참가
|
|
4424
6257
|
*/
|
|
@@ -4467,9 +6300,18 @@ declare class GameAPI {
|
|
|
4467
6300
|
private http;
|
|
4468
6301
|
private gameServerUrl;
|
|
4469
6302
|
private appId?;
|
|
6303
|
+
/**
|
|
6304
|
+
* 게임 기능 opt-in 토글 (v3.1+). `cb.game.config.get/set` 으로 호출.
|
|
6305
|
+
* 자세한 내용은 [GameConfigAPI] 참고.
|
|
6306
|
+
*/
|
|
6307
|
+
readonly config: GameConfigAPI;
|
|
4470
6308
|
constructor(http: HttpClient, gameServerUrl?: string, appId?: string);
|
|
4471
6309
|
/**
|
|
4472
|
-
* 게임 룸 클라이언트
|
|
6310
|
+
* 게임 룸 클라이언트 생성.
|
|
6311
|
+
*
|
|
6312
|
+
* `appId` 는 `createClient({ appId })` 로 호출별 지정하거나, 생략 시 `new ConnectBase({ appId })`
|
|
6313
|
+
* 의 전역 값을 사용한다. 둘 다 없으면 `connect()` 가 명확한 에러로 reject 한다 — 빈 appId 로
|
|
6314
|
+
* `/v1/game//ws` 에 붙어 server 측이 SCRIPT_NOT_FOUND 로 깨지던 silent 회귀 차단 (NJB 019e2210).
|
|
4473
6315
|
*/
|
|
4474
6316
|
createClient(config: Omit<GameClientConfig, 'gameServerUrl'>): GameRoom;
|
|
4475
6317
|
/**
|
|
@@ -4482,98 +6324,138 @@ declare class GameAPI {
|
|
|
4482
6324
|
getRoom(roomId: string): Promise<GameRoomInfo>;
|
|
4483
6325
|
/**
|
|
4484
6326
|
* 룸 생성 (HTTP, gRPC 대안)
|
|
6327
|
+
*
|
|
6328
|
+
* @deprecated 현재 SDK public 경로로 노출되지 않습니다. 콘솔(admin) 에서 진행하거나 백엔드 public 경로 오픈을 요청하세요.
|
|
4485
6329
|
*/
|
|
4486
|
-
createRoom(
|
|
6330
|
+
createRoom(_appId: string, _config?: GameRoomConfig): Promise<GameRoomInfo>;
|
|
4487
6331
|
/**
|
|
4488
6332
|
* 룸 삭제 (HTTP)
|
|
4489
|
-
|
|
4490
|
-
|
|
6333
|
+
*
|
|
6334
|
+
* @deprecated 현재 SDK public 경로로 노출되지 않습니다. 콘솔(admin) 에서 진행하거나 백엔드 public 경로 오픈을 요청하세요.
|
|
6335
|
+
*/
|
|
6336
|
+
deleteRoom(_roomId: string): Promise<void>;
|
|
6337
|
+
joinSpectator(roomId: string, playerId: string): Promise<SpectatorInfo>;
|
|
6338
|
+
leaveSpectator(roomId: string, spectatorId: string): Promise<void>;
|
|
6339
|
+
getSpectators(roomId: string): Promise<SpectatorInfo[]>;
|
|
6340
|
+
joinVoiceChannel(roomId: string, playerId: string): Promise<VoiceChannel>;
|
|
6341
|
+
leaveVoiceChannel(roomId: string, playerId: string): Promise<void>;
|
|
6342
|
+
setMute(playerId: string, muted: boolean): Promise<void>;
|
|
6343
|
+
listReplays(roomId?: string): Promise<ReplayInfo[]>;
|
|
6344
|
+
getReplay(replayId: string): Promise<ReplayInfo>;
|
|
6345
|
+
downloadReplay(replayId: string): Promise<ArrayBuffer>;
|
|
6346
|
+
getReplayHighlights(replayId: string): Promise<ReplayHighlight[]>;
|
|
6347
|
+
private getHeaders;
|
|
6348
|
+
private gameFetch;
|
|
4491
6349
|
/**
|
|
4492
|
-
*
|
|
6350
|
+
* matchqueue 에 ticket 등록.
|
|
6351
|
+
*
|
|
6352
|
+
* @example
|
|
6353
|
+
* await cb.game.enqueueMatch(appId, "ranked", userId, {
|
|
6354
|
+
* rating: 1500, region: "asia", team_size: 5,
|
|
6355
|
+
* }, 60)
|
|
6356
|
+
*
|
|
6357
|
+
* @constraints appId/queueKey/ticketId 는 [a-zA-Z0-9_-] 만 허용. ttlSec=0 이면 만료 없음.
|
|
4493
6358
|
*/
|
|
4494
|
-
|
|
6359
|
+
enqueueMatch(appId: string, queueKey: string, ticketId: string, attributes?: Record<string, unknown>, ttlSec?: number): Promise<MatchqueueTicket>;
|
|
4495
6360
|
/**
|
|
4496
|
-
*
|
|
6361
|
+
* matchqueue 의 모든 ticket 목록 반환. 사용자 Lua 가 매칭 후보 선별에 사용.
|
|
6362
|
+
*
|
|
6363
|
+
* @example const tickets = await cb.game.listMatchqueue(appId, "ranked")
|
|
4497
6364
|
*/
|
|
4498
|
-
|
|
6365
|
+
listMatchqueue(appId: string, queueKey: string): Promise<MatchqueueListResponse>;
|
|
4499
6366
|
/**
|
|
4500
|
-
*
|
|
6367
|
+
* 매칭 큐에서 ticket 제거 (예: 사용자가 매칭 취소).
|
|
4501
6368
|
*/
|
|
4502
|
-
|
|
4503
|
-
ticketId?: string;
|
|
4504
|
-
playerId?: string;
|
|
4505
|
-
}): Promise<MatchmakingTicket>;
|
|
6369
|
+
cancelMatch(appId: string, queueKey: string, ticketId: string): Promise<void>;
|
|
4506
6370
|
/**
|
|
4507
|
-
*
|
|
6371
|
+
* leaderboard 점수 기록. mode="set" (기본, overwrite) 또는 "incr" (증감).
|
|
6372
|
+
*
|
|
6373
|
+
* @example
|
|
6374
|
+
* await cb.game.submitScore(appId, "global_elo", userId, 32, "incr")
|
|
4508
6375
|
*/
|
|
4509
|
-
|
|
6376
|
+
submitScore(appId: string, leaderboardKey: string, member: string, score: number, mode?: "set" | "incr"): Promise<LeaderboardScoreEntry>;
|
|
4510
6377
|
/**
|
|
4511
|
-
*
|
|
6378
|
+
* 상위 n 명 조회 (기본 10).
|
|
4512
6379
|
*/
|
|
4513
|
-
|
|
6380
|
+
getTopScores(appId: string, leaderboardKey: string, n?: number): Promise<LeaderboardListResponse>;
|
|
4514
6381
|
/**
|
|
4515
|
-
*
|
|
6382
|
+
* 단일 member 의 rank + score.
|
|
4516
6383
|
*/
|
|
4517
|
-
|
|
6384
|
+
getMemberRank(appId: string, leaderboardKey: string, member: string): Promise<LeaderboardScoreEntry>;
|
|
4518
6385
|
/**
|
|
4519
|
-
*
|
|
6386
|
+
* member 주변 (위 above 명 + 본인 + 아래 below 명) 조회.
|
|
4520
6387
|
*/
|
|
4521
|
-
|
|
4522
|
-
lobbyId: string;
|
|
4523
|
-
}>;
|
|
6388
|
+
getRankAround(appId: string, leaderboardKey: string, member: string, above?: number, below?: number): Promise<LeaderboardListResponse>;
|
|
4524
6389
|
/**
|
|
4525
|
-
*
|
|
6390
|
+
* leaderboard 전체 reset (시즌 종료 시).
|
|
4526
6391
|
*/
|
|
4527
|
-
|
|
6392
|
+
resetLeaderboard(appId: string, leaderboardKey: string): Promise<void>;
|
|
4528
6393
|
/**
|
|
4529
|
-
*
|
|
6394
|
+
* 특정 member 만 제거 (계정 삭제 등).
|
|
4530
6395
|
*/
|
|
4531
|
-
|
|
6396
|
+
removeFromLeaderboard(appId: string, leaderboardKey: string, member: string): Promise<void>;
|
|
4532
6397
|
/**
|
|
4533
|
-
*
|
|
6398
|
+
* 새 스크립트 버전 업로드 (latest+1). idempotent — 동일 hash 면 새 버전 만들지 않음.
|
|
6399
|
+
*
|
|
6400
|
+
* @example
|
|
6401
|
+
* await cb.game.uploadScript(appId, "ranked_br", fs.readFileSync("./ranked.lua", "utf-8"))
|
|
4534
6402
|
*/
|
|
4535
|
-
|
|
4536
|
-
roomId: string;
|
|
4537
|
-
}>;
|
|
6403
|
+
uploadScript(appId: string, name: string, code: string): Promise<ScriptVersion>;
|
|
4538
6404
|
/**
|
|
4539
|
-
*
|
|
6405
|
+
* app 의 모든 스크립트 메타데이터 목록.
|
|
4540
6406
|
*/
|
|
4541
|
-
|
|
6407
|
+
listScripts(appId: string): Promise<ScriptListResponse>;
|
|
4542
6408
|
/**
|
|
4543
|
-
*
|
|
6409
|
+
* 단일 스크립트 메타 + active version code.
|
|
4544
6410
|
*/
|
|
4545
|
-
|
|
6411
|
+
getScript(appId: string, name: string): Promise<ScriptDetailResponse>;
|
|
4546
6412
|
/**
|
|
4547
|
-
*
|
|
6413
|
+
* 단일 스크립트의 모든 버전 이력.
|
|
4548
6414
|
*/
|
|
4549
|
-
|
|
6415
|
+
listScriptVersions(appId: string, name: string): Promise<ScriptVersionListResponse>;
|
|
4550
6416
|
/**
|
|
4551
|
-
*
|
|
6417
|
+
* 특정 버전 활성화 (hot reload 자동). version=0 또는 미지정 → latest 활성화.
|
|
4552
6418
|
*/
|
|
4553
|
-
|
|
6419
|
+
activateScript(appId: string, name: string, version?: number): Promise<ScriptMeta>;
|
|
4554
6420
|
/**
|
|
4555
|
-
*
|
|
6421
|
+
* 직전 active 버전으로 롤백 (hot reload 자동).
|
|
4556
6422
|
*/
|
|
4557
|
-
|
|
4558
|
-
lobbyId: string;
|
|
4559
|
-
}>;
|
|
6423
|
+
rollbackScript(appId: string, name: string): Promise<ScriptMeta>;
|
|
4560
6424
|
/**
|
|
4561
|
-
*
|
|
6425
|
+
* 스크립트 비활성화 (status=disabled, ActiveVersion=0). 코드/모든 버전은 보존되며
|
|
6426
|
+
* 이후 {@link activateScript} 로 재활성화할 수 있다.
|
|
6427
|
+
*
|
|
6428
|
+
* v3.14.0 BREAKING — 이전엔 `disableScript` 가 `DELETE /scripts/:name` 을 호출해
|
|
6429
|
+
* Disable 매핑이었으나, server 측 `DELETE` 가 hard-delete 로 분리됨에 따라
|
|
6430
|
+
* `POST /scripts/:name/deactivate` 로 endpoint 이동. 메서드명도 의도 명확화 위해 rename.
|
|
4562
6431
|
*/
|
|
4563
|
-
|
|
6432
|
+
deactivateScript(appId: string, name: string): Promise<void>;
|
|
4564
6433
|
/**
|
|
4565
|
-
*
|
|
6434
|
+
* 스크립트 영구 삭제 (hard-delete) — 메타 + 모든 버전 영구 제거. 복구 불가.
|
|
6435
|
+
*
|
|
6436
|
+
* 잘못 업로드된 스크립트나 더 이상 사용하지 않는 슬롯을 정리할 때 사용. 단순 비활성화는
|
|
6437
|
+
* {@link deactivateScript} 를 사용한다 (코드 보존, 재활성화 가능).
|
|
6438
|
+
*
|
|
6439
|
+
* v3.14.0 신규 — server 측 `DELETE /scripts/:name` 의 의미가 Disable → hard-delete 로
|
|
6440
|
+
* 변경된 것에 대응.
|
|
4566
6441
|
*/
|
|
4567
|
-
|
|
4568
|
-
private getHeaders;
|
|
6442
|
+
deleteScript(appId: string, name: string): Promise<void>;
|
|
4569
6443
|
}
|
|
4570
6444
|
|
|
4571
|
-
interface
|
|
6445
|
+
interface AdsenseConnectionInfo {
|
|
6446
|
+
is_connected: boolean;
|
|
6447
|
+
email?: string;
|
|
6448
|
+
account_id?: string;
|
|
6449
|
+
}
|
|
6450
|
+
interface AdmobConnectionInfo {
|
|
4572
6451
|
is_connected: boolean;
|
|
4573
6452
|
email?: string;
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
6453
|
+
account_id?: string;
|
|
6454
|
+
publisher_id?: string;
|
|
6455
|
+
}
|
|
6456
|
+
interface GoogleConnectionStatus {
|
|
6457
|
+
adsense: AdsenseConnectionInfo;
|
|
6458
|
+
admob: AdmobConnectionInfo;
|
|
4577
6459
|
}
|
|
4578
6460
|
interface AdReportSummary {
|
|
4579
6461
|
total_earnings: number;
|
|
@@ -4625,15 +6507,18 @@ declare class AdsAPI {
|
|
|
4625
6507
|
*/
|
|
4626
6508
|
private getPublicPrefix;
|
|
4627
6509
|
/**
|
|
4628
|
-
* AdSense 연결 상태 확인
|
|
6510
|
+
* AdSense / AdMob 연결 상태 확인 (중첩 구조)
|
|
4629
6511
|
*
|
|
4630
|
-
* @returns
|
|
6512
|
+
* @returns `{ adsense, admob }` 각각 연결 상태·이메일·계정 ID
|
|
4631
6513
|
*
|
|
4632
6514
|
* @example
|
|
4633
6515
|
* ```typescript
|
|
4634
6516
|
* const status = await cb.ads.getConnectionStatus()
|
|
4635
|
-
* if (status.is_connected) {
|
|
4636
|
-
* console.log('연결됨:', status.email)
|
|
6517
|
+
* if (status.adsense.is_connected) {
|
|
6518
|
+
* console.log('AdSense 연결됨:', status.adsense.email, status.adsense.account_id)
|
|
6519
|
+
* }
|
|
6520
|
+
* if (status.admob.is_connected) {
|
|
6521
|
+
* console.log('AdMob 연결됨:', status.admob.account_id, status.admob.publisher_id)
|
|
4637
6522
|
* }
|
|
4638
6523
|
* ```
|
|
4639
6524
|
*/
|
|
@@ -4708,7 +6593,7 @@ declare class AdsAPI {
|
|
|
4708
6593
|
* ```typescript
|
|
4709
6594
|
* import ConnectBase from 'connectbase-client'
|
|
4710
6595
|
*
|
|
4711
|
-
* const cb = new ConnectBase({
|
|
6596
|
+
* const cb = new ConnectBase({ publicKey: 'your-public-key' })
|
|
4712
6597
|
*
|
|
4713
6598
|
* // 플랫폼 감지
|
|
4714
6599
|
* const platform = cb.native.getPlatform() // 'web' | 'mobile' | 'desktop'
|
|
@@ -5166,7 +7051,47 @@ interface CreateDocumentRequest {
|
|
|
5166
7051
|
source_type?: 'file' | 'text' | 'url';
|
|
5167
7052
|
content?: string;
|
|
5168
7053
|
source_url?: string;
|
|
7054
|
+
/**
|
|
7055
|
+
* source_type='file' 일 때 사용. 바이너리를 base64 로 인코딩해서 보낸다.
|
|
7056
|
+
* 서버가 PDF / DOCX / text 류를 자동으로 텍스트 추출 → 청킹·인덱싱 한다.
|
|
7057
|
+
* 파일 50MB 상한, 이미지 OCR / 스캔 PDF 는 미지원.
|
|
7058
|
+
* 일반적으로는 [`KnowledgeAPI.addDocumentFromFile`](../api/knowledge.ts) 헬퍼를 사용하면
|
|
7059
|
+
* Blob / File 로 직접 넘길 수 있다.
|
|
7060
|
+
*/
|
|
7061
|
+
file_content?: string;
|
|
7062
|
+
/**
|
|
7063
|
+
* file_content 의 MIME (예: 'application/pdf',
|
|
7064
|
+
* 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'text/markdown').
|
|
7065
|
+
* 비어있으면 서버가 매직 바이트로 자동 추정 — 정확도를 위해 명시 권장.
|
|
7066
|
+
*/
|
|
7067
|
+
mime_type?: string;
|
|
7068
|
+
/**
|
|
7069
|
+
* 클라이언트가 지정하는 멱등(upsert) 키. 같은 지식 베이스 안에서 유일하다.
|
|
7070
|
+
* 같은 `external_id` 로 다시 `addDocument` 하면 새 문서를 만들지 않고 기존 문서를
|
|
7071
|
+
* 교체(update)한다 — `listDocuments` 로 기존 문서를 찾아 지울 필요 없이
|
|
7072
|
+
* idempotent 한 동기화가 가능하다. 문서 id 도 그대로 유지된다.
|
|
7073
|
+
*/
|
|
7074
|
+
external_id?: string;
|
|
7075
|
+
metadata?: Record<string, unknown>;
|
|
7076
|
+
}
|
|
7077
|
+
/**
|
|
7078
|
+
* 문서 update/upsert 요청. 보낸 필드만 교체된다 (생략한 필드는 변경 없음).
|
|
7079
|
+
*
|
|
7080
|
+
* `content` / `file_content` / `metadata` 중 하나라도 보내면 서버가 전체 재색인을 수행한다
|
|
7081
|
+
* (기존 청크 삭제 → 재청킹 → 재색인). RAG 특성상 콘텐츠가 바뀌면 청크 경계가 바뀌어
|
|
7082
|
+
* 부분 수정이 불가능하며, 재색인 시 색인 토큰이 다시 과금된다.
|
|
7083
|
+
* `name` / `external_id` 만 보내면 재색인 없이 라벨·멱등 키만 변경된다.
|
|
7084
|
+
* 문서 id 는 항상 유지되므로 검색 결과의 `document_id` 참조가 깨지지 않는다.
|
|
7085
|
+
*/
|
|
7086
|
+
interface UpdateDocumentRequest {
|
|
7087
|
+
name?: string;
|
|
7088
|
+
content?: string;
|
|
7089
|
+
/** base64 인코딩 파일 (파일 재업로드, PDF/DOCX/text). content 보다 우선하며 source_type 무관하게 추출. */
|
|
7090
|
+
file_content?: string;
|
|
7091
|
+
/** file_content 의 MIME. 비어있으면 서버 자동 추정. */
|
|
7092
|
+
mime_type?: string;
|
|
5169
7093
|
metadata?: Record<string, unknown>;
|
|
7094
|
+
external_id?: string;
|
|
5170
7095
|
}
|
|
5171
7096
|
interface DocumentResponse {
|
|
5172
7097
|
id: string;
|
|
@@ -5174,6 +7099,7 @@ interface DocumentResponse {
|
|
|
5174
7099
|
source_type: string;
|
|
5175
7100
|
mime_type?: string;
|
|
5176
7101
|
source_url?: string;
|
|
7102
|
+
external_id?: string;
|
|
5177
7103
|
file_size: number;
|
|
5178
7104
|
chunk_count: number;
|
|
5179
7105
|
status: 'pending' | 'processing' | 'ready' | 'failed';
|
|
@@ -5188,9 +7114,35 @@ interface ListDocumentsResponse {
|
|
|
5188
7114
|
documents: DocumentResponse[];
|
|
5189
7115
|
total_count: number;
|
|
5190
7116
|
}
|
|
7117
|
+
/**
|
|
7118
|
+
* 사용자 격리용 magic 토큰. `where` 값에 이 문자열을 넣으면 서버가
|
|
7119
|
+
* 인증된 AppMember ID 로 자동 치환한다 (클라이언트 변조 불가).
|
|
7120
|
+
*
|
|
7121
|
+
* AppMember JWT 가 함께 오지 않은 호출에서 사용하면 401.
|
|
7122
|
+
*/
|
|
7123
|
+
declare const AUTH_MEMBER_ID_TOKEN: "$auth.member_id";
|
|
7124
|
+
/**
|
|
7125
|
+
* 검색 요청
|
|
7126
|
+
* @example { query: "환불 방법", top_k: 5, agentic: true }
|
|
7127
|
+
*
|
|
7128
|
+
* 사용자별 격리 (AppMember JWT 가 Authorization 헤더로 함께 올 때):
|
|
7129
|
+
* - 서버가 자동으로 본인 metadata.user_id 문서로 결과를 제한.
|
|
7130
|
+
* - `where` 로 추가 필터를 걸 수 있음. magic 토큰 사용 예:
|
|
7131
|
+
* `where: { 'metadata.tag': 'work' }`
|
|
7132
|
+
*/
|
|
5191
7133
|
interface KnowledgeSearchRequest {
|
|
7134
|
+
/** 검색 쿼리 (필수) */
|
|
5192
7135
|
query: string;
|
|
7136
|
+
/** 반환할 결과 수 (기본: KB 설정값) */
|
|
5193
7137
|
top_k?: number;
|
|
7138
|
+
/** Agentic Search 활성화 — AI 가 쿼리를 자동 생성하여 다중 검색 수행 */
|
|
7139
|
+
agentic?: boolean;
|
|
7140
|
+
/**
|
|
7141
|
+
* metadata 기반 필터 (옵셔널). 키 형식 `metadata.<path>` 또는 raw key.
|
|
7142
|
+
* 값에 `AUTH_MEMBER_ID_TOKEN` 사용 시 서버가 인증된 AppMember ID 로 치환.
|
|
7143
|
+
* AppMember JWT 컨텍스트가 있으면 서버가 추가로 metadata.user_id 강제 필터를 AND.
|
|
7144
|
+
*/
|
|
7145
|
+
where?: Record<string, unknown>;
|
|
5194
7146
|
}
|
|
5195
7147
|
interface KnowledgeSearchResult {
|
|
5196
7148
|
chunk_id: string;
|
|
@@ -5206,29 +7158,44 @@ interface KnowledgeSearchResponse {
|
|
|
5206
7158
|
query: string;
|
|
5207
7159
|
results: KnowledgeSearchResult[];
|
|
5208
7160
|
total: number;
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
answer: string;
|
|
5218
|
-
sources: KnowledgeSearchResult[];
|
|
5219
|
-
model: string;
|
|
5220
|
-
token_used?: number;
|
|
7161
|
+
/**
|
|
7162
|
+
* 이 응답이 agentic 다중검색으로 생성됐는지 여부. `agentic: true` 를 보냈더라도
|
|
7163
|
+
* AI provider 미설정·LLM 오류로 단일 키워드 검색에 폴백되면 `false` — 옵션이
|
|
7164
|
+
* placebo 가 되지 않도록 실제 수행 여부를 신호한다.
|
|
7165
|
+
*/
|
|
7166
|
+
agentic?: boolean;
|
|
7167
|
+
/** 수행된 agentic 검색 라운드 수 (1~2). 0 또는 미존재는 폴백을 의미. */
|
|
7168
|
+
agentic_rounds?: number;
|
|
5221
7169
|
}
|
|
5222
7170
|
|
|
7171
|
+
/**
|
|
7172
|
+
* `addDocumentFromFile` 입력 형식.
|
|
7173
|
+
*
|
|
7174
|
+
* - `File` (DOM) — 브라우저 `<input type="file">` 결과를 그대로 사용. name / type 이 자동 추출됨.
|
|
7175
|
+
* - `Blob` (DOM) — Blob.type 이 MIME 으로 사용됨 (없으면 서버 자동 추정).
|
|
7176
|
+
* - `{ data, mimeType?, name? }` — Node.js Buffer / Uint8Array. mimeType 명시 권장.
|
|
7177
|
+
*/
|
|
7178
|
+
type KnowledgeFileInput = File | Blob | {
|
|
7179
|
+
data: Uint8Array | ArrayBuffer;
|
|
7180
|
+
mimeType?: string;
|
|
7181
|
+
name?: string;
|
|
7182
|
+
};
|
|
5223
7183
|
/**
|
|
5224
7184
|
* Knowledge Base API
|
|
5225
7185
|
*
|
|
5226
|
-
*
|
|
7186
|
+
* AI 데이터베이스를 위한 문서 저장 및 검색 API.
|
|
5227
7187
|
* 문서를 업로드하면 자동으로 청킹되어 키워드 기반 검색이 가능합니다.
|
|
5228
7188
|
*
|
|
7189
|
+
* ## 사용자별 격리 (다중 사용자 RAG 시나리오)
|
|
7190
|
+
*
|
|
7191
|
+
* `Authorization: Bearer <appmember-jwt>` 를 함께 보내면 서버가:
|
|
7192
|
+
* - 검색 결과를 본인 문서 (metadata.user_id == member_id) 로 제한
|
|
7193
|
+
* - addDocument 시 metadata.user_id 자동 태깅
|
|
7194
|
+
* - listDocuments / deleteDocument 도 본인 자료만 노출
|
|
7195
|
+
*
|
|
5229
7196
|
* @example
|
|
5230
7197
|
* ```typescript
|
|
5231
|
-
* const cb = new ConnectBase({
|
|
7198
|
+
* const cb = new ConnectBase({ publicKey: 'your-public-key' })
|
|
5232
7199
|
*
|
|
5233
7200
|
* // 텍스트 문서 추가
|
|
5234
7201
|
* await cb.knowledge.addDocument('kb-id', {
|
|
@@ -5237,93 +7204,588 @@ interface ChatResponse {
|
|
|
5237
7204
|
* content: '환불은 구매 후 7일 이내에 가능합니다...'
|
|
5238
7205
|
* })
|
|
5239
7206
|
*
|
|
5240
|
-
* // 문서 검색
|
|
5241
|
-
* const results = await cb.knowledge.search('kb-id', {
|
|
5242
|
-
* query: '환불 정책',
|
|
5243
|
-
* top_k: 5
|
|
7207
|
+
* // 문서 검색
|
|
7208
|
+
* const results = await cb.knowledge.search('kb-id', {
|
|
7209
|
+
* query: '환불 정책',
|
|
7210
|
+
* top_k: 5
|
|
7211
|
+
* })
|
|
7212
|
+
*
|
|
7213
|
+
* // 사용자별 격리 + 추가 필터 (AppMember JWT 가 같이 와야 함)
|
|
7214
|
+
* const results = await cb.knowledge.search('kb-id', {
|
|
7215
|
+
* query: '내 메모',
|
|
7216
|
+
* where: { 'metadata.tag': 'work' },
|
|
7217
|
+
* })
|
|
7218
|
+
* ```
|
|
7219
|
+
*/
|
|
7220
|
+
declare class KnowledgeAPI {
|
|
7221
|
+
private http;
|
|
7222
|
+
constructor(http: HttpClient);
|
|
7223
|
+
/**
|
|
7224
|
+
* 문서 추가
|
|
7225
|
+
*
|
|
7226
|
+
* 지식 베이스에 새 문서를 추가합니다. 텍스트 소스인 경우 즉시 처리됩니다.
|
|
7227
|
+
*
|
|
7228
|
+
* @param kbID - 지식 베이스 ID
|
|
7229
|
+
* @param data - 문서 생성 요청
|
|
7230
|
+
* @returns 생성된 문서 정보
|
|
7231
|
+
*
|
|
7232
|
+
* @example
|
|
7233
|
+
* ```typescript
|
|
7234
|
+
* // 텍스트 문서
|
|
7235
|
+
* const doc = await cb.knowledge.addDocument('kb-id', {
|
|
7236
|
+
* name: 'FAQ',
|
|
7237
|
+
* source_type: 'text',
|
|
7238
|
+
* content: '자주 묻는 질문들...'
|
|
7239
|
+
* })
|
|
7240
|
+
*
|
|
7241
|
+
* // URL에서 가져오기
|
|
7242
|
+
* const doc2 = await cb.knowledge.addDocument('kb-id', {
|
|
7243
|
+
* name: '도움말',
|
|
7244
|
+
* source_type: 'url',
|
|
7245
|
+
* source_url: 'https://example.com/help.html'
|
|
7246
|
+
* })
|
|
7247
|
+
*
|
|
7248
|
+
* // external_id 로 idempotent 동기화 (빌드 스크립트 등)
|
|
7249
|
+
* // 같은 external_id 로 다시 호출하면 기존 문서를 교체한다 (문서 id 유지).
|
|
7250
|
+
* await cb.knowledge.addDocument('kb-id', {
|
|
7251
|
+
* name: '블로그: 제목',
|
|
7252
|
+
* source_type: 'text',
|
|
7253
|
+
* content: '...',
|
|
7254
|
+
* external_id: 'blog/my-post-slug',
|
|
7255
|
+
* })
|
|
7256
|
+
* ```
|
|
7257
|
+
*/
|
|
7258
|
+
addDocument(kbID: string, data: CreateDocumentRequest): Promise<DocumentResponse>;
|
|
7259
|
+
/**
|
|
7260
|
+
* 문서 수정 (update/upsert)
|
|
7261
|
+
*
|
|
7262
|
+
* 기존 문서의 내용·이름·메타데이터를 교체합니다. **문서 id 는 그대로 유지**되므로
|
|
7263
|
+
* 검색 결과의 `document_id` 를 외부에서 참조(인용·캐시)해도 편집 후 깨지지 않습니다.
|
|
7264
|
+
* 보낸 필드만 교체되며, 내용/이름/메타데이터가 실제로 바뀌면 자동으로 재청킹·재색인됩니다.
|
|
7265
|
+
*
|
|
7266
|
+
* @param kbID - 지식 베이스 ID
|
|
7267
|
+
* @param docID - 문서 ID
|
|
7268
|
+
* @param data - 변경할 필드 (생략한 필드는 변경 없음)
|
|
7269
|
+
* @returns 수정된 문서 정보
|
|
7270
|
+
*
|
|
7271
|
+
* @example
|
|
7272
|
+
* ```typescript
|
|
7273
|
+
* // 내용만 교체 — document_id 는 그대로
|
|
7274
|
+
* const doc = await cb.knowledge.updateDocument('kb-id', 'doc-id', {
|
|
7275
|
+
* content: '갱신된 환불 정책...',
|
|
7276
|
+
* })
|
|
7277
|
+
*
|
|
7278
|
+
* // 이름 + 메타데이터 교체
|
|
7279
|
+
* await cb.knowledge.updateDocument('kb-id', 'doc-id', {
|
|
7280
|
+
* name: 'FAQ (2026)',
|
|
7281
|
+
* metadata: { tag: 'faq', updated: '2026-05' },
|
|
7282
|
+
* })
|
|
7283
|
+
* ```
|
|
7284
|
+
*/
|
|
7285
|
+
updateDocument(kbID: string, docID: string, data: UpdateDocumentRequest): Promise<DocumentResponse>;
|
|
7286
|
+
/**
|
|
7287
|
+
* 파일로 문서 수정 (PDF / DOCX / text 파일 재업로드).
|
|
7288
|
+
*
|
|
7289
|
+
* [`addDocumentFromFile`](#addDocumentFromFile) 의 update 버전. 문서 id 는 유지됩니다.
|
|
7290
|
+
*
|
|
7291
|
+
* @param kbID - Knowledge Base ID
|
|
7292
|
+
* @param docID - 문서 ID
|
|
7293
|
+
* @param file - 새 파일. `File` (DOM) / `Blob` / `{ data, mimeType, name }` (Node) 모두 가능
|
|
7294
|
+
* @param options - name / metadata 오버라이드
|
|
7295
|
+
* @returns 수정된 문서 정보
|
|
7296
|
+
*/
|
|
7297
|
+
updateDocumentFromFile(kbID: string, docID: string, file: KnowledgeFileInput, options?: {
|
|
7298
|
+
name?: string;
|
|
7299
|
+
metadata?: Record<string, unknown>;
|
|
7300
|
+
}): Promise<DocumentResponse>;
|
|
7301
|
+
/**
|
|
7302
|
+
* 파일을 KB 에 추가 (PDF / DOCX / text 파일).
|
|
7303
|
+
*
|
|
7304
|
+
* 브라우저에서 `<input type="file">` 로 받은 `File` 또는 `Blob` 을 그대로 넘기면 SDK 가
|
|
7305
|
+
* base64 로 인코딩 + MIME 추정 후 서버에 보낸다. 서버는 PDF / DOCX / text 류를 자동
|
|
7306
|
+
* 텍스트 추출하여 기존 청킹·인덱싱 파이프라인을 태운다.
|
|
7307
|
+
*
|
|
7308
|
+
* 지원 MIME (2026-05 기준):
|
|
7309
|
+
* - `application/pdf` (텍스트 PDF — 스캔 이미지 PDF 는 미지원)
|
|
7310
|
+
* - `application/vnd.openxmlformats-officedocument.wordprocessingml.document` (DOCX)
|
|
7311
|
+
* - `text/*` (plain / markdown / csv / html)
|
|
7312
|
+
* - `application/json`
|
|
7313
|
+
*
|
|
7314
|
+
* 비지원 MIME (이미지 OCR / 한글 HWP / XLSX 등) 은 향후 추가될 예정 — 현재는 명시적 에러.
|
|
7315
|
+
*
|
|
7316
|
+
* 파일 크기 상한: 50MB (원본 바이너리 기준).
|
|
7317
|
+
*
|
|
7318
|
+
* @param kbID - Knowledge Base ID
|
|
7319
|
+
* @param file - 업로드할 파일. `File` (DOM) / `Blob` / `{ data, mimeType, name }` (Node) 모두 가능
|
|
7320
|
+
* @param options - name 오버라이드 + metadata
|
|
7321
|
+
* @returns 생성된 문서 정보 (서버에서 status='processing' → 청킹 완료 후 'ready')
|
|
7322
|
+
*
|
|
7323
|
+
* @example
|
|
7324
|
+
* ```typescript
|
|
7325
|
+
* // 브라우저: <input type="file"> 로 받은 File
|
|
7326
|
+
* const file = event.target.files[0]
|
|
7327
|
+
* const doc = await cb.knowledge.addDocumentFromFile('kb-id', file, {
|
|
7328
|
+
* metadata: { tag: 'manual' }
|
|
7329
|
+
* })
|
|
7330
|
+
*
|
|
7331
|
+
* // Node.js: fs.readFileSync 로 읽은 Buffer
|
|
7332
|
+
* import { readFileSync } from 'node:fs'
|
|
7333
|
+
* const data = readFileSync('./report.pdf')
|
|
7334
|
+
* const doc = await cb.knowledge.addDocumentFromFile('kb-id', {
|
|
7335
|
+
* data,
|
|
7336
|
+
* mimeType: 'application/pdf',
|
|
7337
|
+
* name: 'report.pdf',
|
|
7338
|
+
* })
|
|
7339
|
+
* ```
|
|
7340
|
+
*/
|
|
7341
|
+
addDocumentFromFile(kbID: string, file: KnowledgeFileInput, options?: {
|
|
7342
|
+
name?: string;
|
|
7343
|
+
metadata?: Record<string, unknown>;
|
|
7344
|
+
}): Promise<DocumentResponse>;
|
|
7345
|
+
/**
|
|
7346
|
+
* 문서 목록 조회
|
|
7347
|
+
*
|
|
7348
|
+
* @param kbID - 지식 베이스 ID
|
|
7349
|
+
* @returns 문서 목록
|
|
7350
|
+
*/
|
|
7351
|
+
listDocuments(kbID: string): Promise<ListDocumentsResponse>;
|
|
7352
|
+
/**
|
|
7353
|
+
* 문서 삭제
|
|
7354
|
+
*
|
|
7355
|
+
* 문서와 관련된 모든 청크도 함께 삭제됩니다.
|
|
7356
|
+
*
|
|
7357
|
+
* @param kbID - 지식 베이스 ID
|
|
7358
|
+
* @param docID - 문서 ID
|
|
7359
|
+
*/
|
|
7360
|
+
deleteDocument(kbID: string, docID: string): Promise<void>;
|
|
7361
|
+
/**
|
|
7362
|
+
* 문서 검색
|
|
7363
|
+
*
|
|
7364
|
+
* 키워드 기반으로 관련 문서 청크를 검색합니다.
|
|
7365
|
+
* 제목, 내용, 키워드 필드에서 매칭되며 점수 기반으로 정렬됩니다.
|
|
7366
|
+
*
|
|
7367
|
+
* @param kbID - 지식 베이스 ID
|
|
7368
|
+
* @param request - 검색 요청
|
|
7369
|
+
* @returns 검색 결과
|
|
7370
|
+
*
|
|
7371
|
+
* @example
|
|
7372
|
+
* ```typescript
|
|
7373
|
+
* const results = await cb.knowledge.search('kb-id', {
|
|
7374
|
+
* query: '환불 신청 방법',
|
|
7375
|
+
* top_k: 5 // 상위 5개 결과
|
|
7376
|
+
* })
|
|
7377
|
+
*
|
|
7378
|
+
* for (const result of results.results) {
|
|
7379
|
+
* console.log(`[${result.score}] ${result.title}`)
|
|
7380
|
+
* console.log(result.content)
|
|
7381
|
+
* }
|
|
7382
|
+
* ```
|
|
7383
|
+
*/
|
|
7384
|
+
search(kbID: string, request: KnowledgeSearchRequest): Promise<KnowledgeSearchResponse>;
|
|
7385
|
+
/**
|
|
7386
|
+
* 문서 검색 (GET 방식)
|
|
7387
|
+
*
|
|
7388
|
+
* 쿼리 파라미터로 검색합니다.
|
|
7389
|
+
*
|
|
7390
|
+
* @param kbID - 지식 베이스 ID
|
|
7391
|
+
* @param query - 검색 쿼리
|
|
7392
|
+
* @param topK - 반환할 결과 수 (기본값: 지식 베이스 설정)
|
|
7393
|
+
*/
|
|
7394
|
+
searchGet(kbID: string, query: string, topK?: number): Promise<KnowledgeSearchResponse>;
|
|
7395
|
+
}
|
|
7396
|
+
|
|
7397
|
+
interface AIMessage {
|
|
7398
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
7399
|
+
content: string;
|
|
7400
|
+
toolCalls?: AIToolCall[];
|
|
7401
|
+
toolCallId?: string;
|
|
7402
|
+
}
|
|
7403
|
+
interface AIToolCall {
|
|
7404
|
+
id: string;
|
|
7405
|
+
name: string;
|
|
7406
|
+
arguments: Record<string, unknown>;
|
|
7407
|
+
}
|
|
7408
|
+
interface AITool {
|
|
7409
|
+
name: string;
|
|
7410
|
+
description: string;
|
|
7411
|
+
inputSchema: Record<string, unknown>;
|
|
7412
|
+
}
|
|
7413
|
+
interface AIChatRequest {
|
|
7414
|
+
messages: AIMessage[];
|
|
7415
|
+
maxTokens?: number;
|
|
7416
|
+
temperature?: number;
|
|
7417
|
+
topP?: number;
|
|
7418
|
+
provider?: string;
|
|
7419
|
+
model?: string;
|
|
7420
|
+
tools?: AITool[];
|
|
7421
|
+
appId?: string;
|
|
7422
|
+
knowledgeBaseId?: string;
|
|
7423
|
+
topK?: number;
|
|
7424
|
+
agentic?: boolean;
|
|
7425
|
+
toolGroupId?: string;
|
|
7426
|
+
}
|
|
7427
|
+
interface AIToolEvent {
|
|
7428
|
+
type: 'tool_start' | 'tool_end' | 'heartbeat';
|
|
7429
|
+
name?: string;
|
|
7430
|
+
toolCallId?: string;
|
|
7431
|
+
arguments?: Record<string, unknown>;
|
|
7432
|
+
result?: string;
|
|
7433
|
+
success?: boolean;
|
|
7434
|
+
durationMs?: number;
|
|
7435
|
+
}
|
|
7436
|
+
interface AISource {
|
|
7437
|
+
chunkId: string;
|
|
7438
|
+
documentId: string;
|
|
7439
|
+
documentName: string;
|
|
7440
|
+
content: string;
|
|
7441
|
+
score: number;
|
|
7442
|
+
}
|
|
7443
|
+
interface AIChatResponse {
|
|
7444
|
+
content: string;
|
|
7445
|
+
/**
|
|
7446
|
+
* 추론 모델(Qwen3 reasoning, OpenAI o-series, DeepSeek-R1 등)의 사고 과정.
|
|
7447
|
+
* 최종 답변(`content`)과 분리되어 제공됩니다. 추론 모델이 아니면 비어 있습니다.
|
|
7448
|
+
*/
|
|
7449
|
+
reasoning?: string;
|
|
7450
|
+
finishReason?: string;
|
|
7451
|
+
usage?: {
|
|
7452
|
+
promptTokens: number;
|
|
7453
|
+
completionTokens: number;
|
|
7454
|
+
totalTokens: number;
|
|
7455
|
+
};
|
|
7456
|
+
provider: string;
|
|
7457
|
+
model: string;
|
|
7458
|
+
toolCalls?: AIToolCall[];
|
|
7459
|
+
sources?: AISource[];
|
|
7460
|
+
}
|
|
7461
|
+
/**
|
|
7462
|
+
* Agentic 검색의 한 단계 진행 상황. `chatStream({ agentic: true })` 시
|
|
7463
|
+
* `onSearching` 콜백으로 실시간 전달되어 "검색 중…" 진행 UI 를 구성할 수 있다.
|
|
7464
|
+
*/
|
|
7465
|
+
interface AgenticSearchProgress {
|
|
7466
|
+
/** 'query_generation' = 검색어 생성 중, 'searching' = 검색 실행 중, 'complete' = 검색 종료 */
|
|
7467
|
+
phase: 'query_generation' | 'searching' | 'complete';
|
|
7468
|
+
/** 검색 라운드 (1~2). agentic 은 결과가 부족하면 2라운드까지 수행. */
|
|
7469
|
+
round?: number;
|
|
7470
|
+
/** 이 라운드에서 생성된 검색 쿼리들 (phase='searching'). */
|
|
7471
|
+
queries?: string[];
|
|
7472
|
+
/** phase='complete': 누적 결과 청크 수. */
|
|
7473
|
+
results?: number;
|
|
7474
|
+
/** phase='complete': 총 검색 라운드 수. 0 은 폴백(단일 키워드 검색)을 의미. */
|
|
7475
|
+
rounds?: number;
|
|
7476
|
+
}
|
|
7477
|
+
interface AIStreamChunk {
|
|
7478
|
+
type?: 'sources' | 'token' | 'searching' | 'tool_start' | 'tool_end' | 'heartbeat';
|
|
7479
|
+
content: string;
|
|
7480
|
+
/**
|
|
7481
|
+
* 추론 모델의 사고 과정 델타. 추론 구간의 청크는 `content`가 비어 있고
|
|
7482
|
+
* `reasoning`만 채워져 전달됩니다.
|
|
7483
|
+
*/
|
|
7484
|
+
reasoning?: string;
|
|
7485
|
+
finishReason?: string;
|
|
7486
|
+
done: boolean;
|
|
7487
|
+
toolCalls?: AIToolCall[];
|
|
7488
|
+
sources?: AISource[];
|
|
7489
|
+
name?: string;
|
|
7490
|
+
toolCallId?: string;
|
|
7491
|
+
arguments?: Record<string, unknown>;
|
|
7492
|
+
result?: string;
|
|
7493
|
+
success?: boolean;
|
|
7494
|
+
durationMs?: number;
|
|
7495
|
+
searching?: AgenticSearchProgress;
|
|
7496
|
+
}
|
|
7497
|
+
|
|
7498
|
+
/**
|
|
7499
|
+
* AI API
|
|
7500
|
+
*
|
|
7501
|
+
* AI 채팅 및 AI 데이터베이스 연동 API.
|
|
7502
|
+
* AI 데이터베이스 ID를 지정하면 문서 검색 후 컨텍스트를 포함하여 응답합니다.
|
|
7503
|
+
*
|
|
7504
|
+
* @example
|
|
7505
|
+
* ```typescript
|
|
7506
|
+
* const cb = new ConnectBase({ publicKey: 'your-public-key' })
|
|
7507
|
+
*
|
|
7508
|
+
* // 일반 AI 채팅
|
|
7509
|
+
* const response = await cb.ai.chat({
|
|
7510
|
+
* messages: [{ role: 'user', content: '안녕하세요' }],
|
|
7511
|
+
* provider: 'gemini'
|
|
7512
|
+
* })
|
|
7513
|
+
*
|
|
7514
|
+
* // AI 데이터베이스 연동 채팅
|
|
7515
|
+
* const ragResponse = await cb.ai.chat({
|
|
7516
|
+
* messages: [{ role: 'user', content: '환불 정책이 어떻게 되나요?' }],
|
|
7517
|
+
* knowledgeBaseId: 'kb-id',
|
|
7518
|
+
* topK: 5
|
|
7519
|
+
* })
|
|
7520
|
+
* ```
|
|
7521
|
+
*/
|
|
7522
|
+
declare class AIAPI {
|
|
7523
|
+
private http;
|
|
7524
|
+
constructor(http: HttpClient);
|
|
7525
|
+
/**
|
|
7526
|
+
* AI 채팅 (동기)
|
|
7527
|
+
*/
|
|
7528
|
+
chat(request: AIChatRequest): Promise<AIChatResponse>;
|
|
7529
|
+
/**
|
|
7530
|
+
* AI 채팅 스트리밍 (SSE)
|
|
7531
|
+
*
|
|
7532
|
+
* @example
|
|
7533
|
+
* ```typescript
|
|
7534
|
+
* await cb.ai.chatStream({
|
|
7535
|
+
* messages: [{ role: 'user', content: '안녕하세요' }],
|
|
7536
|
+
* knowledgeBaseId: 'kb-id',
|
|
7537
|
+
* }, {
|
|
7538
|
+
* onSources: (sources) => console.log('참조:', sources),
|
|
7539
|
+
* onReasoning: (reasoning) => process.stdout.write(reasoning),
|
|
7540
|
+
* onToken: (content) => process.stdout.write(content),
|
|
7541
|
+
* onDone: () => console.log('\\n완료'),
|
|
7542
|
+
* onError: (error) => console.error('에러:', error)
|
|
7543
|
+
* })
|
|
7544
|
+
* ```
|
|
7545
|
+
*/
|
|
7546
|
+
chatStream(request: AIChatRequest, callbacks: {
|
|
7547
|
+
onSources?: (sources: AISource[]) => void;
|
|
7548
|
+
/**
|
|
7549
|
+
* 추론 모델의 사고 과정 델타. 추론 모델(Qwen3 reasoning, o-series 등)을
|
|
7550
|
+
* 사용할 때만 호출되며, 최종 답변은 `onToken`으로 별도 전달됩니다.
|
|
7551
|
+
*/
|
|
7552
|
+
onReasoning?: (reasoning: string) => void;
|
|
7553
|
+
onToken?: (content: string) => void;
|
|
7554
|
+
onToolEvent?: (event: AIToolEvent) => void;
|
|
7555
|
+
/**
|
|
7556
|
+
* Agentic 검색(`agentic: true`) 진행 상황. agentic 검색이 검색어를
|
|
7557
|
+
* 생성하고 다중 라운드로 검색하는 각 단계마다 호출되어, "검색 중…"
|
|
7558
|
+
* 진행 UI 를 구성할 수 있다. agentic 미사용 시 호출되지 않는다.
|
|
7559
|
+
*/
|
|
7560
|
+
onSearching?: (progress: AgenticSearchProgress) => void;
|
|
7561
|
+
onDone?: () => void;
|
|
7562
|
+
onError?: (error: string) => void;
|
|
7563
|
+
}): Promise<void>;
|
|
7564
|
+
}
|
|
7565
|
+
|
|
7566
|
+
/**
|
|
7567
|
+
* EndpointAPI — 사용자 PC GPU 모델을 `cb_pk_*` 한 키로 호출하는 dumb pipe.
|
|
7568
|
+
*
|
|
7569
|
+
* 핵심 비전: ConnectBase 는 **모델·API·워크플로우를 알지 않는다**. 사용자가 자기 PC 에서
|
|
7570
|
+
* 자기 모델을 띄우고 자기 API 를 정한다. SDK 는 라벨 → tunnel 매핑만 알고 페이로드
|
|
7571
|
+
* 그대로 forward.
|
|
7572
|
+
*
|
|
7573
|
+
* @example ComfyUI 호출
|
|
7574
|
+
* ```typescript
|
|
7575
|
+
* const cb = new ConnectBase({ publicKey: "cb_pk_..." })
|
|
7576
|
+
* const res = await cb.endpoint.call("comfyui-main", {
|
|
7577
|
+
* method: "POST",
|
|
7578
|
+
* path: "/prompt",
|
|
7579
|
+
* body: JSON.stringify({
|
|
7580
|
+
* prompt: {
|
|
7581
|
+
* // ComfyUI 노드 그래프
|
|
7582
|
+
* },
|
|
7583
|
+
* }),
|
|
7584
|
+
* headers: { "Content-Type": "application/json" },
|
|
7585
|
+
* })
|
|
7586
|
+
* const data = await res.json()
|
|
7587
|
+
* ```
|
|
7588
|
+
*
|
|
7589
|
+
* @example 스트리밍 응답 (SSE / chunked)
|
|
7590
|
+
* ```typescript
|
|
7591
|
+
* const res = await cb.endpoint.call("vllm-local", {
|
|
7592
|
+
* method: "POST",
|
|
7593
|
+
* path: "/v1/chat/completions",
|
|
7594
|
+
* body: JSON.stringify({
|
|
7595
|
+
* stream: true,
|
|
7596
|
+
* messages: [
|
|
7597
|
+
* // { role, content }
|
|
7598
|
+
* ],
|
|
7599
|
+
* }),
|
|
7600
|
+
* headers: { "Content-Type": "application/json" },
|
|
7601
|
+
* })
|
|
7602
|
+
* if (!res.body) throw new Error("no stream")
|
|
7603
|
+
* const reader = res.body.getReader()
|
|
7604
|
+
* while (true) {
|
|
7605
|
+
* const { done, value } = await reader.read()
|
|
7606
|
+
* if (done) break
|
|
7607
|
+
* // value 는 Uint8Array — 디코드 후 처리
|
|
7608
|
+
* }
|
|
7609
|
+
* ```
|
|
7610
|
+
*
|
|
7611
|
+
* @example AbortSignal 으로 취소
|
|
7612
|
+
* ```typescript
|
|
7613
|
+
* const ctrl = new AbortController()
|
|
7614
|
+
* setTimeout(() => ctrl.abort(), 30_000) // 30초 후 취소
|
|
7615
|
+
* const res = await cb.endpoint.call("hunyuan-laptop", {
|
|
7616
|
+
* method: "POST",
|
|
7617
|
+
* path: "/generate",
|
|
7618
|
+
* signal: ctrl.signal,
|
|
7619
|
+
* body: JSON.stringify({
|
|
7620
|
+
* // 모델 입력
|
|
7621
|
+
* }),
|
|
5244
7622
|
* })
|
|
5245
7623
|
* ```
|
|
5246
7624
|
*/
|
|
5247
|
-
declare class
|
|
7625
|
+
declare class EndpointAPI {
|
|
5248
7626
|
private http;
|
|
5249
7627
|
constructor(http: HttpClient);
|
|
5250
7628
|
/**
|
|
5251
|
-
*
|
|
7629
|
+
* 라벨 + path 로 사용자 PC 모델 호출. fetch() 시그니처 호환.
|
|
5252
7630
|
*
|
|
5253
|
-
*
|
|
7631
|
+
* 동작:
|
|
7632
|
+
* - URL 조립: `${baseUrl}/v1/proxy/${label}${path}`
|
|
7633
|
+
* - X-Public-Key 헤더 자동 주입 (호출자가 명시하면 그 값 우선)
|
|
7634
|
+
* - body / method / 추가 헤더 / signal 그대로 전달
|
|
7635
|
+
* - 응답 그대로 반환 (Response 객체) — 스트리밍은 res.body 로 read
|
|
5254
7636
|
*
|
|
5255
|
-
* @param
|
|
5256
|
-
* @param
|
|
5257
|
-
|
|
7637
|
+
* @param label - 콘솔에서 등록한 endpoint 라벨 (예: "comfyui-main")
|
|
7638
|
+
* @param init - fetch() 의 RequestInit + path. path 는 사용자 모델 서버의 엔드포인트 경로 (예: "/prompt", "/v1/chat/completions").
|
|
7639
|
+
*/
|
|
7640
|
+
call(label: string, init: EndpointCallInit): Promise<Response>;
|
|
7641
|
+
/**
|
|
7642
|
+
* 라벨 + path 의 최종 호출 URL `${baseUrl}/v1/proxy/${label}${path}` 을 조립해서
|
|
7643
|
+
* 반환. URL 을 다른 시스템 (Service Worker, 백엔드 워커, 로깅) 에 넘기거나
|
|
7644
|
+
* 디버깅 용도일 때 사용.
|
|
5258
7645
|
*
|
|
5259
|
-
*
|
|
7646
|
+
* ⚠️ **`<img src>` / 네이티브 `WebSocket` / `<script src>` / `EventSource` 처럼
|
|
7647
|
+
* 커스텀 헤더를 못 보내는 브라우저 API 에 직접 넘기면 401 입니다.** ConnectBase
|
|
7648
|
+
* 프록시는 모든 요청에 `X-Public-Key` 헤더를 요구하고, 쿼리 파라미터 폴백은
|
|
7649
|
+
* 제공하지 않습니다. 그런 경우엔 `call()` 로 받아서 Blob URL 로 변환하세요.
|
|
7650
|
+
*
|
|
7651
|
+
* @example URL 을 워커로 넘겨 호출 (✅)
|
|
5260
7652
|
* ```typescript
|
|
5261
|
-
*
|
|
5262
|
-
*
|
|
5263
|
-
*
|
|
5264
|
-
* source_type: 'text',
|
|
5265
|
-
* content: '자주 묻는 질문들...'
|
|
7653
|
+
* sw.postMessage({
|
|
7654
|
+
* url: cb.endpoint.url("comfyui-main", "/prompt"),
|
|
7655
|
+
* key: publicKey, // 워커가 X-Public-Key 헤더로 부착
|
|
5266
7656
|
* })
|
|
7657
|
+
* ```
|
|
5267
7658
|
*
|
|
5268
|
-
*
|
|
5269
|
-
*
|
|
5270
|
-
*
|
|
5271
|
-
*
|
|
5272
|
-
* source_url: 'https://example.com/help.html'
|
|
7659
|
+
* @example 이미지 렌더링은 call() + Blob URL 패턴으로 (✅)
|
|
7660
|
+
* ```typescript
|
|
7661
|
+
* const res = await cb.endpoint.call("comfyui-main", {
|
|
7662
|
+
* path: `/view?filename=${encodeURIComponent(name)}`,
|
|
5273
7663
|
* })
|
|
7664
|
+
* img.src = URL.createObjectURL(await res.blob())
|
|
7665
|
+
* // 나중에 URL.revokeObjectURL(img.src)
|
|
5274
7666
|
* ```
|
|
5275
7667
|
*/
|
|
5276
|
-
|
|
7668
|
+
url(label: string, path: string): string;
|
|
5277
7669
|
/**
|
|
5278
|
-
*
|
|
7670
|
+
* 같은 endpoint 호출을 `predicate` 가 값을 반환할 때까지 주기적으로 반복.
|
|
7671
|
+
* ComfyUI `/history/{id}`, A1111 `/sdapi/v1/progress`, 자체 큐 API 처럼
|
|
7672
|
+
* "작업 제출 → 폴링" 패턴을 한 줄로 처리하기 위한 헬퍼.
|
|
5279
7673
|
*
|
|
5280
|
-
*
|
|
5281
|
-
*
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
*
|
|
7674
|
+
* 동작:
|
|
7675
|
+
* - `call(label, init)` → predicate(response) 호출
|
|
7676
|
+
* - predicate 가 `undefined` 면 `intervalMs` 만큼 대기 후 재시도
|
|
7677
|
+
* - predicate 가 값을 반환하면 그 값을 즉시 resolve
|
|
7678
|
+
* - `timeoutMs` 초과 또는 `signal` abort 시 reject
|
|
7679
|
+
* - HTTP 5xx/네트워크 오류는 재시도, 4xx 는 즉시 reject (작업 자체가 잘못된 경우)
|
|
5286
7680
|
*
|
|
5287
|
-
*
|
|
7681
|
+
* predicate 는 같은 Response 를 한 번만 읽을 수 있으므로, 헬퍼 내부에서
|
|
7682
|
+
* `res.clone().json()` 형태로 안전하게 파싱한 뒤 호출자에게 전달.
|
|
5288
7683
|
*
|
|
5289
|
-
* @
|
|
5290
|
-
*
|
|
7684
|
+
* @example ComfyUI 결과 폴링
|
|
7685
|
+
* ```typescript
|
|
7686
|
+
* type Hist = Record<string, { outputs: Record<string, { images?: { filename: string }[] }> }>
|
|
7687
|
+
*
|
|
7688
|
+
* const filename = await cb.endpoint.pollUntil<string>(
|
|
7689
|
+
* "comfyui-main",
|
|
7690
|
+
* { path: `/history/${promptId}` },
|
|
7691
|
+
* (data: Hist) => {
|
|
7692
|
+
* const entry = data[promptId]
|
|
7693
|
+
* if (!entry) return undefined // 아직 큐에 있음
|
|
7694
|
+
* for (const out of Object.values(entry.outputs)) {
|
|
7695
|
+
* const img = out.images?.[0]
|
|
7696
|
+
* if (img) return img.filename
|
|
7697
|
+
* }
|
|
7698
|
+
* return undefined
|
|
7699
|
+
* },
|
|
7700
|
+
* { intervalMs: 1000, timeoutMs: 5 * 60_000 },
|
|
7701
|
+
* )
|
|
7702
|
+
* ```
|
|
5291
7703
|
*/
|
|
5292
|
-
deleteDocument(kbID: string, docID: string): Promise<void>;
|
|
5293
7704
|
/**
|
|
5294
|
-
*
|
|
7705
|
+
* Open a native browser `WebSocket` to a user model server (e.g. ComfyUI's
|
|
7706
|
+
* `/ws` event channel) through the ConnectBase proxy.
|
|
5295
7707
|
*
|
|
5296
|
-
*
|
|
5297
|
-
*
|
|
7708
|
+
* Background: native browser `WebSocket` cannot send custom headers, so
|
|
7709
|
+
* `X-Public-Key` cannot be supplied on the Upgrade request. This helper
|
|
7710
|
+
* solves that by:
|
|
7711
|
+
* 1. POSTing to `/v1/proxy/<label>/ws-ticket` (with `X-Public-Key`)
|
|
7712
|
+
* to obtain a single-use 30s token,
|
|
7713
|
+
* 2. Opening `wss://<base>/v1/proxy/<label><path>?ticket=<token>&...`,
|
|
7714
|
+
* 3. Returning the established `WebSocket` to the caller.
|
|
5298
7715
|
*
|
|
5299
|
-
*
|
|
5300
|
-
*
|
|
5301
|
-
* @returns 검색 결과
|
|
7716
|
+
* Both the client→upstream and upstream→client byte streams flow
|
|
7717
|
+
* transparently — the proxy server adds no framing of its own.
|
|
5302
7718
|
*
|
|
5303
|
-
* @example
|
|
5304
|
-
* ```
|
|
5305
|
-
* const
|
|
5306
|
-
*
|
|
5307
|
-
*
|
|
7719
|
+
* @example ComfyUI native event channel
|
|
7720
|
+
* ```ts
|
|
7721
|
+
* const ws = await cb.endpoint.connectWebSocket("comfyui-main", {
|
|
7722
|
+
* path: "/ws",
|
|
7723
|
+
* query: { clientId: crypto.randomUUID() },
|
|
5308
7724
|
* })
|
|
5309
|
-
*
|
|
5310
|
-
*
|
|
5311
|
-
*
|
|
5312
|
-
*
|
|
7725
|
+
* ws.onmessage = (e) => {
|
|
7726
|
+
* const msg = JSON.parse(typeof e.data === "string" ? e.data : "")
|
|
7727
|
+
* if (msg.type === "progress") {
|
|
7728
|
+
* // progress.value / progress.max — exact node-level progress
|
|
7729
|
+
* }
|
|
5313
7730
|
* }
|
|
5314
7731
|
* ```
|
|
5315
7732
|
*/
|
|
5316
|
-
|
|
7733
|
+
connectWebSocket(label: string, opts?: ConnectWebSocketOptions): Promise<WebSocket>;
|
|
7734
|
+
pollUntil<T>(label: string, init: EndpointCallInit, predicate: (body: any, res: Response) => T | undefined | Promise<T | undefined>, opts?: PollUntilOptions): Promise<T>;
|
|
7735
|
+
}
|
|
7736
|
+
/**
|
|
7737
|
+
* EndpointAPI.call 의 init 인자.
|
|
7738
|
+
*
|
|
7739
|
+
* 표준 RequestInit 와 거의 동일하지만 `path` 가 필수 (URL 은 SDK 가 조립).
|
|
7740
|
+
*/
|
|
7741
|
+
interface EndpointCallInit {
|
|
5317
7742
|
/**
|
|
5318
|
-
*
|
|
5319
|
-
*
|
|
5320
|
-
* 쿼리 파라미터로 검색합니다.
|
|
5321
|
-
*
|
|
5322
|
-
* @param kbID - 지식 베이스 ID
|
|
5323
|
-
* @param query - 검색 쿼리
|
|
5324
|
-
* @param topK - 반환할 결과 수 (기본값: 지식 베이스 설정)
|
|
7743
|
+
* 사용자 모델 서버의 엔드포인트 경로. 반드시 `/` 로 시작.
|
|
7744
|
+
* 예: `"/prompt"`, `"/v1/chat/completions"`, `"/sdapi/v1/txt2img"`.
|
|
5325
7745
|
*/
|
|
5326
|
-
|
|
7746
|
+
path: string;
|
|
7747
|
+
/** HTTP 메서드 (기본: GET). */
|
|
7748
|
+
method?: string;
|
|
7749
|
+
/** 추가 요청 헤더. Content-Type / Authorization 등 사용자 모델 서버가 요구하는 것 그대로. */
|
|
7750
|
+
headers?: HeadersInit;
|
|
7751
|
+
/** 요청 본문. Blob / ArrayBuffer / FormData / string / ReadableStream 모두 지원. */
|
|
7752
|
+
body?: BodyInit | null;
|
|
7753
|
+
/** 취소 신호. */
|
|
7754
|
+
signal?: AbortSignal;
|
|
7755
|
+
}
|
|
7756
|
+
/**
|
|
7757
|
+
* EndpointAPI.connectWebSocket 옵션.
|
|
7758
|
+
*/
|
|
7759
|
+
interface ConnectWebSocketOptions {
|
|
7760
|
+
/**
|
|
7761
|
+
* 사용자 모델 서버의 WebSocket 엔드포인트 경로. `/` 로 시작 (기본 `/`).
|
|
7762
|
+
* 예: `/ws`, `/socket`.
|
|
7763
|
+
*/
|
|
7764
|
+
path?: string;
|
|
7765
|
+
/** 추가 query 파라미터 (ticket 은 자동 주입). */
|
|
7766
|
+
query?: Record<string, string>;
|
|
7767
|
+
/** WebSocket subprotocols (Sec-WebSocket-Protocol 헤더). */
|
|
7768
|
+
protocols?: string | string[];
|
|
7769
|
+
/** Abort 시 ws.close(1000) 호출. */
|
|
7770
|
+
signal?: AbortSignal;
|
|
7771
|
+
}
|
|
7772
|
+
/**
|
|
7773
|
+
* EndpointAPI.pollUntil 옵션.
|
|
7774
|
+
*/
|
|
7775
|
+
interface PollUntilOptions {
|
|
7776
|
+
/** 폴링 간격 (ms). 기본 1500. */
|
|
7777
|
+
intervalMs?: number;
|
|
7778
|
+
/** 전체 timeout (ms). 기본 300000 (5분). */
|
|
7779
|
+
timeoutMs?: number;
|
|
7780
|
+
/**
|
|
7781
|
+
* 응답 본문 파싱 방식.
|
|
7782
|
+
* - `"json"` (기본) — `res.json()`. 파싱 실패 시 predicate 에 `undefined` 전달.
|
|
7783
|
+
* - `"text"` — 문자열 그대로.
|
|
7784
|
+
* - `"none"` — 본문 사용 안 함 (예: 헤더 / status 만 보고 판단).
|
|
7785
|
+
*/
|
|
7786
|
+
parse?: "json" | "text" | "none";
|
|
7787
|
+
/** 외부 취소 신호. abort 시 즉시 reject. */
|
|
7788
|
+
signal?: AbortSignal;
|
|
5327
7789
|
}
|
|
5328
7790
|
|
|
5329
7791
|
interface PublishMessageRequest {
|
|
@@ -5389,7 +7851,7 @@ interface QueueInfoResponse {
|
|
|
5389
7851
|
*
|
|
5390
7852
|
* @example
|
|
5391
7853
|
* ```typescript
|
|
5392
|
-
* const cb = new ConnectBase({
|
|
7854
|
+
* const cb = new ConnectBase({ publicKey: 'your-public-key' })
|
|
5393
7855
|
*
|
|
5394
7856
|
* // 메시지 발행
|
|
5395
7857
|
* await cb.queue.publish('queue-id', {
|
|
@@ -5438,6 +7900,334 @@ declare class QueueAPI {
|
|
|
5438
7900
|
getInfo(queueID: string): Promise<QueueInfoResponse>;
|
|
5439
7901
|
}
|
|
5440
7902
|
|
|
7903
|
+
/**
|
|
7904
|
+
* 사용자 제보 (end-user issue) — 카테고리.
|
|
7905
|
+
*
|
|
7906
|
+
* - `bug` 버그 리포트
|
|
7907
|
+
* - `question` 질문/문의
|
|
7908
|
+
* - `feature_request` 기능 요청
|
|
7909
|
+
* - `incident` 긴급 장애
|
|
7910
|
+
* - `other` 그 외
|
|
7911
|
+
*/
|
|
7912
|
+
type SupportIssueCategory = 'bug' | 'question' | 'feature_request' | 'incident' | 'other';
|
|
7913
|
+
/**
|
|
7914
|
+
* 제보 발행 요청 본문.
|
|
7915
|
+
*/
|
|
7916
|
+
interface ReportIssueRequest {
|
|
7917
|
+
/** 제목 (최대 200자) */
|
|
7918
|
+
title: string;
|
|
7919
|
+
/** 본문 (최대 16KB, 마크다운) */
|
|
7920
|
+
body: string;
|
|
7921
|
+
/** 카테고리. 기본값 `other`. AI 가 자동 보정해 줌. */
|
|
7922
|
+
category?: SupportIssueCategory;
|
|
7923
|
+
/** 자유 컨텍스트 (페이지 URL, 앱 버전, trace_id 등) */
|
|
7924
|
+
metadata?: Record<string, unknown>;
|
|
7925
|
+
/**
|
|
7926
|
+
* 익명 발행 시 회신 받을 이메일 (선택).
|
|
7927
|
+
* 로그인된 사용자면 무시되고 AppMember 정보로 대체.
|
|
7928
|
+
*/
|
|
7929
|
+
anonymousEmail?: string;
|
|
7930
|
+
/**
|
|
7931
|
+
* Google reCAPTCHA v3 토큰. 익명 발행 + 운영자가 reCAPTCHA 활성화한 앱은 권장.
|
|
7932
|
+
* 미주입 시 score=0 으로 처리 — 거부될 수 있음.
|
|
7933
|
+
*/
|
|
7934
|
+
recaptchaToken?: string;
|
|
7935
|
+
}
|
|
7936
|
+
/**
|
|
7937
|
+
* 제보 발행 응답. 보안상 최소 정보만 (id + status + created_at).
|
|
7938
|
+
*/
|
|
7939
|
+
interface ReportIssueResponse {
|
|
7940
|
+
id: string;
|
|
7941
|
+
status: 'open';
|
|
7942
|
+
created_at: string;
|
|
7943
|
+
}
|
|
7944
|
+
/**
|
|
7945
|
+
* Cross-app 위임 user 발행 요청 본문.
|
|
7946
|
+
*
|
|
7947
|
+
* 일반 `reportIssue` 와 다른 점:
|
|
7948
|
+
* - target_app 이 호출자(source_app) 와 다름 (다른 앱에 보내는 위임 제보)
|
|
7949
|
+
* - SDK 인증 컨텍스트(publicKey/secretKey) 가 아닌 cross-app OAuth Bearer access_token 사용
|
|
7950
|
+
* - reporter_member_id 는 access_token 의 end_user_id claim 에서 자동 추출 (body 로 전달 불가)
|
|
7951
|
+
*/
|
|
7952
|
+
interface ReportIssueOnBehalfOfUserRequest {
|
|
7953
|
+
/** 수신 앱(target) 의 ID. OAuth token 의 audience(`app:<uuid>`) 와 일치해야 함. */
|
|
7954
|
+
targetAppId: string;
|
|
7955
|
+
/**
|
|
7956
|
+
* Cross-app OAuth access_token (`authorization_code` grant — user binding 필수).
|
|
7957
|
+
* scope 에 `issue:report:on-behalf-of-user` 가 포함돼야 한다.
|
|
7958
|
+
* `client_credentials` grant 토큰은 401 `delegated_user_required` 로 거부됨.
|
|
7959
|
+
*/
|
|
7960
|
+
accessToken: string;
|
|
7961
|
+
/** 제목 (최대 200자) */
|
|
7962
|
+
title: string;
|
|
7963
|
+
/** 본문 (최대 16KB, 마크다운) */
|
|
7964
|
+
body: string;
|
|
7965
|
+
/** 카테고리. 기본값 `other`. AI triage 가 보정. */
|
|
7966
|
+
category?: SupportIssueCategory;
|
|
7967
|
+
/** 자유 컨텍스트 (페이지 URL, request_id 등). PII 직접 포함 금지. */
|
|
7968
|
+
metadata?: Record<string, unknown>;
|
|
7969
|
+
}
|
|
7970
|
+
/**
|
|
7971
|
+
* Support API — 사용자 제보 (end-user issue) 발행.
|
|
7972
|
+
*
|
|
7973
|
+
* 사용자가 앱 운영자에게 직접 버그/질문/요청을 보내는 채널. AppMember JWT 가 있으면
|
|
7974
|
+
* 강 신뢰로 회신 가능, 없으면 익명 발행 (reCAPTCHA 권장).
|
|
7975
|
+
*
|
|
7976
|
+
* 발행된 이슈는 운영자 콘솔의 inbox 에 들어가며, AI 가 자동으로 요약/긴급도/카테고리를
|
|
7977
|
+
* 분류해줘 운영자/AI 의 처리 효율을 높여준다.
|
|
7978
|
+
*
|
|
7979
|
+
* @example 로그인 사용자가 버그 리포트
|
|
7980
|
+
* ```typescript
|
|
7981
|
+
* // (cb.auth.signIn 으로 로그인 완료된 상태)
|
|
7982
|
+
* await cb.support.reportIssue({
|
|
7983
|
+
* title: "결제 화면이 멈춰요",
|
|
7984
|
+
* body: "결제 버튼 클릭 후 로딩이 끝나지 않습니다.",
|
|
7985
|
+
* category: "bug",
|
|
7986
|
+
* metadata: { pageUrl: window.location.href, browser: navigator.userAgent },
|
|
7987
|
+
* })
|
|
7988
|
+
* ```
|
|
7989
|
+
*
|
|
7990
|
+
* @example 익명 + reCAPTCHA v3
|
|
7991
|
+
* ```typescript
|
|
7992
|
+
* // window.grecaptcha 는 reCAPTCHA v3 site script 로드 후 전역으로 노출됨
|
|
7993
|
+
* const grecaptcha = (globalThis as any).grecaptcha
|
|
7994
|
+
* const SITE_KEY = "your-recaptcha-site-key"
|
|
7995
|
+
* const token = await grecaptcha.execute(SITE_KEY, { action: 'report_issue' })
|
|
7996
|
+
* await cb.support.reportIssue({
|
|
7997
|
+
* title: "버그 제보",
|
|
7998
|
+
* body: "익명으로 제보합니다",
|
|
7999
|
+
* category: "question",
|
|
8000
|
+
* anonymousEmail: "user@example.com",
|
|
8001
|
+
* recaptchaToken: token,
|
|
8002
|
+
* })
|
|
8003
|
+
* ```
|
|
8004
|
+
*/
|
|
8005
|
+
declare class SupportAPI {
|
|
8006
|
+
private http;
|
|
8007
|
+
constructor(http: HttpClient);
|
|
8008
|
+
/**
|
|
8009
|
+
* 앱 운영자에게 이슈/문의/요청을 발행한다.
|
|
8010
|
+
*
|
|
8011
|
+
* 로그인된 사용자(AppMember)면 신뢰 등급이 높고 reporter_member_id 가 자동으로 채워진다.
|
|
8012
|
+
* 익명 발행도 가능하나 운영자가 reCAPTCHA 를 활성화한 경우 `recaptchaToken` 이 권장된다.
|
|
8013
|
+
*
|
|
8014
|
+
* @returns 발행된 이슈 id + 초기 status (`open`) + 생성 시각.
|
|
8015
|
+
* @throws ApiError — 본문 길이 초과 / 쿼터 초과(429) / reCAPTCHA 거부(403) 등.
|
|
8016
|
+
*/
|
|
8017
|
+
reportIssue(req: ReportIssueRequest): Promise<ReportIssueResponse>;
|
|
8018
|
+
/**
|
|
8019
|
+
* 다른 앱(target) 에 자기 사용자(end-user) 명의로 cross-app 위임 이슈를 발행한다.
|
|
8020
|
+
*
|
|
8021
|
+
* **사용 시나리오**: source_app(예: Makers) 의 backend 가 OAuth `authorization_code` grant 로
|
|
8022
|
+
* 발급받은 사용자 access_token 을 사용해, target_app(예: ai-tool) 에게 "Makers user A 가 보낸"
|
|
8023
|
+
* 이슈로 제보. target 측 콘솔 inbox 에는 `reporter_kind=user` + `reporter_app_id=Makers` 로 표시.
|
|
8024
|
+
*
|
|
8025
|
+
* 본 메서드는 SDK 의 publicKey/secretKey 인증 컨텍스트를 사용하지 않고, 호출자가 명시적으로 전달한
|
|
8026
|
+
* cross-app OAuth Bearer access_token 만 사용 (server-to-server 위임 흐름).
|
|
8027
|
+
*
|
|
8028
|
+
* @param req - 발행 본문
|
|
8029
|
+
* @param req.targetAppId - 수신 앱 ID (= OAuth token 의 audience `app:<uuid>` 와 일치해야 함)
|
|
8030
|
+
* @param req.accessToken - source_app 이 발급받은 cross-app OAuth access_token (authorization_code grant)
|
|
8031
|
+
*
|
|
8032
|
+
* @throws ApiError 401 — `delegated_user_required`: access_token 이 client_credentials grant
|
|
8033
|
+
* @throws ApiError 403 — `trust_chain_violation`: source_app 이 target_app 의 cross-app provider 로 등록되지 않음
|
|
8034
|
+
* @throws ApiError 403 — `insufficient_scope`: token 에 `issue:report:on-behalf-of-user` 미포함
|
|
8035
|
+
*
|
|
8036
|
+
* @example Makers backend → ai-tool 에 사용자 명의 제보
|
|
8037
|
+
* ```typescript
|
|
8038
|
+
* await cb.support.reportIssueOnBehalfOfUser({
|
|
8039
|
+
* targetAppId: 'ai-tool-uuid',
|
|
8040
|
+
* accessToken: makersUserAccessToken, // authorization_code grant 토큰
|
|
8041
|
+
* title: '생성 실패',
|
|
8042
|
+
* body: 'prompt X 가 5분째 응답 없음',
|
|
8043
|
+
* category: 'bug',
|
|
8044
|
+
* metadata: { prompt_id: 'p_42' },
|
|
8045
|
+
* })
|
|
8046
|
+
* ```
|
|
8047
|
+
*/
|
|
8048
|
+
reportIssueOnBehalfOfUser(req: ReportIssueOnBehalfOfUserRequest): Promise<ReportIssueResponse>;
|
|
8049
|
+
/**
|
|
8050
|
+
* ConnectBase 플랫폼 자체 버그/요청/문의를 발행한다.
|
|
8051
|
+
*
|
|
8052
|
+
* `reportIssue` 와 다른 점: target 이 앱 운영자가 아닌 **ConnectBase 운영팀**.
|
|
8053
|
+
* SDK 가 자체 버그를 던지거나, 결제/문서/플랫폼 동작이 이상할 때 사용.
|
|
8054
|
+
*
|
|
8055
|
+
* 자동 첨부 (opt-out 가능):
|
|
8056
|
+
* - SDK 버전 + 플랫폼 (web/node)
|
|
8057
|
+
* - 마지막 N(20)개 API 호출 breadcrumb (PII strip 후)
|
|
8058
|
+
* - error 객체의 stack trace (sanitize)
|
|
8059
|
+
*
|
|
8060
|
+
* @example SDK 가 throw 한 에러를 자동 첨부해서 발행
|
|
8061
|
+
* ```typescript
|
|
8062
|
+
* try {
|
|
8063
|
+
* await cb.functions.invoke('foo', {})
|
|
8064
|
+
* } catch (err) {
|
|
8065
|
+
* await cb.support.reportPlatformBug({
|
|
8066
|
+
* title: 'functions.invoke 가 504 만 반환',
|
|
8067
|
+
* body: '같은 인자로 5분 째 504. 콘솔에선 정상.',
|
|
8068
|
+
* category: 'sdk',
|
|
8069
|
+
* severity: 'high',
|
|
8070
|
+
* error: err as Error,
|
|
8071
|
+
* })
|
|
8072
|
+
* }
|
|
8073
|
+
* ```
|
|
8074
|
+
*/
|
|
8075
|
+
reportPlatformBug(req: ReportPlatformBugRequest): Promise<ReportPlatformBugResponse>;
|
|
8076
|
+
/** 디버깅용: 마지막 N개 API 호출 breadcrumb. PII 제거 후 저장돼있다. */
|
|
8077
|
+
getRecentApiCalls(): RecentApiCall[];
|
|
8078
|
+
/** breadcrumb buffer 비우기 (사용자 명시적 요청 / 프라이버시). */
|
|
8079
|
+
clearRecentApiCalls(): void;
|
|
8080
|
+
/**
|
|
8081
|
+
* Platform issue 의 reply thread 조회.
|
|
8082
|
+
*
|
|
8083
|
+
* 본인이 발행한 issue 만 조회 가능 (server-side ownership guard). admin 의 internal 메모는 응답 제외.
|
|
8084
|
+
*
|
|
8085
|
+
* @param issueId - `reportPlatformBug` 가 반환한 id
|
|
8086
|
+
* @throws ApiError 404 — 본인 issue 가 아니거나 존재하지 않음
|
|
8087
|
+
*/
|
|
8088
|
+
listPlatformIssueReplies(issueId: string): Promise<PlatformIssueReply[]>;
|
|
8089
|
+
/**
|
|
8090
|
+
* Platform issue 에 follow-up reply 작성.
|
|
8091
|
+
*
|
|
8092
|
+
* 단말 상태(resolved/wontfix/duplicate) issue 는 reply 거부 — 새 issue 발행 권장.
|
|
8093
|
+
*/
|
|
8094
|
+
replyToPlatformIssue(issueId: string, body: string): Promise<PlatformIssueReply>;
|
|
8095
|
+
/**
|
|
8096
|
+
* 본인이 발행한 platform issue 의 처리 진행 상황을 단건 조회.
|
|
8097
|
+
*
|
|
8098
|
+
* AI 가 "내가 발행한 이슈 처리됐어?" 를 폴링하는 표준 경로. status / resolution_note /
|
|
8099
|
+
* external_links 로 ConnectBase 운영팀의 처리 상태를 확인.
|
|
8100
|
+
*
|
|
8101
|
+
* @throws ApiError 404 — 본인 issue 가 아니거나 존재하지 않음
|
|
8102
|
+
*/
|
|
8103
|
+
getPlatformIssue(issueId: string): Promise<PlatformIssueDetail>;
|
|
8104
|
+
/**
|
|
8105
|
+
* 본인 app 으로 발행한 platform issue 목록 (cursor 페이지네이션).
|
|
8106
|
+
*
|
|
8107
|
+
* status/severity/category 필터 + `since_updated_at` 으로 미해결만 폴링하는 사용 패턴 권장:
|
|
8108
|
+
*
|
|
8109
|
+
* ```typescript
|
|
8110
|
+
* const { issues } = await cb.support.listMyPlatformIssues({ status: ['open', 'triaged', 'in_progress'] })
|
|
8111
|
+
* ```
|
|
8112
|
+
*/
|
|
8113
|
+
listMyPlatformIssues(opts?: ListMyPlatformIssuesOptions): Promise<PlatformIssueListPage>;
|
|
8114
|
+
}
|
|
8115
|
+
/**
|
|
8116
|
+
* ConnectBase 플랫폼 이슈 카테고리.
|
|
8117
|
+
*/
|
|
8118
|
+
type PlatformIssueCategory = 'bug' | 'feature_request' | 'sdk' | 'billing' | 'security' | 'performance' | 'docs' | 'other';
|
|
8119
|
+
type PlatformIssueSeverity = 'low' | 'medium' | 'high' | 'critical';
|
|
8120
|
+
interface ReportPlatformBugRequest {
|
|
8121
|
+
/** 제목 (≤200) */
|
|
8122
|
+
title: string;
|
|
8123
|
+
/** 본문 (≤16KB, markdown) */
|
|
8124
|
+
body: string;
|
|
8125
|
+
/** 카테고리. 기본 `other`. 운영자가 admin 콘솔에서 조정 가능. */
|
|
8126
|
+
category?: PlatformIssueCategory;
|
|
8127
|
+
/** 긴급도. 기본 `medium`. 운영자가 admin 콘솔에서 조정 가능. */
|
|
8128
|
+
severity?: PlatformIssueSeverity;
|
|
8129
|
+
/** AppMember 가 없는 경우 회신용 이메일. */
|
|
8130
|
+
reporterEmail?: string;
|
|
8131
|
+
/** 익명 발행 시 reCAPTCHA v3 토큰 (운영팀이 RECAPTCHA_SECRET 설정한 경우). */
|
|
8132
|
+
recaptchaToken?: string;
|
|
8133
|
+
/** 자유 컨텍스트 (페이지 URL, request_id 등). PII 직접 포함 금지 — 호출자 책임. */
|
|
8134
|
+
metadata?: Record<string, unknown>;
|
|
8135
|
+
/**
|
|
8136
|
+
* 자동 컨텍스트 첨부 on/off (기본 true).
|
|
8137
|
+
* false 로 설정하면 SDK 버전/플랫폼/breadcrumb/stack 모두 미첨부.
|
|
8138
|
+
*/
|
|
8139
|
+
attachAutomaticContext?: boolean;
|
|
8140
|
+
/**
|
|
8141
|
+
* 에러 객체. .stack 을 sanitize 후 자동 첨부.
|
|
8142
|
+
* stackTrace 와 둘 중 하나만 — error 가 우선.
|
|
8143
|
+
*/
|
|
8144
|
+
error?: Error;
|
|
8145
|
+
/** 직접 작성한 stack trace (≤32KB, SDK 가 sanitize). */
|
|
8146
|
+
stackTrace?: string;
|
|
8147
|
+
/** 호출자가 명시적으로 buffer 를 override (테스트/특수 케이스). */
|
|
8148
|
+
recentApiCalls?: RecentApiCall[];
|
|
8149
|
+
/** SDK 버전 override (자동 감지 실패 시). */
|
|
8150
|
+
sdkVersion?: string;
|
|
8151
|
+
/** SDK 플랫폼 override. */
|
|
8152
|
+
sdkPlatform?: 'web' | 'node' | 'unity' | 'godot' | 'unreal' | 'cli' | 'mcp' | 'other';
|
|
8153
|
+
/** 환경 (production/staging/development). 기본 unknown. */
|
|
8154
|
+
environment?: 'production' | 'staging' | 'development' | 'unknown';
|
|
8155
|
+
}
|
|
8156
|
+
interface ReportPlatformBugResponse {
|
|
8157
|
+
id: string;
|
|
8158
|
+
status: 'open';
|
|
8159
|
+
created_at: string;
|
|
8160
|
+
}
|
|
8161
|
+
/**
|
|
8162
|
+
* Platform issue thread 의 단일 reply.
|
|
8163
|
+
*
|
|
8164
|
+
* - `author_kind=admin`: ConnectBase 운영팀 응답
|
|
8165
|
+
* - `author_kind=user`: 본인이 작성
|
|
8166
|
+
* - `author_kind=system`: 자동 메시지 (상태 변경 알림 등)
|
|
8167
|
+
*/
|
|
8168
|
+
/**
|
|
8169
|
+
* Platform issue 의 처리 진행 상황 (reporter 시점).
|
|
8170
|
+
*
|
|
8171
|
+
* admin-only 필드(`assignee_user_id` 등) 는 server 가 redact.
|
|
8172
|
+
*
|
|
8173
|
+
* 주요 필드:
|
|
8174
|
+
* - `status` : `open` → `triaged` → `in_progress` → `resolved` | `wontfix` | `duplicate`
|
|
8175
|
+
* - `resolution_note` : 단말 상태 진입 시 ConnectBase 운영팀이 작성한 처리 결과 (markdown)
|
|
8176
|
+
* - `external_links` : 운영팀이 연결한 GitHub PR / Linear ticket 등
|
|
8177
|
+
* - `resolved_at` : `resolved`/`wontfix` 진입 시각 (해결됐는지 빠른 검사용)
|
|
8178
|
+
*/
|
|
8179
|
+
interface PlatformIssueDetail {
|
|
8180
|
+
id: string;
|
|
8181
|
+
reporter_kind: 'user' | 'app' | 'system';
|
|
8182
|
+
title: string;
|
|
8183
|
+
body: string;
|
|
8184
|
+
category: PlatformIssueCategory;
|
|
8185
|
+
severity: PlatformIssueSeverity;
|
|
8186
|
+
status: 'open' | 'triaged' | 'in_progress' | 'resolved' | 'wontfix' | 'duplicate';
|
|
8187
|
+
resolution_note?: string;
|
|
8188
|
+
sdk_version?: string;
|
|
8189
|
+
sdk_platform?: 'web' | 'node' | 'unity' | 'godot' | 'unreal' | 'cli' | 'mcp' | 'other';
|
|
8190
|
+
environment?: 'production' | 'staging' | 'development' | 'unknown';
|
|
8191
|
+
external_links?: Array<{
|
|
8192
|
+
kind: string;
|
|
8193
|
+
url: string;
|
|
8194
|
+
label?: string;
|
|
8195
|
+
}>;
|
|
8196
|
+
duplicate_of_id?: string;
|
|
8197
|
+
created_at: string;
|
|
8198
|
+
updated_at: string;
|
|
8199
|
+
resolved_at?: string;
|
|
8200
|
+
}
|
|
8201
|
+
/** `listMyPlatformIssues` 응답 페이지. */
|
|
8202
|
+
interface PlatformIssueListPage {
|
|
8203
|
+
issues: PlatformIssueDetail[];
|
|
8204
|
+
/** 다음 페이지 cursor. 빈 문자열/undefined 면 끝. */
|
|
8205
|
+
next_cursor?: string;
|
|
8206
|
+
}
|
|
8207
|
+
/** `listMyPlatformIssues` 옵션. */
|
|
8208
|
+
interface ListMyPlatformIssuesOptions {
|
|
8209
|
+
/** 다중 status 필터. 미지정 시 전체. 처리중만 보려면 `['open','triaged','in_progress']`. */
|
|
8210
|
+
status?: PlatformIssueDetail['status'][];
|
|
8211
|
+
severity?: PlatformIssueSeverity[];
|
|
8212
|
+
category?: PlatformIssueCategory[];
|
|
8213
|
+
/** RFC3339 timestamp — 이후 update 된 issue 만. polling 시 마지막 fetch 시각 전달. */
|
|
8214
|
+
sinceUpdatedAt?: string;
|
|
8215
|
+
/** 이전 페이지 응답의 `next_cursor`. */
|
|
8216
|
+
cursor?: string;
|
|
8217
|
+
/** 페이지 크기. 기본 50, 최대 200. */
|
|
8218
|
+
limit?: number;
|
|
8219
|
+
}
|
|
8220
|
+
interface PlatformIssueReply {
|
|
8221
|
+
id: string;
|
|
8222
|
+
issue_id: string;
|
|
8223
|
+
author_kind: 'admin' | 'user' | 'system';
|
|
8224
|
+
author_user_id?: string;
|
|
8225
|
+
author_member_id?: string;
|
|
8226
|
+
body: string;
|
|
8227
|
+
is_internal: boolean;
|
|
8228
|
+
created_at: string;
|
|
8229
|
+
}
|
|
8230
|
+
|
|
5441
8231
|
/**
|
|
5442
8232
|
* WebTransport-based Game Client
|
|
5443
8233
|
*
|
|
@@ -5484,6 +8274,8 @@ declare class GameRoomTransport {
|
|
|
5484
8274
|
private actionSequence;
|
|
5485
8275
|
private _roomId;
|
|
5486
8276
|
private _state;
|
|
8277
|
+
private _scriptName;
|
|
8278
|
+
private _scriptVersion;
|
|
5487
8279
|
private _isConnected;
|
|
5488
8280
|
private _connectionStatus;
|
|
5489
8281
|
private _lastError;
|
|
@@ -5510,6 +8302,10 @@ declare class GameRoomTransport {
|
|
|
5510
8302
|
* Connection status
|
|
5511
8303
|
*/
|
|
5512
8304
|
get isConnected(): boolean;
|
|
8305
|
+
/** Attached lua script 이름 — createRoom 이후 server 가 echo 한 값. */
|
|
8306
|
+
get scriptName(): string | null;
|
|
8307
|
+
/** Attached lua script 의 active version. */
|
|
8308
|
+
get scriptVersion(): number | null;
|
|
5513
8309
|
/**
|
|
5514
8310
|
* Connection state information
|
|
5515
8311
|
*/
|
|
@@ -5531,9 +8327,16 @@ declare class GameRoomTransport {
|
|
|
5531
8327
|
*/
|
|
5532
8328
|
disconnect(): void;
|
|
5533
8329
|
/**
|
|
5534
|
-
* Create a new room
|
|
8330
|
+
* Create a new room (호환 시그니처) — 초기 상태만 반환.
|
|
8331
|
+
*
|
|
8332
|
+
* Attached script 메타가 필요하면 {@link createRoomDetailed} 또는 인스턴스 getter
|
|
8333
|
+
* (`transport.scriptName`, `transport.scriptVersion`) 를 사용.
|
|
5535
8334
|
*/
|
|
5536
8335
|
createRoom(config?: GameRoomConfig): Promise<GameState>;
|
|
8336
|
+
/**
|
|
8337
|
+
* Create a new room — server 가 attach 한 lua script 메타 함께 반환.
|
|
8338
|
+
*/
|
|
8339
|
+
createRoomDetailed(config?: GameRoomConfig): Promise<CreateRoomResult>;
|
|
5537
8340
|
/**
|
|
5538
8341
|
* Join an existing room
|
|
5539
8342
|
*/
|
|
@@ -5606,9 +8409,21 @@ interface ConnectBaseConfig {
|
|
|
5606
8409
|
*/
|
|
5607
8410
|
appId?: string;
|
|
5608
8411
|
/**
|
|
5609
|
-
*
|
|
8412
|
+
* Public Key (cb_pk_* 형식, 콘솔 → 설정 → API 에서 발급)
|
|
8413
|
+
* 브라우저/클라이언트에서 사용. RLS(Row Level Security) 적용.
|
|
8414
|
+
*/
|
|
8415
|
+
publicKey?: string;
|
|
8416
|
+
/**
|
|
8417
|
+
* User Secret Key (`cb_sk_*` 형식, 사용자 프로필에서 발급).
|
|
8418
|
+
*
|
|
8419
|
+
* **SDK 의 앱 레벨 API (database, storage, function 등) 용 자격증명이 아닙니다.**
|
|
8420
|
+
* 앱 API 호출에는 `publicKey` 를 사용하세요. 앱 API 경로(`/v1/public/*`)는
|
|
8421
|
+
* Public Key(`cb_pk_*`) 만 허용하며, Secret Key 로 호출하면 서버가 401 을 반환합니다.
|
|
8422
|
+
*
|
|
8423
|
+
* 이 필드는 CLI (`connectbase tunnel`, `connectbase mcp`) 및 사용자 레벨 API 인증에
|
|
8424
|
+
* 사용됩니다. 서버 환경에서만 사용하고 절대 브라우저에 노출하지 마세요.
|
|
5610
8425
|
*/
|
|
5611
|
-
|
|
8426
|
+
secretKey?: string;
|
|
5612
8427
|
/**
|
|
5613
8428
|
* 토큰이 갱신될 때 호출되는 콜백
|
|
5614
8429
|
*/
|
|
@@ -5626,7 +8441,7 @@ interface ConnectBaseConfig {
|
|
|
5626
8441
|
* @example
|
|
5627
8442
|
* ```typescript
|
|
5628
8443
|
* const cb = new ConnectBase({
|
|
5629
|
-
*
|
|
8444
|
+
* publicKey: 'your-public-key',
|
|
5630
8445
|
* onTokenExpired: () => {
|
|
5631
8446
|
* // 로그인 페이지로 리다이렉트
|
|
5632
8447
|
* window.location.href = '/login'
|
|
@@ -5635,10 +8450,64 @@ interface ConnectBaseConfig {
|
|
|
5635
8450
|
* ```
|
|
5636
8451
|
*/
|
|
5637
8452
|
onTokenExpired?: () => void;
|
|
8453
|
+
/**
|
|
8454
|
+
* `/v1/auth/re-issue` 의 일시적 실패 (5xx, 네트워크 오류, abort, 손상된 200 응답) 시 호출.
|
|
8455
|
+
*
|
|
8456
|
+
* 이 경우 토큰은 폐기되지 않으며 `onTokenExpired` 도 호출되지 않습니다 — 다음 호출에서
|
|
8457
|
+
* backoff 만료 후 자동 재시도. 사용자에게 "연결이 잠시 불안정합니다" 같은 비파괴적
|
|
8458
|
+
* 알림을 띄울 때 사용. permanent (refresh token 자체 무효) 와 분리하기 위함.
|
|
8459
|
+
*
|
|
8460
|
+
* @example
|
|
8461
|
+
* ```typescript
|
|
8462
|
+
* const cb = new ConnectBase({
|
|
8463
|
+
* publicKey: '...',
|
|
8464
|
+
* onTransientRefreshFailure: (err) => toast.warn('연결이 불안정합니다. 잠시 후 자동 복구됩니다.'),
|
|
8465
|
+
* onTokenExpired: () => { window.location.href = '/login' },
|
|
8466
|
+
* })
|
|
8467
|
+
* ```
|
|
8468
|
+
*/
|
|
8469
|
+
onTransientRefreshFailure?: (error: Error) => void;
|
|
8470
|
+
/**
|
|
8471
|
+
* 토큰 저장 방식.
|
|
8472
|
+
* - 'none' (기본·권장): access token 만 메모리. refresh token 은 서버 HttpOnly cookie 로
|
|
8473
|
+
* 보관되어 JS 가 접근할 수 없음 (XSS 시 탈취 불가). 새로고침 후에는 `autoRestoreSession`
|
|
8474
|
+
* (기본 true) 으로 cookie 만으로 자동 복구.
|
|
8475
|
+
* - 'sessionStorage': 탭 종료 시 삭제 (XSS 시 탭 세션 탈취 가능 — 콘솔 경고 출력)
|
|
8476
|
+
* - 'localStorage': 브라우저 종료 후에도 유지 (XSS 시 영구 탈취 가능 — 콘솔 경고 출력)
|
|
8477
|
+
* @default 'none'
|
|
8478
|
+
*/
|
|
8479
|
+
persistence?: TokenPersistence;
|
|
8480
|
+
/**
|
|
8481
|
+
* 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 자동 복구할지 여부.
|
|
8482
|
+
* 브라우저 환경에서 기본 true. cookie 가 없거나 만료되었으면 조용히 실패.
|
|
8483
|
+
*
|
|
8484
|
+
* cookie 가 없는 미로그인 상태에서 콘솔에 401 노이즈가 찍히지 않도록 silent 처리한다.
|
|
8485
|
+
*
|
|
8486
|
+
* @default true (브라우저), false (Node.js / RN)
|
|
8487
|
+
*/
|
|
8488
|
+
autoRestoreSession?: boolean;
|
|
5638
8489
|
/**
|
|
5639
8490
|
* 에러 트래커 설정
|
|
5640
8491
|
*/
|
|
5641
8492
|
errorTracker?: ErrorTrackerConfig;
|
|
8493
|
+
/**
|
|
8494
|
+
* 기본 요청 타임아웃(ms). 개별 호출의 `timeout` 이 지정되면 해당 값이 우선.
|
|
8495
|
+
* 기본값 30000ms. 0 또는 음수 지정 시 타임아웃 비활성화.
|
|
8496
|
+
*/
|
|
8497
|
+
requestTimeoutMs?: number;
|
|
8498
|
+
/**
|
|
8499
|
+
* 전역 에러 관찰자. 모든 `ApiError` / `AuthError` 발생 시 호출됩니다.
|
|
8500
|
+
* Sentry/Datadog 등 관측성 파이프라인과 연결하기 위한 훅.
|
|
8501
|
+
*
|
|
8502
|
+
* @example
|
|
8503
|
+
* ```ts
|
|
8504
|
+
* const cb = new ConnectBase({
|
|
8505
|
+
* publicKey: '...',
|
|
8506
|
+
* onError: (err) => Sentry.captureException(err),
|
|
8507
|
+
* })
|
|
8508
|
+
* ```
|
|
8509
|
+
*/
|
|
8510
|
+
onError?: (error: ApiError | AuthError) => void;
|
|
5642
8511
|
}
|
|
5643
8512
|
/**
|
|
5644
8513
|
* Connect Base SDK
|
|
@@ -5649,13 +8518,13 @@ interface ConnectBaseConfig {
|
|
|
5649
8518
|
*
|
|
5650
8519
|
* // 간단 초기화 (API Key만 필요)
|
|
5651
8520
|
* const cb = new ConnectBase({
|
|
5652
|
-
*
|
|
8521
|
+
* publicKey: 'your-public-key'
|
|
5653
8522
|
* })
|
|
5654
8523
|
*
|
|
5655
8524
|
* // 커스텀 서버 URL 사용 시
|
|
5656
8525
|
* const cb = new ConnectBase({
|
|
5657
8526
|
* baseUrl: 'https://your-server.com',
|
|
5658
|
-
*
|
|
8527
|
+
* publicKey: 'your-public-key'
|
|
5659
8528
|
* })
|
|
5660
8529
|
*
|
|
5661
8530
|
* // 데이터 조회
|
|
@@ -5689,9 +8558,10 @@ declare class ConnectBase {
|
|
|
5689
8558
|
*/
|
|
5690
8559
|
readonly storage: StorageAPI;
|
|
5691
8560
|
/**
|
|
5692
|
-
*
|
|
8561
|
+
* Public Key 관리 API
|
|
5693
8562
|
*/
|
|
5694
|
-
readonly
|
|
8563
|
+
readonly publicKey: PublicKeyAPI;
|
|
8564
|
+
/**
|
|
5695
8565
|
/**
|
|
5696
8566
|
* 서버리스 함수 API
|
|
5697
8567
|
*/
|
|
@@ -5742,16 +8612,54 @@ declare class ConnectBase {
|
|
|
5742
8612
|
*/
|
|
5743
8613
|
readonly native: NativeAPI;
|
|
5744
8614
|
/**
|
|
5745
|
-
*
|
|
8615
|
+
* AI 데이터베이스 API (문서 검색)
|
|
5746
8616
|
* 문서를 업로드하고 키워드 기반으로 검색하여 AI 챗봇을 구축할 수 있습니다.
|
|
5747
8617
|
*/
|
|
5748
8618
|
readonly knowledge: KnowledgeAPI;
|
|
8619
|
+
/**
|
|
8620
|
+
* AI API (채팅 + AI 데이터베이스 연동)
|
|
8621
|
+
* AI 채팅, AI 데이터베이스 문서 참조 응답, 스트리밍 지원.
|
|
8622
|
+
*/
|
|
8623
|
+
readonly ai: AIAPI;
|
|
5749
8624
|
/**
|
|
5750
8625
|
* Queue API (메시지 큐)
|
|
5751
8626
|
* NATS JetStream 기반 고신뢰 메시지 큐. 발행, 소비, Ack, Nack 지원.
|
|
5752
8627
|
*/
|
|
5753
8628
|
readonly queue: QueueAPI;
|
|
8629
|
+
/**
|
|
8630
|
+
* Analytics API (웹 분석)
|
|
8631
|
+
* 페이지뷰, 커스텀 이벤트, 세션 추적. GA4 수준의 웹 분석 제공.
|
|
8632
|
+
*/
|
|
8633
|
+
readonly analytics: AnalyticsAPI;
|
|
8634
|
+
/**
|
|
8635
|
+
* Endpoint API (로컬 모델 터널 dumb pipe)
|
|
8636
|
+
* 사용자 PC GPU 모델 (ComfyUI / A1111 / Hunyuan3D / 자체 FastAPI 등) 을 `cb_pk_*`
|
|
8637
|
+
* 한 키로 호출. 라벨로 라우팅, 페이로드/응답은 그대로 통과.
|
|
8638
|
+
*/
|
|
8639
|
+
readonly endpoint: EndpointAPI;
|
|
8640
|
+
/**
|
|
8641
|
+
* Support API (사용자 제보 / 이슈 큐)
|
|
8642
|
+
* 앱 사용자가 운영자에게 버그/질문/요청을 발행. AI 가 자동 분류/우선순위 처리.
|
|
8643
|
+
*/
|
|
8644
|
+
readonly support: SupportAPI;
|
|
5754
8645
|
constructor(config?: ConnectBaseConfig);
|
|
8646
|
+
/**
|
|
8647
|
+
* 현재 페이지가 OAuth 리다이렉트 콜백 페이지인지 판별.
|
|
8648
|
+
*
|
|
8649
|
+
* `?access_token=...&refresh_token=...` (legacy 토큰-in-URL 흐름) 또는
|
|
8650
|
+
* `?code=...` (OAUTH_CODE_ONLY 흐름) 또는 OAuth 에러 응답 (`?error=...&state=...`)
|
|
8651
|
+
* 패턴이면 true. 콜백 페이지에서 `autoRestoreSession` 을 건너뛰어 rotation race 를 피한다.
|
|
8652
|
+
*/
|
|
8653
|
+
private isOAuthCallbackUrl;
|
|
8654
|
+
/**
|
|
8655
|
+
* 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
|
|
8656
|
+
*
|
|
8657
|
+
* 일반적으로는 ConnectBase 생성 시 `autoRestoreSession: true` (기본값) 으로 자동 호출되지만,
|
|
8658
|
+
* 호출 결과를 await 해서 로그인 상태에 따라 다른 UI 를 그리고 싶다면 명시적으로 호출한다.
|
|
8659
|
+
*
|
|
8660
|
+
* @returns access token 복원 성공 시 true, 미로그인/cookie 만료 시 false
|
|
8661
|
+
*/
|
|
8662
|
+
restoreSession(): Promise<boolean>;
|
|
5755
8663
|
/**
|
|
5756
8664
|
* 수동으로 토큰 설정 (기존 토큰으로 세션 복원 시)
|
|
5757
8665
|
*/
|
|
@@ -5766,4 +8674,4 @@ declare class ConnectBase {
|
|
|
5766
8674
|
updateConfig(config: Partial<ConnectBaseConfig>): void;
|
|
5767
8675
|
}
|
|
5768
8676
|
|
|
5769
|
-
export { type AckMessagesRequest, type AdReportResponse, type AdReportSummary, AdsAPI, type AggregateResult, type AggregateStage, ApiError, type
|
|
8677
|
+
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, detectInAppBrowser, escapeToExternalBrowser, isWebTransportSupported, toCreateRoomWire };
|