connectbase-client 3.44.0 → 3.46.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 +16 -0
- package/dist/cli.js +121 -13
- package/dist/connect-base.umd.js +4 -4
- package/dist/index.d.mts +64 -1
- package/dist/index.d.ts +64 -1
- package/dist/index.js +105 -0
- package/dist/index.mjs +105 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
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.46.0] - 2026-07-11
|
|
7
|
+
|
|
8
|
+
### Added — 크로스 플랫폼 음성 인식(STT) API
|
|
9
|
+
|
|
10
|
+
패키징된 WebView 앱(특히 iOS WKWebView)에서 `webkitSpeechRecognition` 이 없어 음성 입력이 불가능하던 문제를 해결하기 위해 `cb.native.speech` 를 추가했다.
|
|
11
|
+
|
|
12
|
+
- `cb.native.speech.isAvailable()` — 음성 인식 지원 여부
|
|
13
|
+
- `cb.native.speech.recognize({ lang, interim, continuous, onPartial })` — 음성 인식 시작, 최종 transcript 반환. `onPartial` 로 중간 결과 실시간 수신.
|
|
14
|
+
- `cb.native.speech.stop()` — 진행 중 인식 중지
|
|
15
|
+
- 플랫폼 자동 분기: 웹/데스크톱=Web Speech API, 패키징 모바일 앱=네이티브 브릿지(iOS `SFSpeechRecognizer` / Android `SpeechRecognizer`). 패키징 시 `native_features.speech` 필요.
|
|
16
|
+
- 신규 타입 export: `SpeechRecognizeOptions`, `SpeechResult`.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- `NativeBridge.push.getToken()` 반환 타입에 `platform`('ios' | 'android') 추가. 패키징 셸이 이제 raw APNS/FCM 디바이스 토큰을 제공하므로 `cb.push.registerDevice({ device_token, platform })` 에 그대로 전달 가능.
|
|
21
|
+
|
|
6
22
|
## [3.44.0] - 2026-07-03
|
|
7
23
|
|
|
8
24
|
### Added — CLI 비대화형 init + SDK 문서 수명주기 정비
|
package/dist/cli.js
CHANGED
|
@@ -429,7 +429,10 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
|
|
|
429
429
|
const presignFiles = largeBinaries.map((f) => ({
|
|
430
430
|
path: f.path,
|
|
431
431
|
size: Buffer.from(f.content, "base64").length,
|
|
432
|
-
mime_type: guessMimeType(f.path)
|
|
432
|
+
mime_type: guessMimeType(f.path),
|
|
433
|
+
// 신형 서버: hash 제공 시 content-addressed blob 키({id}/blobs/{hash})로 presign 되고,
|
|
434
|
+
// 동일 blob 이 이미 있으면 already_exists 로 업로드를 생략할 수 있다. 구버전 서버는 무시.
|
|
435
|
+
hash: f.hash
|
|
433
436
|
}));
|
|
434
437
|
let presigned;
|
|
435
438
|
try {
|
|
@@ -445,22 +448,30 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
|
|
|
445
448
|
warn(`\uB300\uC6A9\uB7C9 \uC5D0\uC14B\uC740 \uD30C\uC77C \uC2A4\uD1A0\uB9AC\uC9C0(${colors.cyan}cb.storage${colors.reset})\uC5D0 \uC62C\uB9AC\uACE0 URL \uB85C \uCC38\uC870\uD558\uAC70\uB098, \uC11C\uBC84 \uC5C5\uB370\uC774\uD2B8 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.`);
|
|
446
449
|
process.exit(1);
|
|
447
450
|
}
|
|
448
|
-
const
|
|
451
|
+
const entryByPath = new Map(presigned.map((p) => [p.path, p]));
|
|
449
452
|
info(`\uB300\uC6A9\uB7C9 \uBC14\uC774\uB108\uB9AC ${largeBinaries.length}\uAC1C\uB97C Object Storage \uB85C \uC9C1\uC811 \uC5C5\uB85C\uB4DC\uD569\uB2C8\uB2E4...`);
|
|
450
453
|
for (const f of largeBinaries) {
|
|
451
|
-
const
|
|
452
|
-
if (!
|
|
453
|
-
error(`presign \uC751\uB2F5\uC5D0 \
|
|
454
|
+
const entry = entryByPath.get(f.path);
|
|
455
|
+
if (!entry) {
|
|
456
|
+
error(`presign \uC751\uB2F5\uC5D0 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${f.path}`);
|
|
454
457
|
process.exit(1);
|
|
455
458
|
}
|
|
456
459
|
const bytes = Buffer.from(f.content, "base64");
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
|
|
460
|
+
if (entry.already_exists) {
|
|
461
|
+
success(`\uC5C5\uB85C\uB4DC \uC0DD\uB7B5(\uAE30\uC874 blob \uC7AC\uC0AC\uC6A9): ${f.path} (${formatSize(bytes.length)})`);
|
|
462
|
+
} else {
|
|
463
|
+
if (!entry.upload_url) {
|
|
464
|
+
error(`presign \uC751\uB2F5\uC5D0 \uC5C5\uB85C\uB4DC URL \uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${f.path}`);
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
await putToPresignedUrl(entry.upload_url, bytes, guessMimeType(f.path), timeoutMs);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
error(`\uC5C5\uB85C\uB4DC \uC2E4\uD328 (${f.path}): ${err instanceof Error ? err.message : err}`);
|
|
471
|
+
process.exit(1);
|
|
472
|
+
}
|
|
473
|
+
success(`\uC9C1\uC811 \uC5C5\uB85C\uB4DC: ${f.path} (${formatSize(bytes.length)})`);
|
|
462
474
|
}
|
|
463
|
-
success(`\uC9C1\uC811 \uC5C5\uB85C\uB4DC: ${f.path} (${formatSize(bytes.length)})`);
|
|
464
475
|
f.pre_uploaded = true;
|
|
465
476
|
f.size = bytes.length;
|
|
466
477
|
f.content = "";
|
|
@@ -743,8 +754,25 @@ async function fullDeploy(baseStorageUrl, headers, files, envLabel, endpoint, ti
|
|
|
743
754
|
}
|
|
744
755
|
function handleDeployResponse(response, envLabel) {
|
|
745
756
|
if (response.status >= 200 && response.status < 300) {
|
|
746
|
-
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC!`);
|
|
747
757
|
const data = response.data;
|
|
758
|
+
const dep = data?.deployment ?? null;
|
|
759
|
+
if (dep?.deployment_id) {
|
|
760
|
+
if (dep.promoted) {
|
|
761
|
+
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC! (release #${dep.seq})`);
|
|
762
|
+
if (dep.production_url) log(`
|
|
763
|
+
${colors.cyan}Production: ${dep.production_url}${colors.reset}`);
|
|
764
|
+
if (dep.qa_url) log(`${colors.dim}QA: ${dep.qa_url}${colors.reset}
|
|
765
|
+
`);
|
|
766
|
+
} else {
|
|
767
|
+
success(`QA \uD658\uACBD\uC5D0 \uBC30\uD3EC\uB418\uC5C8\uC2B5\uB2C8\uB2E4 (release #${dep.seq}) \u2014 production \uC740 \uC544\uC9C1 \uC774\uC804 \uBC84\uC804\uC785\uB2C8\uB2E4`);
|
|
768
|
+
if (dep.qa_url) log(`
|
|
769
|
+
${colors.yellow}QA URL: ${dep.qa_url}${colors.reset}`);
|
|
770
|
+
log(`${colors.dim}\uAC80\uC99D \uD6C4 \uBC18\uC601: ${colors.cyan}npx connectbase promote${colors.reset}
|
|
771
|
+
`);
|
|
772
|
+
}
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC!`);
|
|
748
776
|
if (data?.dev_url) {
|
|
749
777
|
log(`
|
|
750
778
|
${colors.yellow}Dev URL: ${data.dev_url}${colors.reset}
|
|
@@ -761,6 +789,64 @@ ${colors.cyan}URL: ${data.url}${colors.reset}
|
|
|
761
789
|
process.exit(1);
|
|
762
790
|
}
|
|
763
791
|
}
|
|
792
|
+
async function promote(config, deploymentId) {
|
|
793
|
+
const baseStorageUrl = `${config.baseUrl}/v1/public/storages/webs/${config.storageId}`;
|
|
794
|
+
const headers = { "X-Public-Key": config.publicKey };
|
|
795
|
+
info(deploymentId ? `release ${deploymentId} \uB97C production \uC73C\uB85C \uC2B9\uACA9\uD569\uB2C8\uB2E4...` : "\uCD5C\uC2E0 \uBC30\uD3EC\uB97C production \uC73C\uB85C \uC2B9\uACA9\uD569\uB2C8\uB2E4...");
|
|
796
|
+
const body = JSON.stringify(deploymentId ? { deployment_id: deploymentId } : {});
|
|
797
|
+
const res = await makeRequest(`${baseStorageUrl}/promote`, "POST", headers, body);
|
|
798
|
+
if (res.status === 404 && !res.data?.error) {
|
|
799
|
+
error("\uC11C\uBC84\uAC00 promote \uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 (\uAD6C\uBC84\uC804 \uC11C\uBC84) \u2014 \uC11C\uBC84 \uC5C5\uB370\uC774\uD2B8 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694");
|
|
800
|
+
process.exit(1);
|
|
801
|
+
}
|
|
802
|
+
if (res.status < 200 || res.status >= 300) {
|
|
803
|
+
const data2 = res.data;
|
|
804
|
+
const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
|
|
805
|
+
error(`promote \uC2E4\uD328: ${msg}`);
|
|
806
|
+
process.exit(1);
|
|
807
|
+
}
|
|
808
|
+
const data = res.data;
|
|
809
|
+
success(`production \uC2B9\uACA9 \uC644\uB8CC! (release #${data.seq})`);
|
|
810
|
+
if (data.production_url) {
|
|
811
|
+
log(`
|
|
812
|
+
${colors.cyan}Production: ${data.production_url}${colors.reset}
|
|
813
|
+
`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
async function listReleases(config) {
|
|
817
|
+
const baseStorageUrl = `${config.baseUrl}/v1/public/storages/webs/${config.storageId}`;
|
|
818
|
+
const headers = { "X-Public-Key": config.publicKey };
|
|
819
|
+
const res = await makeRequest(`${baseStorageUrl}/deployments`, "GET", headers);
|
|
820
|
+
if (res.status < 200 || res.status >= 300) {
|
|
821
|
+
const data2 = res.data;
|
|
822
|
+
const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
|
|
823
|
+
error(`\uBC30\uD3EC \uC774\uB825 \uC870\uD68C \uC2E4\uD328: ${msg}`);
|
|
824
|
+
process.exit(1);
|
|
825
|
+
}
|
|
826
|
+
const data = res.data;
|
|
827
|
+
if (!data.deployments || data.deployments.length === 0) {
|
|
828
|
+
info("\uBC30\uD3EC \uC774\uB825\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 deploy \uD558\uC138\uC694.");
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
log(`
|
|
832
|
+
${colors.cyan}\uBC30\uD3EC \uC774\uB825${colors.reset} (promote_mode: ${data.promote_mode || "auto"}, \uCD1D ${data.total}\uAC1C)
|
|
833
|
+
`);
|
|
834
|
+
for (const d of data.deployments) {
|
|
835
|
+
const markers = [];
|
|
836
|
+
if (d.is_production) markers.push(`${colors.green}production${colors.reset}`);
|
|
837
|
+
if (d.is_qa) markers.push(`${colors.yellow}qa${colors.reset}`);
|
|
838
|
+
const marker = markers.length > 0 ? ` \u2190 ${markers.join(", ")}` : "";
|
|
839
|
+
const statusColor = d.status === "ready" ? colors.green : d.status === "failed" ? colors.red : colors.yellow;
|
|
840
|
+
log(
|
|
841
|
+
` #${String(d.seq).padEnd(4)} ${statusColor}${d.status.padEnd(9)}${colors.reset} ${String(d.file_count).padStart(4)}\uAC1C ${formatSize(d.total_size).padStart(9)} ${d.created_at} ${colors.dim}${d.id}${colors.reset}${marker}`
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
log("");
|
|
845
|
+
if (data.production_url) log(`${colors.cyan}Production: ${data.production_url}${colors.reset}`);
|
|
846
|
+
if (data.qa_url) log(`${colors.yellow}QA: ${data.qa_url}${colors.reset}`);
|
|
847
|
+
log(`${colors.dim}\uB864\uBC31: npx connectbase promote <deployment_id>${colors.reset}
|
|
848
|
+
`);
|
|
849
|
+
}
|
|
764
850
|
function prompt(question) {
|
|
765
851
|
const rl = readline.createInterface({
|
|
766
852
|
input: process.stdin,
|
|
@@ -2786,6 +2872,9 @@ ${colors.yellow}\uBA85\uB839\uC5B4:${colors.reset}
|
|
|
2786
2872
|
docs SDK \uBB38\uC11C \uB2E4\uC6B4\uB85C\uB4DC/\uC5C5\uB370\uC774\uD2B8 (\uBAA8\uB178\uB808\uD3EC \uC790\uB3D9 \uAC10\uC9C0)
|
|
2787
2873
|
mcp MCP \uC11C\uBC84 \uC124\uC815 (.mcp.json \uC0DD\uC131/\uC5C5\uB370\uC774\uD2B8, \uBAA8\uB178\uB808\uD3EC \uC790\uB3D9 \uAC10\uC9C0)
|
|
2788
2874
|
deploy <directory> \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0\uC5D0 \uD30C\uC77C \uBC30\uD3EC (--dev: Dev \uD658\uACBD)
|
|
2875
|
+
promote_mode=manual \uC774\uBA74 QA \uD658\uACBD\uC5D0\uB9CC \uBC30\uD3EC\uB429\uB2C8\uB2E4
|
|
2876
|
+
promote [releaseId] QA \uAC80\uC99D \uD6C4 production \uBC18\uC601 (\uBBF8\uC9C0\uC815: \uCD5C\uC2E0 \uBC30\uD3EC, \uACFC\uAC70 id: \uB864\uBC31)
|
|
2877
|
+
releases \uBC30\uD3EC \uC774\uB825 \uC870\uD68C (release \uBC88\uD638/\uC0C1\uD0DC/production\xB7QA \uD3EC\uC778\uD130)
|
|
2789
2878
|
tunnel <port> \uB85C\uCEEC \uC11C\uBE44\uC2A4\uB97C \uC778\uD130\uB137\uC5D0 \uB178\uCD9C
|
|
2790
2879
|
|
|
2791
2880
|
${colors.yellow}\uCF54\uB4DC \uBD84\uC11D (MCP \uD1B5\uD574 \uC0AC\uC6A9):${colors.reset}
|
|
@@ -2842,7 +2931,12 @@ ${colors.yellow}\uBE60\uB978 \uC2DC\uC791:${colors.reset}
|
|
|
2842
2931
|
${colors.dim}# 4. Prod \uBC30\uD3EC${colors.reset}
|
|
2843
2932
|
npm run deploy
|
|
2844
2933
|
|
|
2845
|
-
${colors.dim}# 4.
|
|
2934
|
+
${colors.dim}# 4-1. QA \uC6CC\uD06C\uD50C\uB85C\uC6B0 (\uCF58\uC194\uC5D0\uC11C promote_mode=manual \uC124\uC815 \uC2DC)${colors.reset}
|
|
2935
|
+
npx connectbase deploy ./dist ${colors.dim}# QA \uD658\uACBD\uC5D0\uB9CC \uBC30\uD3EC${colors.reset}
|
|
2936
|
+
npx connectbase releases ${colors.dim}# \uBC30\uD3EC \uC774\uB825 \uD655\uC778${colors.reset}
|
|
2937
|
+
npx connectbase promote ${colors.dim}# QA \uAC80\uC99D \uD6C4 production \uBC18\uC601${colors.reset}
|
|
2938
|
+
|
|
2939
|
+
${colors.dim}# 4-2. Dev \uD658\uACBD \uBC30\uD3EC (\uB0B4\uBD80 \uD655\uC778\uC6A9)${colors.reset}
|
|
2846
2940
|
npx connectbase deploy ./dist --dev
|
|
2847
2941
|
|
|
2848
2942
|
${colors.dim}# 5. \uD130\uB110 (\uAE30\uBCF8)${colors.reset}
|
|
@@ -2996,6 +3090,20 @@ async function main() {
|
|
|
2996
3090
|
} catch {
|
|
2997
3091
|
}
|
|
2998
3092
|
}
|
|
3093
|
+
} else if (parsed.command === "promote" || parsed.command === "releases") {
|
|
3094
|
+
if (!config.publicKey) {
|
|
3095
|
+
error('Public Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -k \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694');
|
|
3096
|
+
process.exit(1);
|
|
3097
|
+
}
|
|
3098
|
+
if (!config.storageId) {
|
|
3099
|
+
error('\uC2A4\uD1A0\uB9AC\uC9C0 ID\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -s \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694');
|
|
3100
|
+
process.exit(1);
|
|
3101
|
+
}
|
|
3102
|
+
if (parsed.command === "promote") {
|
|
3103
|
+
await promote(config, parsed.args[0]);
|
|
3104
|
+
} else {
|
|
3105
|
+
await listReleases(config);
|
|
3106
|
+
}
|
|
2999
3107
|
} else if (parsed.command === "tunnel") {
|
|
3000
3108
|
const portStr = parsed.args[0];
|
|
3001
3109
|
if (!portStr) {
|