connectbase-client 3.35.2 → 3.37.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 +43 -0
- package/dist/connect-base.umd.js +2 -2
- package/dist/index.d.mts +98 -25
- package/dist/index.d.ts +98 -25
- package/dist/index.js +99 -51
- package/dist/index.mjs +99 -51
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5517,11 +5517,23 @@ var PaymentAPI = class {
|
|
|
5517
5517
|
* // Mount PaymentElement, then:
|
|
5518
5518
|
* // stripe.confirmPayment({ elements, redirect: 'if_required' })
|
|
5519
5519
|
* }
|
|
5520
|
+
*
|
|
5521
|
+
* // PayApp 결제 플로우 (위젯 불필요 — 호스팅 결제창으로 redirect)
|
|
5522
|
+
* // 결제 결과는 서버의 feedbackurl webhook 으로 확정되며, 사용자는 success_url 로 복귀한다.
|
|
5523
|
+
* if (result.payment_provider === 'payapp' && result.payapp_pay_url) {
|
|
5524
|
+
* window.location.href = result.payapp_pay_url
|
|
5525
|
+
* }
|
|
5520
5526
|
* ```
|
|
5527
|
+
*
|
|
5528
|
+
* @remarks
|
|
5529
|
+
* PayApp 은 `customer_phone` 이 필수입니다(결제요청 수신 번호). 미지정 시 서버가 거부합니다.
|
|
5521
5530
|
*/
|
|
5522
5531
|
async prepare(data) {
|
|
5523
5532
|
const prefix = this.getPublicPrefix();
|
|
5524
|
-
return this.http.post(
|
|
5533
|
+
return this.http.post(
|
|
5534
|
+
`${prefix}/payments/prepare`,
|
|
5535
|
+
data
|
|
5536
|
+
);
|
|
5525
5537
|
}
|
|
5526
5538
|
/**
|
|
5527
5539
|
* Stripe-hosted 결제 페이지 세션 생성 (client_secret 미노출 플로우).
|
|
@@ -5585,7 +5597,10 @@ var PaymentAPI = class {
|
|
|
5585
5597
|
*/
|
|
5586
5598
|
async confirm(data) {
|
|
5587
5599
|
const prefix = this.getPublicPrefix();
|
|
5588
|
-
return this.http.post(
|
|
5600
|
+
return this.http.post(
|
|
5601
|
+
`${prefix}/payments/confirm`,
|
|
5602
|
+
data
|
|
5603
|
+
);
|
|
5589
5604
|
}
|
|
5590
5605
|
/**
|
|
5591
5606
|
* 결제 취소
|
|
@@ -6139,20 +6154,22 @@ var PushAPI = class {
|
|
|
6139
6154
|
}
|
|
6140
6155
|
// ============ Server-side Send (Functions / cb_sk_ 전용) ============
|
|
6141
6156
|
/**
|
|
6142
|
-
* 회원 ID 목록으로 푸시 발송 — Functions /
|
|
6157
|
+
* 회원 ID 목록으로 푸시 발송 — Functions / 서버사이드 인증 환경 전용.
|
|
6143
6158
|
*
|
|
6144
|
-
* 백엔드 라우트 `POST /v1/apps/:appID/push/send` 는 콘솔 JWT
|
|
6145
|
-
* (`
|
|
6146
|
-
* 자체가
|
|
6159
|
+
* 백엔드 라우트 `POST /v1/apps/:appID/push/send` 는 콘솔 JWT, User Secret Key(`cb_sk_`),
|
|
6160
|
+
* 또는 service_role 토큰(`ctx.cbAdmin`)을 받는다. 브라우저 SDK 의 Public Key(`cb_pk_`)
|
|
6161
|
+
* **단독** 인스턴스에서는 호출 자체가 차단되며(권한 누설 방지), 명확한 에러를 던진다.
|
|
6162
|
+
* 위임 Bearer 토큰을 가진 인스턴스(`ctx.cbAdmin`/`ctx.cb`)는 cb_pk_ 가 앱 식별용으로만
|
|
6163
|
+
* 실리므로 허용된다.
|
|
6147
6164
|
*
|
|
6148
|
-
* @param appId 발송 대상 앱 ID
|
|
6165
|
+
* @param appId 발송 대상 앱 ID.
|
|
6149
6166
|
* @param memberIds 받는 회원 ID 배열 (UUID).
|
|
6150
6167
|
* @param payload 푸시 내용/옵션.
|
|
6151
6168
|
*
|
|
6152
6169
|
* @example
|
|
6153
6170
|
* ```ts
|
|
6154
|
-
* // ConnectBase Function (
|
|
6155
|
-
* const result = await
|
|
6171
|
+
* // ConnectBase Function (service_role=true) — 시크릿 키 없이 ctx.cbAdmin 으로 발송
|
|
6172
|
+
* const result = await ctx.cbAdmin.push.sendToMembers(ctx.appId, [memberId], {
|
|
6156
6173
|
* title: '새 알림',
|
|
6157
6174
|
* body: '확인해보세요',
|
|
6158
6175
|
* data: { route: '/notifications' },
|
|
@@ -6161,9 +6178,9 @@ var PushAPI = class {
|
|
|
6161
6178
|
* ```
|
|
6162
6179
|
*/
|
|
6163
6180
|
async sendToMembers(appId, memberIds, payload) {
|
|
6164
|
-
if (this.http.hasPublicKey()) {
|
|
6181
|
+
if (this.http.hasPublicKey() && !this.http.hasJWT()) {
|
|
6165
6182
|
throw new Error(
|
|
6166
|
-
"cb.push.sendToMembers() \uB294 User Secret Key(cb_sk_) \
|
|
6183
|
+
"cb.push.sendToMembers() \uB294 User Secret Key(cb_sk_), \uCF58\uC194 JWT, \uB610\uB294 service_role(ctx.cbAdmin) \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."
|
|
6167
6184
|
);
|
|
6168
6185
|
}
|
|
6169
6186
|
if (memberIds.length === 0) {
|
|
@@ -6615,8 +6632,9 @@ var VideoAPI = class {
|
|
|
6615
6632
|
*
|
|
6616
6633
|
* @example
|
|
6617
6634
|
* ```ts
|
|
6618
|
-
* const {
|
|
6619
|
-
* videoElement.src =
|
|
6635
|
+
* const { master_playlist, qualities, expires_at } = await cb.video.getStreamUrl(videoId)
|
|
6636
|
+
* videoElement.src = master_playlist // 적응형 비트레이트(HLS.js 등으로 재생)
|
|
6637
|
+
* // 특정 화질 고정이 필요하면 qualities['720p'] 사용
|
|
6620
6638
|
* ```
|
|
6621
6639
|
*/
|
|
6622
6640
|
async getStreamUrl(videoId, quality) {
|
|
@@ -6702,31 +6720,37 @@ var VideoAPI = class {
|
|
|
6702
6720
|
}
|
|
6703
6721
|
// ========== Playlist Operations ==========
|
|
6704
6722
|
/**
|
|
6705
|
-
* Create a playlist
|
|
6723
|
+
* Create a playlist for a channel.
|
|
6724
|
+
* 백엔드는 `POST /v1/public/playlists` 에 body 로 `channel_id` 를 받는다 (AppMember 인증).
|
|
6706
6725
|
*/
|
|
6707
6726
|
async createPlaylist(channelId, data) {
|
|
6708
6727
|
const prefix = this.getPublicPrefix();
|
|
6709
|
-
return this.videoFetch("POST", `${prefix}/
|
|
6728
|
+
return this.videoFetch("POST", `${prefix}/playlists`, {
|
|
6729
|
+
...data,
|
|
6730
|
+
channel_id: channelId
|
|
6731
|
+
});
|
|
6710
6732
|
}
|
|
6711
6733
|
/**
|
|
6712
|
-
* Get
|
|
6734
|
+
* Get a channel's public playlists.
|
|
6735
|
+
* 백엔드는 `GET /v1/public/playlists/public?channel_id=` 로 공개 플레이리스트를 반환한다.
|
|
6713
6736
|
*/
|
|
6714
6737
|
async getPlaylists(channelId) {
|
|
6715
6738
|
const prefix = this.getPublicPrefix();
|
|
6716
6739
|
const response = await this.videoFetch(
|
|
6717
6740
|
"GET",
|
|
6718
|
-
`${prefix}/
|
|
6741
|
+
`${prefix}/playlists/public?channel_id=${encodeURIComponent(channelId)}`
|
|
6719
6742
|
);
|
|
6720
6743
|
return response.playlists;
|
|
6721
6744
|
}
|
|
6722
6745
|
/**
|
|
6723
|
-
* Get playlist items
|
|
6746
|
+
* Get playlist items.
|
|
6747
|
+
* 백엔드는 별도 items 라우트가 없고 `GET /v1/public/playlists/:id` 응답에 items 를 포함한다.
|
|
6724
6748
|
*/
|
|
6725
6749
|
async getPlaylistItems(playlistId) {
|
|
6726
6750
|
const prefix = this.getPublicPrefix();
|
|
6727
6751
|
const response = await this.videoFetch(
|
|
6728
6752
|
"GET",
|
|
6729
|
-
`${prefix}/playlists/${playlistId}
|
|
6753
|
+
`${prefix}/playlists/${playlistId}`
|
|
6730
6754
|
);
|
|
6731
6755
|
return response.items;
|
|
6732
6756
|
}
|
|
@@ -6795,7 +6819,8 @@ var VideoAPI = class {
|
|
|
6795
6819
|
*/
|
|
6796
6820
|
async postComment(videoId, content, parentId) {
|
|
6797
6821
|
const prefix = this.getPublicPrefix();
|
|
6798
|
-
return this.videoFetch("POST", `${prefix}/
|
|
6822
|
+
return this.videoFetch("POST", `${prefix}/comments`, {
|
|
6823
|
+
video_id: videoId,
|
|
6799
6824
|
content,
|
|
6800
6825
|
parent_id: parentId
|
|
6801
6826
|
});
|
|
@@ -6862,42 +6887,55 @@ var VideoAPI = class {
|
|
|
6862
6887
|
const prefix = this.getPublicPrefix();
|
|
6863
6888
|
const response = await this.videoFetch(
|
|
6864
6889
|
"GET",
|
|
6865
|
-
`${prefix}/channels/${channelId}/
|
|
6890
|
+
`${prefix}/channels/${channelId}/membership/tiers`
|
|
6866
6891
|
);
|
|
6867
6892
|
return response.tiers;
|
|
6868
6893
|
}
|
|
6869
6894
|
/**
|
|
6870
6895
|
* Join a membership tier
|
|
6896
|
+
*
|
|
6897
|
+
* @param _channelId 현재 서버에서 무시됩니다 (tier 가 채널을 함의). API 안정성을 위해 시그니처만 유지.
|
|
6898
|
+
* @param tierId 가입할 멤버십 티어 ID
|
|
6871
6899
|
*/
|
|
6872
|
-
async joinMembership(
|
|
6900
|
+
async joinMembership(_channelId, tierId) {
|
|
6873
6901
|
const prefix = this.getPublicPrefix();
|
|
6874
|
-
return this.videoFetch(
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
{}
|
|
6878
|
-
);
|
|
6902
|
+
return this.videoFetch("POST", `${prefix}/membership/join`, {
|
|
6903
|
+
tier_id: tierId
|
|
6904
|
+
});
|
|
6879
6905
|
}
|
|
6880
6906
|
/**
|
|
6881
6907
|
* Cancel membership
|
|
6908
|
+
*
|
|
6909
|
+
* @param _channelId 현재 서버에서 무시됩니다. API 안정성을 위해 시그니처만 유지.
|
|
6910
|
+
* @param membershipId 취소할 멤버십 ID
|
|
6882
6911
|
*/
|
|
6883
|
-
async cancelMembership(
|
|
6912
|
+
async cancelMembership(_channelId, membershipId) {
|
|
6884
6913
|
const prefix = this.getPublicPrefix();
|
|
6885
|
-
await this.videoFetch(
|
|
6886
|
-
"POST",
|
|
6887
|
-
`${prefix}/channels/${channelId}/memberships/${membershipId}/cancel`,
|
|
6888
|
-
{}
|
|
6889
|
-
);
|
|
6914
|
+
await this.videoFetch("POST", `${prefix}/membership/${membershipId}/cancel`, {});
|
|
6890
6915
|
}
|
|
6891
6916
|
// ========== Super Chat Operations ==========
|
|
6892
6917
|
/**
|
|
6893
|
-
* Send a super chat
|
|
6918
|
+
* Send a super chat to a channel.
|
|
6919
|
+
*
|
|
6920
|
+
* 백엔드는 `POST /v1/public/super-chats` 에 `channel_id`/`type`/`amount` 를 요구하고,
|
|
6921
|
+
* `video_id` 또는 `live_id` 중 하나로 대상을 지정한다 (AppMember 인증 필수).
|
|
6922
|
+
* 응답의 `client_secret` 으로 결제(Stripe)를 확정해야 슈퍼챗이 노출된다.
|
|
6923
|
+
*
|
|
6924
|
+
* @param channelId 슈퍼챗을 받을 채널 ID
|
|
6925
|
+
* @param amount 금액(최소 단위 정수)
|
|
6926
|
+
* @param options videoId 또는 liveId 중 하나로 대상 지정 + 종류/메시지/통화
|
|
6894
6927
|
*/
|
|
6895
|
-
async sendSuperChat(
|
|
6928
|
+
async sendSuperChat(channelId, amount, options) {
|
|
6896
6929
|
const prefix = this.getPublicPrefix();
|
|
6897
|
-
return this.videoFetch("POST", `${prefix}/
|
|
6930
|
+
return this.videoFetch("POST", `${prefix}/super-chats`, {
|
|
6931
|
+
channel_id: channelId,
|
|
6932
|
+
type: options?.type ?? "super_thanks",
|
|
6898
6933
|
amount,
|
|
6899
|
-
|
|
6900
|
-
|
|
6934
|
+
video_id: options?.videoId,
|
|
6935
|
+
live_id: options?.liveId,
|
|
6936
|
+
currency: options?.currency ?? "USD",
|
|
6937
|
+
message: options?.message,
|
|
6938
|
+
sticker_id: options?.stickerId
|
|
6901
6939
|
});
|
|
6902
6940
|
}
|
|
6903
6941
|
/**
|
|
@@ -6914,13 +6952,15 @@ var VideoAPI = class {
|
|
|
6914
6952
|
// ========== Recommendation Operations ==========
|
|
6915
6953
|
/**
|
|
6916
6954
|
* Get recommended videos
|
|
6955
|
+
*
|
|
6956
|
+
* @deprecated Use getHomeFeed() — 동일한 개인화 피드(`/recommendations/feed`)를 반환합니다.
|
|
6917
6957
|
*/
|
|
6918
6958
|
async getRecommendations(limit) {
|
|
6919
6959
|
const prefix = this.getPublicPrefix();
|
|
6920
6960
|
const params = limit ? `?limit=${limit}` : "";
|
|
6921
6961
|
const response = await this.videoFetch(
|
|
6922
6962
|
"GET",
|
|
6923
|
-
`${prefix}/recommendations${params}`
|
|
6963
|
+
`${prefix}/recommendations/feed${params}`
|
|
6924
6964
|
);
|
|
6925
6965
|
return response.videos;
|
|
6926
6966
|
}
|
|
@@ -6932,7 +6972,7 @@ var VideoAPI = class {
|
|
|
6932
6972
|
const params = limit ? `?limit=${limit}` : "";
|
|
6933
6973
|
const response = await this.videoFetch(
|
|
6934
6974
|
"GET",
|
|
6935
|
-
`${prefix}/recommendations/
|
|
6975
|
+
`${prefix}/recommendations/feed${params}`
|
|
6936
6976
|
);
|
|
6937
6977
|
return response.videos;
|
|
6938
6978
|
}
|
|
@@ -6944,7 +6984,7 @@ var VideoAPI = class {
|
|
|
6944
6984
|
const params = limit ? `?limit=${limit}` : "";
|
|
6945
6985
|
const response = await this.videoFetch(
|
|
6946
6986
|
"GET",
|
|
6947
|
-
`${prefix}/
|
|
6987
|
+
`${prefix}/videos/${videoId}/related${params}`
|
|
6948
6988
|
);
|
|
6949
6989
|
return response.videos;
|
|
6950
6990
|
}
|
|
@@ -6965,9 +7005,17 @@ var VideoAPI = class {
|
|
|
6965
7005
|
*/
|
|
6966
7006
|
async submitFeedback(videoId, feedback) {
|
|
6967
7007
|
const prefix = this.getPublicPrefix();
|
|
6968
|
-
|
|
7008
|
+
if (feedback === "not_interested") {
|
|
7009
|
+
await this.videoFetch(
|
|
7010
|
+
"POST",
|
|
7011
|
+
`${prefix}/recommendations/not-interested/${videoId}`,
|
|
7012
|
+
{}
|
|
7013
|
+
);
|
|
7014
|
+
return;
|
|
7015
|
+
}
|
|
7016
|
+
await this.videoFetch("POST", `${prefix}/recommendations/interactions`, {
|
|
6969
7017
|
video_id: videoId,
|
|
6970
|
-
|
|
7018
|
+
type: "like"
|
|
6971
7019
|
});
|
|
6972
7020
|
}
|
|
6973
7021
|
};
|
|
@@ -7009,7 +7057,7 @@ var GameConfigAPI = class {
|
|
|
7009
7057
|
/**
|
|
7010
7058
|
* 단일 기능 활성화 — set() 의 편의 wrapper.
|
|
7011
7059
|
*
|
|
7012
|
-
* @example await cb.game.config.enable(appId, "
|
|
7060
|
+
* @example await cb.game.config.enable(appId, "matchqueue_enabled")
|
|
7013
7061
|
*/
|
|
7014
7062
|
async enable(appId, feature) {
|
|
7015
7063
|
return this.set(appId, { [feature]: true });
|
|
@@ -7667,7 +7715,7 @@ var GameAPI = class {
|
|
|
7667
7715
|
const response = await fetch(`${this.gameServerUrl}/v1/game/${id}/rooms/${roomId}/spectators`, {
|
|
7668
7716
|
method: "POST",
|
|
7669
7717
|
headers: { ...this.getHeaders(), "Content-Type": "application/json" },
|
|
7670
|
-
body: JSON.stringify({
|
|
7718
|
+
body: JSON.stringify({ user_id: playerId })
|
|
7671
7719
|
});
|
|
7672
7720
|
if (!response.ok) throw new Error(`Failed to join spectator: ${response.statusText}`);
|
|
7673
7721
|
return response.json();
|
|
@@ -10993,11 +11041,11 @@ var GameRoomTransport = class {
|
|
|
10993
11041
|
});
|
|
10994
11042
|
}
|
|
10995
11043
|
// Private methods
|
|
10996
|
-
send(type, data) {
|
|
11044
|
+
send(type, data, msgId) {
|
|
10997
11045
|
if (!this.transport?.isConnected()) {
|
|
10998
11046
|
throw new GameError({ code: "NOT_CONNECTED", message: "Not connected" });
|
|
10999
11047
|
}
|
|
11000
|
-
const message = JSON.stringify({ type, data });
|
|
11048
|
+
const message = JSON.stringify({ type, data, msg_id: msgId });
|
|
11001
11049
|
this.transport.send(message, true);
|
|
11002
11050
|
}
|
|
11003
11051
|
sendWithHandler(type, data, handler) {
|
|
@@ -11006,14 +11054,14 @@ var GameRoomTransport = class {
|
|
|
11006
11054
|
setTimeout(() => {
|
|
11007
11055
|
this.pendingHandlers.delete(id);
|
|
11008
11056
|
}, 1e4);
|
|
11009
|
-
this.send(type,
|
|
11057
|
+
this.send(type, data, id);
|
|
11010
11058
|
}
|
|
11011
11059
|
handleMessage(data) {
|
|
11012
11060
|
try {
|
|
11013
11061
|
const msg = JSON.parse(data);
|
|
11014
|
-
if (msg.
|
|
11015
|
-
const handler = this.pendingHandlers.get(msg.
|
|
11016
|
-
this.pendingHandlers.delete(msg.
|
|
11062
|
+
if (msg.msg_id && this.pendingHandlers.has(msg.msg_id)) {
|
|
11063
|
+
const handler = this.pendingHandlers.get(msg.msg_id);
|
|
11064
|
+
this.pendingHandlers.delete(msg.msg_id);
|
|
11017
11065
|
handler(msg);
|
|
11018
11066
|
return;
|
|
11019
11067
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -5471,11 +5471,23 @@ var PaymentAPI = class {
|
|
|
5471
5471
|
* // Mount PaymentElement, then:
|
|
5472
5472
|
* // stripe.confirmPayment({ elements, redirect: 'if_required' })
|
|
5473
5473
|
* }
|
|
5474
|
+
*
|
|
5475
|
+
* // PayApp 결제 플로우 (위젯 불필요 — 호스팅 결제창으로 redirect)
|
|
5476
|
+
* // 결제 결과는 서버의 feedbackurl webhook 으로 확정되며, 사용자는 success_url 로 복귀한다.
|
|
5477
|
+
* if (result.payment_provider === 'payapp' && result.payapp_pay_url) {
|
|
5478
|
+
* window.location.href = result.payapp_pay_url
|
|
5479
|
+
* }
|
|
5474
5480
|
* ```
|
|
5481
|
+
*
|
|
5482
|
+
* @remarks
|
|
5483
|
+
* PayApp 은 `customer_phone` 이 필수입니다(결제요청 수신 번호). 미지정 시 서버가 거부합니다.
|
|
5475
5484
|
*/
|
|
5476
5485
|
async prepare(data) {
|
|
5477
5486
|
const prefix = this.getPublicPrefix();
|
|
5478
|
-
return this.http.post(
|
|
5487
|
+
return this.http.post(
|
|
5488
|
+
`${prefix}/payments/prepare`,
|
|
5489
|
+
data
|
|
5490
|
+
);
|
|
5479
5491
|
}
|
|
5480
5492
|
/**
|
|
5481
5493
|
* Stripe-hosted 결제 페이지 세션 생성 (client_secret 미노출 플로우).
|
|
@@ -5539,7 +5551,10 @@ var PaymentAPI = class {
|
|
|
5539
5551
|
*/
|
|
5540
5552
|
async confirm(data) {
|
|
5541
5553
|
const prefix = this.getPublicPrefix();
|
|
5542
|
-
return this.http.post(
|
|
5554
|
+
return this.http.post(
|
|
5555
|
+
`${prefix}/payments/confirm`,
|
|
5556
|
+
data
|
|
5557
|
+
);
|
|
5543
5558
|
}
|
|
5544
5559
|
/**
|
|
5545
5560
|
* 결제 취소
|
|
@@ -6093,20 +6108,22 @@ var PushAPI = class {
|
|
|
6093
6108
|
}
|
|
6094
6109
|
// ============ Server-side Send (Functions / cb_sk_ 전용) ============
|
|
6095
6110
|
/**
|
|
6096
|
-
* 회원 ID 목록으로 푸시 발송 — Functions /
|
|
6111
|
+
* 회원 ID 목록으로 푸시 발송 — Functions / 서버사이드 인증 환경 전용.
|
|
6097
6112
|
*
|
|
6098
|
-
* 백엔드 라우트 `POST /v1/apps/:appID/push/send` 는 콘솔 JWT
|
|
6099
|
-
* (`
|
|
6100
|
-
* 자체가
|
|
6113
|
+
* 백엔드 라우트 `POST /v1/apps/:appID/push/send` 는 콘솔 JWT, User Secret Key(`cb_sk_`),
|
|
6114
|
+
* 또는 service_role 토큰(`ctx.cbAdmin`)을 받는다. 브라우저 SDK 의 Public Key(`cb_pk_`)
|
|
6115
|
+
* **단독** 인스턴스에서는 호출 자체가 차단되며(권한 누설 방지), 명확한 에러를 던진다.
|
|
6116
|
+
* 위임 Bearer 토큰을 가진 인스턴스(`ctx.cbAdmin`/`ctx.cb`)는 cb_pk_ 가 앱 식별용으로만
|
|
6117
|
+
* 실리므로 허용된다.
|
|
6101
6118
|
*
|
|
6102
|
-
* @param appId 발송 대상 앱 ID
|
|
6119
|
+
* @param appId 발송 대상 앱 ID.
|
|
6103
6120
|
* @param memberIds 받는 회원 ID 배열 (UUID).
|
|
6104
6121
|
* @param payload 푸시 내용/옵션.
|
|
6105
6122
|
*
|
|
6106
6123
|
* @example
|
|
6107
6124
|
* ```ts
|
|
6108
|
-
* // ConnectBase Function (
|
|
6109
|
-
* const result = await
|
|
6125
|
+
* // ConnectBase Function (service_role=true) — 시크릿 키 없이 ctx.cbAdmin 으로 발송
|
|
6126
|
+
* const result = await ctx.cbAdmin.push.sendToMembers(ctx.appId, [memberId], {
|
|
6110
6127
|
* title: '새 알림',
|
|
6111
6128
|
* body: '확인해보세요',
|
|
6112
6129
|
* data: { route: '/notifications' },
|
|
@@ -6115,9 +6132,9 @@ var PushAPI = class {
|
|
|
6115
6132
|
* ```
|
|
6116
6133
|
*/
|
|
6117
6134
|
async sendToMembers(appId, memberIds, payload) {
|
|
6118
|
-
if (this.http.hasPublicKey()) {
|
|
6135
|
+
if (this.http.hasPublicKey() && !this.http.hasJWT()) {
|
|
6119
6136
|
throw new Error(
|
|
6120
|
-
"cb.push.sendToMembers() \uB294 User Secret Key(cb_sk_) \
|
|
6137
|
+
"cb.push.sendToMembers() \uB294 User Secret Key(cb_sk_), \uCF58\uC194 JWT, \uB610\uB294 service_role(ctx.cbAdmin) \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."
|
|
6121
6138
|
);
|
|
6122
6139
|
}
|
|
6123
6140
|
if (memberIds.length === 0) {
|
|
@@ -6569,8 +6586,9 @@ var VideoAPI = class {
|
|
|
6569
6586
|
*
|
|
6570
6587
|
* @example
|
|
6571
6588
|
* ```ts
|
|
6572
|
-
* const {
|
|
6573
|
-
* videoElement.src =
|
|
6589
|
+
* const { master_playlist, qualities, expires_at } = await cb.video.getStreamUrl(videoId)
|
|
6590
|
+
* videoElement.src = master_playlist // 적응형 비트레이트(HLS.js 등으로 재생)
|
|
6591
|
+
* // 특정 화질 고정이 필요하면 qualities['720p'] 사용
|
|
6574
6592
|
* ```
|
|
6575
6593
|
*/
|
|
6576
6594
|
async getStreamUrl(videoId, quality) {
|
|
@@ -6656,31 +6674,37 @@ var VideoAPI = class {
|
|
|
6656
6674
|
}
|
|
6657
6675
|
// ========== Playlist Operations ==========
|
|
6658
6676
|
/**
|
|
6659
|
-
* Create a playlist
|
|
6677
|
+
* Create a playlist for a channel.
|
|
6678
|
+
* 백엔드는 `POST /v1/public/playlists` 에 body 로 `channel_id` 를 받는다 (AppMember 인증).
|
|
6660
6679
|
*/
|
|
6661
6680
|
async createPlaylist(channelId, data) {
|
|
6662
6681
|
const prefix = this.getPublicPrefix();
|
|
6663
|
-
return this.videoFetch("POST", `${prefix}/
|
|
6682
|
+
return this.videoFetch("POST", `${prefix}/playlists`, {
|
|
6683
|
+
...data,
|
|
6684
|
+
channel_id: channelId
|
|
6685
|
+
});
|
|
6664
6686
|
}
|
|
6665
6687
|
/**
|
|
6666
|
-
* Get
|
|
6688
|
+
* Get a channel's public playlists.
|
|
6689
|
+
* 백엔드는 `GET /v1/public/playlists/public?channel_id=` 로 공개 플레이리스트를 반환한다.
|
|
6667
6690
|
*/
|
|
6668
6691
|
async getPlaylists(channelId) {
|
|
6669
6692
|
const prefix = this.getPublicPrefix();
|
|
6670
6693
|
const response = await this.videoFetch(
|
|
6671
6694
|
"GET",
|
|
6672
|
-
`${prefix}/
|
|
6695
|
+
`${prefix}/playlists/public?channel_id=${encodeURIComponent(channelId)}`
|
|
6673
6696
|
);
|
|
6674
6697
|
return response.playlists;
|
|
6675
6698
|
}
|
|
6676
6699
|
/**
|
|
6677
|
-
* Get playlist items
|
|
6700
|
+
* Get playlist items.
|
|
6701
|
+
* 백엔드는 별도 items 라우트가 없고 `GET /v1/public/playlists/:id` 응답에 items 를 포함한다.
|
|
6678
6702
|
*/
|
|
6679
6703
|
async getPlaylistItems(playlistId) {
|
|
6680
6704
|
const prefix = this.getPublicPrefix();
|
|
6681
6705
|
const response = await this.videoFetch(
|
|
6682
6706
|
"GET",
|
|
6683
|
-
`${prefix}/playlists/${playlistId}
|
|
6707
|
+
`${prefix}/playlists/${playlistId}`
|
|
6684
6708
|
);
|
|
6685
6709
|
return response.items;
|
|
6686
6710
|
}
|
|
@@ -6749,7 +6773,8 @@ var VideoAPI = class {
|
|
|
6749
6773
|
*/
|
|
6750
6774
|
async postComment(videoId, content, parentId) {
|
|
6751
6775
|
const prefix = this.getPublicPrefix();
|
|
6752
|
-
return this.videoFetch("POST", `${prefix}/
|
|
6776
|
+
return this.videoFetch("POST", `${prefix}/comments`, {
|
|
6777
|
+
video_id: videoId,
|
|
6753
6778
|
content,
|
|
6754
6779
|
parent_id: parentId
|
|
6755
6780
|
});
|
|
@@ -6816,42 +6841,55 @@ var VideoAPI = class {
|
|
|
6816
6841
|
const prefix = this.getPublicPrefix();
|
|
6817
6842
|
const response = await this.videoFetch(
|
|
6818
6843
|
"GET",
|
|
6819
|
-
`${prefix}/channels/${channelId}/
|
|
6844
|
+
`${prefix}/channels/${channelId}/membership/tiers`
|
|
6820
6845
|
);
|
|
6821
6846
|
return response.tiers;
|
|
6822
6847
|
}
|
|
6823
6848
|
/**
|
|
6824
6849
|
* Join a membership tier
|
|
6850
|
+
*
|
|
6851
|
+
* @param _channelId 현재 서버에서 무시됩니다 (tier 가 채널을 함의). API 안정성을 위해 시그니처만 유지.
|
|
6852
|
+
* @param tierId 가입할 멤버십 티어 ID
|
|
6825
6853
|
*/
|
|
6826
|
-
async joinMembership(
|
|
6854
|
+
async joinMembership(_channelId, tierId) {
|
|
6827
6855
|
const prefix = this.getPublicPrefix();
|
|
6828
|
-
return this.videoFetch(
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
{}
|
|
6832
|
-
);
|
|
6856
|
+
return this.videoFetch("POST", `${prefix}/membership/join`, {
|
|
6857
|
+
tier_id: tierId
|
|
6858
|
+
});
|
|
6833
6859
|
}
|
|
6834
6860
|
/**
|
|
6835
6861
|
* Cancel membership
|
|
6862
|
+
*
|
|
6863
|
+
* @param _channelId 현재 서버에서 무시됩니다. API 안정성을 위해 시그니처만 유지.
|
|
6864
|
+
* @param membershipId 취소할 멤버십 ID
|
|
6836
6865
|
*/
|
|
6837
|
-
async cancelMembership(
|
|
6866
|
+
async cancelMembership(_channelId, membershipId) {
|
|
6838
6867
|
const prefix = this.getPublicPrefix();
|
|
6839
|
-
await this.videoFetch(
|
|
6840
|
-
"POST",
|
|
6841
|
-
`${prefix}/channels/${channelId}/memberships/${membershipId}/cancel`,
|
|
6842
|
-
{}
|
|
6843
|
-
);
|
|
6868
|
+
await this.videoFetch("POST", `${prefix}/membership/${membershipId}/cancel`, {});
|
|
6844
6869
|
}
|
|
6845
6870
|
// ========== Super Chat Operations ==========
|
|
6846
6871
|
/**
|
|
6847
|
-
* Send a super chat
|
|
6872
|
+
* Send a super chat to a channel.
|
|
6873
|
+
*
|
|
6874
|
+
* 백엔드는 `POST /v1/public/super-chats` 에 `channel_id`/`type`/`amount` 를 요구하고,
|
|
6875
|
+
* `video_id` 또는 `live_id` 중 하나로 대상을 지정한다 (AppMember 인증 필수).
|
|
6876
|
+
* 응답의 `client_secret` 으로 결제(Stripe)를 확정해야 슈퍼챗이 노출된다.
|
|
6877
|
+
*
|
|
6878
|
+
* @param channelId 슈퍼챗을 받을 채널 ID
|
|
6879
|
+
* @param amount 금액(최소 단위 정수)
|
|
6880
|
+
* @param options videoId 또는 liveId 중 하나로 대상 지정 + 종류/메시지/통화
|
|
6848
6881
|
*/
|
|
6849
|
-
async sendSuperChat(
|
|
6882
|
+
async sendSuperChat(channelId, amount, options) {
|
|
6850
6883
|
const prefix = this.getPublicPrefix();
|
|
6851
|
-
return this.videoFetch("POST", `${prefix}/
|
|
6884
|
+
return this.videoFetch("POST", `${prefix}/super-chats`, {
|
|
6885
|
+
channel_id: channelId,
|
|
6886
|
+
type: options?.type ?? "super_thanks",
|
|
6852
6887
|
amount,
|
|
6853
|
-
|
|
6854
|
-
|
|
6888
|
+
video_id: options?.videoId,
|
|
6889
|
+
live_id: options?.liveId,
|
|
6890
|
+
currency: options?.currency ?? "USD",
|
|
6891
|
+
message: options?.message,
|
|
6892
|
+
sticker_id: options?.stickerId
|
|
6855
6893
|
});
|
|
6856
6894
|
}
|
|
6857
6895
|
/**
|
|
@@ -6868,13 +6906,15 @@ var VideoAPI = class {
|
|
|
6868
6906
|
// ========== Recommendation Operations ==========
|
|
6869
6907
|
/**
|
|
6870
6908
|
* Get recommended videos
|
|
6909
|
+
*
|
|
6910
|
+
* @deprecated Use getHomeFeed() — 동일한 개인화 피드(`/recommendations/feed`)를 반환합니다.
|
|
6871
6911
|
*/
|
|
6872
6912
|
async getRecommendations(limit) {
|
|
6873
6913
|
const prefix = this.getPublicPrefix();
|
|
6874
6914
|
const params = limit ? `?limit=${limit}` : "";
|
|
6875
6915
|
const response = await this.videoFetch(
|
|
6876
6916
|
"GET",
|
|
6877
|
-
`${prefix}/recommendations${params}`
|
|
6917
|
+
`${prefix}/recommendations/feed${params}`
|
|
6878
6918
|
);
|
|
6879
6919
|
return response.videos;
|
|
6880
6920
|
}
|
|
@@ -6886,7 +6926,7 @@ var VideoAPI = class {
|
|
|
6886
6926
|
const params = limit ? `?limit=${limit}` : "";
|
|
6887
6927
|
const response = await this.videoFetch(
|
|
6888
6928
|
"GET",
|
|
6889
|
-
`${prefix}/recommendations/
|
|
6929
|
+
`${prefix}/recommendations/feed${params}`
|
|
6890
6930
|
);
|
|
6891
6931
|
return response.videos;
|
|
6892
6932
|
}
|
|
@@ -6898,7 +6938,7 @@ var VideoAPI = class {
|
|
|
6898
6938
|
const params = limit ? `?limit=${limit}` : "";
|
|
6899
6939
|
const response = await this.videoFetch(
|
|
6900
6940
|
"GET",
|
|
6901
|
-
`${prefix}/
|
|
6941
|
+
`${prefix}/videos/${videoId}/related${params}`
|
|
6902
6942
|
);
|
|
6903
6943
|
return response.videos;
|
|
6904
6944
|
}
|
|
@@ -6919,9 +6959,17 @@ var VideoAPI = class {
|
|
|
6919
6959
|
*/
|
|
6920
6960
|
async submitFeedback(videoId, feedback) {
|
|
6921
6961
|
const prefix = this.getPublicPrefix();
|
|
6922
|
-
|
|
6962
|
+
if (feedback === "not_interested") {
|
|
6963
|
+
await this.videoFetch(
|
|
6964
|
+
"POST",
|
|
6965
|
+
`${prefix}/recommendations/not-interested/${videoId}`,
|
|
6966
|
+
{}
|
|
6967
|
+
);
|
|
6968
|
+
return;
|
|
6969
|
+
}
|
|
6970
|
+
await this.videoFetch("POST", `${prefix}/recommendations/interactions`, {
|
|
6923
6971
|
video_id: videoId,
|
|
6924
|
-
|
|
6972
|
+
type: "like"
|
|
6925
6973
|
});
|
|
6926
6974
|
}
|
|
6927
6975
|
};
|
|
@@ -6963,7 +7011,7 @@ var GameConfigAPI = class {
|
|
|
6963
7011
|
/**
|
|
6964
7012
|
* 단일 기능 활성화 — set() 의 편의 wrapper.
|
|
6965
7013
|
*
|
|
6966
|
-
* @example await cb.game.config.enable(appId, "
|
|
7014
|
+
* @example await cb.game.config.enable(appId, "matchqueue_enabled")
|
|
6967
7015
|
*/
|
|
6968
7016
|
async enable(appId, feature) {
|
|
6969
7017
|
return this.set(appId, { [feature]: true });
|
|
@@ -7621,7 +7669,7 @@ var GameAPI = class {
|
|
|
7621
7669
|
const response = await fetch(`${this.gameServerUrl}/v1/game/${id}/rooms/${roomId}/spectators`, {
|
|
7622
7670
|
method: "POST",
|
|
7623
7671
|
headers: { ...this.getHeaders(), "Content-Type": "application/json" },
|
|
7624
|
-
body: JSON.stringify({
|
|
7672
|
+
body: JSON.stringify({ user_id: playerId })
|
|
7625
7673
|
});
|
|
7626
7674
|
if (!response.ok) throw new Error(`Failed to join spectator: ${response.statusText}`);
|
|
7627
7675
|
return response.json();
|
|
@@ -10947,11 +10995,11 @@ var GameRoomTransport = class {
|
|
|
10947
10995
|
});
|
|
10948
10996
|
}
|
|
10949
10997
|
// Private methods
|
|
10950
|
-
send(type, data) {
|
|
10998
|
+
send(type, data, msgId) {
|
|
10951
10999
|
if (!this.transport?.isConnected()) {
|
|
10952
11000
|
throw new GameError({ code: "NOT_CONNECTED", message: "Not connected" });
|
|
10953
11001
|
}
|
|
10954
|
-
const message = JSON.stringify({ type, data });
|
|
11002
|
+
const message = JSON.stringify({ type, data, msg_id: msgId });
|
|
10955
11003
|
this.transport.send(message, true);
|
|
10956
11004
|
}
|
|
10957
11005
|
sendWithHandler(type, data, handler) {
|
|
@@ -10960,14 +11008,14 @@ var GameRoomTransport = class {
|
|
|
10960
11008
|
setTimeout(() => {
|
|
10961
11009
|
this.pendingHandlers.delete(id);
|
|
10962
11010
|
}, 1e4);
|
|
10963
|
-
this.send(type,
|
|
11011
|
+
this.send(type, data, id);
|
|
10964
11012
|
}
|
|
10965
11013
|
handleMessage(data) {
|
|
10966
11014
|
try {
|
|
10967
11015
|
const msg = JSON.parse(data);
|
|
10968
|
-
if (msg.
|
|
10969
|
-
const handler = this.pendingHandlers.get(msg.
|
|
10970
|
-
this.pendingHandlers.delete(msg.
|
|
11016
|
+
if (msg.msg_id && this.pendingHandlers.has(msg.msg_id)) {
|
|
11017
|
+
const handler = this.pendingHandlers.get(msg.msg_id);
|
|
11018
|
+
this.pendingHandlers.delete(msg.msg_id);
|
|
10971
11019
|
handler(msg);
|
|
10972
11020
|
return;
|
|
10973
11021
|
}
|