connectbase-client 3.45.0 → 3.47.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 +27 -0
- package/dist/connect-base.umd.js +5 -5
- package/dist/index.d.mts +264 -3
- package/dist/index.d.ts +264 -3
- package/dist/index.js +228 -0
- package/dist/index.mjs +227 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
GameRoom: () => GameRoom,
|
|
34
34
|
GameRoomTransport: () => GameRoomTransport,
|
|
35
35
|
NativeAPI: () => NativeAPI,
|
|
36
|
+
RolesAPI: () => RolesAPI,
|
|
36
37
|
SessionManager: () => SessionManager,
|
|
37
38
|
VideoProcessingError: () => VideoProcessingError,
|
|
38
39
|
default: () => index_default,
|
|
@@ -5448,6 +5449,38 @@ var PaymentAPI = class {
|
|
|
5448
5449
|
const prefix = this.getPublicPrefix();
|
|
5449
5450
|
return this.http.get(`${prefix}/payments/orders/${orderId}`);
|
|
5450
5451
|
}
|
|
5452
|
+
/**
|
|
5453
|
+
* 앱의 결제 내역 목록을 조회한다 (필터/페이지네이션 지원).
|
|
5454
|
+
*
|
|
5455
|
+
* 서버사이드 전용 — service_role(ctx.cbAdmin, management_scopes=["payment:read"]) 또는 콘솔 JWT
|
|
5456
|
+
* 인증이 필요하다. 재무 데이터이므로 Public Key(cb_pk_) 단독 브라우저 SDK 인스턴스로는 호출할 수
|
|
5457
|
+
* 없다. (다른 payment 메서드와 달리 /v1/public 이 아니라 앱 스코프 dual-auth 라우트를 쓴다.)
|
|
5458
|
+
*
|
|
5459
|
+
* @example
|
|
5460
|
+
* ```typescript
|
|
5461
|
+
* // 함수(service_role, management_scopes: ["payment:read"]) 안에서
|
|
5462
|
+
* const { payments, total } = await ctx.cbAdmin.payment.list(ctx.appId, { status: 'done', limit: 20 })
|
|
5463
|
+
* ```
|
|
5464
|
+
*/
|
|
5465
|
+
async list(appId, options) {
|
|
5466
|
+
if (this.http.hasPublicKey() && !this.http.hasJWT()) {
|
|
5467
|
+
throw new Error(
|
|
5468
|
+
'cb.payment.list() \uB294 \uCF58\uC194 JWT \uB610\uB294 service_role(ctx.cbAdmin, management_scopes: ["payment:read"]) \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. Public Key(cb_pk_) \uB2E8\uB3C5 SDK \uC778\uC2A4\uD134\uC2A4\uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.'
|
|
5469
|
+
);
|
|
5470
|
+
}
|
|
5471
|
+
const params = new URLSearchParams();
|
|
5472
|
+
if (options?.status) params.set("status", options.status);
|
|
5473
|
+
if (options?.startDate) params.set("start_date", options.startDate);
|
|
5474
|
+
if (options?.endDate) params.set("end_date", options.endDate);
|
|
5475
|
+
if (options?.limit !== void 0)
|
|
5476
|
+
params.set("limit", String(options.limit));
|
|
5477
|
+
if (options?.offset !== void 0)
|
|
5478
|
+
params.set("offset", String(options.offset));
|
|
5479
|
+
const qs = params.toString();
|
|
5480
|
+
return this.http.get(
|
|
5481
|
+
`/v1/apps/${appId}/payments${qs ? `?${qs}` : ""}`
|
|
5482
|
+
);
|
|
5483
|
+
}
|
|
5451
5484
|
};
|
|
5452
5485
|
|
|
5453
5486
|
// src/api/subscription.ts
|
|
@@ -6049,6 +6082,28 @@ var PushAPI = class {
|
|
|
6049
6082
|
};
|
|
6050
6083
|
return this.http.post(`/v1/apps/${appId}/push/send-to-topic`, body);
|
|
6051
6084
|
}
|
|
6085
|
+
/**
|
|
6086
|
+
* 앱의 푸시 통계(도달/오픈율/클릭율, 디바이스/메시지/토픽 수)를 조회한다.
|
|
6087
|
+
*
|
|
6088
|
+
* 서버사이드 전용 — service_role(ctx.cbAdmin, management_scopes=["push:read"]) 또는 콘솔 JWT
|
|
6089
|
+
* 인증이 필요하다. sendToMembers/sendToTopic 과 동일한 위임 가드 — cb_pk_ 단독(브라우저) SDK
|
|
6090
|
+
* 인스턴스는 차단하고, 위임 Bearer(ctx.cbAdmin) 인스턴스는 허용한다.
|
|
6091
|
+
*
|
|
6092
|
+
* @example
|
|
6093
|
+
* ```typescript
|
|
6094
|
+
* // 함수(service_role, management_scopes: ["push:read"]) 안에서
|
|
6095
|
+
* const stats = await ctx.cbAdmin.push.getStats(ctx.appId)
|
|
6096
|
+
* console.log(stats.open_rate, stats.click_rate)
|
|
6097
|
+
* ```
|
|
6098
|
+
*/
|
|
6099
|
+
async getStats(appId) {
|
|
6100
|
+
if (this.http.hasPublicKey() && !this.http.hasJWT()) {
|
|
6101
|
+
throw new Error(
|
|
6102
|
+
'cb.push.getStats() \uB294 \uCF58\uC194 JWT \uB610\uB294 service_role(ctx.cbAdmin, management_scopes: ["push:read"]) \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. Public Key(cb_pk_) \uB2E8\uB3C5 SDK \uC778\uC2A4\uD134\uC2A4\uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.'
|
|
6103
|
+
);
|
|
6104
|
+
}
|
|
6105
|
+
return this.http.get(`/v1/apps/${appId}/push/stats`);
|
|
6106
|
+
}
|
|
6052
6107
|
// ============ Helper Methods ============
|
|
6053
6108
|
/**
|
|
6054
6109
|
* 브라우저 고유 ID 생성 (localStorage에 저장)
|
|
@@ -6109,6 +6164,72 @@ var PushAPI = class {
|
|
|
6109
6164
|
}
|
|
6110
6165
|
};
|
|
6111
6166
|
|
|
6167
|
+
// src/api/roles.ts
|
|
6168
|
+
var RolesAPI = class {
|
|
6169
|
+
constructor(http) {
|
|
6170
|
+
this.http = http;
|
|
6171
|
+
}
|
|
6172
|
+
ensureServerAuth(method) {
|
|
6173
|
+
if (this.http.hasPublicKey() && !this.http.hasJWT()) {
|
|
6174
|
+
throw new Error(
|
|
6175
|
+
`cb.roles.${method}() \uB294 \uCF58\uC194 JWT \uB610\uB294 service_role(ctx.cbAdmin, management_scopes: ["role:read" | "role:manage"]) \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. Public Key(cb_pk_) \uB2E8\uB3C5 SDK \uC778\uC2A4\uD134\uC2A4\uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.`
|
|
6176
|
+
);
|
|
6177
|
+
}
|
|
6178
|
+
}
|
|
6179
|
+
/** 앱의 역할 목록을 조회한다. (management_scope: `role:read`) */
|
|
6180
|
+
async list(appId) {
|
|
6181
|
+
this.ensureServerAuth("list");
|
|
6182
|
+
return this.http.get(`/v1/apps/${appId}/app-roles`);
|
|
6183
|
+
}
|
|
6184
|
+
/** 역할 상세(권한/할당 사용자 포함)를 조회한다. (management_scope: `role:read`) */
|
|
6185
|
+
async get(appId, roleId) {
|
|
6186
|
+
this.ensureServerAuth("get");
|
|
6187
|
+
return this.http.get(`/v1/apps/${appId}/app-roles/${roleId}`);
|
|
6188
|
+
}
|
|
6189
|
+
/** 역할을 생성한다. (management_scope: `role:manage`) */
|
|
6190
|
+
async create(appId, payload) {
|
|
6191
|
+
this.ensureServerAuth("create");
|
|
6192
|
+
return this.http.post(`/v1/apps/${appId}/app-roles`, {
|
|
6193
|
+
role_title: payload.title,
|
|
6194
|
+
role_description: payload.description,
|
|
6195
|
+
selected_permission_ids: payload.permissionIds ?? []
|
|
6196
|
+
});
|
|
6197
|
+
}
|
|
6198
|
+
/**
|
|
6199
|
+
* 역할을 수정한다 — **전체 동기화(replace)**. permissionIds / userIds 는 전달값으로 대체된다.
|
|
6200
|
+
* (management_scope: `role:manage`)
|
|
6201
|
+
*/
|
|
6202
|
+
async update(appId, roleId, payload) {
|
|
6203
|
+
this.ensureServerAuth("update");
|
|
6204
|
+
await this.http.put(`/v1/apps/${appId}/app-roles/${roleId}`, {
|
|
6205
|
+
role_title: payload.title,
|
|
6206
|
+
role_description: payload.description,
|
|
6207
|
+
selected_permission_ids: payload.permissionIds,
|
|
6208
|
+
selected_user_ids: payload.userIds
|
|
6209
|
+
});
|
|
6210
|
+
}
|
|
6211
|
+
/**
|
|
6212
|
+
* 역할에 사용자를 할당한다 (제목/설명/권한 보존). EditRole 이 전체 동기화라, 현재 상세를 읽어
|
|
6213
|
+
* 나머지 필드를 유지한 채 사용자 목록만 교체해 PUT 한다. userIds 는 "이 역할을 가질 사용자
|
|
6214
|
+
* 전체"다 (추가가 아니라 동기화). (management_scope: `role:manage`)
|
|
6215
|
+
*/
|
|
6216
|
+
async assign(appId, roleId, userIds) {
|
|
6217
|
+
this.ensureServerAuth("assign");
|
|
6218
|
+
const detail = await this.http.get(`/v1/apps/${appId}/app-roles/${roleId}`);
|
|
6219
|
+
await this.http.put(`/v1/apps/${appId}/app-roles/${roleId}`, {
|
|
6220
|
+
role_title: detail.role_name,
|
|
6221
|
+
role_description: detail.role_description,
|
|
6222
|
+
selected_permission_ids: detail.permission_item.map((p) => p.id),
|
|
6223
|
+
selected_user_ids: userIds
|
|
6224
|
+
});
|
|
6225
|
+
}
|
|
6226
|
+
/** 역할을 삭제한다. (management_scope: `role:manage`) */
|
|
6227
|
+
async delete(appId, roleId) {
|
|
6228
|
+
this.ensureServerAuth("delete");
|
|
6229
|
+
await this.http.delete(`/v1/apps/${appId}/app-roles/${roleId}`);
|
|
6230
|
+
}
|
|
6231
|
+
};
|
|
6232
|
+
|
|
6112
6233
|
// src/api/video.ts
|
|
6113
6234
|
var DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024;
|
|
6114
6235
|
var VideoProcessingError = class extends Error {
|
|
@@ -8061,6 +8182,8 @@ var AdsAPI = class {
|
|
|
8061
8182
|
// src/api/native.ts
|
|
8062
8183
|
var NativeAPI = class {
|
|
8063
8184
|
constructor() {
|
|
8185
|
+
/** 웹 Web Speech API 인스턴스 (stop() 용) */
|
|
8186
|
+
this._webSpeechRecognition = null;
|
|
8064
8187
|
/**
|
|
8065
8188
|
* 크로스 플랫폼 클립보드 API
|
|
8066
8189
|
*/
|
|
@@ -8487,6 +8610,109 @@ var NativeAPI = class {
|
|
|
8487
8610
|
return false;
|
|
8488
8611
|
}
|
|
8489
8612
|
};
|
|
8613
|
+
/**
|
|
8614
|
+
* 크로스 플랫폼 음성 인식(STT) API
|
|
8615
|
+
*
|
|
8616
|
+
* - 모바일(패키징 앱): 네이티브 STT 브릿지(expo-speech-recognition — iOS SFSpeechRecognizer / Android SpeechRecognizer)
|
|
8617
|
+
* - 웹/데스크톱: Web Speech API(webkitSpeechRecognition)
|
|
8618
|
+
*
|
|
8619
|
+
* iOS WKWebView 는 Web Speech API 를 지원하지 않으므로, 패키징 앱에서는 speech 네이티브 기능을 켜면
|
|
8620
|
+
* 자동으로 네이티브 브릿지 경로를 사용한다.
|
|
8621
|
+
*
|
|
8622
|
+
* @example
|
|
8623
|
+
* ```typescript
|
|
8624
|
+
* if (await cb.native.speech.isAvailable()) {
|
|
8625
|
+
* const result = await cb.native.speech.recognize({
|
|
8626
|
+
* lang: 'ko-KR',
|
|
8627
|
+
* onPartial: (text) => console.log('부분 결과:', text),
|
|
8628
|
+
* })
|
|
8629
|
+
* console.log('최종:', result.transcript)
|
|
8630
|
+
* }
|
|
8631
|
+
* ```
|
|
8632
|
+
*/
|
|
8633
|
+
this.speech = {
|
|
8634
|
+
/**
|
|
8635
|
+
* 음성 인식 지원 여부
|
|
8636
|
+
*/
|
|
8637
|
+
isAvailable: async () => {
|
|
8638
|
+
if (this.getPlatform() === "mobile" && window.NativeBridge?.speech) {
|
|
8639
|
+
const r = await window.NativeBridge.speech.isAvailable();
|
|
8640
|
+
return r.available;
|
|
8641
|
+
}
|
|
8642
|
+
if (typeof window === "undefined") return false;
|
|
8643
|
+
return "SpeechRecognition" in window || "webkitSpeechRecognition" in window;
|
|
8644
|
+
},
|
|
8645
|
+
/**
|
|
8646
|
+
* 음성 인식 시작 → 최종 transcript 반환.
|
|
8647
|
+
* `options.onPartial` 로 중간 결과를 실시간 수신할 수 있다.
|
|
8648
|
+
*/
|
|
8649
|
+
recognize: async (options) => {
|
|
8650
|
+
if (this.getPlatform() === "mobile" && window.NativeBridge?.speech) {
|
|
8651
|
+
let partialHandler;
|
|
8652
|
+
if (options?.onPartial) {
|
|
8653
|
+
partialHandler = (e) => {
|
|
8654
|
+
const detail = e.detail;
|
|
8655
|
+
if (detail?.transcript) options.onPartial?.(detail.transcript);
|
|
8656
|
+
};
|
|
8657
|
+
window.addEventListener("nativeSpeechPartial", partialHandler);
|
|
8658
|
+
}
|
|
8659
|
+
try {
|
|
8660
|
+
return await window.NativeBridge.speech.recognize(options);
|
|
8661
|
+
} finally {
|
|
8662
|
+
if (partialHandler) window.removeEventListener("nativeSpeechPartial", partialHandler);
|
|
8663
|
+
}
|
|
8664
|
+
}
|
|
8665
|
+
const SR = window;
|
|
8666
|
+
const Ctor = SR.SpeechRecognition || SR.webkitSpeechRecognition;
|
|
8667
|
+
if (!Ctor) {
|
|
8668
|
+
throw new Error("\uC774 \uD50C\uB7AB\uD3FC\uC5D0\uC11C\uB294 \uC74C\uC131 \uC778\uC2DD\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uD328\uD0A4\uC9D5 \uC571\uC5D0\uC11C\uB294 speech \uB124\uC774\uD2F0\uBE0C \uAE30\uB2A5\uC744 \uD65C\uC131\uD654\uD558\uC138\uC694.");
|
|
8669
|
+
}
|
|
8670
|
+
return new Promise((resolve, reject) => {
|
|
8671
|
+
const recognition = new Ctor();
|
|
8672
|
+
this._webSpeechRecognition = recognition;
|
|
8673
|
+
recognition.lang = options?.lang ?? "en-US";
|
|
8674
|
+
recognition.interimResults = options?.interim ?? true;
|
|
8675
|
+
recognition.continuous = options?.continuous ?? false;
|
|
8676
|
+
let finalTranscript = "";
|
|
8677
|
+
recognition.onresult = (event) => {
|
|
8678
|
+
let interim = "";
|
|
8679
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
8680
|
+
const res = event.results[i];
|
|
8681
|
+
if (res.isFinal) {
|
|
8682
|
+
finalTranscript += res[0].transcript;
|
|
8683
|
+
} else {
|
|
8684
|
+
interim += res[0].transcript;
|
|
8685
|
+
}
|
|
8686
|
+
}
|
|
8687
|
+
if (interim && options?.onPartial) options.onPartial(interim);
|
|
8688
|
+
};
|
|
8689
|
+
recognition.onerror = (event) => {
|
|
8690
|
+
this._webSpeechRecognition = null;
|
|
8691
|
+
reject(new Error(event?.error || "\uC74C\uC131 \uC778\uC2DD \uC624\uB958"));
|
|
8692
|
+
};
|
|
8693
|
+
recognition.onend = () => {
|
|
8694
|
+
this._webSpeechRecognition = null;
|
|
8695
|
+
resolve({ transcript: finalTranscript.trim(), isFinal: true });
|
|
8696
|
+
};
|
|
8697
|
+
recognition.start();
|
|
8698
|
+
});
|
|
8699
|
+
},
|
|
8700
|
+
/**
|
|
8701
|
+
* 진행 중인 음성 인식 중지
|
|
8702
|
+
*/
|
|
8703
|
+
stop: async () => {
|
|
8704
|
+
if (this.getPlatform() === "mobile" && window.NativeBridge?.speech) {
|
|
8705
|
+
await window.NativeBridge.speech.stop();
|
|
8706
|
+
return;
|
|
8707
|
+
}
|
|
8708
|
+
if (this._webSpeechRecognition) {
|
|
8709
|
+
try {
|
|
8710
|
+
this._webSpeechRecognition.stop();
|
|
8711
|
+
} catch {
|
|
8712
|
+
}
|
|
8713
|
+
}
|
|
8714
|
+
}
|
|
8715
|
+
};
|
|
8490
8716
|
}
|
|
8491
8717
|
/**
|
|
8492
8718
|
* 현재 플랫폼 감지
|
|
@@ -11092,6 +11318,7 @@ var ConnectBase = class {
|
|
|
11092
11318
|
this.payment = new PaymentAPI(this.http);
|
|
11093
11319
|
this.subscription = new SubscriptionAPI(this.http);
|
|
11094
11320
|
this.push = new PushAPI(this.http);
|
|
11321
|
+
this.roles = new RolesAPI(this.http);
|
|
11095
11322
|
this.video = new VideoAPI(this.http, config.videoUrl || env("CB_VIDEO_URL") || DEFAULT_VIDEO_URL);
|
|
11096
11323
|
this.game = new GameAPI(this.http, config.gameUrl || env("CB_GAME_URL") || DEFAULT_GAME_URL, config.appId);
|
|
11097
11324
|
this.ads = new AdsAPI(this.http);
|
|
@@ -11187,6 +11414,7 @@ var index_default = ConnectBase;
|
|
|
11187
11414
|
GameRoom,
|
|
11188
11415
|
GameRoomTransport,
|
|
11189
11416
|
NativeAPI,
|
|
11417
|
+
RolesAPI,
|
|
11190
11418
|
SessionManager,
|
|
11191
11419
|
VideoProcessingError,
|
|
11192
11420
|
detectInAppBrowser,
|
package/dist/index.mjs
CHANGED
|
@@ -5403,6 +5403,38 @@ var PaymentAPI = class {
|
|
|
5403
5403
|
const prefix = this.getPublicPrefix();
|
|
5404
5404
|
return this.http.get(`${prefix}/payments/orders/${orderId}`);
|
|
5405
5405
|
}
|
|
5406
|
+
/**
|
|
5407
|
+
* 앱의 결제 내역 목록을 조회한다 (필터/페이지네이션 지원).
|
|
5408
|
+
*
|
|
5409
|
+
* 서버사이드 전용 — service_role(ctx.cbAdmin, management_scopes=["payment:read"]) 또는 콘솔 JWT
|
|
5410
|
+
* 인증이 필요하다. 재무 데이터이므로 Public Key(cb_pk_) 단독 브라우저 SDK 인스턴스로는 호출할 수
|
|
5411
|
+
* 없다. (다른 payment 메서드와 달리 /v1/public 이 아니라 앱 스코프 dual-auth 라우트를 쓴다.)
|
|
5412
|
+
*
|
|
5413
|
+
* @example
|
|
5414
|
+
* ```typescript
|
|
5415
|
+
* // 함수(service_role, management_scopes: ["payment:read"]) 안에서
|
|
5416
|
+
* const { payments, total } = await ctx.cbAdmin.payment.list(ctx.appId, { status: 'done', limit: 20 })
|
|
5417
|
+
* ```
|
|
5418
|
+
*/
|
|
5419
|
+
async list(appId, options) {
|
|
5420
|
+
if (this.http.hasPublicKey() && !this.http.hasJWT()) {
|
|
5421
|
+
throw new Error(
|
|
5422
|
+
'cb.payment.list() \uB294 \uCF58\uC194 JWT \uB610\uB294 service_role(ctx.cbAdmin, management_scopes: ["payment:read"]) \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. Public Key(cb_pk_) \uB2E8\uB3C5 SDK \uC778\uC2A4\uD134\uC2A4\uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.'
|
|
5423
|
+
);
|
|
5424
|
+
}
|
|
5425
|
+
const params = new URLSearchParams();
|
|
5426
|
+
if (options?.status) params.set("status", options.status);
|
|
5427
|
+
if (options?.startDate) params.set("start_date", options.startDate);
|
|
5428
|
+
if (options?.endDate) params.set("end_date", options.endDate);
|
|
5429
|
+
if (options?.limit !== void 0)
|
|
5430
|
+
params.set("limit", String(options.limit));
|
|
5431
|
+
if (options?.offset !== void 0)
|
|
5432
|
+
params.set("offset", String(options.offset));
|
|
5433
|
+
const qs = params.toString();
|
|
5434
|
+
return this.http.get(
|
|
5435
|
+
`/v1/apps/${appId}/payments${qs ? `?${qs}` : ""}`
|
|
5436
|
+
);
|
|
5437
|
+
}
|
|
5406
5438
|
};
|
|
5407
5439
|
|
|
5408
5440
|
// src/api/subscription.ts
|
|
@@ -6004,6 +6036,28 @@ var PushAPI = class {
|
|
|
6004
6036
|
};
|
|
6005
6037
|
return this.http.post(`/v1/apps/${appId}/push/send-to-topic`, body);
|
|
6006
6038
|
}
|
|
6039
|
+
/**
|
|
6040
|
+
* 앱의 푸시 통계(도달/오픈율/클릭율, 디바이스/메시지/토픽 수)를 조회한다.
|
|
6041
|
+
*
|
|
6042
|
+
* 서버사이드 전용 — service_role(ctx.cbAdmin, management_scopes=["push:read"]) 또는 콘솔 JWT
|
|
6043
|
+
* 인증이 필요하다. sendToMembers/sendToTopic 과 동일한 위임 가드 — cb_pk_ 단독(브라우저) SDK
|
|
6044
|
+
* 인스턴스는 차단하고, 위임 Bearer(ctx.cbAdmin) 인스턴스는 허용한다.
|
|
6045
|
+
*
|
|
6046
|
+
* @example
|
|
6047
|
+
* ```typescript
|
|
6048
|
+
* // 함수(service_role, management_scopes: ["push:read"]) 안에서
|
|
6049
|
+
* const stats = await ctx.cbAdmin.push.getStats(ctx.appId)
|
|
6050
|
+
* console.log(stats.open_rate, stats.click_rate)
|
|
6051
|
+
* ```
|
|
6052
|
+
*/
|
|
6053
|
+
async getStats(appId) {
|
|
6054
|
+
if (this.http.hasPublicKey() && !this.http.hasJWT()) {
|
|
6055
|
+
throw new Error(
|
|
6056
|
+
'cb.push.getStats() \uB294 \uCF58\uC194 JWT \uB610\uB294 service_role(ctx.cbAdmin, management_scopes: ["push:read"]) \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. Public Key(cb_pk_) \uB2E8\uB3C5 SDK \uC778\uC2A4\uD134\uC2A4\uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.'
|
|
6057
|
+
);
|
|
6058
|
+
}
|
|
6059
|
+
return this.http.get(`/v1/apps/${appId}/push/stats`);
|
|
6060
|
+
}
|
|
6007
6061
|
// ============ Helper Methods ============
|
|
6008
6062
|
/**
|
|
6009
6063
|
* 브라우저 고유 ID 생성 (localStorage에 저장)
|
|
@@ -6064,6 +6118,72 @@ var PushAPI = class {
|
|
|
6064
6118
|
}
|
|
6065
6119
|
};
|
|
6066
6120
|
|
|
6121
|
+
// src/api/roles.ts
|
|
6122
|
+
var RolesAPI = class {
|
|
6123
|
+
constructor(http) {
|
|
6124
|
+
this.http = http;
|
|
6125
|
+
}
|
|
6126
|
+
ensureServerAuth(method) {
|
|
6127
|
+
if (this.http.hasPublicKey() && !this.http.hasJWT()) {
|
|
6128
|
+
throw new Error(
|
|
6129
|
+
`cb.roles.${method}() \uB294 \uCF58\uC194 JWT \uB610\uB294 service_role(ctx.cbAdmin, management_scopes: ["role:read" | "role:manage"]) \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. Public Key(cb_pk_) \uB2E8\uB3C5 SDK \uC778\uC2A4\uD134\uC2A4\uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.`
|
|
6130
|
+
);
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
/** 앱의 역할 목록을 조회한다. (management_scope: `role:read`) */
|
|
6134
|
+
async list(appId) {
|
|
6135
|
+
this.ensureServerAuth("list");
|
|
6136
|
+
return this.http.get(`/v1/apps/${appId}/app-roles`);
|
|
6137
|
+
}
|
|
6138
|
+
/** 역할 상세(권한/할당 사용자 포함)를 조회한다. (management_scope: `role:read`) */
|
|
6139
|
+
async get(appId, roleId) {
|
|
6140
|
+
this.ensureServerAuth("get");
|
|
6141
|
+
return this.http.get(`/v1/apps/${appId}/app-roles/${roleId}`);
|
|
6142
|
+
}
|
|
6143
|
+
/** 역할을 생성한다. (management_scope: `role:manage`) */
|
|
6144
|
+
async create(appId, payload) {
|
|
6145
|
+
this.ensureServerAuth("create");
|
|
6146
|
+
return this.http.post(`/v1/apps/${appId}/app-roles`, {
|
|
6147
|
+
role_title: payload.title,
|
|
6148
|
+
role_description: payload.description,
|
|
6149
|
+
selected_permission_ids: payload.permissionIds ?? []
|
|
6150
|
+
});
|
|
6151
|
+
}
|
|
6152
|
+
/**
|
|
6153
|
+
* 역할을 수정한다 — **전체 동기화(replace)**. permissionIds / userIds 는 전달값으로 대체된다.
|
|
6154
|
+
* (management_scope: `role:manage`)
|
|
6155
|
+
*/
|
|
6156
|
+
async update(appId, roleId, payload) {
|
|
6157
|
+
this.ensureServerAuth("update");
|
|
6158
|
+
await this.http.put(`/v1/apps/${appId}/app-roles/${roleId}`, {
|
|
6159
|
+
role_title: payload.title,
|
|
6160
|
+
role_description: payload.description,
|
|
6161
|
+
selected_permission_ids: payload.permissionIds,
|
|
6162
|
+
selected_user_ids: payload.userIds
|
|
6163
|
+
});
|
|
6164
|
+
}
|
|
6165
|
+
/**
|
|
6166
|
+
* 역할에 사용자를 할당한다 (제목/설명/권한 보존). EditRole 이 전체 동기화라, 현재 상세를 읽어
|
|
6167
|
+
* 나머지 필드를 유지한 채 사용자 목록만 교체해 PUT 한다. userIds 는 "이 역할을 가질 사용자
|
|
6168
|
+
* 전체"다 (추가가 아니라 동기화). (management_scope: `role:manage`)
|
|
6169
|
+
*/
|
|
6170
|
+
async assign(appId, roleId, userIds) {
|
|
6171
|
+
this.ensureServerAuth("assign");
|
|
6172
|
+
const detail = await this.http.get(`/v1/apps/${appId}/app-roles/${roleId}`);
|
|
6173
|
+
await this.http.put(`/v1/apps/${appId}/app-roles/${roleId}`, {
|
|
6174
|
+
role_title: detail.role_name,
|
|
6175
|
+
role_description: detail.role_description,
|
|
6176
|
+
selected_permission_ids: detail.permission_item.map((p) => p.id),
|
|
6177
|
+
selected_user_ids: userIds
|
|
6178
|
+
});
|
|
6179
|
+
}
|
|
6180
|
+
/** 역할을 삭제한다. (management_scope: `role:manage`) */
|
|
6181
|
+
async delete(appId, roleId) {
|
|
6182
|
+
this.ensureServerAuth("delete");
|
|
6183
|
+
await this.http.delete(`/v1/apps/${appId}/app-roles/${roleId}`);
|
|
6184
|
+
}
|
|
6185
|
+
};
|
|
6186
|
+
|
|
6067
6187
|
// src/api/video.ts
|
|
6068
6188
|
var DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024;
|
|
6069
6189
|
var VideoProcessingError = class extends Error {
|
|
@@ -8016,6 +8136,8 @@ var AdsAPI = class {
|
|
|
8016
8136
|
// src/api/native.ts
|
|
8017
8137
|
var NativeAPI = class {
|
|
8018
8138
|
constructor() {
|
|
8139
|
+
/** 웹 Web Speech API 인스턴스 (stop() 용) */
|
|
8140
|
+
this._webSpeechRecognition = null;
|
|
8019
8141
|
/**
|
|
8020
8142
|
* 크로스 플랫폼 클립보드 API
|
|
8021
8143
|
*/
|
|
@@ -8442,6 +8564,109 @@ var NativeAPI = class {
|
|
|
8442
8564
|
return false;
|
|
8443
8565
|
}
|
|
8444
8566
|
};
|
|
8567
|
+
/**
|
|
8568
|
+
* 크로스 플랫폼 음성 인식(STT) API
|
|
8569
|
+
*
|
|
8570
|
+
* - 모바일(패키징 앱): 네이티브 STT 브릿지(expo-speech-recognition — iOS SFSpeechRecognizer / Android SpeechRecognizer)
|
|
8571
|
+
* - 웹/데스크톱: Web Speech API(webkitSpeechRecognition)
|
|
8572
|
+
*
|
|
8573
|
+
* iOS WKWebView 는 Web Speech API 를 지원하지 않으므로, 패키징 앱에서는 speech 네이티브 기능을 켜면
|
|
8574
|
+
* 자동으로 네이티브 브릿지 경로를 사용한다.
|
|
8575
|
+
*
|
|
8576
|
+
* @example
|
|
8577
|
+
* ```typescript
|
|
8578
|
+
* if (await cb.native.speech.isAvailable()) {
|
|
8579
|
+
* const result = await cb.native.speech.recognize({
|
|
8580
|
+
* lang: 'ko-KR',
|
|
8581
|
+
* onPartial: (text) => console.log('부분 결과:', text),
|
|
8582
|
+
* })
|
|
8583
|
+
* console.log('최종:', result.transcript)
|
|
8584
|
+
* }
|
|
8585
|
+
* ```
|
|
8586
|
+
*/
|
|
8587
|
+
this.speech = {
|
|
8588
|
+
/**
|
|
8589
|
+
* 음성 인식 지원 여부
|
|
8590
|
+
*/
|
|
8591
|
+
isAvailable: async () => {
|
|
8592
|
+
if (this.getPlatform() === "mobile" && window.NativeBridge?.speech) {
|
|
8593
|
+
const r = await window.NativeBridge.speech.isAvailable();
|
|
8594
|
+
return r.available;
|
|
8595
|
+
}
|
|
8596
|
+
if (typeof window === "undefined") return false;
|
|
8597
|
+
return "SpeechRecognition" in window || "webkitSpeechRecognition" in window;
|
|
8598
|
+
},
|
|
8599
|
+
/**
|
|
8600
|
+
* 음성 인식 시작 → 최종 transcript 반환.
|
|
8601
|
+
* `options.onPartial` 로 중간 결과를 실시간 수신할 수 있다.
|
|
8602
|
+
*/
|
|
8603
|
+
recognize: async (options) => {
|
|
8604
|
+
if (this.getPlatform() === "mobile" && window.NativeBridge?.speech) {
|
|
8605
|
+
let partialHandler;
|
|
8606
|
+
if (options?.onPartial) {
|
|
8607
|
+
partialHandler = (e) => {
|
|
8608
|
+
const detail = e.detail;
|
|
8609
|
+
if (detail?.transcript) options.onPartial?.(detail.transcript);
|
|
8610
|
+
};
|
|
8611
|
+
window.addEventListener("nativeSpeechPartial", partialHandler);
|
|
8612
|
+
}
|
|
8613
|
+
try {
|
|
8614
|
+
return await window.NativeBridge.speech.recognize(options);
|
|
8615
|
+
} finally {
|
|
8616
|
+
if (partialHandler) window.removeEventListener("nativeSpeechPartial", partialHandler);
|
|
8617
|
+
}
|
|
8618
|
+
}
|
|
8619
|
+
const SR = window;
|
|
8620
|
+
const Ctor = SR.SpeechRecognition || SR.webkitSpeechRecognition;
|
|
8621
|
+
if (!Ctor) {
|
|
8622
|
+
throw new Error("\uC774 \uD50C\uB7AB\uD3FC\uC5D0\uC11C\uB294 \uC74C\uC131 \uC778\uC2DD\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uD328\uD0A4\uC9D5 \uC571\uC5D0\uC11C\uB294 speech \uB124\uC774\uD2F0\uBE0C \uAE30\uB2A5\uC744 \uD65C\uC131\uD654\uD558\uC138\uC694.");
|
|
8623
|
+
}
|
|
8624
|
+
return new Promise((resolve, reject) => {
|
|
8625
|
+
const recognition = new Ctor();
|
|
8626
|
+
this._webSpeechRecognition = recognition;
|
|
8627
|
+
recognition.lang = options?.lang ?? "en-US";
|
|
8628
|
+
recognition.interimResults = options?.interim ?? true;
|
|
8629
|
+
recognition.continuous = options?.continuous ?? false;
|
|
8630
|
+
let finalTranscript = "";
|
|
8631
|
+
recognition.onresult = (event) => {
|
|
8632
|
+
let interim = "";
|
|
8633
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
8634
|
+
const res = event.results[i];
|
|
8635
|
+
if (res.isFinal) {
|
|
8636
|
+
finalTranscript += res[0].transcript;
|
|
8637
|
+
} else {
|
|
8638
|
+
interim += res[0].transcript;
|
|
8639
|
+
}
|
|
8640
|
+
}
|
|
8641
|
+
if (interim && options?.onPartial) options.onPartial(interim);
|
|
8642
|
+
};
|
|
8643
|
+
recognition.onerror = (event) => {
|
|
8644
|
+
this._webSpeechRecognition = null;
|
|
8645
|
+
reject(new Error(event?.error || "\uC74C\uC131 \uC778\uC2DD \uC624\uB958"));
|
|
8646
|
+
};
|
|
8647
|
+
recognition.onend = () => {
|
|
8648
|
+
this._webSpeechRecognition = null;
|
|
8649
|
+
resolve({ transcript: finalTranscript.trim(), isFinal: true });
|
|
8650
|
+
};
|
|
8651
|
+
recognition.start();
|
|
8652
|
+
});
|
|
8653
|
+
},
|
|
8654
|
+
/**
|
|
8655
|
+
* 진행 중인 음성 인식 중지
|
|
8656
|
+
*/
|
|
8657
|
+
stop: async () => {
|
|
8658
|
+
if (this.getPlatform() === "mobile" && window.NativeBridge?.speech) {
|
|
8659
|
+
await window.NativeBridge.speech.stop();
|
|
8660
|
+
return;
|
|
8661
|
+
}
|
|
8662
|
+
if (this._webSpeechRecognition) {
|
|
8663
|
+
try {
|
|
8664
|
+
this._webSpeechRecognition.stop();
|
|
8665
|
+
} catch {
|
|
8666
|
+
}
|
|
8667
|
+
}
|
|
8668
|
+
}
|
|
8669
|
+
};
|
|
8445
8670
|
}
|
|
8446
8671
|
/**
|
|
8447
8672
|
* 현재 플랫폼 감지
|
|
@@ -11047,6 +11272,7 @@ var ConnectBase = class {
|
|
|
11047
11272
|
this.payment = new PaymentAPI(this.http);
|
|
11048
11273
|
this.subscription = new SubscriptionAPI(this.http);
|
|
11049
11274
|
this.push = new PushAPI(this.http);
|
|
11275
|
+
this.roles = new RolesAPI(this.http);
|
|
11050
11276
|
this.video = new VideoAPI(this.http, config.videoUrl || env("CB_VIDEO_URL") || DEFAULT_VIDEO_URL);
|
|
11051
11277
|
this.game = new GameAPI(this.http, config.gameUrl || env("CB_GAME_URL") || DEFAULT_GAME_URL, config.appId);
|
|
11052
11278
|
this.ads = new AdsAPI(this.http);
|
|
@@ -11141,6 +11367,7 @@ export {
|
|
|
11141
11367
|
GameRoom,
|
|
11142
11368
|
GameRoomTransport,
|
|
11143
11369
|
NativeAPI,
|
|
11370
|
+
RolesAPI,
|
|
11144
11371
|
SessionManager,
|
|
11145
11372
|
VideoProcessingError,
|
|
11146
11373
|
index_default as default,
|