connectbase-client 3.28.0 → 3.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,40 @@
3
3
  본 SDK 의 모든 주요 변경사항을 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/) 형식으로 기록합니다.
4
4
  버전은 [Semantic Versioning](https://semver.org/lang/ko/) 을 따릅니다.
5
5
 
6
+ ## [3.29.0] - 2026-06-02
7
+
8
+ ### Added — `connectbase tunnel --token <고정값>` proxy_token 핀 (platform-issue 019e8623)
9
+
10
+ 비-public 터널의 `proxy_token` 이 **재연결마다 회전**해, 그 토큰을 스냅샷 저장하는 통합
11
+ (외부 MCP `custom_header X-Proxy-Token`, 터널을 가리키는 웹훅 등)이 다음 재연결부터
12
+ `401 invalid or missing proxy token` 으로 깨지던 문제를 해결한다.
13
+
14
+ - **`cli.ts` — `tunnel --token <value>`** — proxy_token 을 고정값으로 핀. 재연결 핸드셰이크마다
15
+ 같은 값(`?proxy_token=`)을 보내 회전을 방지한다. 16~128자 `[A-Za-z0-9_-]` 검증(클라 선검사 +
16
+ 서버 `ValidateProxyToken` 동일 규칙). 미지정 시 기존 동작(매 연결 새 발급) 유지 — 하위호환.
17
+ - **CLI 라벨 재등록 idempotent** — 같은 라벨 + 같은 `tunnel_id` 재등록은 더 이상 409 경고를
18
+ 내지 않고 기존 바인딩을 그대로 유지한다(콘솔 수동 PATCH 안내 제거). 라벨이 *다른* tunnel_id 에
19
+ 묶여 있을 때만 충돌로 처리. 회전이 이미 일어나 깨진 경우의 즉시 복구용으로 백엔드에
20
+ 현재 토큰 조회 경로(MCP `get_tunnel_label_proxy_token` / REST `GET .../endpoint-proxy-token/:label`)도
21
+ 추가됐다.
22
+
23
+ ## [3.28.1] - 2026-06-02
24
+
25
+ ### Added — `RelationType` 에 `many-to-one` 추가 (platform-issue 019e83a0-c7d0)
26
+
27
+ `cb.database.createRelation` 의 `relation_type` 으로 `many-to-one`(외래키→부모 단일 객체
28
+ populate 의 정석)을 지정할 수 있게 됐다. 기존엔 문서/MCP 는 `many-to-one` 을 안내하면서도
29
+ SDK/REST 관계 API 는 `one-to-one | one-to-many | many-to-many` 3종만 허용해, 문서대로
30
+ `many-to-one` 을 쓰면 `400 유효하지 않은 관계 타입` + TS 타입 에러가 났다.
31
+
32
+ - **`types/database.ts` — `RelationType`** 에 `'many-to-one'` 추가(4종 동등). additive 변경이라
33
+ 하위호환. 백엔드(data-server)도 ent enum·populate(단일 객체) 동등 처리하도록 함께 수정됨.
34
+
35
+ ### Fixed — `oauth.signIn` JSDoc 예시 정정
36
+
37
+ `createIfNotExists` 예시가 `callbackUrl` 자리에 클라이언트 인스턴스(`cb`)를 넘기는 오기였다.
38
+ 실제 URL 문자열로 교정(런타임/타입 영향 없음, 문서만).
39
+
6
40
  ## [3.28.0] - 2026-06-02
7
41
 
8
42
  ### Added — `cb.ai.chatStream` 취소(AbortSignal) 지원 (platform-issue 019e82e1)
package/dist/cli.js CHANGED
@@ -1779,7 +1779,7 @@ async function registerEndpointBinding(baseUrl, appId, secretKey, tunnelId, labe
1779
1779
  success(`Endpoint "${label}" \uC790\uB3D9 \uB4F1\uB85D \uC644\uB8CC`);
1780
1780
  log(`${colors.green}\u2192${colors.reset} SDK: ${colors.cyan}cb.endpoint.call("${label}", { path: "/...", method: "POST", body: ... })${colors.reset}`);
1781
1781
  } else if (res.status === 409) {
1782
- warn(`"${label}" \uB77C\uBCA8\uC774 \uC774\uBBF8 \uB4F1\uB85D\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. \uC0C8 tunnel_id \uB85C \uAC31\uC2E0\uD558\uB824\uBA74 \uCF58\uC194\uC5D0\uC11C \uC218\uB3D9 PATCH.`);
1782
+ warn(`"${label}" \uB77C\uBCA8\uC774 \uB2E4\uB978 \uD130\uB110\uC5D0 \uC774\uBBF8 \uBB36\uC5EC \uC788\uC2B5\uB2C8\uB2E4. \uC774 \uD130\uB110\uB85C \uC62E\uAE30\uB824\uBA74 \uCF58\uC194\uC5D0\uC11C endpoint \uC758 tunnel_id \uB97C \uC218\uC815(PATCH)\uD558\uC138\uC694.`);
1783
1783
  } else {
1784
1784
  const text = await res.text().catch(() => "");
1785
1785
  error(`Endpoint "${label}" \uC790\uB3D9 \uB4F1\uB85D \uC2E4\uD328 (status ${res.status}): ${text || "(\uBE48 \uC751\uB2F5)"}`);
@@ -1867,6 +1867,9 @@ async function startTunnel(port, config, tunnelOpts) {
1867
1867
  if (tunnelOpts?.public) {
1868
1868
  wsPath += `&public=1`;
1869
1869
  }
1870
+ if (tunnelOpts?.token) {
1871
+ wsPath += `&proxy_token=${encodeURIComponent(tunnelOpts.token)}`;
1872
+ }
1870
1873
  let reconnectAttempts = 0;
1871
1874
  const maxReconnectAttempts = 10;
1872
1875
  let shouldReconnect = true;
@@ -2350,6 +2353,7 @@ ${colors.yellow}\uC635\uC158:${colors.reset}
2350
2353
  --force \uD130\uB110 lockfile \uBB34\uC2DC (\uC911\uBCF5 \uC2E4\uD589 \uAC15\uC81C, tunnel \uC804\uC6A9)
2351
2354
  --public proxy_token \uAC80\uC99D \uBE44\uD65C\uC131\uD654 \u2014 \uC6F9\uD6C5/\uC678\uBD80 \uC9C1\uC811 \uD638\uCD9C\uC6A9 (tunnel \uC804\uC6A9)
2352
2355
  --show-token proxy_token \uACFC curl \uC608\uC2DC\uB97C \uCD9C\uB825 (--public \uC544\uB2CC \uACBD\uC6B0)
2356
+ --token <value> proxy_token \uACE0\uC815\uAC12 (16-128\uC790 [A-Za-z0-9_-]) \u2014 \uC7AC\uC5F0\uACB0\uC5D0\uB3C4 \uD1A0\uD070 \uD68C\uC804 \uBC29\uC9C0. \uC678\uBD80 MCP custom_header \uB4F1 \uC548\uC815 \uD1B5\uD569\uC6A9 (tunnel \uC804\uC6A9)
2353
2357
  --label <name> Endpoint binding \uC790\uB3D9 \uB4F1\uB85D (tunnel \uC804\uC6A9) \u2014 SDK \uC758 cb.endpoint.call(label) \uD638\uCD9C \uAC00\uB2A5
2354
2358
  --description <text> Endpoint binding \uC124\uBA85 (--label \uB3D9\uBC18 \uC2DC\uB9CC)
2355
2359
  -d, --dev Dev \uD658\uACBD\uC5D0 \uBC30\uD3EC (deploy \uC804\uC6A9)
@@ -2429,6 +2433,8 @@ function parseArgs(args) {
2429
2433
  result.options.label = args[++i];
2430
2434
  } else if (arg === "--description") {
2431
2435
  result.options.description = args[++i];
2436
+ } else if (arg === "--token") {
2437
+ result.options.token = args[++i];
2432
2438
  } else if (arg === "-a" || arg === "--app") {
2433
2439
  result.options.appId = args[++i];
2434
2440
  } else if (arg === "--force") {
@@ -2548,6 +2554,13 @@ async function main() {
2548
2554
  if (parsed.options.description) {
2549
2555
  tunnelOpts.description = parsed.options.description;
2550
2556
  }
2557
+ if (parsed.options.token) {
2558
+ if (!/^[A-Za-z0-9_-]{16,128}$/.test(parsed.options.token)) {
2559
+ error("--token \uC740 16-128\uC790\uC758 [A-Za-z0-9_-] \uC5EC\uC57C \uD569\uB2C8\uB2E4");
2560
+ process.exit(1);
2561
+ }
2562
+ tunnelOpts.token = parsed.options.token;
2563
+ }
2551
2564
  await startTunnel(port, config, tunnelOpts);
2552
2565
  } else {
2553
2566
  error(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839\uC5B4: ${parsed.command}`);
package/dist/index.d.mts CHANGED
@@ -1372,7 +1372,7 @@ interface SlowQueryInfo {
1372
1372
  count: number;
1373
1373
  avg_time: number;
1374
1374
  }
1375
- type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-many';
1375
+ type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-one' | 'many-to-many';
1376
1376
  interface TableRelation {
1377
1377
  id: string;
1378
1378
  app_id: string;
@@ -3874,7 +3874,7 @@ declare class OAuthAPI {
3874
3874
  * await cb.oauth.signIn('google', 'https://myapp.com/oauth/callback')
3875
3875
  *
3876
3876
  * // 로그인=가입 통합 버튼 — 신규면 자동 가입
3877
- * await cb.oauth.signIn('google', cb, undefined, { createIfNotExists: true })
3877
+ * await cb.oauth.signIn('google', 'https://myapp.com/oauth/callback', undefined, { createIfNotExists: true })
3878
3878
  * ```
3879
3879
  *
3880
3880
  * @example
package/dist/index.d.ts CHANGED
@@ -1372,7 +1372,7 @@ interface SlowQueryInfo {
1372
1372
  count: number;
1373
1373
  avg_time: number;
1374
1374
  }
1375
- type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-many';
1375
+ type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-one' | 'many-to-many';
1376
1376
  interface TableRelation {
1377
1377
  id: string;
1378
1378
  app_id: string;
@@ -3874,7 +3874,7 @@ declare class OAuthAPI {
3874
3874
  * await cb.oauth.signIn('google', 'https://myapp.com/oauth/callback')
3875
3875
  *
3876
3876
  * // 로그인=가입 통합 버튼 — 신규면 자동 가입
3877
- * await cb.oauth.signIn('google', cb, undefined, { createIfNotExists: true })
3877
+ * await cb.oauth.signIn('google', 'https://myapp.com/oauth/callback', undefined, { createIfNotExists: true })
3878
3878
  * ```
3879
3879
  *
3880
3880
  * @example
package/dist/index.js CHANGED
@@ -4837,7 +4837,7 @@ var OAuthAPI = class {
4837
4837
  * await cb.oauth.signIn('google', 'https://myapp.com/oauth/callback')
4838
4838
  *
4839
4839
  * // 로그인=가입 통합 버튼 — 신규면 자동 가입
4840
- * await cb.oauth.signIn('google', cb, undefined, { createIfNotExists: true })
4840
+ * await cb.oauth.signIn('google', 'https://myapp.com/oauth/callback', undefined, { createIfNotExists: true })
4841
4841
  * ```
4842
4842
  *
4843
4843
  * @example
package/dist/index.mjs CHANGED
@@ -4791,7 +4791,7 @@ var OAuthAPI = class {
4791
4791
  * await cb.oauth.signIn('google', 'https://myapp.com/oauth/callback')
4792
4792
  *
4793
4793
  * // 로그인=가입 통합 버튼 — 신규면 자동 가입
4794
- * await cb.oauth.signIn('google', cb, undefined, { createIfNotExists: true })
4794
+ * await cb.oauth.signIn('google', 'https://myapp.com/oauth/callback', undefined, { createIfNotExists: true })
4795
4795
  * ```
4796
4796
  *
4797
4797
  * @example
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "3.28.0",
3
+ "version": "3.29.0",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",