connectbase-client 6.3.0 → 6.3.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/cli.js +66 -39
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
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
+ ## [6.3.1] - 2026-09-08
7
+
8
+ ### Fixed — `init` 의 웹 스토리지 생성 실패가 `[object Object]` 로만 찍히던 문제
9
+
10
+ 앱에 이미 같은 이름의 웹 스토리지가 있는 상태에서 `init` 이 같은 이름으로 생성을 시도하면
11
+ 서버는 409 를 돌려주는데, CLI 가 그 응답 본문(`{ error: { code, message } }`)을 문자열로
12
+ 찍어 `생성 실패: [object Object]` 만 보였습니다. 원인이 이름 충돌이라는 사실을 알 길이
13
+ 없었고, 스토리지 선택 프롬프트에서 Enter 만 치면 정확히 이 경로로 들어갔습니다.
14
+
15
+ - 에러 응답 형식이 `{ error: "문자열" }` 이든 표준 봉투 `{ error: { code, message,
16
+ suggestion } }` 이든 사람이 읽을 메시지로 정리하는 헬퍼(`apiErrorMessage`)를 두고,
17
+ `init` / `deploy` / `promote` / 배포 이력 / presign / 앱 생성 / 인증 세션의 에러 출력을
18
+ 전부 이 헬퍼로 통일했습니다. 서버가 제안(suggestion)을 보내면 함께 보여 줍니다.
19
+ - 대화형 `init` 도 `--yes` 와 같은 규칙으로, 입력한 이름과 같은 웹 스토리지가 이미 있으면
20
+ 생성 대신 그 스토리지를 재사용합니다. 서버 요청 없이 즉시 넘어가므로 409 자체가 나지
21
+ 않습니다.
22
+ - 서버(core-server)도 같은 앱의 이름 중복을 미리 검사해 `같은 이름의 웹 스토리지가 이미
23
+ 있습니다: "<이름>"` 이라는 409 를 돌려줍니다. 구버전 CLI 로도 이제 이 문장이 보입니다.
24
+
6
25
  ## [6.3.0] - 2026-09-07
7
26
 
8
27
  ### Fixed — Secret Key 가 만료되면 `init` 을 다시 돌릴 수 없던 문제
package/dist/cli.js CHANGED
@@ -32,6 +32,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var cli_exports = {};
33
33
  __export(cli_exports, {
34
34
  TunnelConnectSelector: () => TunnelConnectSelector,
35
+ apiErrorMessage: () => apiErrorMessage,
35
36
  computeDeployDiff: () => computeDeployDiff,
36
37
  getTunnelConnectCandidates: () => getTunnelConnectCandidates,
37
38
  guessMimeType: () => guessMimeType,
@@ -147,7 +148,7 @@ function handleTunnelError(msg, appId, localPort) {
147
148
  }
148
149
 
149
150
  // src/cli.ts
150
- var VERSION = "6.3.0" ? "6.3.0" : getPackageVersion();
151
+ var VERSION = "6.3.1" ? "6.3.1" : getPackageVersion();
151
152
  var DEFAULT_BASE_URL = "https://api.connectbase.world";
152
153
  var colors = {
153
154
  reset: "\x1B[0m",
@@ -420,9 +421,9 @@ async function requestDeployPresign(baseStorageUrl, headers, files, timeoutMs) {
420
421
  );
421
422
  if (res.status === 404) return null;
422
423
  if (res.status < 200 || res.status >= 300) {
423
- const data = res.data;
424
- const msg = typeof data === "object" && data !== null ? data.message || data.error || JSON.stringify(data) : `HTTP ${res.status}`;
425
- throw new Error(`presign \uC694\uCCAD \uC2E4\uD328: ${msg}`);
424
+ throw new Error(
425
+ `presign \uC694\uCCAD \uC2E4\uD328: ${apiErrorMessage(res.data, res.status)}`
426
+ );
426
427
  }
427
428
  const parsed = res.data;
428
429
  return parsed?.urls ?? [];
@@ -580,6 +581,27 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
580
581
  }
581
582
  }
582
583
  var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
584
+ function apiErrorMessage(data, status) {
585
+ const fallback = `HTTP ${status}`;
586
+ if (typeof data === "string") return data.trim() || fallback;
587
+ if (typeof data !== "object" || data === null) return fallback;
588
+ const body = data;
589
+ const str = (v) => typeof v === "string" && v.trim() ? v.trim() : void 0;
590
+ let message;
591
+ let suggestion;
592
+ if (typeof body.error === "object" && body.error !== null) {
593
+ const detail = body.error;
594
+ message = str(detail.message) ?? str(detail.code);
595
+ suggestion = str(detail.suggestion);
596
+ } else {
597
+ message = str(body.message) ?? str(body.error);
598
+ }
599
+ if (!message) {
600
+ const raw = JSON.stringify(body);
601
+ return raw === "{}" ? fallback : `${fallback}: ${raw}`;
602
+ }
603
+ return suggestion ? `${message} (${suggestion})` : message;
604
+ }
583
605
  async function makeRequest(url, method, headers, body, reqOpts = {}) {
584
606
  return new Promise((resolve2, reject) => {
585
607
  const parsedUrl = new URL(url);
@@ -768,9 +790,9 @@ async function tryFetchManifest(baseStorageUrl, headers) {
768
790
  );
769
791
  if (res.status === 404) return null;
770
792
  if (res.status < 200 || res.status >= 300) {
771
- const data = res.data;
772
- const msg = typeof data === "object" && data !== null ? data.message || data.error || JSON.stringify(data) : `HTTP ${res.status}`;
773
- throw new Error(`manifest \uC870\uD68C \uC2E4\uD328: ${msg}`);
793
+ throw new Error(
794
+ `manifest \uC870\uD68C \uC2E4\uD328: ${apiErrorMessage(res.data, res.status)}`
795
+ );
774
796
  }
775
797
  const parsed = res.data;
776
798
  if (!parsed || !Array.isArray(parsed.files)) {
@@ -920,8 +942,7 @@ ${colors.cyan}URL: ${data.url}${colors.reset}
920
942
  `);
921
943
  }
922
944
  } else {
923
- const data = response.data;
924
- const errorMsg = typeof data === "object" && data !== null ? data.message || data.error || JSON.stringify(data) : typeof data === "string" ? data : `HTTP ${response.status}`;
945
+ const errorMsg = apiErrorMessage(response.data, response.status);
925
946
  if (!reportDeployAuthError(response.status, response.data)) {
926
947
  error(`\uBC30\uD3EC \uC2E4\uD328: ${errorMsg}`);
927
948
  }
@@ -950,8 +971,7 @@ async function promote(config, deploymentId) {
950
971
  process.exit(1);
951
972
  }
952
973
  if (res.status < 200 || res.status >= 300) {
953
- const data2 = res.data;
954
- const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
974
+ const msg = apiErrorMessage(res.data, res.status);
955
975
  if (!reportDeployAuthError(res.status, res.data)) {
956
976
  error(`promote \uC2E4\uD328: ${msg}`);
957
977
  }
@@ -974,8 +994,7 @@ async function listReleases(config) {
974
994
  headers
975
995
  );
976
996
  if (res.status < 200 || res.status >= 300) {
977
- const data2 = res.data;
978
- const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
997
+ const msg = apiErrorMessage(res.data, res.status);
979
998
  if (!reportDeployAuthError(res.status, res.data)) {
980
999
  error(`\uBC30\uD3EC \uC774\uB825 \uC870\uD68C \uC2E4\uD328: ${msg}`);
981
1000
  }
@@ -1100,9 +1119,9 @@ async function browserAuthFlow(baseUrl = DEFAULT_BASE_URL) {
1100
1119
  {}
1101
1120
  );
1102
1121
  if (startRes.status !== 200) {
1103
- const errData = startRes.data;
1104
- const detail = errData?.error || errData?.message || `HTTP ${startRes.status}`;
1105
- error(`\uC778\uC99D \uC138\uC158 \uC2DC\uC791\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4: ${detail}`);
1122
+ error(
1123
+ `\uC778\uC99D \uC138\uC158 \uC2DC\uC791\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4: ${apiErrorMessage(startRes.data, startRes.status)}`
1124
+ );
1106
1125
  process.exit(1);
1107
1126
  }
1108
1127
  const { session_id } = startRes.data;
@@ -1413,8 +1432,9 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
1413
1432
  process.exit(1);
1414
1433
  }
1415
1434
  if (createRes.status !== 201) {
1416
- const data = createRes.data;
1417
- error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${data?.error || `HTTP ${createRes.status}`}`);
1435
+ error(
1436
+ `\uC571 \uC0DD\uC131 \uC2E4\uD328: ${apiErrorMessage(createRes.data, createRes.status)}`
1437
+ );
1418
1438
  process.exit(1);
1419
1439
  }
1420
1440
  const createData = createRes.data;
@@ -1498,27 +1518,34 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
1498
1518
  const name = nonInteractive ? projectName : await prompt(
1499
1519
  `${colors.blue}?${colors.reset} \uC2A4\uD1A0\uB9AC\uC9C0 \uC774\uB984 (${projectName}): `
1500
1520
  ) || projectName;
1501
- info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC911...");
1502
- const createRes = await makeRequest(
1503
- `${baseUrl}/v1/public/storages/webs`,
1504
- "POST",
1505
- {
1506
- "X-Public-Key": publicKeyForSdk,
1507
- "X-User-Secret-Key": secretKey,
1508
- Authorization: `Bearer ${secretKey}`
1509
- },
1510
- JSON.stringify({ name })
1511
- );
1512
- if (createRes.status !== 200) {
1513
- const data = createRes.data;
1514
- error(
1515
- `\uC0DD\uC131 \uC2E4\uD328: ${data?.error || data?.message || `HTTP ${createRes.status}`}`
1521
+ const sameName = storages.find((s) => s.name === name);
1522
+ if (sameName) {
1523
+ storageId = sameName.id;
1524
+ success(
1525
+ `\uAC19\uC740 \uC774\uB984\uC758 \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0\uAC00 \uC774\uBBF8 \uC788\uC5B4 \uC7AC\uC0AC\uC6A9\uD569\uB2C8\uB2E4: ${sameName.name}`
1516
1526
  );
1517
- process.exit(1);
1527
+ } else {
1528
+ info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC911...");
1529
+ const createRes = await makeRequest(
1530
+ `${baseUrl}/v1/public/storages/webs`,
1531
+ "POST",
1532
+ {
1533
+ "X-Public-Key": publicKeyForSdk,
1534
+ "X-User-Secret-Key": secretKey,
1535
+ Authorization: `Bearer ${secretKey}`
1536
+ },
1537
+ JSON.stringify({ name })
1538
+ );
1539
+ if (createRes.status !== 200) {
1540
+ error(
1541
+ `\uC0DD\uC131 \uC2E4\uD328: ${apiErrorMessage(createRes.data, createRes.status)}`
1542
+ );
1543
+ process.exit(1);
1544
+ }
1545
+ const createData = createRes.data;
1546
+ storageId = createData.storage_web_id;
1547
+ success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC644\uB8CC: ${createData.name}`);
1518
1548
  }
1519
- const createData = createRes.data;
1520
- storageId = createData.storage_web_id;
1521
- success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC644\uB8CC: ${createData.name}`);
1522
1549
  }
1523
1550
  } else {
1524
1551
  throw new Error(`HTTP ${listRes.status}`);
@@ -2664,8 +2691,7 @@ ${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `
2664
2691
  process.exit(1);
2665
2692
  }
2666
2693
  if (createRes.status !== 201) {
2667
- const data = createRes.data;
2668
- error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${data?.error || `HTTP ${createRes.status}`}`);
2694
+ error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${apiErrorMessage(createRes.data, createRes.status)}`);
2669
2695
  process.exit(1);
2670
2696
  }
2671
2697
  const createData = createRes.data;
@@ -3744,6 +3770,7 @@ main().catch((err) => {
3744
3770
  // Annotate the CommonJS export names for ESM import in node:
3745
3771
  0 && (module.exports = {
3746
3772
  TunnelConnectSelector,
3773
+ apiErrorMessage,
3747
3774
  computeDeployDiff,
3748
3775
  getTunnelConnectCandidates,
3749
3776
  guessMimeType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "6.3.0",
3
+ "version": "6.3.1",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",