connectbase-client 3.53.0 → 4.0.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 +53 -0
- package/dist/connect-base.umd.js +1 -1
- package/dist/index.d.mts +123 -13
- package/dist/index.d.ts +123 -13
- package/dist/index.js +57 -0
- package/dist/index.mjs +57 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,59 @@
|
|
|
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
|
+
## [4.0.0] - 2026-07-23
|
|
7
|
+
|
|
8
|
+
### Changed — ⚠️ BREAKING: MoR 결제 필드 프로바이더 중립화 + Dodo Payments 지원
|
|
9
|
+
|
|
10
|
+
Paddle 전용이던 결제/구독 필드를 프로바이더 중립(MoR = Merchant of Record) 이름으로 정규화하고,
|
|
11
|
+
`dodo`(Dodo Payments)를 새 MoR 프로바이더로 추가했다. 결제 엔진을 Paddle↔Dodo(향후 Polar/Lemon
|
|
12
|
+
Squeezy) 간에 코드 변경 없이 바꿀 수 있다.
|
|
13
|
+
|
|
14
|
+
**필드 리네임 (subscription / payment 응답·요청):**
|
|
15
|
+
|
|
16
|
+
| 이전 (`paddle_*`) | 이후 (중립) |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `paddle_price_id` | `provider_price_ref` |
|
|
19
|
+
| `paddle_transaction_id` | `provider_ref` |
|
|
20
|
+
| `paddle_client_token` | `client_token` |
|
|
21
|
+
| `paddle_environment` | `environment` |
|
|
22
|
+
| `paddle_checkout_url` | `checkout_url` |
|
|
23
|
+
| `new_paddle_price_id` | `new_provider_price_ref` |
|
|
24
|
+
|
|
25
|
+
**추가:**
|
|
26
|
+
- checkout 응답에 `launch_mode`(`"overlay"` | `"redirect"`) — 프론트가 결제창 여는 방식을 결정
|
|
27
|
+
(Paddle=오버레이 client_token, Dodo=리다이렉트 checkout_url).
|
|
28
|
+
- provider 유니온에 `"dodo"`.
|
|
29
|
+
|
|
30
|
+
**마이그레이션:** `cb.subscription.create/changePlan/previewChangePlan`, `cb.payment.prepare/confirm`
|
|
31
|
+
사용부에서 `paddle_price_id`→`provider_price_ref`, `paddle_transaction_id`→`provider_ref`,
|
|
32
|
+
`paddle_client_token`→`client_token`, `paddle_environment`→`environment`,
|
|
33
|
+
`paddle_checkout_url`→`checkout_url` 로 교체. `proration_mode`/`on_payment_failure` 값 어휘는 그대로
|
|
34
|
+
(프로바이더 중립 어휘로 문서화).
|
|
35
|
+
|
|
36
|
+
## [3.54.0] - 2026-07-23
|
|
37
|
+
|
|
38
|
+
### Added — 구독 요금제 스왑 + Paddle proration (`cb.subscription.changePlan` / `previewChangePlan`)
|
|
39
|
+
|
|
40
|
+
기존 구독을 **하나로 유지한 채(id 동일) 다른 요금제로 갈아타고 남은 금액을 즉시 일할 정산(proration)**
|
|
41
|
+
하는 메서드를 추가했다. Paddle(MoR) 구독 전용. 예) 혼자→가족 in-place 업그레이드 — 이미 낸 혼자
|
|
42
|
+
미사용분을 Paddle 이 자동 크레딧해 첫 가족 청구에서 차감한다. 기존 `create`/`update`/`cancel` 등에는
|
|
43
|
+
영향 없는 **순수 additive** 변경.
|
|
44
|
+
|
|
45
|
+
- `cb.subscription.changePlan(subscriptionId, { paddle_price_id, plan_name?, amount?, proration_mode?, on_payment_failure?, metadata? })`
|
|
46
|
+
— 요금제 스왑 + 정산 적용. 내부적으로 Paddle `PATCH /subscriptions/{id}` 의 items 전체 교체 +
|
|
47
|
+
`proration_billing_mode` 로 처리하고, `subscription.updated`/`transaction.completed` 웹훅으로 재조정한다.
|
|
48
|
+
- `cb.subscription.previewChangePlan(subscriptionId, data)` — 실제 적용 없이 정산 결과
|
|
49
|
+
(지금 청구/크레딧 순액 · 새 정기 청구액 · 다음 청구일)를 계산. 세금·기존 크레딧까지 반영된 Paddle
|
|
50
|
+
계산값이라 확정 화면에 그대로 노출하면 된다.
|
|
51
|
+
- 신규 타입: `ChangePlanRequest`, `ChangePlanPreview`, `ProrationMode`(`prorated_immediately`(기본) |
|
|
52
|
+
`prorated_next_billing_period` | `full_immediately` | `full_next_billing_period` | `do_not_bill`),
|
|
53
|
+
`OnPaymentFailure`(`prevent_change`(기본) | `apply_change`).
|
|
54
|
+
- `SubscriptionResponse` — 현재 물린 recurring price 를 나타내는 `paddle_price_id` 필드 추가.
|
|
55
|
+
|
|
56
|
+
> 참고: toss/stripe/payapp/paypal 구독은 PG 네이티브 proration 이 없어 미지원이다(`plan_change_unsupported`).
|
|
57
|
+
> Paddle 구독이 최초 결제(checkout) 전이면 아직 Paddle 측 구독이 없어 `subscription_not_activated` 를 반환한다.
|
|
58
|
+
|
|
6
59
|
## [3.53.0] - 2026-07-23
|
|
7
60
|
|
|
8
61
|
### Changed — 웹 스토리지 배포: `--dev` → QA 채널 별칭(deprecated)
|
package/dist/connect-base.umd.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";var ConnectBaseModule=(()=>{var ge=Object.defineProperty;var De=Object.getOwnPropertyDescriptor;var Le=Object.getOwnPropertyNames;var qe=Object.prototype.hasOwnProperty;var He=(a,e)=>{for(var t in e)ge(a,t,{get:e[t],enumerable:!0})},Be=(a,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Le(e))!qe.call(a,r)&&r!==t&&ge(a,r,{get:()=>e[r],enumerable:!(s=De(e,r))||s.enumerable});return a};var Fe=a=>Be(ge({},"__esModule",{value:!0}),a);var et={};He(et,{AIAPI:()=>x,AUTH_MEMBER_ID_TOKEN:()=>ze,AdsAPI:()=>E,ApiError:()=>g,AuthError:()=>T,ConnectBase:()=>ue,EndpointAPI:()=>$,GameAPI:()=>U,GameConfigAPI:()=>A,GameError:()=>b,GameRoom:()=>B,GameRoomTransport:()=>pe,NativeAPI:()=>M,RolesAPI:()=>O,SessionManager:()=>H,VideoProcessingError:()=>L,default:()=>Ze,detectInAppBrowser:()=>N,escapeToExternalBrowser:()=>Q,isWebTransportSupported:()=>Pe,toCreateRoomWire:()=>F});var E=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async getConnectionStatus(){let e=this.getPublicPrefix();return this.http.get(`${e}/ads/connection`)}async getReport(e,t){let s=this.getPublicPrefix(),r=new URLSearchParams;e&&r.set("start",e),t&&r.set("end",t);let i=r.toString();return this.http.get(`${s}/ads/reports${i?`?${i}`:""}`)}async getReportSummary(){let e=this.getPublicPrefix();return this.http.get(`${e}/ads/reports/summary`)}async getAdMobReport(e,t){let s=this.getPublicPrefix(),r=new URLSearchParams;e&&r.set("start",e),t&&r.set("end",t);let i=r.toString();return this.http.get(`${s}/ads/admob/reports${i?`?${i}`:""}`)}async getAdMobReportSummary(){let e=this.getPublicPrefix();return this.http.get(`${e}/ads/admob/reports/summary`)}};var x=class{constructor(e){this.http=e}async chat(e){return this.http.post("/v1/public/ai/chat",e)}async chatStream(e,t,s){let r=s?.signal,i=o=>r?.aborted===!0||o instanceof DOMException&&o.name==="AbortError"||typeof o=="object"&&o!==null&&o.name==="AbortError",n;try{let o=await this.http.fetchRaw("/v1/public/ai/chat/stream",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:r});if(!o.ok){let d=await o.json().catch(()=>({error:"Stream request failed"}));t.onError?.(d.error||"Stream request failed");return}if(n=o.body?.getReader(),!n){t.onError?.("ReadableStream not supported");return}let c=new TextDecoder,l="";for(;;){let{done:d,value:p}=await n.read();if(d)break;l+=c.decode(p,{stream:!0});let u=l.split(`
|
|
2
2
|
`);l=u.pop()||"";for(let f of u){if(!f.startsWith("data: "))continue;let y=f.slice(6).trim();if(y==="[DONE]"){t.onDone?.();return}try{let h=JSON.parse(y);if(h.error){t.onError?.(h.message||h.error||"stream error");return}if(h.type==="sources"&&h.sources){t.onSources?.(h.sources);continue}if(h.type==="tool_start"||h.type==="tool_end"){t.onToolEvent?.({type:h.type,name:h.name,toolCallId:h.toolCallId,arguments:h.arguments,result:h.result,success:h.success,durationMs:h.durationMs});continue}if(h.type==="searching"){h.searching&&t.onSearching?.(h.searching);continue}if(h.type==="heartbeat")continue;if(h.reasoning&&t.onReasoning?.(h.reasoning),h.content&&t.onToken?.(h.content),h.done){t.onDone?.();return}}catch{}}}}catch(o){if(i(o)){t.onAbort?.();return}throw o}finally{if(n)try{await n.cancel()}catch{}}}};var me="__cb_session",fe="__cb_visitor_uid",G="__cb_last_activity";function q(){return crypto?.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,a=>{let e=Math.random()*16|0;return(a==="x"?e:e&3|8).toString(16)})}var H=class{constructor(){this._sessionId=null;this._visitorUid=null;this._lastActivity=0;this._isNewSession=!1}get sessionId(){return this.ensureSession(),this._sessionId}get visitorUid(){return this._visitorUid||(this._visitorUid=this.loadOrCreateVisitorUid()),this._visitorUid}get isNewSession(){return this._isNewSession}touch(){this._lastActivity=Date.now(),this._isNewSession=!1;try{typeof sessionStorage<"u"&&sessionStorage.setItem(G,String(this._lastActivity))}catch{}}reset(){this._sessionId=null,this._isNewSession=!1;try{typeof sessionStorage<"u"&&(sessionStorage.removeItem(me),sessionStorage.removeItem(G))}catch{}}regenerateVisitorUid(){let e=q();this._visitorUid=e;try{typeof localStorage<"u"&&localStorage.setItem(fe,e)}catch{}return this.reset(),e}ensureSession(){let e=Date.now();if(!this._sessionId)try{if(typeof sessionStorage<"u"){this._sessionId=sessionStorage.getItem(me);let t=sessionStorage.getItem(G);this._lastActivity=t?parseInt(t,10):0}}catch{}if(!this._sessionId||this._lastActivity>0&&e-this._lastActivity>18e5){this._sessionId=q(),this._isNewSession=!0,this._lastActivity=e;try{typeof sessionStorage<"u"&&(sessionStorage.setItem(me,this._sessionId),sessionStorage.setItem(G,String(e)))}catch{}}}loadOrCreateVisitorUid(){try{if(typeof localStorage<"u"){let e=localStorage.getItem(fe);if(e)return e;let t=q();return localStorage.setItem(fe,t),t}}catch{}return q()}};function Re(a){if(!a)return"";let e=new URLSearchParams;a.start_date!==void 0&&e.set("start_date",String(a.start_date)),a.end_date!==void 0&&e.set("end_date",String(a.end_date)),a.limit!==void 0&&e.set("limit",String(a.limit));let t=e.toString();return t?`?${t}`:""}function Ne(){if(typeof window>"u")return{};try{let a=new URLSearchParams(window.location.search),e={};for(let t of["utm_source","utm_medium","utm_campaign","utm_content","utm_term"]){let s=a.get(t);s&&(e[t]=s)}return e}catch{return{}}}var V=class{constructor(e){this.storageWebId=null;this.memberId=null;this.eventQueue=[];this.batchTimer=null;this.isInitialized=!1;this.heartbeatTimer=null;this.visibilityHandler=null;this.unloadHeartbeatHandler=null;this.popstateHandler=null;this.beforeUnloadHandler=null;this.origPushState=null;this.origReplaceState=null;this.heatmapClickHandler=null;this.heatmapScrollHandler=null;this.utm={};this.handleHeatmapClick=e=>{let t=e.clientX/window.innerWidth*100,s=e.clientY/window.innerHeight*100;this.recordHeatmapEvent("click",t,s)};this.heatmapQueue=[];this.http=e,this.config={trackPageViews:!0,trackEvents:!0,trackSessions:!0,heatmap:!1,recording:!1,batchSize:10,flushInterval:5e3,respectDoNotTrack:!0,debug:!1},this.consent={analytics:!0,heatmap:!1,recording:!1},this.session=new H}init(e,t){if(this.isInitialized){this.log("Analytics already initialized");return}if(typeof window>"u"){this.log("Analytics only works in browser environment");return}if(t?.respectDoNotTrack!==!1&&this.isDNT()){this.log("Do Not Track enabled, analytics disabled");return}this.storageWebId=e,Object.assign(this.config,t),this.isInitialized=!0,this.utm=Ne(),this.config.trackSessions&&(this.trackSessionStart(),this.startHeartbeat()),this.config.trackPageViews&&(this.trackPageView(),this.setupAutoPageView()),this.startBatchTimer(),this.beforeUnloadHandler=()=>this.flushSync(),window.addEventListener("beforeunload",this.beforeUnloadHandler),this.log("Analytics initialized",{storageWebId:e})}destroy(){this.isInitialized&&(this.stopBatchTimer(),this.stopHeartbeat(),this.removeAutoPageView(),this.removeHeatmapListeners(),this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.flush(),this.isInitialized=!1,this.log("Analytics destroyed"))}setConsent(e){Object.assign(this.consent,e),this.log("Consent updated",e),e.analytics===!1&&this.isInitialized&&this.destroy()}getConsent(){return{...this.consent}}trackPageView(e){if(!this.canTrack())return;this.session.touch();let t=this.createBaseEvent("page_view");t.page_path=e||window.location.pathname,t.page_url=window.location.href,t.page_title=document.title,t.referrer=document.referrer||void 0,t.screen_width=window.screen.width,t.screen_height=window.screen.height,Object.assign(t,this.utm),this.enqueue(t)}trackEvent(e,t){if(!this.canTrack()||!this.config.trackEvents)return;this.session.touch();let s=this.createBaseEvent("event");s.event_name=e,s.event_properties=t,s.page_path=window.location.pathname,s.page_url=window.location.href,this.enqueue(s)}identify(e){this.memberId&&this.memberId!==e&&this.reset(),this.setMemberId(e),this.linkMemberSilent(e)}reset(){this.setMemberId(null),this.eventQueue=[],this.heatmapQueue=[];let e=this.session.regenerateVisitorUid();this.log("Analytics reset",{newVisitorUid:e})}setMemberId(e){if(this.memberId=e||null,typeof window<"u"){let t=window;e?typeof t.__cbSetMember=="function"&&t.__cbSetMember(e):typeof t.__cbClearMember=="function"&&t.__cbClearMember()}this.log("Member id set",{memberId:e})}linkMemberSilent(e,t=!1){if(!this.storageWebId)return;let s=this.http.hasPublicKey()?"/v1/public":"/v1";this.http.post(`${s}/storages/web/${this.storageWebId}/visitors/link-member`,{visitor_uid:this.session.visitorUid,app_member_id:e}).then(r=>{r&&(this.log("link-member response",r),!t&&r.success===!1&&r.code==="VISITOR_LINKED_TO_OTHER_MEMBER"&&(this.log("user-switch detected \u2014 regenerating visitor_uid"),this.eventQueue=[],this.heatmapQueue=[],this.session.regenerateVisitorUid(),this.linkMemberSilent(e,!0)))}).catch(r=>{this.log("link-member silent fail",r)})}getMemberId(){return this.memberId}enableHeatmap(e){if(!this.canTrack()||!this.consent.heatmap)return;let t=e?.click??!0,s=e?.scroll??!0;if(t&&(this.heatmapClickHandler=this.handleHeatmapClick,document.addEventListener("click",this.heatmapClickHandler)),s){let r=null,i=0;this.heatmapScrollHandler=()=>{let n=Math.round((window.scrollY+window.innerHeight)/document.documentElement.scrollHeight*100);i=Math.max(i,n),r&&clearTimeout(r),r=setTimeout(()=>{this.recordHeatmapEvent("scroll",50,i*(window.innerHeight/100),i)},500)},window.addEventListener("scroll",this.heatmapScrollHandler,{passive:!0})}this.log("Heatmap enabled",e)}enableHeartbeat(){if(!this.canTrack()||this.heartbeatTimer)return;let e=()=>{if(!this.canTrack())return;let t=this.http.hasPublicKey()?"/v1/public":"/v1";this.http.post(`${t}/storages/web/${this.storageWebId}/sessions/heartbeat`,{visitor_uid:this.session.visitorUid,session_id:this.session.sessionId}).catch(()=>{})};this.heartbeatTimer=setInterval(e,3e4),document.addEventListener("visibilitychange",()=>{document.hidden?this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null):this.heartbeatTimer||(this.heartbeatTimer=setInterval(e,3e4),e())}),window.addEventListener("beforeunload",()=>{if(navigator?.sendBeacon&&this.storageWebId){let t=this.http.hasPublicKey()?"/v1/public":"/v1",s=`${this.http.getBaseUrl()}${t}/storages/web/${this.storageWebId}/sessions/heartbeat`;navigator.sendBeacon(s,JSON.stringify({visitor_uid:this.session.visitorUid,session_id:this.session.sessionId}))}}),e(),this.log("Heartbeat enabled (30s interval)")}async flush(){await this.flushQueue()}async getPopularPages(e,t){let s=this.requireServerSideStorageId(e,"getPopularPages"),r=Re(t);return this.http.get(`/v1/storages/web/${s}/popular-pages${r}`)}async getNavigationFlow(e,t){let s=this.requireServerSideStorageId(e,"getNavigationFlow"),r=Re(t);return this.http.get(`/v1/storages/web/${s}/navigation/flow${r}`)}async getVisitors(e,t){let s=this.requireServerSideStorageId(e,"getVisitors"),r=new URLSearchParams;t?.limit!==void 0&&r.set("limit",String(t.limit)),t?.offset!==void 0&&r.set("offset",String(t.offset)),t?.sort_by&&r.set("sort_by",t.sort_by);let i=r.toString()?`?${r.toString()}`:"";return this.http.get(`/v1/storages/web/${s}/visitors${i}`)}async getVisitorGroups(e,t){let s=this.requireServerSideStorageId(e,"getVisitorGroups"),r=new URLSearchParams;t?.limit!==void 0&&r.set("limit",String(t.limit)),t?.offset!==void 0&&r.set("offset",String(t.offset)),t?.sort_by&&r.set("sort_by",t.sort_by);let i=r.toString()?`?${r.toString()}`:"";return this.http.get(`/v1/storages/web/${s}/visitor-groups${i}`)}async getVisitorByMember(e,t){let s=this.requireServerSideStorageId(e,"getVisitorByMember");return this.http.get(`/v1/storages/web/${s}/members/${t}/visitor`)}async mergeVisitors(e,t){let s=this.requireServerSideStorageId(e,"mergeVisitors");return this.http.post(`/v1/storages/web/${s}/visitors/merge`,t)}requireServerSideStorageId(e,t){if(this.http.hasPublicKey())throw new Error(`cb.analytics.${t}() \uB294 \uCF58\uC194 JWT \uB610\uB294 User Secret Key(cb_sk_) \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uBE0C\uB77C\uC6B0\uC800 SDK \uC758 Public Key(cb_pk_) \uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.`);let s=e??this.storageWebId;if(!s)throw new Error(`cb.analytics.${t}() \uD638\uCD9C \uC2DC storageWebId \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (init \uB610\uB294 \uC778\uC790\uB85C \uC804\uB2EC).`);return s}getSession(){return this.session}canTrack(){return!(!this.isInitialized||!this.storageWebId||!this.consent.analytics)}isDNT(){if(typeof navigator>"u")return!1;let e=navigator.globalPrivacyControl;return navigator.doNotTrack==="1"||e===!0}createBaseEvent(e){return{type:e,event_id:q(),timestamp:new Date().toISOString(),session_id:this.session.sessionId,visitor_uid:this.session.visitorUid}}enqueue(e){this.eventQueue.push(e),this.eventQueue.length>=this.config.batchSize&&this.flushQueue()}async flushQueue(){if(this.eventQueue.length===0||!this.storageWebId)return;let e=this.eventQueue.splice(0),t=this.http.hasPublicKey()?"/v1/public":"/v1";try{await this.http.post(`${t}/storages/web/${this.storageWebId}/visitors/batch`,{visitor_uid:this.session.visitorUid,...this.memberId?{app_member_id:this.memberId}:{},events:e.map(s=>({event_id:s.event_id,timestamp:s.timestamp,page_path:s.page_path||"",page_url:s.page_url||"",page_title:s.page_title||"",referrer:s.referrer||"",user_agent:typeof navigator<"u"?navigator.userAgent:"",screen_width:s.screen_width||0,screen_height:s.screen_height||0,session_id:s.session_id,session_start:s.type==="session_start",is_page_view:s.type==="page_view",event_name:s.event_name,event_properties:s.event_properties,utm_source:s.utm_source,utm_medium:s.utm_medium,utm_campaign:s.utm_campaign,utm_content:s.utm_content,utm_term:s.utm_term}))}),this.log(`Flushed ${e.length} events`)}catch(s){this.eventQueue.length<this.config.batchSize*3&&this.eventQueue.unshift(...e),this.log("Flush failed",s)}}flushSync(){if(this.eventQueue.length===0||!this.storageWebId||typeof navigator>"u"||!navigator.sendBeacon)return;let e=this.eventQueue.splice(0),t=this.http.hasPublicKey()?"/v1/public":"/v1",r=`${this.http.getBaseUrl()}${t}/storages/web/${this.storageWebId}/visitors/batch`,i=typeof navigator<"u"?navigator.userAgent:"",n=JSON.stringify({visitor_uid:this.session.visitorUid,...this.memberId?{app_member_id:this.memberId}:{},events:e.map(o=>({event_id:o.event_id,timestamp:o.timestamp,page_path:o.page_path||"",page_url:o.page_url||"",page_title:o.page_title||"",referrer:o.referrer||"",user_agent:i,screen_width:o.screen_width||0,screen_height:o.screen_height||0,session_id:o.session_id,session_start:o.type==="session_start",is_page_view:o.type==="page_view",event_name:o.event_name,event_properties:o.event_properties,utm_source:o.utm_source,utm_medium:o.utm_medium,utm_campaign:o.utm_campaign,utm_content:o.utm_content,utm_term:o.utm_term}))});try{navigator.sendBeacon(r,new Blob([n],{type:"application/json"}))}catch{}}trackSessionStart(){let e=this.createBaseEvent("session_start");e.page_path=window.location.pathname,e.page_url=window.location.href,e.referrer=document.referrer||void 0,e.screen_width=window.screen.width,e.screen_height=window.screen.height,Object.assign(e,this.utm),this.enqueue(e)}startBatchTimer(){this.batchTimer=setInterval(()=>this.flushQueue(),this.config.flushInterval)}stopBatchTimer(){this.batchTimer&&(clearInterval(this.batchTimer),this.batchTimer=null)}startHeartbeat(){this.heartbeatTimer=setInterval(()=>{this.canTrack()&&this.sendHeartbeat()},30*1e3),typeof document<"u"&&(this.visibilityHandler=()=>{document.visibilityState==="hidden"?(this.sendHeartbeatBeacon(),this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)):document.visibilityState==="visible"&&(this.heartbeatTimer||(this.sendHeartbeat(),this.heartbeatTimer=setInterval(()=>{this.canTrack()&&this.sendHeartbeat()},30*1e3)))},document.addEventListener("visibilitychange",this.visibilityHandler)),typeof window<"u"&&(this.unloadHeartbeatHandler=()=>this.sendHeartbeatBeacon(),window.addEventListener("beforeunload",this.unloadHeartbeatHandler))}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),typeof document<"u"&&this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),typeof window<"u"&&this.unloadHeartbeatHandler&&(window.removeEventListener("beforeunload",this.unloadHeartbeatHandler),this.unloadHeartbeatHandler=null)}sendHeartbeat(){let e=this.createBaseEvent("heartbeat");if(e.page_path=window.location.pathname,this.enqueue(e),this.storageWebId){let t=this.http.hasPublicKey()?"/v1/public":"/v1";this.http.post(`${t}/storages/web/${this.storageWebId}/sessions/heartbeat`,{visitor_uid:this.session.visitorUid,session_id:this.session.sessionId}).catch(()=>{})}}sendHeartbeatBeacon(){if(!this.storageWebId||typeof navigator>"u"||!navigator.sendBeacon)return;let e=this.http.hasPublicKey()?"/v1/public":"/v1",s=`${this.http.getBaseUrl()}${e}/storages/web/${this.storageWebId}/sessions/heartbeat`,r=JSON.stringify({visitor_uid:this.session.visitorUid,session_id:this.session.sessionId});try{navigator.sendBeacon(s,new Blob([r],{type:"application/json"}))}catch{}}setupAutoPageView(){this.popstateHandler=()=>this.trackPageView(),window.addEventListener("popstate",this.popstateHandler),this.origPushState=history.pushState.bind(history),this.origReplaceState=history.replaceState.bind(history),history.pushState=(...e)=>{this.origPushState(...e),this.trackPageView()},history.replaceState=(...e)=>{this.origReplaceState(...e),this.trackPageView()}}removeAutoPageView(){this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.origPushState&&(history.pushState=this.origPushState,this.origPushState=null),this.origReplaceState&&(history.replaceState=this.origReplaceState,this.origReplaceState=null)}removeHeatmapListeners(){this.heatmapClickHandler&&(document.removeEventListener("click",this.heatmapClickHandler),this.heatmapClickHandler=null),this.heatmapScrollHandler&&(window.removeEventListener("scroll",this.heatmapScrollHandler),this.heatmapScrollHandler=null)}recordHeatmapEvent(e,t,s,r){if(!this.canTrack()||!this.storageWebId)return;let i=this.http.hasPublicKey()?"/v1/public":"/v1";if(this.heatmapQueue.push({page_path:window.location.pathname,event_type:e,x_percent:Math.round(t*100)/100,y_percent:Math.round(s*100)/100,viewport_width:window.innerWidth,viewport_height:window.innerHeight,scroll_depth_percent:r,session_id:this.session.sessionId}),this.heatmapQueue.length>=50){let n=this.heatmapQueue.splice(0);this.http.post(`${i}/storages/web/${this.storageWebId}/heatmap/batch`,{visitor_uid:this.session.visitorUid,events:n}).catch(()=>{})}}log(...e){this.config.debug&&console.log("[Analytics]",...e)}};var g=class extends Error{constructor(t,s,r,i){super(s);this.statusCode=t;this.name="ApiError",this.code=r,this.details=i}},T=class extends Error{constructor(e){super(e),this.name="AuthError"}},b=class extends Error{constructor(e){super(e.message||e.code||"GameError"),this.name="GameError",this.code=e.code||"UNKNOWN",this.phase=e.phase,this.feature=e.feature,this.roomId=e.roomId,this.scriptId=e.scriptId,this.originClientId=e.originClientId,this.requested=e.requested,this.available=e.available}};function Ge(a,e){switch(e){case"string":return typeof a=="string";case"number":return typeof a=="number"&&Number.isFinite(a);case"boolean":return typeof a=="boolean";case"array":return Array.isArray(a);case"object":return typeof a=="object"&&a!==null&&!Array.isArray(a);case"string-or-number":return typeof a=="string"||typeof a=="number"&&Number.isFinite(a)}}function k(a,e,t){if(typeof a!="object"||a===null||Array.isArray(a))throw new g(502,`[${t}] expected object, got ${typeof a}`,"SCHEMA_MISMATCH");let s=a;for(let[r,i]of Object.entries(e)){if(!(r in s)){if(i.optional)continue;throw new g(502,`[${t}] missing required field "${r}"`,"SCHEMA_MISMATCH")}let o=s[r];if(!(i.optional&&o==null)&&!Ge(o,i.type))throw new g(502,`[${t}] field "${r}" expected ${i.type}, got ${typeof o}`,"SCHEMA_MISMATCH")}return a}var W=class{constructor(e){this.http=e;this.analytics=null}_attachAnalytics(e){this.analytics=e}notifyVisitorTracker(e){if(this.analytics){this.analytics.setMemberId(e);return}if(typeof window>"u")return;let t=window;e?typeof t.__cbSetMember=="function"&&t.__cbSetMember(e):typeof t.__cbClearMember=="function"&&t.__cbClearMember()}async getAuthSettings(){return this.http.get("/v1/public/auth-settings",{skipAuth:!0})}async signUpMember(e){let t=await this.http.post("/v1/public/app-members/signup",e,{skipAuth:!0});return k(t,{access_token:{type:"string"},refresh_token:{type:"string"},member_id:{type:"string-or-number"}},"auth.signUpMember"),this.http.setTokens(t.access_token,t.refresh_token),this.notifyVisitorTracker(t.member_id),t}async signInMember(e){let t=await this.http.post("/v1/public/app-members/signin",e,{skipAuth:!0});return k(t,{access_token:{type:"string"},refresh_token:{type:"string"},member_id:{type:"string-or-number"}},"auth.signInMember"),this.http.setTokens(t.access_token,t.refresh_token),this.notifyVisitorTracker(t.member_id),t}async getMe(){let e=await this.http.get("/v1/public/app-members/me");return k(e,{member_id:{type:"string-or-number"}},"auth.getMe"),e}async updateCustomData(e){return this.http.patch("/v1/public/app-members/me/custom-data",e)}async adminUpdateMember(e,t){if(!this.http.getSecretKey())throw new Error("adminUpdateMember requires a secretKey. Initialize ConnectBase with both publicKey and secretKey.");return this.http.patch(`/v1/public/app-members/${encodeURIComponent(e)}`,t)}async signOut(){try{await this.http.post("/v1/auth/logout")}finally{this.http.clearTokens(),this.notifyVisitorTracker(null)}}};var K=class{constructor(e){this.realtimeWs=null;this.realtimeState="disconnected";this.realtimeHandlers=new Map;this.realtimeRetryCount=0;this.realtimeOptions=null;this.pendingRequests=new Map;this.pingInterval=null;this.realtimeOnStateChange=null;this.realtimeOnError=null;this.activeSubscriptions=new Map;this.http=e}getPublicPrefix(){return"/v1/public"}async getTables(){let e=this.getPublicPrefix();return(await this.http.get(`${e}/tables`)).tables}async getTable(e){let t=this.getPublicPrefix();return this.http.get(`${t}/tables/${e}`)}async createTable(e){let t=this.getPublicPrefix(),s={title:e.name,access_level:e.accessLevel??"Creator"};e.schema&&Object.keys(e.schema).length>0&&(s.schema=e.schema),await this.http.post(`${t}/tables`,s)}async updateTable(e,t){let s=this.getPublicPrefix(),r={};t.name!==void 0&&(r.title=t.name),t.schema!==void 0&&(r.schema=t.schema),t.accessLevel!==void 0&&(r.access_level=t.accessLevel),t.description!==void 0&&(r.description=t.description),await this.http.patch(`${s}/tables/${e}`,r)}async deleteTable(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/tables/${e}`)}async getValidationSchema(e){let t=this.getPublicPrefix();return(await this.http.get(`${t}/tables/${e}/validation-schema`)).validation_schema}async setValidationSchema(e,t){await this.http.put(`/v1/apps/${this.requireAppId()}/databases/tables/${e}/validation-schema`,{validation_schema:t})}async deleteValidationSchema(e){await this.http.delete(`/v1/apps/${this.requireAppId()}/databases/tables/${e}/validation-schema`)}requireAppId(){let e=this.http.getAppId();if(!e)throw new Error("setValidationSchema/deleteValidationSchema \uB294 \uCF58\uC194 (JWT) \uC778\uC99D\uC774 \uD544\uC694\uD558\uBA70 ConnectBase config \uC5D0 appId \uAC00 \uC124\uC815\uB418\uC5B4\uC57C \uD569\uB2C8\uB2E4.");return e}async getColumns(e){let t=await this.getTable(e),s=t.schema??{},r=new Set(Array.isArray(s.$required)?s.$required:[]),i=[],n=0;for(let[o,c]of Object.entries(s)){if(o.startsWith("$")||c===void 0||Array.isArray(c))continue;let l="string",d=r.has(o),p,u,f;typeof c=="string"?l=c:(l=c.type,c.required===!0&&(d=!0),c.default!==void 0&&(p=c.default),c.description!==void 0&&(u=c.description),c.encrypted!==void 0&&(f=c.encrypted)),i.push({id:o,name:o,data_type:l,is_required:d,default_value:p,description:u,encrypted:f,order:n++,created_at:t.created_at})}return i}async createColumn(e,t){let s=this.getPublicPrefix();await this.http.post(`${s}/tables/${e}/columns`,t)}async updateColumn(e,t,s){let r=this.getPublicPrefix();await this.http.patch(`${r}/tables/${e}/columns/${t}`,s)}async deleteColumn(e,t){let s=this.getPublicPrefix();await this.http.delete(`${s}/tables/${e}/columns/${t}`)}async getData(e,t){let s=this.getPublicPrefix();if(t?.where||t?.select||t?.exclude)return this.queryData(e,t);let r=new URLSearchParams;t?.limit&&r.append("limit",t.limit.toString()),t?.offset&&r.append("offset",t.offset.toString()),t?.count===!1&&r.append("count","false"),t?.cursor&&r.append("cursor",t.cursor);let i=r.toString(),n=i?`${s}/tables/${e}/data?${i}`:`${s}/tables/${e}/data`;return this.http.get(n)}async queryData(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data/query`,{where:t.where,order_by:t.orderBy,order_direction:t.orderDirection,limit:t.limit,offset:t.offset,select:t.select,exclude:t.exclude,count:t.count,cursor:t.cursor})}async getDataById(e,t){let s=this.getPublicPrefix();return this.http.get(`${s}/tables/${e}/data/${t}`)}async createData(e,t,s){let r=this.getPublicPrefix();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return this.http.post(`${r}/tables/${e}/data`,t);let n=s?.autoCreate?"?auto_create=true":"";return this.http.post(`${r}/tables/name/${encodeURIComponent(e)}/data${n}`,t)}async updateData(e,t,s){let r=this.getPublicPrefix();return this.http.patch(`${r}/tables/${e}/data/${t}`,s)}async deleteData(e,t){let s=this.getPublicPrefix();await this.http.delete(`${s}/tables/${e}/data/${t}`)}async createMany(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data/bulk`,{data:t.map(r=>r.data)})}async deleteWhere(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data/delete-where`,{where:t})}async aggregate(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/aggregate`,{table_id:e,pipeline:t})}async search(e,t,s,r){let i=this.getPublicPrefix();return this.http.post(`${i}/search`,{table_id:e,query:t,fields:s,options:r})}async autocomplete(e,t,s,r){let i=this.getPublicPrefix();return this.http.post(`${i}/autocomplete`,{table_id:e,query:t,field:s,...r})}async geoQuery(e,t,s,r){let i=this.getPublicPrefix();return this.http.post(`${i}/geo`,{table_id:e,field:t,query:s,...r})}async batch(e){let t=this.getPublicPrefix(),s=await this.http.post(`${t}/batch`,{operations:e});if(s&&s.success===!1){let r=s.results?.find(i=>i&&i.success===!1);throw new Error(r?.error||"batch operation failed")}return s}async transaction(e,t){let s=this.getPublicPrefix(),r=await this.http.post(`${s}/transactions`,{reads:e,writes:t});if(r&&r.success===!1)throw new Error(r.error||"transaction failed");return r}async getDataWithPopulate(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data/query`,{where:t.where,order_by:t.orderBy,order_direction:t.orderDirection,limit:t.limit,offset:t.offset,select:t.select,exclude:t.exclude,populate:t.populate,count:t.count})}async listSecurityRules(e){return(await this.http.get(`/v1/apps/${e}/databases/security/rules`)).rules}async createSecurityRule(e,t){return this.http.post(`/v1/apps/${e}/databases/security/rules`,t)}async updateSecurityRule(e,t,s){return this.http.put(`/v1/apps/${e}/databases/security/rules/${t}`,s)}async deleteSecurityRule(e,t){await this.http.delete(`/v1/apps/${e}/databases/security/rules/${t}`)}async listIndexes(e,t){return(await this.http.get(`/v1/apps/${e}/databases/tables/${t}/indexes`)).indexes}async createIndex(e,t,s){return this.http.post(`/v1/apps/${e}/databases/tables/${t}/indexes`,s)}async deleteIndex(e,t,s){await this.http.delete(`/v1/apps/${e}/databases/tables/${t}/indexes/${s}`)}async analyzeIndexes(e,t){return this.http.get(`/v1/apps/${e}/databases/tables/${t}/indexes/analyze`)}async listSearchIndexes(e,t){return(await this.http.get(`/v1/apps/${e}/databases/tables/${t}/search-indexes`)).indexes}async createSearchIndex(e,t,s){return this.http.post(`/v1/apps/${e}/databases/tables/${t}/search-indexes`,s)}async deleteSearchIndex(e,t,s){await this.http.delete(`/v1/apps/${e}/databases/tables/${t}/search-indexes/${s}`)}async listGeoIndexes(e,t){return(await this.http.get(`/v1/apps/${e}/databases/tables/${t}/geo-indexes`)).indexes}async createGeoIndex(e,t,s){return this.http.post(`/v1/apps/${e}/databases/tables/${t}/geo-indexes`,s)}async deleteGeoIndex(e,t,s){await this.http.delete(`/v1/apps/${e}/databases/tables/${t}/geo-indexes/${s}`)}async listRelations(e,t){let s=t?`?source_table=${encodeURIComponent(t)}`:"";return(await this.http.get(`/v1/apps/${e}/databases/relations${s}`)).relations}async createRelation(e,t){return this.http.post(`/v1/apps/${e}/databases/relations`,t)}async deleteRelation(e,t){await this.http.delete(`/v1/apps/${e}/databases/relations/${t}`)}async listTriggers(e){return(await this.http.get(`/v1/apps/${e}/databases/triggers`)).triggers}async createTrigger(e,t){return this.http.post(`/v1/apps/${e}/databases/triggers`,t)}async updateTrigger(e,t,s){return this.http.put(`/v1/apps/${e}/databases/triggers/${t}`,s)}async deleteTrigger(e,t){await this.http.delete(`/v1/apps/${e}/databases/triggers/${t}`)}async setTTL(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/ttl`,t)}async getTTL(e,t){return this.http.get(`/v1/apps/${e}/lifecycle/ttl/${t}`)}async setRetentionPolicy(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/retention`,t)}async getRetentionPolicy(e,t){return this.http.get(`/v1/apps/${e}/lifecycle/retention/${t}`)}async setArchivePolicy(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/archive`,t)}async getArchivePolicy(e,t){return this.http.get(`/v1/apps/${e}/lifecycle/archive/${t}`)}async executeTTL(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/ttl/${t}/execute`,{})}async executeArchive(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/archive/${t}/execute`,{})}async executeRetention(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/retention/${t}/execute`,{})}async listPolicies(e){return(await this.http.get(`/v1/apps/${e}/lifecycle`)).policies}async deletePolicy(e,t){await this.http.delete(`/v1/apps/${e}/lifecycle/${t}`)}async generateTypes(e){return this.http.get(`/v1/apps/${e}/types`)}async listBackups(e){return this.http.get(`/v1/apps/${e}/backups`)}async createBackup(e,t){return this.http.post(`/v1/apps/${e}/backups`,t)}async getBackup(e,t){return this.http.get(`/v1/apps/${e}/backups/${t}`)}async deleteBackup(e,t){await this.http.delete(`/v1/apps/${e}/backups/${t}`)}async restoreBackup(e,t,s){return this.http.post(`/v1/apps/${e}/backups/${t}/restore`,s||{backup_id:t})}async exportData(e,t){return this.http.post(`/v1/apps/${e}/data/export`,t||{format:"json"})}async importData(e,t){return this.http.post(`/v1/apps/${e}/data/import`,t)}async copyTable(e,t){return this.http.post(`/v1/apps/${e}/tables/copy`,t)}async migrateData(e,t){return this.http.post(`/v1/apps/${e}/tables/migrate`,t)}connectRealtime(e){return this.realtimeState==="connected"?Promise.resolve():this.realtimeState==="connecting"?Promise.reject(new Error("Already connecting")):(this.realtimeOptions=e,this.realtimeRetryCount=0,this.doRealtimeConnect())}disconnectRealtime(){this.realtimeOptions=null,this.setRealtimeState("disconnected"),this.realtimeRetryCount=0,this.stopRealtimePing(),this.realtimeWs&&(this.realtimeWs.close(),this.realtimeWs=null),this.pendingRequests.forEach(e=>{clearTimeout(e.timeout),e.reject(new Error("Connection closed"))}),this.pendingRequests.clear(),this.realtimeHandlers.clear(),this.activeSubscriptions.clear()}subscribe(e,t,s){if(this.realtimeState!=="connected")throw new Error("Not connected. Call connectRealtime() first.");let r=`csub_${Date.now()}_${Math.random().toString(36).substring(2,9)}`;this.activeSubscriptions.set(r,{tableId:e,options:s,handlers:t});let i=this.sendSubscribeRequest(e,t,s);return i.catch(n=>{this.activeSubscriptions.delete(r),t.onError?.(n instanceof Error?n:new Error(String(n)))}),{subscriptionId:r,unsubscribe:()=>{this.activeSubscriptions.delete(r),i.then(n=>{this.realtimeHandlers.delete(n),this.realtimeState==="connected"&&this.sendRealtimeMessage({type:"unsubscribe",request_id:this.generateRequestId(),subscription_id:n})}).catch(()=>{})},loadMore:(n,o)=>{this.realtimeState==="connected"&&i.then(c=>{let l={type:"snapshot_more",request_id:this.generateRequestId(),subscription_id:c,offset:n};o!==void 0&&(l.limit=o),this.sendRealtimeMessage(l)}).catch(()=>{})}}}isRealtimeConnected(){return this.realtimeState==="connected"}getRealtimeState(){return this.realtimeState}onRealtimeStateChange(e){return this.realtimeOnStateChange=e,()=>{this.realtimeOnStateChange=null}}onRealtimeError(e){return this.realtimeOnError=e,()=>{this.realtimeOnError=null}}setRealtimeState(e){this.realtimeState!==e&&(this.realtimeState=e,this.realtimeOnStateChange?.(e))}doRealtimeConnect(){if(!this.realtimeOptions)return Promise.reject(new Error("No realtime options"));this.setRealtimeState("connecting");let s=`${(this.realtimeOptions.dataServerUrl||this.http.getBaseUrl()).replace(/^http/,"ws")}/v1/database/realtime/ws?access_token=${encodeURIComponent(this.realtimeOptions.accessToken)}`;return new Promise((r,i)=>{try{this.realtimeWs=new WebSocket(s);let n=!1,o=setTimeout(()=>{n||(n=!0,this.realtimeWs&&(this.realtimeWs.close(),this.realtimeWs=null),this.setRealtimeState("disconnected"),i(new Error("Connection timeout")))},15e3);this.realtimeWs.onopen=()=>{n||(n=!0,clearTimeout(o)),this.setRealtimeState("connected"),this.realtimeRetryCount=0,this.startRealtimePing(),this.debugLog("Database realtime connected"),this.resubscribeAll(),r()},this.realtimeWs.onmessage=c=>{try{let l=JSON.parse(c.data);this.handleRealtimeMessage(l)}catch{this.debugLog("Failed to parse realtime message")}},this.realtimeWs.onclose=()=>{this.debugLog("Database realtime disconnected"),this.realtimeWs=null,this.stopRealtimePing(),n||(n=!0,clearTimeout(o),i(new Error("Connection closed during handshake"))),this.realtimeOptions&&this.realtimeState!=="disconnected"&&this.attemptRealtimeReconnect()},this.realtimeWs.onerror=()=>{this.debugLog("Database realtime error"),this.realtimeOnError?.(new Error("WebSocket connection error"))}}catch(n){this.setRealtimeState("disconnected"),i(n)}})}sendSubscribeRequest(e,t,s){let r=this.generateRequestId();this.realtimeHandlers.set(r,t);let i=s?.where?{filters:s.where.map(n=>({field:n.field,operator:n.operator,value:n.value}))}:void 0;return this.sendRealtimeMessage({type:"subscribe",request_id:r,table_id:e,doc_id:s?.docId,query:i,options:{include_self:s?.includeSelf??!1,include_metadata_changes:s?.includeMetadataChanges??!1}}),new Promise((n,o)=>{let c=setTimeout(()=>{this.pendingRequests.delete(r),this.realtimeHandlers.delete(r),o(new Error("Subscribe request timeout"))},3e4);this.pendingRequests.set(r,{resolve:l=>{let d=l,p=this.realtimeHandlers.get(r);p&&(this.realtimeHandlers.delete(r),this.realtimeHandlers.set(d,p)),n(d)},reject:o,timeout:c})})}resubscribeAll(){if(this.activeSubscriptions.size!==0){this.realtimeHandlers.clear(),this.pendingRequests.forEach(e=>clearTimeout(e.timeout)),this.pendingRequests.clear(),this.debugLog(`Resubscribing ${this.activeSubscriptions.size} subscriptions`);for(let[,e]of this.activeSubscriptions)this.sendSubscribeRequest(e.tableId,e.handlers,e.options).catch(t=>{e.handlers.onError?.(t instanceof Error?t:new Error(String(t)))})}}handleRealtimeMessage(e){switch(e.type){case"subscribed":{let s=e.request_id,r=e.subscription_id,i=this.pendingRequests.get(s);i&&(clearTimeout(i.timeout),i.resolve(r),this.pendingRequests.delete(s));break}case"snapshot":{let s=e.subscription_id,r=this.realtimeHandlers.get(s);if(r?.onSnapshot){let i=e.docs||[],n=e.has_more||!1,o=n?e.next_offset:void 0;r.onSnapshot(i,{totalCount:e.total_count||0,hasMore:n,nextOffset:o})}break}case"change":{let s=e.subscription_id,r=this.realtimeHandlers.get(s);if(r?.onChange){let i=e.changes||[];r.onChange(i)}break}case"presence":{let s=this.realtimeHandlers.get("__presence__");if(s?.onSnapshot){let r=e.states,i=Object.entries(r||{}).map(([n,o])=>({id:n,data:o,exists:!0}));s.onSnapshot(i,{totalCount:i.length,hasMore:!1})}break}case"error":{let s=e.request_id,r=e.message||"Unknown error";if(s){let i=this.pendingRequests.get(s);i&&(clearTimeout(i.timeout),i.reject(new Error(r)),this.pendingRequests.delete(s));let n=this.realtimeHandlers.get(s);n?.onError&&n.onError(new Error(r))}else this.realtimeOnError?.(new Error(r));break}case"pong":break;case"unsubscribed":case"presence_set_ack":case"presence_subscribed":case"typing_subscribed":break}}attemptRealtimeReconnect(){let e=this.realtimeOptions?.maxRetries??5,t=this.realtimeOptions?.retryInterval??1e3;if(this.realtimeRetryCount>=e){this.setRealtimeState("disconnected"),this.realtimeOnError?.(new Error("Realtime connection lost. Max retries exceeded."));return}this.setRealtimeState("connecting"),this.realtimeRetryCount++;let s=Math.min(t*2**(this.realtimeRetryCount-1),3e4);this.debugLog(`Reconnecting in ${s}ms (attempt ${this.realtimeRetryCount}/${e})`),setTimeout(()=>{this.realtimeOptions&&this.doRealtimeConnect().catch(r=>{this.debugLog(`Reconnect failed: ${r}`)})},s)}startRealtimePing(){this.stopRealtimePing(),this.pingInterval=setInterval(()=>{this.realtimeState==="connected"&&this.realtimeWs?.readyState===WebSocket.OPEN&&this.sendRealtimeMessage({type:"ping",timestamp:Date.now()})},3e4)}stopRealtimePing(){this.pingInterval&&(clearInterval(this.pingInterval),this.pingInterval=null)}sendRealtimeMessage(e){this.realtimeWs?.readyState===WebSocket.OPEN&&this.realtimeWs.send(JSON.stringify(e))}generateRequestId(){return`req_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}debugLog(e){this.realtimeOptions?.debug&&console.log(`[DatabaseRealtime] ${e}`)}};var $=class{constructor(e){this.http=e}async call(e,t){if(!e)throw new Error("EndpointAPI.call: label required");if(!t.path||!t.path.startsWith("/"))throw new Error(`EndpointAPI.call: path must start with '/', got ${JSON.stringify(t.path)}`);let s=this.url(e,t.path),r=new Headers(t.headers??{});if(!r.has("X-Public-Key")){let i=this.http.getPublicKey();if(!i)throw new Error("EndpointAPI.call: publicKey not configured. Pass `publicKey` to ConnectBase constructor.");r.set("X-Public-Key",i)}return fetch(s,{method:t.method??"GET",headers:r,body:t.body,signal:t.signal,redirect:"follow"})}url(e,t){if(!e)throw new Error("EndpointAPI.url: label required");if(!t||!t.startsWith("/"))throw new Error(`EndpointAPI.url: path must start with '/', got ${JSON.stringify(t)}`);return`${this.http.getBaseUrl().replace(/\/+$/,"")}/v1/proxy/${encodeURIComponent(e)}${t}`}async connectWebSocket(e,t={}){if(!e)throw new Error("EndpointAPI.connectWebSocket: label required");let s=t.path??"/";if(!s.startsWith("/"))throw new Error(`EndpointAPI.connectWebSocket: path must start with '/', got ${JSON.stringify(s)}`);let r=this.http.getBaseUrl().replace(/\/+$/,""),i=`${r}/v1/proxy/${encodeURIComponent(e)}/ws-ticket`,n=new Headers,o=this.http.getPublicKey();if(!o)throw new Error("EndpointAPI.connectWebSocket: publicKey not configured. Pass `publicKey` to ConnectBase constructor.");n.set("X-Public-Key",o);let c=await fetch(i,{method:"POST",headers:n,signal:t.signal});if(!c.ok){let u=await c.text().catch(()=>"");throw new Error(`EndpointAPI.connectWebSocket: ticket issuance failed (${c.status}) ${u.slice(0,200)}`)}let{ticket:l}=await c.json();if(!l)throw new Error("EndpointAPI.connectWebSocket: server returned empty ticket");let d=new URL(`${r}/v1/proxy/${encodeURIComponent(e)}${s}`);if(d.protocol=d.protocol==="https:"?"wss:":"ws:",t.query)for(let[u,f]of Object.entries(t.query))d.searchParams.set(u,f);d.searchParams.set("ticket",l);let p=t.protocols?new WebSocket(d.toString(),t.protocols):new WebSocket(d.toString());if(t.signal){let u=()=>{try{p.close(1e3,"client aborted")}catch{}};t.signal.aborted?u():t.signal.addEventListener("abort",u,{once:!0})}return p}async pollUntil(e,t,s,r={}){let i=r.intervalMs??1500,n=r.timeoutMs??5*6e4,o=r.parse??"json",c=Date.now();for(;;){if(r.signal?.aborted)throw new DOMException("aborted","AbortError");if(Date.now()-c>n)throw new Error(`EndpointAPI.pollUntil: timeout after ${n}ms (label=${e}, path=${t.path})`);let l;try{l=await this.call(e,{...t,signal:r.signal})}catch(u){if(u.name==="AbortError")throw u;await be(i,r.signal);continue}if(l.status>=500){await Ve(l),await be(i,r.signal);continue}if(l.status>=400){let u=await l.text().catch(()=>"");throw new Error(`EndpointAPI.pollUntil: ${l.status} ${l.statusText} (label=${e}, path=${t.path})${u?` \u2014 ${u.slice(0,200)}`:""}`)}let d;o==="text"?d=await l.text():o==="none"?d=void 0:d=await l.json().catch(()=>{});let p=await s(d,l);if(p!==void 0)return p;await be(i,r.signal)}}};async function be(a,e){if(e?.aborted)throw new DOMException("aborted","AbortError");return new Promise((t,s)=>{let r=setTimeout(()=>{e?.removeEventListener("abort",i),t()},a),i=()=>{clearTimeout(r),s(new DOMException("aborted","AbortError"))};e?.addEventListener("abort",i,{once:!0})})}async function Ve(a){try{await a.text()}catch{}}var z=class{constructor(e,t={}){this.storageWebId=null;this.errorQueue=[];this.batchTimer=null;this.isInitialized=!1;this.originalOnError=null;this.originalOnUnhandledRejection=null;this.http=e,this.config={autoCapture:t.autoCapture??!0,captureTypes:t.captureTypes??["error","unhandledrejection"],batchInterval:t.batchInterval??5e3,maxBatchSize:t.maxBatchSize??10,beforeSend:t.beforeSend??(s=>s),debug:t.debug??!1}}init(e){if(this.isInitialized){this.log("ErrorTracker already initialized");return}if(typeof window>"u"){this.log("ErrorTracker only works in browser environment");return}this.storageWebId=e,this.isInitialized=!0,this.config.autoCapture&&this.setupAutoCapture(),this.startBatchTimer(),this.log("ErrorTracker initialized",{storageWebId:e})}destroy(){this.stopBatchTimer(),this.removeAutoCapture(),this.flushQueue(),this.isInitialized=!1,this.log("ErrorTracker destroyed")}async captureError(e,t){let s=this.createErrorReport(e,t);s&&this.queueError(s)}async captureMessage(e,t){let s={message:e,error_type:"custom",url:typeof window<"u"?window.location.href:void 0,referrer:typeof document<"u"?document.referrer:void 0,...t};this.queueError(s)}async flush(){await this.flushQueue()}log(...e){this.config.debug&&console.log("[ErrorTracker]",...e)}setupAutoCapture(){typeof window>"u"||(this.config.captureTypes.includes("error")&&(this.originalOnError=window.onerror,window.onerror=(e,t,s,r,i)=>(this.handleGlobalError(e,t,s,r,i),this.originalOnError?this.originalOnError(e,t,s,r,i):!1)),this.config.captureTypes.includes("unhandledrejection")&&(this.originalOnUnhandledRejection=window.onunhandledrejection,window.onunhandledrejection=e=>{this.handleUnhandledRejection(e),this.originalOnUnhandledRejection&&this.originalOnUnhandledRejection(e)}),this.log("Auto capture enabled",{types:this.config.captureTypes}))}removeAutoCapture(){typeof window>"u"||(this.originalOnError!==null&&(window.onerror=this.originalOnError),this.originalOnUnhandledRejection!==null&&(window.onunhandledrejection=this.originalOnUnhandledRejection))}handleGlobalError(e,t,s,r,i){let n={message:typeof e=="string"?e:e.type||"Unknown error",source:t||void 0,lineno:s||void 0,colno:r||void 0,stack:i?.stack,error_type:"error",url:typeof window<"u"?window.location.href:void 0,referrer:typeof document<"u"?document.referrer:void 0};this.queueError(n)}handleUnhandledRejection(e){let t=e.reason,s="Unhandled Promise Rejection",r;t instanceof Error?(s=t.message,r=t.stack):typeof t=="string"?s=t:t&&typeof t=="object"&&(s=JSON.stringify(t));let i={message:s,stack:r,error_type:"unhandledrejection",url:typeof window<"u"?window.location.href:void 0,referrer:typeof document<"u"?document.referrer:void 0};this.queueError(i)}createErrorReport(e,t){let s;e instanceof Error?s={message:e.message,stack:e.stack,error_type:"error",url:typeof window<"u"?window.location.href:void 0,referrer:typeof document<"u"?document.referrer:void 0,...t}:s={message:e,error_type:"custom",url:typeof window<"u"?window.location.href:void 0,referrer:typeof document<"u"?document.referrer:void 0,...t};let r=this.config.beforeSend(s);return r===!1||r===null?(this.log("Error filtered out by beforeSend"),null):r}queueError(e){this.errorQueue.push(e),this.log("Error queued",{message:e.message,queueSize:this.errorQueue.length}),this.errorQueue.length>=this.config.maxBatchSize&&this.flushQueue()}startBatchTimer(){this.batchTimer||(this.batchTimer=setInterval(()=>{this.flushQueue()},this.config.batchInterval))}stopBatchTimer(){this.batchTimer&&(clearInterval(this.batchTimer),this.batchTimer=null)}async flushQueue(){if(!this.storageWebId||this.errorQueue.length===0)return;let e=[...this.errorQueue];this.errorQueue=[];try{e.length===1?await this.http.post(`/v1/public/storages/web/${this.storageWebId}/errors/report`,e[0]):await this.http.post(`/v1/public/storages/web/${this.storageWebId}/errors/batch`,{errors:e,user_agent:typeof navigator<"u"?navigator.userAgent:void 0}),this.log("Errors sent",{count:e.length})}catch(t){let s=this.config.maxBatchSize-this.errorQueue.length;s>0&&this.errorQueue.unshift(...e.slice(0,s)),this.log("Failed to send errors, re-queued",{error:t})}}};var j=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async invoke(e,t,s){let r=this.getPublicPrefix(),i={};return t!==void 0&&(i.payload=t),s!==void 0&&(i.timeout=s),this.http.post(`${r}/functions/${e}/invoke`,i)}async invokeCrossApp(e,t,s,r,i){let n={};return r!==void 0&&(n.payload=r),i!==void 0&&(n.timeout=i),this.http.post(`/v1/integrations/providers/${e}/functions/${t}/invoke`,n,{skipAuth:!0,headers:{Authorization:`Bearer ${s}`}})}async call(e,t){let s=await this.invoke(e,t);if(!s.success)throw new g(500,s.error||"Function execution failed","FUNCTION_EXECUTION_FAILED");return s.result}getWebhookURL(e){return`${this.http.getBaseUrl().replace(/\/+$/,"")}/v1/public/functions/${encodeURIComponent(e)}/webhook`}};var A=class{constructor(e,t){this.http=e,this.appId=t}async get(e){let t=this.resolveAppId(e);return this.http.get(`/v1/apps/${t}/game/config`)}async set(e,t){let s,r;return typeof e=="string"?(s=e,r=t??{}):(s=this.resolveAppId(),r=e),this.http.patch(`/v1/apps/${s}/game/config`,r)}async enable(e,t){return this.set(e,{[t]:!0})}async disable(e,t){return this.set(e,{[t]:!1})}resolveAppId(e){let t=e??this.appId;if(!t)throw new Error("appId not provided (pass it explicitly or set in client constructor)");return t}};function _(a){let e=a.data??a,t=s=>e[s];return new b({code:t("code")||a.code,message:t("message")||"Unknown error",phase:t("phase")||a.phase,feature:t("feature")||a.feature,roomId:t("room_id")||a.room_id,scriptId:t("script_id")||a.script_id,originClientId:t("origin_client_id")||a.origin_client_id,requested:t("requested")||a.requested,available:t("available")||a.available})}var _e=()=>{if(typeof window<"u"){let a=window.location.hostname;if(a==="localhost"||a==="127.0.0.1")return"ws://localhost:8087"}return"wss://game.connectbase.world"};function F(a){let e={};return a.roomId&&(e.room_id=a.roomId),a.categoryId&&(e.category_id=a.categoryId),typeof a.tickRate=="number"&&(e.tick_rate=a.tickRate),typeof a.maxPlayers=="number"&&(e.max_players=a.maxPlayers),a.scriptName&&(e.script_name=a.scriptName),a.metadata&&(e.metadata=a.metadata),e}var B=class{constructor(e){this.ws=null;this.handlers={};this.reconnectAttempts=0;this.reconnectTimer=null;this.pingInterval=null;this.actionSequence=0;this._roomId=null;this._state=null;this._scriptName=null;this._scriptVersion=null;this._isConnected=!1;this.msgIdCounter=0;this.config={gameServerUrl:_e(),autoReconnect:!0,maxReconnectAttempts:5,reconnectInterval:1e3,...e}}get roomId(){return this._roomId}get state(){return this._state}get isConnected(){return this._isConnected}on(e,t){return this.handlers[e]=t,this}connect(e){return new Promise((t,s)=>{if(this.ws?.readyState===WebSocket.OPEN){t();return}if(!this.config.appId){s(new Error("cb.game: appId is required to connect. Pass it to `new ConnectBase({ appId })` or per-client via `cb.game.createClient({ appId, clientId })`."));return}let r=this.buildConnectionUrl(e);this.ws=new WebSocket(r);let i=()=>{this._isConnected=!0,this.reconnectAttempts=0,this.startPingInterval(),this.handlers.onConnect?.(),t()},n=l=>{this._isConnected=!1,this.stopPingInterval(),this.handlers.onDisconnect?.(l),this.config.autoReconnect&&l.code!==1e3&&this.scheduleReconnect(e)},o=l=>{this.handlers.onError?.(l),s(new Error("WebSocket connection failed"))},c=l=>{this.handleMessage(l.data)};this.ws.addEventListener("open",i,{once:!0}),this.ws.addEventListener("close",n),this.ws.addEventListener("error",o,{once:!0}),this.ws.addEventListener("message",c)})}disconnect(){this.stopPingInterval(),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.close(1e3,"Client disconnected"),this.ws=null),this._isConnected=!1,this._roomId=null,this._scriptName=null,this._scriptVersion=null}createRoom(e={}){return this.createRoomDetailed(e).then(t=>t.state)}createRoomDetailed(e={}){return new Promise((t,s)=>{let r=i=>{if(i.type==="room_created"){let n=i.data;return this._roomId=n.room_id,this._state=n.initial_state,this._scriptName=n.script_name??null,this._scriptVersion=n.script_version??null,t({roomId:n.room_id,state:n.initial_state,scriptName:n.script_name,scriptVersion:n.script_version}),!0}else if(i.type==="error")return s(_(i)),!0;return!1};this.sendWithHandler("create_room",F(e),r,15e3,s)})}get scriptName(){return this._scriptName}get scriptVersion(){return this._scriptVersion}joinRoom(e,t){return new Promise((s,r)=>{let i=n=>{if(n.type==="room_joined"){let o=n.data;return this._roomId=o.room_id,this._state=o.initial_state,this._scriptName=null,this._scriptVersion=null,s(o.initial_state),!0}else if(n.type==="error")return r(_(n)),!0;return!1};this.sendWithHandler("join_room",{room_id:e,metadata:t},i,15e3,r)})}leaveRoom(){return new Promise((e,t)=>{if(!this._roomId){t(new b({code:"NOT_IN_ROOM",message:"Not in a room"}));return}let s=r=>r.type==="room_left"?(this._roomId=null,this._state=null,this._scriptName=null,this._scriptVersion=null,e(),!0):r.type==="error"?(t(_(r)),!0):!1;this.sendWithHandler("leave_room",{},s,15e3,t)})}sendAction(e){if(!this._roomId)throw new b({code:"NOT_IN_ROOM",message:"Not in a room"});this.send("action",{type:e.type,data:e.data,client_timestamp:e.clientTimestamp??Date.now(),sequence:this.actionSequence++})}sendChat(e){if(!this._roomId)throw new b({code:"NOT_IN_ROOM",message:"Not in a room"});this.send("chat",{message:e})}requestState(){return new Promise((e,t)=>{if(!this._roomId){t(new b({code:"NOT_IN_ROOM",message:"Not in a room"}));return}let s=r=>{if(r.type==="state"){let i=r.data;return this._state=i,e(i),!0}else if(r.type==="error")return t(_(r)),!0;return!1};this.sendWithHandler("get_state",{},s,15e3,t)})}listRooms(){return new Promise((e,t)=>{let s=r=>{if(r.type==="room_list"){let i=r.data;return e(i.rooms),!0}else if(r.type==="error")return t(_(r)),!0;return!1};this.sendWithHandler("list_rooms",{},s,15e3,t)})}ping(){return new Promise((e,t)=>{let s=Date.now(),r=i=>{if(i.type==="pong"){let n=i.data,o=Date.now()-n.clientTimestamp;return this.handlers.onPong?.(n),e(o),!0}else if(i.type==="error")return t(_(i)),!0;return!1};this.sendWithHandler("ping",{timestamp:s},r,15e3,t)})}buildConnectionUrl(e){let s=this.config.gameServerUrl.replace(/^http/,"ws"),r=new URLSearchParams;if(r.set("client_id",this.config.clientId),e&&r.set("room_id",e),this.config.publicKey&&r.set("public_key",this.config.publicKey),this.config.accessToken&&r.set("token",this.config.accessToken),!this.config.appId)throw new Error("cb.game: appId is required to build a game connection URL");return`${s}/v1/game/${this.config.appId}/ws?${r.toString()}`}send(e,t,s){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("WebSocket is not connected");this.ws.send(JSON.stringify({type:e,data:t,msg_id:s}))}sendWithHandler(e,t,s,r=15e3,i){let n=`${e}-${++this.msgIdCounter}`,o=null,c=()=>{this.ws?.removeEventListener("message",l),o&&(clearTimeout(o),o=null)},l=d=>{try{let p=JSON.parse(d.data);if(p.msg_id&&p.msg_id!==n)return;s(p)&&c()}catch{}};this.ws?.addEventListener("message",l),o=setTimeout(()=>{c();let d=new b({code:"TIMEOUT",message:`Request '${e}' timed out after ${r}ms`});i?.(d),this.handlers.onError?.(d)},r);try{this.send(e,t,n)}catch(d){c();let p=d instanceof Error?d:new Error(String(d));i?.(p)}}handleMessage(e){try{let t=JSON.parse(e);switch(t.type){case"delta":this.handleDelta(t);break;case"state":this._state=t.data,this.handlers.onStateUpdate?.(this._state);break;case"player_event":this.handlePlayerEvent(t);break;case"chat":this.handlers.onChat?.({roomId:t.room_id||"",clientId:t.client_id||"",userId:t.user_id,message:t.message||"",serverTime:t.server_time||0});break;case"error":this.handlers.onError?.(_(t));break;default:this.handlers.onMessage?.(t);break}}catch{console.error("Failed to parse game message:",e)}}handleDelta(e){let t=e.delta;if(!t)return;let s={fromVersion:t.from_version,toVersion:t.to_version,changes:t.changes.map(r=>({path:r.path,operation:r.operation,value:r.value,oldValue:r.old_value})),tick:t.tick};if(this._state){for(let r of s.changes)this.applyChange(r);this._state.version=s.toVersion}if(this.handlers.onAction){for(let r of s.changes)if(r.path.startsWith("actions.")&&r.operation==="set"&&r.value){let i=r.value;this.handlers.onAction({type:i.type||"",clientId:i.client_id||"",data:i.data,timestamp:i.timestamp||0})}}this.handlers.onDelta?.(s)}applyChange(e){if(!this._state)return;let t=e.path.split("."),s=this._state.state;for(let i=0;i<t.length-1;i++){let n=t[i];n in s||(s[n]={}),s=s[n]}let r=t[t.length-1];e.operation==="delete"?delete s[r]:s[r]=e.value}handlePlayerEvent(e){let t={clientId:e.player?.client_id||"",userId:e.player?.user_id,joinedAt:e.player?.joined_at||0,metadata:e.player?.metadata};e.event==="joined"?this.handlers.onPlayerJoined?.(t):e.event==="left"&&this.handlers.onPlayerLeft?.(t)}scheduleReconnect(e){if(this.reconnectAttempts>=(this.config.maxReconnectAttempts??5)){console.error("Max reconnect attempts reached"),this.handlers.onError?.(new b({code:"MAX_RECONNECT_ATTEMPTS",message:"Maximum reconnection attempts reached"}));return}let t=Math.min((this.config.reconnectInterval??1e3)*2**this.reconnectAttempts,3e4);this.reconnectAttempts++;let s=e||this._roomId;this.reconnectTimer=setTimeout(async()=>{console.log(`Reconnecting... (attempt ${this.reconnectAttempts})`);try{if(await this.connect(),s){console.log(`Rejoining room ${s}...`);try{await this.joinRoom(s),console.log(`Successfully rejoined room ${s}`)}catch(r){console.error(`Failed to rejoin room ${s}:`,r),this.handlers.onError?.(new b({code:"REJOIN_FAILED",message:`Failed to rejoin room: ${r}`,roomId:s}))}}}catch{}},t)}startPingInterval(){this.pingInterval=setInterval(()=>{this.ping().catch(()=>{})},3e4)}stopPingInterval(){this.pingInterval&&(clearInterval(this.pingInterval),this.pingInterval=null)}},U=class{constructor(e,t,s){this.http=e,this.gameServerUrl=t||_e().replace(/^ws/,"http"),this.appId=s,this.config=new A(e,s)}createClient(e){return new B({...e,gameServerUrl:this.gameServerUrl.replace(/^http/,"ws"),appId:e.appId??this.appId,publicKey:this.http.getPublicKey(),accessToken:this.http.getAccessToken()})}async listRooms(e){let t=e||this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/rooms`,{headers:this.getHeaders()});if(!s.ok)throw new g(s.status,`Failed to list rooms: ${s.statusText}`,"GAME_LIST_ROOMS_FAILED");return(await s.json()).rooms}async getRoom(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/rooms/${e}`,{headers:this.getHeaders()});if(!s.ok)throw new g(s.status,`Failed to get room: ${s.statusText}`,"GAME_GET_ROOM_FAILED");return(await s.json()).room}async createRoom(e,t={}){throw new Error("cb.game.createRoom is not yet publicly available \u2014 use the admin console or request a backend public route.")}async deleteRoom(e){throw new Error("cb.game.deleteRoom is not yet publicly available \u2014 use the admin console or request a backend public route.")}async joinSpectator(e,t){let s=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${s}/rooms/${e}/spectators`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({user_id:t})});if(!r.ok)throw new Error(`Failed to join spectator: ${r.statusText}`);return r.json()}async leaveSpectator(e,t){let s=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${s}/rooms/${e}/spectators/${t}`,{method:"DELETE",headers:this.getHeaders()});if(!r.ok)throw new Error(`Failed to leave spectator: ${r.statusText}`)}async getSpectators(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/rooms/${e}/spectators`,{headers:this.getHeaders()});if(!s.ok)throw new Error(`Failed to get spectators: ${s.statusText}`);return(await s.json()).spectators||[]}async joinVoiceChannel(e,t){let s=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${s}/voice/rooms/${e}/join`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t})});if(!r.ok)throw new Error(`Failed to join voice channel: ${r.statusText}`);return r.json()}async leaveVoiceChannel(e,t){let s=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${s}/voice/rooms/${e}/leave`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t})});if(!r.ok)throw new Error(`Failed to leave voice channel: ${r.statusText}`)}async setMute(e,t){let s=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${s}/voice/mute`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:e,muted:t})});if(!r.ok)throw new Error(`Failed to set mute: ${r.statusText}`)}async listReplays(e){let t=this.appId||"",s=`${this.gameServerUrl}/v1/game/${t}/replays`;e&&(s+=`?room_id=${e}`);let r=await fetch(s,{headers:this.getHeaders()});if(r.status===404)throw new Error("cb.game.listReplays: replay storage is not configured on this server (REPLAY_STORAGE_PATH unset).");if(!r.ok)throw new Error(`Failed to list replays: ${r.statusText}`);return(await r.json()).replays||[]}async getReplay(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/replays/${e}`,{headers:this.getHeaders()});if(s.status===404)throw new Error("cb.game.getReplay: replay not found or replay storage unconfigured.");if(!s.ok)throw new Error(`Failed to get replay: ${s.statusText}`);return s.json()}async downloadReplay(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/replays/${e}/download`,{headers:this.getHeaders()});if(s.status===404)throw new Error("cb.game.downloadReplay: replay not found or replay storage unconfigured.");if(!s.ok)throw new Error(`Failed to download replay: ${s.statusText}`);return s.arrayBuffer()}async getReplayHighlights(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/replays/${e}/highlights`,{headers:this.getHeaders()});if(s.status===404)throw new Error("cb.game.getReplayHighlights: replay not found or replay storage unconfigured.");if(!s.ok)throw new Error(`Failed to get highlights: ${s.statusText}`);return(await s.json()).highlights||[]}getHeaders(){let e={},t=this.http.getPublicKey();t&&(e["X-Public-Key"]=t);let s=this.http.getAccessToken();return s&&(e.Authorization=`Bearer ${s}`),e}async gameFetch(e,t,s,r="GAME_REQUEST_FAILED"){let i={method:e,headers:{...this.getHeaders()}};s!==void 0&&(i.headers["Content-Type"]="application/json",i.body=JSON.stringify(s));let n=await fetch(`${this.gameServerUrl}${t}`,i);if(!n.ok){let c=r,l=`${e} ${t} failed: ${n.statusText}`;try{let d=await n.json();d&&typeof d.error=="string"&&(l=d.error,c=d.error)}catch{}throw new g(n.status,l,c)}if(!(n.status===204||!(n.headers.get("content-type")||"").includes("application/json")))return n.json()}async enqueueMatch(e,t,s,r,i){return this.gameFetch("POST",`/v1/game/${e}/matchqueue/${t}/tickets`,{ticket_id:s,attributes:r,ttl_sec:i??0},"GAME_ENQUEUE_MATCH_FAILED")}async listMatchqueue(e,t){return this.gameFetch("GET",`/v1/game/${e}/matchqueue/${t}`,void 0,"GAME_LIST_MATCHQUEUE_FAILED")}async cancelMatch(e,t,s){await this.gameFetch("DELETE",`/v1/game/${e}/matchqueue/${t}/tickets/${s}`,void 0,"GAME_CANCEL_MATCH_FAILED")}async submitScore(e,t,s,r,i="set"){return this.gameFetch("POST",`/v1/game/${e}/leaderboards/${t}/scores`,{member:s,score:r,mode:i},"GAME_SUBMIT_SCORE_FAILED")}async getTopScores(e,t,s=10){return this.gameFetch("GET",`/v1/game/${e}/leaderboards/${t}/top?n=${s}`,void 0,"GAME_GET_TOP_SCORES_FAILED")}async getMemberRank(e,t,s){return this.gameFetch("GET",`/v1/game/${e}/leaderboards/${t}/members/${s}`,void 0,"GAME_GET_MEMBER_RANK_FAILED")}async getRankAround(e,t,s,r=5,i=5){return this.gameFetch("GET",`/v1/game/${e}/leaderboards/${t}/around/${s}?above=${r}&below=${i}`,void 0,"GAME_GET_RANK_AROUND_FAILED")}async resetLeaderboard(e,t){await this.gameFetch("DELETE",`/v1/game/${e}/leaderboards/${t}`,void 0,"GAME_RESET_LEADERBOARD_FAILED")}async removeFromLeaderboard(e,t,s){await this.gameFetch("DELETE",`/v1/game/${e}/leaderboards/${t}/members/${s}`,void 0,"GAME_REMOVE_FROM_LEADERBOARD_FAILED")}async uploadScript(e,t,s){return this.gameFetch("POST",`/v1/game/${e}/scripts`,{name:t,code:s},"GAME_UPLOAD_SCRIPT_FAILED")}async listScripts(e){return this.gameFetch("GET",`/v1/game/${e}/scripts`,void 0,"GAME_LIST_SCRIPTS_FAILED")}async getScript(e,t){return this.gameFetch("GET",`/v1/game/${e}/scripts/${t}`,void 0,"GAME_GET_SCRIPT_FAILED")}async listScriptVersions(e,t){return this.gameFetch("GET",`/v1/game/${e}/scripts/${t}/versions`,void 0,"GAME_LIST_SCRIPT_VERSIONS_FAILED")}async activateScript(e,t,s){return this.gameFetch("POST",`/v1/game/${e}/scripts/${t}/activate`,{version:s??0},"GAME_ACTIVATE_SCRIPT_FAILED")}async rollbackScript(e,t){return this.gameFetch("POST",`/v1/game/${e}/scripts/${t}/rollback`,{},"GAME_ROLLBACK_SCRIPT_FAILED")}async deactivateScript(e,t){await this.gameFetch("POST",`/v1/game/${e}/scripts/${t}/deactivate`,{},"GAME_DEACTIVATE_SCRIPT_FAILED")}async deleteScript(e,t){await this.gameFetch("DELETE",`/v1/game/${e}/scripts/${t}`,void 0,"GAME_DELETE_SCRIPT_FAILED")}};async function Se(a){if(typeof Blob<"u"&&a instanceof Blob){let s=await a.arrayBuffer(),r="name"in a?a.name:void 0;return{data:ke(new Uint8Array(s)),mimeType:a.type||"",defaultName:r}}let e=a;if(!e||e.data===void 0)throw new Error("addDocumentFromFile: file must be a Blob/File or { data, mimeType?, name? }");let t=e.data instanceof Uint8Array?e.data:new Uint8Array(e.data);return{data:ke(t),mimeType:e.mimeType??"",defaultName:e.name}}function ke(a){let e=globalThis;if(e.Buffer)return e.Buffer.from(a).toString("base64");let t=8192,s="";for(let r=0;r<a.length;r+=t){let i=a.subarray(r,Math.min(r+t,a.length));s+=String.fromCharCode.apply(null,Array.from(i))}return btoa(s)}var J=class{constructor(e){this.http=e}async addDocument(e,t){return this.http.post(`/v1/public/knowledge-bases/${e}/documents`,t)}async updateDocument(e,t,s){return this.http.put(`/v1/public/knowledge-bases/${e}/documents/${t}`,s)}async updateDocumentFromFile(e,t,s,r){let{data:i,mimeType:n}=await Se(s);return this.updateDocument(e,t,{name:r?.name,file_content:i,mime_type:n,metadata:r?.metadata})}async addDocumentFromFile(e,t,s){let{data:r,mimeType:i,defaultName:n}=await Se(t),o=s?.name??n??"document";return this.addDocument(e,{name:o,source_type:"file",file_content:r,mime_type:i,metadata:s?.metadata})}async listDocuments(e){return this.http.get(`/v1/public/knowledge-bases/${e}/documents`)}async deleteDocument(e,t){await this.http.delete(`/v1/public/knowledge-bases/${e}/documents/${t}`)}async search(e,t){return this.http.post(`/v1/public/knowledge-bases/${e}/search`,t)}async searchGet(e,t,s){let r=new URLSearchParams({query:t});return s&&r.append("top_k",String(s)),this.http.get(`/v1/public/knowledge-bases/${e}/search?${r.toString()}`)}};var M=class{constructor(){this._webSpeechRecognition=null;this.clipboard={writeText:async e=>{this.getPlatform()==="desktop"&&window.NativeBridge?.clipboard?await window.NativeBridge.clipboard.writeText(e):await navigator.clipboard.writeText(e)},readText:async()=>this.getPlatform()==="desktop"&&window.NativeBridge?.clipboard?window.NativeBridge.clipboard.readText():navigator.clipboard.readText(),writeHTML:async e=>{window.NativeBridge?.clipboard?.writeHTML?await window.NativeBridge.clipboard.writeHTML(e):await navigator.clipboard.writeText(e)},writeImage:async e=>{if(window.NativeBridge?.clipboard?.writeImage)await window.NativeBridge.clipboard.writeImage(e);else throw new Error("Image clipboard not supported on this platform")},readImage:async()=>window.NativeBridge?.clipboard?.readImage?window.NativeBridge.clipboard.readImage():null};this.filesystem={pickFile:async e=>{if(this.getPlatform()==="desktop"&&window.NativeBridge?.filesystem?.showOpenDialog){let s=await window.NativeBridge.filesystem.showOpenDialog({properties:e?.multiple?["openFile","multiSelections"]:["openFile"],filters:e?.filters});return s.canceled||!s.filePaths.length?null:s.filePaths.map(r=>new File([],r.split("/").pop()||"file"))}return new Promise(s=>{let r=document.createElement("input");r.type="file",e?.accept&&(r.accept=e.accept),e?.multiple&&(r.multiple=!0),r.onchange=()=>{s(r.files?Array.from(r.files):null)},r.click()})},saveFile:async(e,t,s)=>{let r=this.getPlatform();if(r==="desktop"&&window.NativeBridge?.filesystem){let c=await window.NativeBridge.filesystem.showSaveDialog?.({defaultPath:t,filters:s?.filters});if(c?.canceled||!c?.filePath)return!1;let l=e instanceof Blob?await e.text():e;return(await window.NativeBridge.filesystem.writeFile(c.filePath,l)).success}if(r==="mobile"&&window.NativeBridge?.filesystem){let c=e instanceof Blob?await e.text():e;return(await window.NativeBridge.filesystem.writeFile(t,c)).success}let i=e instanceof Blob?e:new Blob([e],{type:"text/plain"}),n=URL.createObjectURL(i),o=document.createElement("a");return o.href=n,o.download=t,o.click(),URL.revokeObjectURL(n),!0},readFile:async e=>{if(window.NativeBridge?.filesystem?.readFile){let t=await window.NativeBridge.filesystem.readFile(e);return t.success?t.content??null:null}return null},exists:async e=>window.NativeBridge?.filesystem?.exists?window.NativeBridge.filesystem.exists(e):!1};this.camera={takePicture:async e=>this.getPlatform()==="mobile"&&window.NativeBridge?.camera?window.NativeBridge.camera.takePicture(e):new Promise(s=>{let r=document.createElement("input");r.type="file",r.accept="image/*",r.capture="environment",r.onchange=async()=>{let i=r.files?.[0];if(!i){s(null);return}let n=new FileReader;n.onload=()=>{let o=new Image;o.onload=()=>{s({uri:URL.createObjectURL(i),base64:e?.base64?n.result.split(",")[1]:void 0,width:o.width,height:o.height})},o.src=n.result},n.readAsDataURL(i)},r.click()}),pickImage:async e=>this.getPlatform()==="mobile"&&window.NativeBridge?.camera?window.NativeBridge.camera.pickImage(e):new Promise(s=>{let r=document.createElement("input");r.type="file",r.accept="image/*",e?.multiple&&(r.multiple=!0),r.onchange=async()=>{let i=r.files;if(!i?.length){s(null);return}let n=[];for(let o of Array.from(i)){let c=await new Promise(l=>{let d=new FileReader;d.onload=()=>{let p=new Image;p.onload=()=>{l({uri:URL.createObjectURL(o),base64:e?.base64?d.result.split(",")[1]:void 0,width:p.width,height:p.height})},p.src=d.result},d.readAsDataURL(o)});n.push(c)}s(n)},r.click()})};this.location={getCurrentPosition:async e=>this.getPlatform()==="mobile"&&window.NativeBridge?.location?window.NativeBridge.location.getCurrentPosition(e):new Promise((s,r)=>{navigator.geolocation.getCurrentPosition(i=>{s({latitude:i.coords.latitude,longitude:i.coords.longitude,altitude:i.coords.altitude,accuracy:i.coords.accuracy,timestamp:i.timestamp})},r,{enableHighAccuracy:e?.accuracy==="high",timeout:1e4,maximumAge:0})})};this.notification={show:async e=>this.getPlatform()==="desktop"&&window.NativeBridge?.notification?(await window.NativeBridge.notification.show(e)).success:!("Notification"in window)||Notification.permission!=="granted"&&await Notification.requestPermission()!=="granted"?!1:(new Notification(e.title,{body:e.body,icon:e.icon,silent:e.silent}),!0),requestPermission:async()=>this.getPlatform()==="mobile"&&window.NativeBridge?.push?(await window.NativeBridge.push.requestPermission()).granted:"Notification"in window?await Notification.requestPermission()==="granted":!1};this.shell={openExternal:async e=>this.getPlatform()==="desktop"&&window.NativeBridge?.shell?(await window.NativeBridge.shell.openExternal(e)).success:(window.open(e,"_blank"),!0)};this.window={minimize:async()=>{await window.NativeBridge?.window?.minimize()},maximize:async()=>{await window.NativeBridge?.window?.maximize()},unmaximize:async()=>{await window.NativeBridge?.window?.unmaximize()},close:async()=>{await window.NativeBridge?.window?.close()},isMaximized:async()=>await window.NativeBridge?.window?.isMaximized()??!1,setTitle:async e=>{window.NativeBridge?.window?await window.NativeBridge.window.setTitle(e):document.title=e},setFullScreen:async e=>{window.NativeBridge?.window?await window.NativeBridge.window.setFullScreen(e):document.documentElement.requestFullscreen&&(e?await document.documentElement.requestFullscreen():document.exitFullscreen&&await document.exitFullscreen())}};this.system={getInfo:async()=>window.NativeBridge?.system?window.NativeBridge.system.getInfo():null,getMemory:async()=>window.NativeBridge?.system?window.NativeBridge.system.getMemory():null};this.biometric={isAvailable:async()=>window.NativeBridge?.biometric?window.NativeBridge.biometric.isAvailable():null,authenticate:async e=>window.NativeBridge?.biometric?window.NativeBridge.biometric.authenticate(e):null};this.secureStore={setItem:async(e,t)=>window.NativeBridge?.secureStore?(await window.NativeBridge.secureStore.setItem(e,t)).success:(localStorage.setItem(e,t),!0),getItem:async e=>window.NativeBridge?.secureStore?(await window.NativeBridge.secureStore.getItem(e)).value:localStorage.getItem(e),deleteItem:async e=>window.NativeBridge?.secureStore?(await window.NativeBridge.secureStore.deleteItem(e)).success:(localStorage.removeItem(e),!0)};this.admob={showInterstitial:async()=>window.NativeBridge?.admob?(await window.NativeBridge.admob.showInterstitial()).shown:!1,showRewarded:async()=>window.NativeBridge?.admob?(await window.NativeBridge.admob.showRewarded()).rewarded:!1};this.speech={isAvailable:async()=>this.getPlatform()==="mobile"&&window.NativeBridge?.speech?(await window.NativeBridge.speech.isAvailable()).available:typeof window>"u"?!1:"SpeechRecognition"in window||"webkitSpeechRecognition"in window,recognize:async e=>{if(this.getPlatform()==="mobile"&&window.NativeBridge?.speech){let r;e?.onPartial&&(r=i=>{let n=i.detail;n?.transcript&&e.onPartial?.(n.transcript)},window.addEventListener("nativeSpeechPartial",r));try{return await window.NativeBridge.speech.recognize(e)}finally{r&&window.removeEventListener("nativeSpeechPartial",r)}}let t=window,s=t.SpeechRecognition||t.webkitSpeechRecognition;if(!s)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.");return new Promise((r,i)=>{let n=new s;this._webSpeechRecognition=n,n.lang=e?.lang??"en-US",n.interimResults=e?.interim??!0,n.continuous=e?.continuous??!1;let o="";n.onresult=c=>{let l="";for(let d=c.resultIndex;d<c.results.length;d++){let p=c.results[d];p.isFinal?o+=p[0].transcript:l+=p[0].transcript}l&&e?.onPartial&&e.onPartial(l)},n.onerror=c=>{this._webSpeechRecognition=null,i(new Error(c?.error||"\uC74C\uC131 \uC778\uC2DD \uC624\uB958"))},n.onend=()=>{this._webSpeechRecognition=null,r({transcript:o.trim(),isFinal:!0})},n.start()})},stop:async()=>{if(this.getPlatform()==="mobile"&&window.NativeBridge?.speech){await window.NativeBridge.speech.stop();return}if(this._webSpeechRecognition)try{this._webSpeechRecognition.stop()}catch{}}}}getPlatform(){if(typeof window>"u")return"web";let e=window.NativeBridge;return e?.platform==="electron"?"desktop":e?.platform==="react-native"||e?.platform==="ios"||e?.platform==="android"||e?.camera||window.ReactNativeWebView?"mobile":"web"}hasFeature(e){return typeof window>"u"?!1:!!window.NativeBridge?.[e]}get bridge(){if(!(typeof window>"u"))return window.NativeBridge}};function Te(){if(typeof window>"u")return null;let a=window.NativeBridge?.oauth;return!a||typeof a.signIn!="function"||typeof a.getCallbackScheme!="function"?null:a}function N(){if(typeof navigator>"u")return null;let a=navigator.userAgent||"";return/KAKAOTALK/i.test(a)?{type:"kakaotalk",ua:a}:/Line\//i.test(a)?{type:"line",ua:a}:/Instagram/i.test(a)?{type:"instagram",ua:a}:/FBAN|FBAV|FB_IAB/i.test(a)?{type:"facebook",ua:a}:/NAVER\(inapp/i.test(a)?{type:"naverapp",ua:a}:/MicroMessenger/i.test(a)?{type:"wechat",ua:a}:null}function Q(a){if(typeof window>"u")return!1;let e=N();if(!e)return!1;let t=a??window.location.href,s=encodeURIComponent(t);switch(e.type){case"kakaotalk":return window.location.href=`kakaotalk://web/openExternal?url=${s}`,!0;case"line":return window.location.href=`line://nv/openURL?url=${s}`,!0;default:return!1}}var X=class{constructor(e){this.http=e;this.bootConsumePromise=null}async getEnabledProviders(){return this.http.get("/v1/public/oauth/providers")}async signIn(e,t,s,r){return this.startCentralOAuth(e,t,s,r?.createIfNotExists?"signup":"signin")}async signUp(e,t,s){return this.startCentralOAuth(e,t,s,"signup")}async startCentralOAuth(e,t,s,r){let i=Te();if(i){let l=i.getCallbackScheme(),d=new URLSearchParams({app_callback:l,intent:r});s&&d.append("state",s);let p=await this.http.get(`/v1/public/oauth/${e}/authorize/central?${d.toString()}`),{url:u}=await i.signIn(p.authorization_url,l),f=new URL(u),y=new URL(t,window.location.href);f.searchParams.forEach((h,w)=>{y.searchParams.set(w,h)}),window.location.href=y.toString();return}let n=N();if(n&&(e==="google"||e==="apple")){if(Q())return;throw new Error(`\uD604\uC7AC ${n.type==="kakaotalk"?"\uCE74\uCE74\uC624\uD1A1":n.type==="line"?"\uB77C\uC778":"\uC778\uC571"} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C\uB294 Google/Apple \uB85C\uADF8\uC778\uC774 \uC81C\uD55C\uB429\uB2C8\uB2E4. \uC6B0\uCE21 \uC0C1\uB2E8 \uBA54\uB274\uC5D0\uC11C "\uB2E4\uB978 \uBE0C\uB77C\uC6B0\uC800\uB85C \uC5F4\uAE30"\uB97C \uC120\uD0DD\uD574\uC8FC\uC138\uC694.`)}let o=new URLSearchParams({app_callback:t,intent:r});s&&o.append("state",s);let c=await this.http.get(`/v1/public/oauth/${e}/authorize/central?${o.toString()}`);window.location.href=c.authorization_url}async signInWithPopup(e,t,s={}){let r=s.intent??"signin",i=Te();if(i){let y=i.getCallbackScheme(),h=new URLSearchParams({app_callback:y,intent:r}),w=await this.http.get(`/v1/public/oauth/${e}/authorize/central?${h.toString()}`),{url:R}=await i.signIn(w.authorization_url,y),v=new URL(R),S=v.searchParams.get("error");if(S)throw new Error(S);let m=v.searchParams.get("access_token")||"",P=v.searchParams.get("refresh_token")||"",C=v.searchParams.get("member_id")||"";if(!m||!P||!C)throw new Error("OAuth \uC751\uB2F5\uC5D0 \uD1A0\uD070\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");return this.http.setTokens(m,P),{access_token:m,refresh_token:P,member_id:C,is_new_member:v.searchParams.get("is_new_member")==="true"}}let n=N();if(n&&(e==="google"||e==="apple"))throw Q()?new Error("\uC678\uBD80 \uBE0C\uB77C\uC6B0\uC800\uB85C \uC774\uB3D9\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694."):new Error(`\uD604\uC7AC ${n.type==="kakaotalk"?"\uCE74\uCE74\uC624\uD1A1":n.type==="line"?"\uB77C\uC778":"\uC778\uC571"} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C\uB294 Google/Apple \uB85C\uADF8\uC778\uC774 \uC81C\uD55C\uB429\uB2C8\uB2E4. \uC678\uBD80 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uC5B4\uC8FC\uC138\uC694.`);let o=new URLSearchParams;o.set("intent",r),t&&o.set("app_callback",t);let c=await this.http.get(`/v1/public/oauth/${e}/authorize/central?${o.toString()}`),l=500,d=600,p=window.screenX+(window.outerWidth-l)/2,u=window.screenY+(window.outerHeight-d)/2,f=window.open(c.authorization_url,"oauth-popup",`width=${l},height=${d},left=${p},top=${u}`);if(!f)throw new Error("\uD31D\uC5C5\uC774 \uCC28\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uD31D\uC5C5 \uCC28\uB2E8\uC744 \uD574\uC81C\uD574\uC8FC\uC138\uC694.");return new Promise((y,h)=>{let w=!1,R=()=>{w=!0,window.removeEventListener("message",v),clearInterval(S),clearTimeout(m)},v=async P=>{if(P.data?.type!=="oauth-callback"||w)return;if(R(),P.data.error){h(new Error(P.data.error));return}let C=P.data.email,he={member_id:P.data.member_id,access_token:P.data.access_token,refresh_token:P.data.refresh_token,is_new_member:P.data.is_new_member==="true"||P.data.is_new_member===!0,...typeof C=="string"&&C.length>0?{email:C}:{}};this.http.setTokens(he.access_token,he.refresh_token);try{await this.http.bootstrapRefreshCookie()}catch{}y(he)};window.addEventListener("message",v);let S=setInterval(()=>{try{f.closed&&(w||(R(),h(new Error("\uB85C\uADF8\uC778\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."))))}catch{clearInterval(S)}},500),m=setTimeout(()=>{if(!w){R();try{f.close()}catch{}h(new Error("\uB85C\uADF8\uC778 \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694."))}},18e4)})}async getCallbackResult(){let e=new URLSearchParams(window.location.search),t=e.get("error");if(t){let c={error:t};return window.opener&&(window.opener.postMessage({type:"oauth-callback",...c},"*"),window.close()),c}let s=e.get("access_token"),r=e.get("refresh_token"),i=e.get("member_id");if(!s||!r||!i)return null;let n=e.get("email"),o={access_token:s,refresh_token:r,member_id:i,is_new_member:e.get("is_new_member")==="true",...n?{email:n}:{},state:e.get("state")||void 0};return window.opener?(window.opener.postMessage({type:"oauth-callback",...o},"*"),window.close(),o):(this.bootConsumePromise&&await this.bootConsumePromise||(this.http.setTokens(s,r),await this.http.bootstrapRefreshCookie()),o)}consumeRedirectCallbackOnBoot(){return this.bootConsumePromise||(this.bootConsumePromise=this.doConsumeRedirectOnBoot()),this.bootConsumePromise}async doConsumeRedirectOnBoot(){try{if(typeof window>"u"||!window.location)return!1;let e=new URLSearchParams(window.location.search);if(e.get("error"))return!1;let t=e.get("access_token"),s=e.get("refresh_token"),r=e.get("member_id");return!t||!s||!r?!1:(this.http.setTokens(t,s),await this.http.bootstrapRefreshCookie(),!0)}catch{return!1}}async exchangeCodeFromCallback(){let e=new URLSearchParams(window.location.search),t=e.get("code");if(!t||e.get("error"))return null;let s=await this.http.post("/v1/auth/oauth/exchange",{code:t}),r={access_token:s.access_token,refresh_token:s.refresh_token,member_id:s.member_id,is_new_member:s.is_new_member,...s.email?{email:s.email}:{},state:e.get("state")||void 0};return window.opener?(window.opener.postMessage({type:"oauth-callback",...r},"*"),window.close(),r):(this.http.setTokens(r.access_token,r.refresh_token),await this.http.bootstrapRefreshCookie(),r)}};var Y=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async prepare(e){let t=this.getPublicPrefix();return this.http.post(`${t}/payments/prepare`,e)}async createCheckoutSession(e){let t=this.getPublicPrefix(),s=await this.http.post(`${t}/payments/checkout-session`,e);return k(s,{session_id:{type:"string"},session_url:{type:"string"}},"payment.createCheckoutSession"),s}async confirm(e){let t=this.getPublicPrefix();return this.http.post(`${t}/payments/confirm`,e)}async cancel(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/payments/cancel`,{payment_id:e,...t})}async getByOrderId(e){let t=this.getPublicPrefix();return this.http.get(`${t}/payments/orders/${e}`)}async list(e,t){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error('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.');let s=new URLSearchParams;t?.status&&s.set("status",t.status),t?.startDate&&s.set("start_date",t.startDate),t?.endDate&&s.set("end_date",t.endDate),t?.limit!==void 0&&s.set("limit",String(t.limit)),t?.offset!==void 0&&s.set("offset",String(t.offset));let r=s.toString();return this.http.get(`/v1/apps/${e}/payments${r?`?${r}`:""}`)}};var Z=class{constructor(e){this.http=e}async getPublicKeys(e){return this.http.get(`/v1/apps/${e}/public-keys`)}async createPublicKey(e,t){return this.http.post(`/v1/apps/${e}/public-keys`,t)}async updatePublicKey(e,t,s){return this.http.patch(`/v1/apps/${e}/public-keys/${t}`,s)}async deletePublicKey(e,t){await this.http.delete(`/v1/apps/${e}/public-keys/${t}`)}};var ee=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async registerDevice(e){let t=this.getPublicPrefix(),s=await this.http.post(`${t}/push/devices`,e);return k(s,{device_token:{type:"string"}},"push.registerDevice"),s}async unregisterDevice(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/push/devices/${e}`)}async subscribeTopic(e,t){let s=this.getPublicPrefix(),r={topic_name:t};await this.http.post(`${s}/push/devices/${e}/topics/subscribe`,r)}async unsubscribeTopic(e,t){let s=this.getPublicPrefix();await this.http.delete(`${s}/push/devices/${e}/topics/${t}/unsubscribe`)}async getVAPIDPublicKey(){let e=this.getPublicPrefix();return this.http.get(`${e}/push/vapid-public-key`)}async registerWebPush(e){let t=this.getPublicPrefix(),s;if("toJSON"in e){let i=e.toJSON();s={endpoint:i.endpoint||"",expirationTime:i.expirationTime,keys:{p256dh:i.keys?.p256dh||"",auth:i.keys?.auth||""}}}else s=e;let r={device_token:s.endpoint,platform:"web",device_id:this.generateDeviceId(),device_name:this.getBrowserName(),os_version:this.getOSInfo()};return this.http.post(`${t}/push/devices`,{...r,web_push_subscription:s})}async unregisterWebPush(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/push/devices/${encodeURIComponent(e)}`)}async sendToMembers(e,t,s){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error("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.");if(t.length===0)throw new Error("cb.push.sendToMembers(): memberIds \uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4.");let r={target_type:"members",target_member_ids:t,title:s.title,body:s.body,...s.imageUrl!==void 0?{image_url:s.imageUrl}:{},...s.data!==void 0?{data:s.data}:{},...s.platforms!==void 0?{platforms:s.platforms}:{},...s.ttlSeconds!==void 0?{ttl:s.ttlSeconds}:{},...s.priority!==void 0?{priority:s.priority}:{},...s.clickAction!==void 0?{click_action:s.clickAction}:{},...s.scheduledAt!==void 0?{scheduled_at:s.scheduledAt}:{}};return this.http.post(`/v1/apps/${e}/push/send`,r)}async sendToTopic(e,t,s){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error("cb.push.sendToTopic() \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.");if(!t)throw new Error("cb.push.sendToTopic(): topicName \uC774 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4.");let r={topic_name:t,title:s.title,body:s.body,...s.imageUrl!==void 0?{image_url:s.imageUrl}:{},...s.data!==void 0?{data:s.data}:{},...s.platforms!==void 0?{platforms:s.platforms}:{},...s.ttlSeconds!==void 0?{ttl:s.ttlSeconds}:{},...s.priority!==void 0?{priority:s.priority}:{},...s.clickAction!==void 0?{click_action:s.clickAction}:{},...s.scheduledAt!==void 0?{scheduled_at:s.scheduledAt}:{}};return this.http.post(`/v1/apps/${e}/push/send-to-topic`,r)}async getStats(e){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error('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.');return this.http.get(`/v1/apps/${e}/push/stats`)}generateDeviceId(){if(typeof window>"u"||typeof localStorage>"u")return`device_${Date.now()}_${Math.random().toString(36).substr(2,9)}`;let e="cb_push_device_id",t=localStorage.getItem(e);return t||(t=`web_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,localStorage.setItem(e,t)),t}getBrowserName(){if(typeof navigator>"u")return"Unknown Browser";let e=navigator.userAgent;return e.includes("Chrome")&&!e.includes("Edg")?"Chrome":e.includes("Safari")&&!e.includes("Chrome")?"Safari":e.includes("Firefox")?"Firefox":e.includes("Edg")?"Edge":e.includes("Opera")||e.includes("OPR")?"Opera":"Unknown Browser"}getOSInfo(){if(typeof navigator>"u")return"Unknown OS";let e=navigator.userAgent;return e.includes("Windows")?"Windows":e.includes("Mac OS")?"macOS":e.includes("Linux")?"Linux":e.includes("Android")?"Android":e.includes("iOS")||e.includes("iPhone")||e.includes("iPad")?"iOS":"Unknown OS"}};var te=class{constructor(e){this.http=e}async publish(e,t){return this.http.post(`/v1/public/queues/${e}/messages`,t)}async publishBatch(e,t){return this.http.post(`/v1/public/queues/${e}/messages/batch`,t)}async consume(e,t){let s=new URLSearchParams;t?.max_messages&&s.set("max_messages",String(t.max_messages)),t?.visibility_timeout&&s.set("visibility_timeout",String(t.visibility_timeout)),t?.auto_ack!==void 0&&s.set("auto_ack",String(t.auto_ack));let r=s.toString(),i=await this.http.get(`/v1/public/queues/${e}/messages${r?`?${r}`:""}`);return k(i,{messages:{type:"array"}},"queue.consume"),i}async ack(e,t,s){let r={message_ids:t,ack_token:s};return this.http.post(`/v1/public/queues/${e}/messages/ack`,r)}async nack(e,t,s){return this.http.post(`/v1/public/queues/${e}/messages/${t}/nack`,s||{})}async getInfo(e){return this.http.get(`/v1/public/queues/${e}`)}};var se=class{constructor(e,t){this.ws=null;this.state="disconnected";this._connectionId=null;this._appId=null;this.activeTransport=null;this.sseFallbackActive=!1;this.sseSessions=new Map;this.options={maxRetries:5,retryInterval:1e3,userId:"",accessToken:"",timeout:3e4,debug:!1,fallback:"sse"};this.retryCount=0;this.pendingRequests=new Map;this.subscriptions=new Map;this.streamSessions=new Map;this.stateHandlers=[];this.errorHandlers=[];this.presenceHandlers=[];this.presenceSubscriptions=new Map;this.typingHandlers=new Map;this.readReceiptHandlers=new Map;this.connectPromise=null;this.http=e,this.socketUrl=t,this.clientId=this.generateClientId()}get connectionId(){return this._connectionId}get appId(){return this._appId}get transport(){return this.activeTransport}async connect(e={}){if(this.state!=="connected"){if(this.state==="connecting"&&this.connectPromise)return this.connectPromise;this.options={...this.options,...e},e.userId&&(this.userId=e.userId),this.connectPromise=this.doConnect();try{await this.connectPromise}catch(t){let s=!!(this.options.accessToken||this.http.getPublicKey());if(this.options.fallback!=="none"&&s)this.activateSseFallback(t);else throw t}finally{this.connectPromise=null}}}activateSseFallback(e){if(this.ws){try{this.ws.close()}catch{}this.ws=null}this.sseFallbackActive=!0,this.activeTransport="sse",this.state="connected",this.retryCount=0,this.log(`WS unavailable (${e instanceof Error?e.message:String(e)}); using SSE fallback transport for AI streaming`),this.notifyStateChange()}disconnect(){this.state="disconnected",this.notifyStateChange(),this.ws&&(this.ws.close(),this.ws=null),this.pendingRequests.forEach(e=>{clearTimeout(e.timeout),e.reject(new Error("Connection closed"))}),this.pendingRequests.clear(),this.subscriptions.clear(),this.streamSessions.forEach(e=>{e.handlers.onError&&e.handlers.onError(new Error("Connection closed"))}),this.streamSessions.clear(),this.sseSessions.forEach(e=>{try{e.abort()}catch{}}),this.sseSessions.clear(),this.sseFallbackActive=!1,this.activeTransport=null,this.presenceHandlers=[],this.presenceSubscriptions.clear(),this.typingHandlers.clear(),this.readReceiptHandlers.clear(),this._connectionId=null,this._appId=null,this.retryCount=0,this.connectPromise=null}async subscribe(e,t={}){if(this.assertBidirectional("subscribe"),this.state!=="connected")throw new Error("Not connected. Call connect() first.");let s=this.generateRequestId(),r=await this.sendRequest({category:e,action:"subscribe",request_id:s}),i={category:r.category,persist:r.persist,historyCount:r.history_count,readReceipt:r.read_receipt},n=[];return this.subscriptions.set(e,{info:i,handlers:n}),{info:i,send:async(c,l)=>{await this.sendMessage(e,c,l)},getHistory:async c=>this.getHistory(e,c??t.historyLimit),unsubscribe:async()=>{await this.unsubscribe(e)},onMessage:c=>(n.push(c),()=>{let l=n.indexOf(c);l>-1&&n.splice(l,1)})}}async unsubscribe(e){if(this.state!=="connected")return;let t=this.generateRequestId();await this.sendRequest({category:e,action:"unsubscribe",request_id:t}),this.subscriptions.delete(e)}async sendMessage(e,t,s={}){if(this.assertBidirectional("sendMessage"),this.state!=="connected")throw new Error("Not connected");let i=s.includeSelf!==!1,n=this.generateRequestId();await this.sendRequest({category:e,action:"send",data:{data:t,broadcast:i},request_id:n})}async getHistory(e,t){if(this.assertBidirectional("getHistory"),this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId(),r=await this.sendRequest({category:e,action:"history",data:t?{limit:t}:void 0,request_id:s});return{category:r.category,messages:r.messages.map(i=>({id:i.id,category:i.category,from:i.from,data:i.data,sentAt:i.sent_at})),total:r.total}}async stream(e,t,s={}){if(this.state==="connected"&&this.activeTransport==="ws"&&this.ws?.readyState===WebSocket.OPEN)try{return this.streamViaWS(e,t,s)}catch(i){if(this.options.fallback==="none")throw i;this.log(`WS stream send failed (${i instanceof Error?i.message:String(i)}); falling back to SSE`)}if(this.options.fallback!=="none")return this.streamViaSSE(e,t,s);throw new Error("Not connected. Call connect() first.")}streamViaWS(e,t,s){let r=this.generateRequestId(),i=s.sessionId||this.generateRequestId();this.streamSessions.set(i,{handlers:t,requestId:r});try{this.sendRaw({category:"",action:"stream",data:{provider:s.provider,model:s.model,messages:e,system:s.system,temperature:s.temperature,max_tokens:s.maxTokens,session_id:i,metadata:s.metadata,mcp_group:s.mcpGroup},request_id:r})}catch(n){throw this.streamSessions.delete(i),n}return{sessionId:i,stop:async()=>{await this.stopStream(i)}}}streamViaSSE(e,t,s){let r=s.sessionId||this.generateRequestId(),i=new AbortController;return this.sseSessions.set(r,i),s.mcpGroup&&this.log("mcpGroup is not supported in SSE fallback mode; streaming without MCP tools"),this.log(`Streaming via SSE fallback (session ${r})`),this.runSseStream(r,e,t,s,i.signal),{sessionId:r,stop:async()=>{await this.stopStream(r)}}}async runSseStream(e,t,s,r,i){let n=Date.now(),o=0,c="",l,p={messages:r.system?[{role:"system",content:r.system},...t]:t,provider:r.provider,model:r.model,temperature:r.temperature,maxTokens:r.maxTokens};try{let u=await this.http.fetchRaw("/v1/public/ai/chat/stream",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(p),signal:i});if(!u.ok){let h=await u.json().catch(()=>({error:"Stream request failed"}));s.onError?.(new Error(h.error||h.message||"Stream request failed"));return}if(l=u.body?.getReader(),!l){s.onError?.(new Error("ReadableStream not supported"));return}let f=new TextDecoder,y="";for(;;){let{done:h,value:w}=await l.read();if(h)break;y+=f.decode(w,{stream:!0});let R=y.split(`
|
|
3
3
|
`);y=R.pop()||"";for(let v of R){if(!v.startsWith("data: "))continue;let S=v.slice(6).trim();if(S==="[DONE]"){s.onDone?.({sessionId:e,fullText:c,totalTokens:0,promptTokens:0,duration:Date.now()-n});return}try{let m=JSON.parse(S);if(m.error){s.onError?.(new Error(m.message||m.error||"stream error"));return}if(m.type==="tool_start"){s.onToolCall?.(m.name||"",m.arguments||{},o);continue}if(m.type==="tool_end"){s.onToolResult?.(m.name||"",m.success!==!1,m.durationMs||0,o);continue}if(m.type==="sources"||m.type==="searching"||m.type==="heartbeat")continue;if(m.content&&(c+=m.content,s.onToken?.(m.content,o++)),m.done){s.onDone?.({sessionId:e,fullText:c,totalTokens:0,promptTokens:0,duration:Date.now()-n});return}}catch{}}}}catch(u){i.aborted||u instanceof DOMException&&u.name==="AbortError"||typeof u=="object"&&u!==null&&u.name==="AbortError"||s.onError?.(u instanceof Error?u:new Error(String(u)))}finally{if(this.sseSessions.delete(e),l)try{await l.cancel()}catch{}}}async stopStream(e){let t=this.sseSessions.get(e);if(t){t.abort(),this.sseSessions.delete(e);return}if(this.state!=="connected"||!this.ws||this.ws.readyState!==WebSocket.OPEN){this.streamSessions.delete(e);return}let s=this.generateRequestId();this.sendRaw({category:"",action:"stream_stop",data:{session_id:e},request_id:s}),this.streamSessions.delete(e)}getState(){return this.state}isConnected(){return this.state==="connected"}onStateChange(e){return this.stateHandlers.push(e),()=>{let t=this.stateHandlers.indexOf(e);t>-1&&this.stateHandlers.splice(t,1)}}onError(e){return this.errorHandlers.push(e),()=>{let t=this.errorHandlers.indexOf(e);t>-1&&this.errorHandlers.splice(t,1)}}async setPresence(e,t={}){if(this.assertBidirectional("setPresence"),this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId();await this.sendRequest({category:"",action:"presence_set",data:{status:e,device:t.device,metadata:t.metadata},request_id:s})}async setPresenceOnDisconnect(e,t){if(this.assertBidirectional("setPresenceOnDisconnect"),this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId();await this.sendRequest({category:"",action:"presence_on_disconnect",data:{status:e,metadata:t},request_id:s})}async getPresence(e){if(this.assertBidirectional("getPresence"),this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId(),s=await this.sendRequest({category:"",action:"presence_get",data:{user_id:e},request_id:t});return{userId:s.user_id,status:s.status,lastSeen:s.last_seen,device:s.device,metadata:s.metadata}}async getPresenceMany(e){if(this.assertBidirectional("getPresenceMany"),this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId(),s=await this.sendRequest({category:"",action:"presence_get_many",data:{user_ids:e},request_id:t}),r={};for(let[i,n]of Object.entries(s.users))r[i]={userId:n.user_id,status:n.status,lastSeen:n.last_seen,device:n.device,metadata:n.metadata};return{users:r}}async subscribePresence(e,t){if(this.assertBidirectional("subscribePresence"),this.state!=="connected")throw new Error("Not connected");if(!this.presenceSubscriptions.has(e)){let r=this.generateRequestId();await this.sendRequest({category:"",action:"presence_subscribe",data:{user_id:e},request_id:r}),this.presenceSubscriptions.set(e,[])}let s=this.presenceSubscriptions.get(e);return s.push(t),()=>{let r=s.indexOf(t);if(r>-1&&s.splice(r,1),s.length===0&&(this.presenceSubscriptions.delete(e),this.state==="connected")){let i=this.generateRequestId();this.sendRequest({category:"",action:"presence_unsubscribe",data:{user_id:e},request_id:i}).catch(n=>{this.log(`Failed to unsubscribe presence for ${e}: ${n}`)})}}}onPresenceChange(e){return this.presenceHandlers.push(e),()=>{let t=this.presenceHandlers.indexOf(e);t>-1&&this.presenceHandlers.splice(t,1)}}async startTyping(e){if(this.assertBidirectional("startTyping"),this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId();await this.sendRequest({category:"",action:"typing_start",data:{room_id:e},request_id:t})}async stopTyping(e){if(this.assertBidirectional("stopTyping"),this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId();await this.sendRequest({category:"",action:"typing_stop",data:{room_id:e},request_id:t})}async onTypingChange(e,t){if(this.assertBidirectional("onTypingChange"),this.state!=="connected")throw new Error("Not connected");if(!this.typingHandlers.has(e)){let r=this.generateRequestId();await this.sendRequest({category:"",action:"typing_subscribe",data:{room_id:e},request_id:r}),this.typingHandlers.set(e,[])}let s=this.typingHandlers.get(e);return s.push(t),()=>{let r=s.indexOf(t);if(r>-1&&s.splice(r,1),s.length===0&&(this.typingHandlers.delete(e),this.state==="connected")){let i=this.generateRequestId();this.sendRequest({category:"",action:"typing_unsubscribe",data:{room_id:e},request_id:i}).catch(n=>{this.log(`Failed to unsubscribe typing for ${e}: ${n}`)})}}}async markRead(e,t){if(this.assertBidirectional("markRead"),this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId();await this.sendRequest({category:e,action:"mark_read",data:{message_ids:t},request_id:s})}onReadReceipt(e,t){this.readReceiptHandlers.has(e)||this.readReceiptHandlers.set(e,[]);let s=this.readReceiptHandlers.get(e);return s.push(t),()=>{let r=s.indexOf(t);r>-1&&s.splice(r,1)}}async doConnect(){return new Promise((e,t)=>{this.state="connecting",this.notifyStateChange(),this.log("Connecting...");let s=this.socketUrl.replace(/^http/,"ws"),r;if(this.options.accessToken)r=`${s}/v1/realtime/auth?access_token=${encodeURIComponent(this.options.accessToken)}&client_id=${this.clientId}`,this.log("Using accessToken authentication");else{let o=this.http.getPublicKey();if(!o){let c=new Error("API Key or accessToken is required for realtime connection");this.log("Connection failed: no API Key or accessToken"),t(c);return}r=`${s}/v1/realtime/auth?public_key=${encodeURIComponent(o)}&client_id=${this.clientId}`,this.log("Using API Key authentication")}this.userId&&(r+=`&user_id=${encodeURIComponent(this.userId)}`);let i=!1,n=setTimeout(()=>{i||(i=!0,this.log(`Connection timeout after ${this.options.timeout}ms`),this.ws&&(this.ws.close(),this.ws=null),this.state="disconnected",this.notifyStateChange(),t(new Error(`Connection timeout after ${this.options.timeout}ms`)))},this.options.timeout);try{this.log(`Connecting to ${s}`),this.ws=new WebSocket(r),this.ws.onopen=()=>{this.log("WebSocket opened, waiting for connected event...")},this.ws.onmessage=o=>{let c=o.data.split(`
|
|
4
|
-
`).filter(l=>l.trim());for(let l of c)try{let d=JSON.parse(l);this.handleServerMessage(d,()=>{i||(i=!0,clearTimeout(n),this.log("Connected successfully"),e())})}catch(d){this.logError("Failed to parse message",{line:l,error:d})}},this.ws.onclose=o=>{this.log(`WebSocket closed: code=${o.code}, reason=${o.reason}`),!i&&this.state==="connecting"&&(i=!0,clearTimeout(n),t(new Error(`Connection closed: ${o.reason||"unknown reason"}`))),(this.state==="connected"||this.state==="connecting")&&this.handleDisconnect()},this.ws.onerror=o=>{this.log("WebSocket error occurred"),this.logError("WebSocket error",o),this.notifyError(new Error("WebSocket connection error")),!i&&this.state==="connecting"&&(i=!0,clearTimeout(n),t(new Error("Failed to connect")))}}catch(o){i=!0,clearTimeout(n),t(o)}})}log(e){this.options.debug&&console.log(`[Realtime] ${e}`)}logError(e,t){this.options.debug&&console.error(`[Realtime] ${e}`,t)}handleServerMessage(e,t){switch(e.event){case"connected":{let s=e.data;this._connectionId=s.connection_id,this._appId=s.app_id,this.state="connected",this.activeTransport="ws",this.sseFallbackActive=!1,this.retryCount=0,this.notifyStateChange(),t&&t();break}case"subscribed":case"unsubscribed":case"sent":case"result":case"history":{if(e.request_id){let s=this.pendingRequests.get(e.request_id);s&&(clearTimeout(s.timeout),s.resolve(e.data),this.pendingRequests.delete(e.request_id))}break}case"message":{let s=e.data,r=this.subscriptions.get(s.category);if(r){let i={id:s.id,category:s.category,from:s.from,data:s.data,sentAt:s.sent_at};r.handlers.forEach(n=>n(i))}break}case"error":{if(e.request_id){let s=this.pendingRequests.get(e.request_id);s&&(clearTimeout(s.timeout),s.reject(new Error(e.error||"Unknown error")),this.pendingRequests.delete(e.request_id))}else this.notifyError(new Error(e.error||"Unknown error"));break}case"pong":break;case"stream_token":{let s=e.data,r=this.streamSessions.get(s.session_id);r?.handlers.onToken&&r.handlers.onToken(s.token,s.index);break}case"stream_done":{let s=e.data,r=this.streamSessions.get(s.session_id);r?.handlers.onDone&&r.handlers.onDone({sessionId:s.session_id,fullText:s.full_text,totalTokens:s.total_tokens,promptTokens:s.prompt_tokens,duration:s.duration_ms}),this.streamSessions.delete(s.session_id);break}case"stream_tool_call":{let s=e.data,r=this.streamSessions.get(s.session_id);r?.handlers.onToolCall&&r.handlers.onToolCall(s.tool_name,s.arguments||{},s.index);break}case"stream_tool_result":{let s=e.data,r=this.streamSessions.get(s.session_id);r?.handlers.onToolResult&&r.handlers.onToolResult(s.tool_name,s.success,s.duration_ms,s.index);break}case"stream_error":{let s=e.data;if(e.request_id){for(let[r,i]of this.streamSessions)if(i.requestId===e.request_id){i.handlers.onError&&i.handlers.onError(new Error(s.message)),this.streamSessions.delete(r);break}}break}case"presence":case"presence_status":{let s=e.data,r={userId:s.user_id,status:s.status,lastSeen:s.last_seen,device:s.device,metadata:s.metadata,eventType:s.event_type};this.presenceHandlers.forEach(n=>n(r));let i=this.presenceSubscriptions.get(s.user_id);if(i&&i.forEach(n=>n(r)),e.request_id){let n=this.pendingRequests.get(e.request_id);n&&(clearTimeout(n.timeout),n.resolve(e.data),this.pendingRequests.delete(e.request_id))}break}case"typing":{let s=e.data,r={roomId:s.room_id,users:s.users},i=this.typingHandlers.get(s.room_id);i&&i.forEach(n=>n(r));break}case"read_receipt":{let s=e.data,r={category:s.category,messageIds:s.message_ids,readerId:s.reader_id,readAt:s.read_at},i=this.readReceiptHandlers.get(s.category);i&&i.forEach(n=>n(r));break}}}handleDisconnect(){if(this.sseFallbackActive){this.ws=null;return}this.ws=null,this._connectionId=null,this.streamSessions.forEach(r=>{r.handlers.onError&&r.handlers.onError(new Error("Connection lost"))}),this.streamSessions.clear();let e=new Map;for(let[r,i]of this.subscriptions)e.set(r,[...i.handlers]);this.subscriptions.clear();let t=new Map;for(let[r,i]of this.presenceSubscriptions)t.set(r,[...i]);this.presenceSubscriptions.clear();let s=new Map;for(let[r,i]of this.typingHandlers)s.set(r,[...i]);if(this.typingHandlers.clear(),this.retryCount<this.options.maxRetries){this.state="reconnecting",this.notifyStateChange(),this.retryCount++;let r=Math.min(this.options.retryInterval*2**(this.retryCount-1),3e4);setTimeout(async()=>{if(!this.sseFallbackActive)try{await this.doConnect(),this.log("Reconnected successfully, restoring subscriptions..."),await this.restoreSubscriptions(e,t,s)}catch(i){this.logError("Reconnect failed",i),this.retryCount>=this.options.maxRetries&&this.options.fallback!=="none"&&!this.sseFallbackActive&&this.activateSseFallback(i)}},r)}else this.options.fallback!=="none"?this.activateSseFallback(new Error("Connection lost. Max WS retries exceeded.")):(this.state="disconnected",this.notifyStateChange(),this.notifyError(new Error("Connection lost. Max retries exceeded.")))}async restoreSubscriptions(e,t,s){for(let[r,i]of e)try{this.log(`Restoring subscription: ${r}`);let n=this.generateRequestId(),o=await this.sendRequest({category:r,action:"subscribe",request_id:n}),c={category:o.category,persist:o.persist,historyCount:o.history_count,readReceipt:o.read_receipt};this.subscriptions.set(r,{info:c,handlers:i}),this.log(`Restored subscription: ${r}`)}catch(n){this.logError(`Failed to restore subscription for ${r}`,n),this.notifyError(new Error(`Failed to restore subscription: ${r}`))}for(let[r,i]of t)try{this.log(`Restoring presence subscription: ${r}`);let n=this.generateRequestId();await this.sendRequest({category:"",action:"presence_subscribe",data:{user_id:r},request_id:n}),this.presenceSubscriptions.set(r,i),this.log(`Restored presence subscription: ${r}`)}catch(n){this.logError(`Failed to restore presence subscription for ${r}`,n)}for(let[r,i]of s)try{this.log(`Restoring typing subscription: ${r}`);let n=this.generateRequestId();await this.sendRequest({category:"",action:"typing_subscribe",data:{room_id:r},request_id:n}),this.typingHandlers.set(r,i),this.log(`Restored typing subscription: ${r}`)}catch(n){this.logError(`Failed to restore typing subscription for ${r}`,n)}}assertBidirectional(e){if(this.activeTransport==="sse")throw new Error(`realtime.${e}() requires a WebSocket connection, which is blocked on this network. Only AI streaming (realtime.stream) works in SSE fallback mode (cb.realtime.transport === 'sse').`)}sendRequest(e){return new Promise((t,s)=>{if(!this.ws||this.ws.readyState!==WebSocket.OPEN){s(new Error("Not connected"));return}let r=setTimeout(()=>{this.pendingRequests.delete(e.request_id),s(new Error("Request timeout"))},this.options.timeout);this.pendingRequests.set(e.request_id,{resolve:t,reject:s,timeout:r}),this.ws.send(JSON.stringify(e))})}sendRaw(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("Not connected");this.ws.send(JSON.stringify(e))}notifyStateChange(){this.stateHandlers.forEach(e=>e(this.state))}notifyError(e){this.errorHandlers.forEach(t=>t(e))}generateClientId(){return`cb_${Math.random().toString(36).substring(2,15)}`}generateRequestId(){return`req_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}};var O=class{constructor(e){this.http=e}ensureServerAuth(e){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error(`cb.roles.${e}() \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.`)}async list(e){return this.ensureServerAuth("list"),this.http.get(`/v1/apps/${e}/app-roles`)}async get(e,t){return this.ensureServerAuth("get"),this.http.get(`/v1/apps/${e}/app-roles/${t}`)}async create(e,t){return this.ensureServerAuth("create"),this.http.post(`/v1/apps/${e}/app-roles`,{role_title:t.title,role_description:t.description,selected_permission_ids:t.permissionIds??[]})}async update(e,t,s){this.ensureServerAuth("update"),await this.http.put(`/v1/apps/${e}/app-roles/${t}`,{role_title:s.title,role_description:s.description,selected_permission_ids:s.permissionIds,selected_user_ids:s.userIds})}async assign(e,t,s){this.ensureServerAuth("assign");let r=await this.http.get(`/v1/apps/${e}/app-roles/${t}`);await this.http.put(`/v1/apps/${e}/app-roles/${t}`,{role_title:r.role_name,role_description:r.role_description,selected_permission_ids:r.permission_item.map(i=>i.id),selected_user_ids:s})}async delete(e,t){this.ensureServerAuth("delete"),await this.http.delete(`/v1/apps/${e}/app-roles/${t}`)}};function I(a={}){let e=new AbortController,t=a.timeout??3e4,s=a.signal,r=null,i=null;return s&&(s.aborted?e.abort(s.reason):(i=()=>e.abort(s.reason),s.addEventListener("abort",i,{once:!0}))),t>0&&Number.isFinite(t)&&(r=setTimeout(()=>{e.abort(new DOMException(`Request timed out after ${t}ms`,"TimeoutError"))},t)),{signal:e.signal,cleanup:()=>{r!==null&&clearTimeout(r),s&&i&&s.removeEventListener("abort",i)}}}var ye=new Set(["localhost","127.0.0.1","::1"]),Ie=2048;function Ce(a,e={}){let t=e.context??"external URL";if(typeof a!="string"||a.length===0)throw new g(400,`${t}: empty URL`,"INVALID_PRESIGNED_URL");if(a.length>Ie)throw new g(400,`${t}: URL exceeds ${Ie} chars`,"INVALID_PRESIGNED_URL");let s;try{s=new URL(a)}catch{throw new g(400,`${t}: cannot parse URL`,"INVALID_PRESIGNED_URL")}let r=e.allowLocalhost&&ye.has(s.hostname);if(s.protocol!=="https:"&&!(r&&s.protocol==="http:"))throw new g(400,`${t}: scheme must be https (got ${s.protocol})`,"INVALID_PRESIGNED_URL");if(!e.allowLocalhost&&ye.has(s.hostname))throw new g(400,`${t}: localhost is not allowed in production`,"INVALID_PRESIGNED_URL");if(e.allowedHosts&&e.allowedHosts.length>0){let i=s.hostname;if(!e.allowedHosts.some(o=>o===i?!0:i.endsWith(`.${o}`)))throw new g(400,`${t}: host ${i} is not in allowlist`,"INVALID_PRESIGNED_URL")}return s}function Ee(){if(typeof window>"u")return!1;let a=window.location.hostname;return ye.has(a)}var re=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async uploadToPresigned(e,t,s){let i=Ce(e,{allowLocalhost:Ee(),context:"storage.presigned-url"}).toString();if(s?.onProgress&&typeof XMLHttpRequest<"u")return this.putViaXhr(i,t,s.onProgress,s);let{signal:n,cleanup:o}=I({timeout:s?.timeout,signal:s?.signal});try{s?.onProgress?.({loaded:0,total:t.size,percentage:0});let c=await fetch(i,{method:"PUT",body:t,headers:{"Content-Type":t.type||"application/octet-stream"},signal:n});if(!c.ok)throw new g(c.status,`Upload failed: ${c.statusText}`,"PRESIGNED_UPLOAD_FAILED");s?.onProgress?.({loaded:t.size,total:t.size,percentage:100})}finally{o()}}putViaXhr(e,t,s,r){return new Promise((i,n)=>{let o=r?.signal;if(o?.aborted){n(o.reason??new DOMException("Aborted","AbortError"));return}let c=new XMLHttpRequest;c.open("PUT",e,!0),c.setRequestHeader("Content-Type",t.type||"application/octet-stream");let l=r?.timeout??3e4;l>0&&Number.isFinite(l)&&(c.timeout=l);let d=null,p=()=>{o&&d&&o.removeEventListener("abort",d)};o&&(d=()=>c.abort(),o.addEventListener("abort",d,{once:!0})),c.upload.onprogress=u=>{if(!u.lengthComputable)return;let f=u.total||t.size;s({loaded:u.loaded,total:f,percentage:f>0?Math.round(u.loaded/f*100):0})},c.onload=()=>{p(),c.status>=200&&c.status<300?(s({loaded:t.size,total:t.size,percentage:100}),i()):n(new g(c.status,`Upload failed: ${c.statusText||"unknown error"}`,"PRESIGNED_UPLOAD_FAILED"))},c.onerror=()=>{p(),n(new g(0,"Upload failed: network error","PRESIGNED_UPLOAD_FAILED"))},c.ontimeout=()=>{p(),n(new DOMException(`Request timed out after ${l}ms`,"TimeoutError"))},c.onabort=()=>{p(),n(o?.reason??new DOMException("Aborted","AbortError"))},c.send(t)})}async getFiles(e,t){let s=this.getPublicPrefix(),r=t?`?parent_id=${encodeURIComponent(t)}`:"";return(await this.http.get(`${s}/storages/files/${e}/items${r}`)).files}async uploadFile(e,t,s){let r=typeof s=="string"?{parentId:s}:s??{},i=this.getPublicPrefix(),n=await this.http.post(`${i}/storages/files/${e}/presigned-url`,{file_name:t.name,file_size:t.size,mime_type:t.type||"application/octet-stream",parent_id:r.parentId});await this.uploadToPresigned(n.upload_url,t,{onProgress:r.onProgress,signal:r.signal,timeout:r.timeout});let o=await this.http.post(`${i}/storages/files/${e}/complete-upload`,{file_id:n.file_id});return{id:o.id,name:o.name,path:o.path,type:o.type,mime_type:o.mime_type,size:o.size,url:o.url,parent_id:o.parent_id,created_at:o.created_at}}async uploadFiles(e,t,s){let r=[];for(let i of t){let n=await this.uploadFile(e,i,s);r.push(n)}return r}async createFolder(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/storages/files/${e}/folders`,t)}async deleteFile(e,t){let s=this.getPublicPrefix();await this.http.delete(`${s}/storages/files/${e}/items/${t}`)}async moveFile(e,t,s){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error("storage.moveFile requires JWT (console token) auth; not available with Public Key alone.");await this.http.post(`/v1/storages/files/${e}/items/${t}/move`,s)}async renameFile(e,t,s){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error("storage.renameFile requires JWT (console token) auth; not available with Public Key alone.");return this.http.patch(`/v1/storages/files/${e}/items/${t}/rename`,s)}getFileUrl(e){return e.url||null}isImageFile(e){return e.mime_type?.startsWith("image/")||!1}async uploadByPath(e,t,s,r){let i=this.getPublicPrefix(),n=t.startsWith("/")?t.slice(1):t,o=r?.overwrite!==!1,c=await this.http.post(`${i}/storages/files/${e}/presigned-url/path/${n}`,{file_name:s.name,file_size:s.size,mime_type:s.type||"application/octet-stream",overwrite:o});await this.uploadToPresigned(c.upload_url,s,{onProgress:r?.onProgress,signal:r?.signal,timeout:r?.timeout});let l=await this.http.post(`${i}/storages/files/${e}/complete-upload`,{file_id:c.file_id});return{id:l.id,name:l.name,path:l.path,type:l.type,mime_type:l.mime_type,size:l.size,url:l.url,parent_id:l.parent_id,created_at:l.created_at}}async getByPath(e,t){let s=this.getPublicPrefix(),r=t.startsWith("/")?t.slice(1):t;return this.http.get(`${s}/storages/files/${e}/path/${r}`)}async getUrlByPath(e,t){try{return(await this.getByPath(e,t)).url||null}catch{return null}}async setPageMeta(e,t){let s=this.getPublicPrefix();return this.http.put(`${s}/storages/webs/${e}/page-metas`,t)}async batchSetPageMeta(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/storages/webs/${e}/page-metas/batch`,t)}async listPageMetas(e,t){let s=this.getPublicPrefix(),r=new URLSearchParams;t?.limit!=null&&r.set("limit",String(t.limit)),t?.offset!=null&&r.set("offset",String(t.offset));let i=r.toString();return this.http.get(`${s}/storages/webs/${e}/page-metas${i?`?${i}`:""}`)}async getPageMeta(e,t){let s=this.getPublicPrefix(),r=encodeURIComponent(t);return this.http.get(`${s}/storages/webs/${e}/page-metas/get?path=${r}`)}async deletePageMeta(e,t){let s=this.getPublicPrefix(),r=encodeURIComponent(t);await this.http.delete(`${s}/storages/webs/${e}/page-metas?path=${r}`)}async deleteAllPageMetas(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/storages/webs/${e}/page-metas/all`)}};var ie=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async issueBillingKey(){let e=this.getPublicPrefix();return this.http.post(`${e}/subscriptions/billing-keys`,{})}async confirmBillingKey(e){let t=this.getPublicPrefix();return this.http.post(`${t}/subscriptions/billing-keys/confirm`,e)}async listBillingKeys(e){let t=this.getPublicPrefix(),s=e?`?customer_id=${e}`:"";return this.http.get(`${t}/subscriptions/billing-keys${s}`)}async getBillingKey(e){let t=this.getPublicPrefix();return this.http.get(`${t}/subscriptions/billing-keys/${e}`)}async updateBillingKey(e,t){let s=this.getPublicPrefix();return this.http.patch(`${s}/subscriptions/billing-keys/${e}`,t)}async deleteBillingKey(e){let t=this.getPublicPrefix();return this.http.delete(`${t}/subscriptions/billing-keys/${e}`)}async create(e){let t=this.getPublicPrefix(),s=await this.http.post(`${t}/subscriptions`,e);return k(s,{id:{type:"string-or-number"},status:{type:"string"}},"subscription.create"),s}async list(e){let t=this.getPublicPrefix(),s=new URLSearchParams;e?.status&&s.set("status",e.status),e?.limit&&s.set("limit",String(e.limit)),e?.offset&&s.set("offset",String(e.offset));let r=s.toString();return this.http.get(`${t}/subscriptions${r?`?${r}`:""}`)}async get(e){let t=this.getPublicPrefix();return this.http.get(`${t}/subscriptions/${e}`)}async update(e,t){let s=this.getPublicPrefix();return this.http.patch(`${s}/subscriptions/${e}`,t)}async pause(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/subscriptions/${e}/pause`,t||{})}async resume(e){let t=this.getPublicPrefix();return this.http.post(`${t}/subscriptions/${e}/resume`,{})}async cancel(e,t={}){let s=this.getPublicPrefix(),r={reason:t.reason,cancel_at_end:!t.immediate};return this.http.post(`${s}/subscriptions/${e}/cancel`,r)}async listPayments(e,t){let s=this.getPublicPrefix(),r=new URLSearchParams;t?.status&&r.set("status",t.status),t?.limit&&r.set("limit",String(t.limit)),t?.offset&&r.set("offset",String(t.offset));let i=r.toString();return this.http.get(`${s}/subscriptions/${e}/payments${i?`?${i}`:""}`)}async chargeWithBillingKey(e){let t=this.getPublicPrefix();return this.http.post(`${t}/subscriptions/charge`,e)}};var ne=class{constructor(e){this.http=e}async reportIssue(e){let t={title:e.title,body:e.body,category:e.category,metadata:e.metadata};return e.anonymousEmail&&(t.anonymous_email=e.anonymousEmail),e.recaptchaToken&&(t.recaptcha_token=e.recaptchaToken),this.http.post("/v1/public/reports",t)}async reportIssueOnBehalfOfUser(e){if(!e.targetAppId)throw new Error("targetAppId is required");if(!e.accessToken)throw new Error("accessToken is required");let t={title:e.title,body:e.body};e.category&&(t.category=e.category),e.metadata&&(t.metadata=e.metadata);let s=`${this.http.getBaseUrl()}/v1/integrations/providers/${encodeURIComponent(e.targetAppId)}/issues/by-user`,r=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.accessToken}`},body:JSON.stringify(t)});if(!r.ok){let i;try{i=await r.json()}catch{i={error:r.statusText}}let n=i??{},o=n.error_description??n.error??`HTTP ${r.status}`;throw new Error(`reportIssueOnBehalfOfUser failed (${r.status}): ${o}`)}return await r.json()}async reportPlatformBug(e){let t=e.attachAutomaticContext!==!1,s={title:e.title,body:e.body,category:e.category??"other",severity:e.severity??"medium",metadata:e.metadata};if(e.reporterEmail&&(s.reporter_email=e.reporterEmail),e.recaptchaToken&&(s.recaptcha_token=e.recaptchaToken),t){s.sdk_version=e.sdkVersion??We(),s.sdk_platform=e.sdkPlatform??Ke(),s.environment=e.environment??"unknown",e.error?s.stack_trace=xe(e.error.stack??String(e.error)):e.stackTrace&&(s.stack_trace=xe(e.stackTrace));let r=e.recentApiCalls??this.http.getRecentCalls();r.length>0&&(s.recent_api_calls=r)}return this.http.post("/v1/public/platform-issues",s)}getRecentApiCalls(){return this.http.getRecentCalls()}clearRecentApiCalls(){this.http.clearRecentCalls()}async listPlatformIssueReplies(e){return(await this.http.get(`/v1/public/platform-issues/${e}/comments`)).comments}async replyToPlatformIssue(e,t){return this.http.post(`/v1/public/platform-issues/${e}/comments`,{body:t})}async getPlatformIssue(e){return this.http.get(`/v1/public/platform-issues/${e}`)}async listMyPlatformIssues(e={}){let t=new URLSearchParams;for(let r of e.status??[])t.append("status",r);for(let r of e.severity??[])t.append("severity",r);for(let r of e.category??[])t.append("category",r);e.sinceUpdatedAt&&t.set("since_updated_at",e.sinceUpdatedAt),e.cursor&&t.set("cursor",e.cursor),typeof e.limit=="number"&&t.set("limit",String(e.limit));let s=t.toString();return this.http.get(`/v1/public/platform-issues${s?`?${s}`:""}`)}};function We(){return typeof __SDK_VERSION__<"u"&&__SDK_VERSION__?__SDK_VERSION__:"unknown"}function Ke(){return typeof window<"u"&&typeof document<"u"?"web":"node"}function xe(a){let e=a;e=e.replace(/(\(|\s|^)(?:file:\/\/)?\/[^\s)]+\/([^\s/)]+)(:\d+:\d+)?/g,"$1$2$3"),e=e.replace(/(https?:\/\/[^\s)?]+)\?[^\s)]*/g,"$1");let t=32*1024;return e.length>t&&(e=`${e.slice(0,t)}
|
|
4
|
+
`).filter(l=>l.trim());for(let l of c)try{let d=JSON.parse(l);this.handleServerMessage(d,()=>{i||(i=!0,clearTimeout(n),this.log("Connected successfully"),e())})}catch(d){this.logError("Failed to parse message",{line:l,error:d})}},this.ws.onclose=o=>{this.log(`WebSocket closed: code=${o.code}, reason=${o.reason}`),!i&&this.state==="connecting"&&(i=!0,clearTimeout(n),t(new Error(`Connection closed: ${o.reason||"unknown reason"}`))),(this.state==="connected"||this.state==="connecting")&&this.handleDisconnect()},this.ws.onerror=o=>{this.log("WebSocket error occurred"),this.logError("WebSocket error",o),this.notifyError(new Error("WebSocket connection error")),!i&&this.state==="connecting"&&(i=!0,clearTimeout(n),t(new Error("Failed to connect")))}}catch(o){i=!0,clearTimeout(n),t(o)}})}log(e){this.options.debug&&console.log(`[Realtime] ${e}`)}logError(e,t){this.options.debug&&console.error(`[Realtime] ${e}`,t)}handleServerMessage(e,t){switch(e.event){case"connected":{let s=e.data;this._connectionId=s.connection_id,this._appId=s.app_id,this.state="connected",this.activeTransport="ws",this.sseFallbackActive=!1,this.retryCount=0,this.notifyStateChange(),t&&t();break}case"subscribed":case"unsubscribed":case"sent":case"result":case"history":{if(e.request_id){let s=this.pendingRequests.get(e.request_id);s&&(clearTimeout(s.timeout),s.resolve(e.data),this.pendingRequests.delete(e.request_id))}break}case"message":{let s=e.data,r=this.subscriptions.get(s.category);if(r){let i={id:s.id,category:s.category,from:s.from,data:s.data,sentAt:s.sent_at};r.handlers.forEach(n=>n(i))}break}case"error":{if(e.request_id){let s=this.pendingRequests.get(e.request_id);s&&(clearTimeout(s.timeout),s.reject(new Error(e.error||"Unknown error")),this.pendingRequests.delete(e.request_id))}else this.notifyError(new Error(e.error||"Unknown error"));break}case"pong":break;case"stream_token":{let s=e.data,r=this.streamSessions.get(s.session_id);r?.handlers.onToken&&r.handlers.onToken(s.token,s.index);break}case"stream_done":{let s=e.data,r=this.streamSessions.get(s.session_id);r?.handlers.onDone&&r.handlers.onDone({sessionId:s.session_id,fullText:s.full_text,totalTokens:s.total_tokens,promptTokens:s.prompt_tokens,duration:s.duration_ms}),this.streamSessions.delete(s.session_id);break}case"stream_tool_call":{let s=e.data,r=this.streamSessions.get(s.session_id);r?.handlers.onToolCall&&r.handlers.onToolCall(s.tool_name,s.arguments||{},s.index);break}case"stream_tool_result":{let s=e.data,r=this.streamSessions.get(s.session_id);r?.handlers.onToolResult&&r.handlers.onToolResult(s.tool_name,s.success,s.duration_ms,s.index);break}case"stream_error":{let s=e.data;if(e.request_id){for(let[r,i]of this.streamSessions)if(i.requestId===e.request_id){i.handlers.onError&&i.handlers.onError(new Error(s.message)),this.streamSessions.delete(r);break}}break}case"presence":case"presence_status":{let s=e.data,r={userId:s.user_id,status:s.status,lastSeen:s.last_seen,device:s.device,metadata:s.metadata,eventType:s.event_type};this.presenceHandlers.forEach(n=>n(r));let i=this.presenceSubscriptions.get(s.user_id);if(i&&i.forEach(n=>n(r)),e.request_id){let n=this.pendingRequests.get(e.request_id);n&&(clearTimeout(n.timeout),n.resolve(e.data),this.pendingRequests.delete(e.request_id))}break}case"typing":{let s=e.data,r={roomId:s.room_id,users:s.users},i=this.typingHandlers.get(s.room_id);i&&i.forEach(n=>n(r));break}case"read_receipt":{let s=e.data,r={category:s.category,messageIds:s.message_ids,readerId:s.reader_id,readAt:s.read_at},i=this.readReceiptHandlers.get(s.category);i&&i.forEach(n=>n(r));break}}}handleDisconnect(){if(this.sseFallbackActive){this.ws=null;return}this.ws=null,this._connectionId=null,this.streamSessions.forEach(r=>{r.handlers.onError&&r.handlers.onError(new Error("Connection lost"))}),this.streamSessions.clear();let e=new Map;for(let[r,i]of this.subscriptions)e.set(r,[...i.handlers]);this.subscriptions.clear();let t=new Map;for(let[r,i]of this.presenceSubscriptions)t.set(r,[...i]);this.presenceSubscriptions.clear();let s=new Map;for(let[r,i]of this.typingHandlers)s.set(r,[...i]);if(this.typingHandlers.clear(),this.retryCount<this.options.maxRetries){this.state="reconnecting",this.notifyStateChange(),this.retryCount++;let r=Math.min(this.options.retryInterval*2**(this.retryCount-1),3e4);setTimeout(async()=>{if(!this.sseFallbackActive)try{await this.doConnect(),this.log("Reconnected successfully, restoring subscriptions..."),await this.restoreSubscriptions(e,t,s)}catch(i){this.logError("Reconnect failed",i),this.retryCount>=this.options.maxRetries&&this.options.fallback!=="none"&&!this.sseFallbackActive&&this.activateSseFallback(i)}},r)}else this.options.fallback!=="none"?this.activateSseFallback(new Error("Connection lost. Max WS retries exceeded.")):(this.state="disconnected",this.notifyStateChange(),this.notifyError(new Error("Connection lost. Max retries exceeded.")))}async restoreSubscriptions(e,t,s){for(let[r,i]of e)try{this.log(`Restoring subscription: ${r}`);let n=this.generateRequestId(),o=await this.sendRequest({category:r,action:"subscribe",request_id:n}),c={category:o.category,persist:o.persist,historyCount:o.history_count,readReceipt:o.read_receipt};this.subscriptions.set(r,{info:c,handlers:i}),this.log(`Restored subscription: ${r}`)}catch(n){this.logError(`Failed to restore subscription for ${r}`,n),this.notifyError(new Error(`Failed to restore subscription: ${r}`))}for(let[r,i]of t)try{this.log(`Restoring presence subscription: ${r}`);let n=this.generateRequestId();await this.sendRequest({category:"",action:"presence_subscribe",data:{user_id:r},request_id:n}),this.presenceSubscriptions.set(r,i),this.log(`Restored presence subscription: ${r}`)}catch(n){this.logError(`Failed to restore presence subscription for ${r}`,n)}for(let[r,i]of s)try{this.log(`Restoring typing subscription: ${r}`);let n=this.generateRequestId();await this.sendRequest({category:"",action:"typing_subscribe",data:{room_id:r},request_id:n}),this.typingHandlers.set(r,i),this.log(`Restored typing subscription: ${r}`)}catch(n){this.logError(`Failed to restore typing subscription for ${r}`,n)}}assertBidirectional(e){if(this.activeTransport==="sse")throw new Error(`realtime.${e}() requires a WebSocket connection, which is blocked on this network. Only AI streaming (realtime.stream) works in SSE fallback mode (cb.realtime.transport === 'sse').`)}sendRequest(e){return new Promise((t,s)=>{if(!this.ws||this.ws.readyState!==WebSocket.OPEN){s(new Error("Not connected"));return}let r=setTimeout(()=>{this.pendingRequests.delete(e.request_id),s(new Error("Request timeout"))},this.options.timeout);this.pendingRequests.set(e.request_id,{resolve:t,reject:s,timeout:r}),this.ws.send(JSON.stringify(e))})}sendRaw(e){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("Not connected");this.ws.send(JSON.stringify(e))}notifyStateChange(){this.stateHandlers.forEach(e=>e(this.state))}notifyError(e){this.errorHandlers.forEach(t=>t(e))}generateClientId(){return`cb_${Math.random().toString(36).substring(2,15)}`}generateRequestId(){return`req_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}};var O=class{constructor(e){this.http=e}ensureServerAuth(e){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error(`cb.roles.${e}() \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.`)}async list(e){return this.ensureServerAuth("list"),this.http.get(`/v1/apps/${e}/app-roles`)}async get(e,t){return this.ensureServerAuth("get"),this.http.get(`/v1/apps/${e}/app-roles/${t}`)}async create(e,t){return this.ensureServerAuth("create"),this.http.post(`/v1/apps/${e}/app-roles`,{role_title:t.title,role_description:t.description,selected_permission_ids:t.permissionIds??[]})}async update(e,t,s){this.ensureServerAuth("update"),await this.http.put(`/v1/apps/${e}/app-roles/${t}`,{role_title:s.title,role_description:s.description,selected_permission_ids:s.permissionIds,selected_user_ids:s.userIds})}async assign(e,t,s){this.ensureServerAuth("assign");let r=await this.http.get(`/v1/apps/${e}/app-roles/${t}`);await this.http.put(`/v1/apps/${e}/app-roles/${t}`,{role_title:r.role_name,role_description:r.role_description,selected_permission_ids:r.permission_item.map(i=>i.id),selected_user_ids:s})}async delete(e,t){this.ensureServerAuth("delete"),await this.http.delete(`/v1/apps/${e}/app-roles/${t}`)}};function I(a={}){let e=new AbortController,t=a.timeout??3e4,s=a.signal,r=null,i=null;return s&&(s.aborted?e.abort(s.reason):(i=()=>e.abort(s.reason),s.addEventListener("abort",i,{once:!0}))),t>0&&Number.isFinite(t)&&(r=setTimeout(()=>{e.abort(new DOMException(`Request timed out after ${t}ms`,"TimeoutError"))},t)),{signal:e.signal,cleanup:()=>{r!==null&&clearTimeout(r),s&&i&&s.removeEventListener("abort",i)}}}var ye=new Set(["localhost","127.0.0.1","::1"]),Ie=2048;function Ce(a,e={}){let t=e.context??"external URL";if(typeof a!="string"||a.length===0)throw new g(400,`${t}: empty URL`,"INVALID_PRESIGNED_URL");if(a.length>Ie)throw new g(400,`${t}: URL exceeds ${Ie} chars`,"INVALID_PRESIGNED_URL");let s;try{s=new URL(a)}catch{throw new g(400,`${t}: cannot parse URL`,"INVALID_PRESIGNED_URL")}let r=e.allowLocalhost&&ye.has(s.hostname);if(s.protocol!=="https:"&&!(r&&s.protocol==="http:"))throw new g(400,`${t}: scheme must be https (got ${s.protocol})`,"INVALID_PRESIGNED_URL");if(!e.allowLocalhost&&ye.has(s.hostname))throw new g(400,`${t}: localhost is not allowed in production`,"INVALID_PRESIGNED_URL");if(e.allowedHosts&&e.allowedHosts.length>0){let i=s.hostname;if(!e.allowedHosts.some(o=>o===i?!0:i.endsWith(`.${o}`)))throw new g(400,`${t}: host ${i} is not in allowlist`,"INVALID_PRESIGNED_URL")}return s}function Ee(){if(typeof window>"u")return!1;let a=window.location.hostname;return ye.has(a)}var re=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async uploadToPresigned(e,t,s){let i=Ce(e,{allowLocalhost:Ee(),context:"storage.presigned-url"}).toString();if(s?.onProgress&&typeof XMLHttpRequest<"u")return this.putViaXhr(i,t,s.onProgress,s);let{signal:n,cleanup:o}=I({timeout:s?.timeout,signal:s?.signal});try{s?.onProgress?.({loaded:0,total:t.size,percentage:0});let c=await fetch(i,{method:"PUT",body:t,headers:{"Content-Type":t.type||"application/octet-stream"},signal:n});if(!c.ok)throw new g(c.status,`Upload failed: ${c.statusText}`,"PRESIGNED_UPLOAD_FAILED");s?.onProgress?.({loaded:t.size,total:t.size,percentage:100})}finally{o()}}putViaXhr(e,t,s,r){return new Promise((i,n)=>{let o=r?.signal;if(o?.aborted){n(o.reason??new DOMException("Aborted","AbortError"));return}let c=new XMLHttpRequest;c.open("PUT",e,!0),c.setRequestHeader("Content-Type",t.type||"application/octet-stream");let l=r?.timeout??3e4;l>0&&Number.isFinite(l)&&(c.timeout=l);let d=null,p=()=>{o&&d&&o.removeEventListener("abort",d)};o&&(d=()=>c.abort(),o.addEventListener("abort",d,{once:!0})),c.upload.onprogress=u=>{if(!u.lengthComputable)return;let f=u.total||t.size;s({loaded:u.loaded,total:f,percentage:f>0?Math.round(u.loaded/f*100):0})},c.onload=()=>{p(),c.status>=200&&c.status<300?(s({loaded:t.size,total:t.size,percentage:100}),i()):n(new g(c.status,`Upload failed: ${c.statusText||"unknown error"}`,"PRESIGNED_UPLOAD_FAILED"))},c.onerror=()=>{p(),n(new g(0,"Upload failed: network error","PRESIGNED_UPLOAD_FAILED"))},c.ontimeout=()=>{p(),n(new DOMException(`Request timed out after ${l}ms`,"TimeoutError"))},c.onabort=()=>{p(),n(o?.reason??new DOMException("Aborted","AbortError"))},c.send(t)})}async getFiles(e,t){let s=this.getPublicPrefix(),r=t?`?parent_id=${encodeURIComponent(t)}`:"";return(await this.http.get(`${s}/storages/files/${e}/items${r}`)).files}async uploadFile(e,t,s){let r=typeof s=="string"?{parentId:s}:s??{},i=this.getPublicPrefix(),n=await this.http.post(`${i}/storages/files/${e}/presigned-url`,{file_name:t.name,file_size:t.size,mime_type:t.type||"application/octet-stream",parent_id:r.parentId});await this.uploadToPresigned(n.upload_url,t,{onProgress:r.onProgress,signal:r.signal,timeout:r.timeout});let o=await this.http.post(`${i}/storages/files/${e}/complete-upload`,{file_id:n.file_id});return{id:o.id,name:o.name,path:o.path,type:o.type,mime_type:o.mime_type,size:o.size,url:o.url,parent_id:o.parent_id,created_at:o.created_at}}async uploadFiles(e,t,s){let r=[];for(let i of t){let n=await this.uploadFile(e,i,s);r.push(n)}return r}async createFolder(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/storages/files/${e}/folders`,t)}async deleteFile(e,t){let s=this.getPublicPrefix();await this.http.delete(`${s}/storages/files/${e}/items/${t}`)}async moveFile(e,t,s){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error("storage.moveFile requires JWT (console token) auth; not available with Public Key alone.");await this.http.post(`/v1/storages/files/${e}/items/${t}/move`,s)}async renameFile(e,t,s){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error("storage.renameFile requires JWT (console token) auth; not available with Public Key alone.");return this.http.patch(`/v1/storages/files/${e}/items/${t}/rename`,s)}getFileUrl(e){return e.url||null}isImageFile(e){return e.mime_type?.startsWith("image/")||!1}async uploadByPath(e,t,s,r){let i=this.getPublicPrefix(),n=t.startsWith("/")?t.slice(1):t,o=r?.overwrite!==!1,c=await this.http.post(`${i}/storages/files/${e}/presigned-url/path/${n}`,{file_name:s.name,file_size:s.size,mime_type:s.type||"application/octet-stream",overwrite:o});await this.uploadToPresigned(c.upload_url,s,{onProgress:r?.onProgress,signal:r?.signal,timeout:r?.timeout});let l=await this.http.post(`${i}/storages/files/${e}/complete-upload`,{file_id:c.file_id});return{id:l.id,name:l.name,path:l.path,type:l.type,mime_type:l.mime_type,size:l.size,url:l.url,parent_id:l.parent_id,created_at:l.created_at}}async getByPath(e,t){let s=this.getPublicPrefix(),r=t.startsWith("/")?t.slice(1):t;return this.http.get(`${s}/storages/files/${e}/path/${r}`)}async getUrlByPath(e,t){try{return(await this.getByPath(e,t)).url||null}catch{return null}}async setPageMeta(e,t){let s=this.getPublicPrefix();return this.http.put(`${s}/storages/webs/${e}/page-metas`,t)}async batchSetPageMeta(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/storages/webs/${e}/page-metas/batch`,t)}async listPageMetas(e,t){let s=this.getPublicPrefix(),r=new URLSearchParams;t?.limit!=null&&r.set("limit",String(t.limit)),t?.offset!=null&&r.set("offset",String(t.offset));let i=r.toString();return this.http.get(`${s}/storages/webs/${e}/page-metas${i?`?${i}`:""}`)}async getPageMeta(e,t){let s=this.getPublicPrefix(),r=encodeURIComponent(t);return this.http.get(`${s}/storages/webs/${e}/page-metas/get?path=${r}`)}async deletePageMeta(e,t){let s=this.getPublicPrefix(),r=encodeURIComponent(t);await this.http.delete(`${s}/storages/webs/${e}/page-metas?path=${r}`)}async deleteAllPageMetas(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/storages/webs/${e}/page-metas/all`)}};var ie=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async issueBillingKey(){let e=this.getPublicPrefix();return this.http.post(`${e}/subscriptions/billing-keys`,{})}async confirmBillingKey(e){let t=this.getPublicPrefix();return this.http.post(`${t}/subscriptions/billing-keys/confirm`,e)}async listBillingKeys(e){let t=this.getPublicPrefix(),s=e?`?customer_id=${e}`:"";return this.http.get(`${t}/subscriptions/billing-keys${s}`)}async getBillingKey(e){let t=this.getPublicPrefix();return this.http.get(`${t}/subscriptions/billing-keys/${e}`)}async updateBillingKey(e,t){let s=this.getPublicPrefix();return this.http.patch(`${s}/subscriptions/billing-keys/${e}`,t)}async deleteBillingKey(e){let t=this.getPublicPrefix();return this.http.delete(`${t}/subscriptions/billing-keys/${e}`)}async create(e){let t=this.getPublicPrefix(),s=await this.http.post(`${t}/subscriptions`,e);return k(s,{id:{type:"string-or-number"},status:{type:"string"}},"subscription.create"),s}async list(e){let t=this.getPublicPrefix(),s=new URLSearchParams;e?.status&&s.set("status",e.status),e?.limit&&s.set("limit",String(e.limit)),e?.offset&&s.set("offset",String(e.offset));let r=s.toString();return this.http.get(`${t}/subscriptions${r?`?${r}`:""}`)}async get(e){let t=this.getPublicPrefix();return this.http.get(`${t}/subscriptions/${e}`)}async update(e,t){let s=this.getPublicPrefix();return this.http.patch(`${s}/subscriptions/${e}`,t)}async pause(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/subscriptions/${e}/pause`,t||{})}async resume(e){let t=this.getPublicPrefix();return this.http.post(`${t}/subscriptions/${e}/resume`,{})}async cancel(e,t={}){let s=this.getPublicPrefix(),r={reason:t.reason,cancel_at_end:!t.immediate};return this.http.post(`${s}/subscriptions/${e}/cancel`,r)}async changePlan(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/subscriptions/${e}/change-plan`,t)}async previewChangePlan(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/subscriptions/${e}/change-plan/preview`,t)}async listPayments(e,t){let s=this.getPublicPrefix(),r=new URLSearchParams;t?.status&&r.set("status",t.status),t?.limit&&r.set("limit",String(t.limit)),t?.offset&&r.set("offset",String(t.offset));let i=r.toString();return this.http.get(`${s}/subscriptions/${e}/payments${i?`?${i}`:""}`)}async chargeWithBillingKey(e){let t=this.getPublicPrefix();return this.http.post(`${t}/subscriptions/charge`,e)}};var ne=class{constructor(e){this.http=e}async reportIssue(e){let t={title:e.title,body:e.body,category:e.category,metadata:e.metadata};return e.anonymousEmail&&(t.anonymous_email=e.anonymousEmail),e.recaptchaToken&&(t.recaptcha_token=e.recaptchaToken),this.http.post("/v1/public/reports",t)}async reportIssueOnBehalfOfUser(e){if(!e.targetAppId)throw new Error("targetAppId is required");if(!e.accessToken)throw new Error("accessToken is required");let t={title:e.title,body:e.body};e.category&&(t.category=e.category),e.metadata&&(t.metadata=e.metadata);let s=`${this.http.getBaseUrl()}/v1/integrations/providers/${encodeURIComponent(e.targetAppId)}/issues/by-user`,r=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.accessToken}`},body:JSON.stringify(t)});if(!r.ok){let i;try{i=await r.json()}catch{i={error:r.statusText}}let n=i??{},o=n.error_description??n.error??`HTTP ${r.status}`;throw new Error(`reportIssueOnBehalfOfUser failed (${r.status}): ${o}`)}return await r.json()}async reportPlatformBug(e){let t=e.attachAutomaticContext!==!1,s={title:e.title,body:e.body,category:e.category??"other",severity:e.severity??"medium",metadata:e.metadata};if(e.reporterEmail&&(s.reporter_email=e.reporterEmail),e.recaptchaToken&&(s.recaptcha_token=e.recaptchaToken),t){s.sdk_version=e.sdkVersion??We(),s.sdk_platform=e.sdkPlatform??Ke(),s.environment=e.environment??"unknown",e.error?s.stack_trace=xe(e.error.stack??String(e.error)):e.stackTrace&&(s.stack_trace=xe(e.stackTrace));let r=e.recentApiCalls??this.http.getRecentCalls();r.length>0&&(s.recent_api_calls=r)}return this.http.post("/v1/public/platform-issues",s)}getRecentApiCalls(){return this.http.getRecentCalls()}clearRecentApiCalls(){this.http.clearRecentCalls()}async listPlatformIssueReplies(e){return(await this.http.get(`/v1/public/platform-issues/${e}/comments`)).comments}async replyToPlatformIssue(e,t){return this.http.post(`/v1/public/platform-issues/${e}/comments`,{body:t})}async getPlatformIssue(e){return this.http.get(`/v1/public/platform-issues/${e}`)}async listMyPlatformIssues(e={}){let t=new URLSearchParams;for(let r of e.status??[])t.append("status",r);for(let r of e.severity??[])t.append("severity",r);for(let r of e.category??[])t.append("category",r);e.sinceUpdatedAt&&t.set("since_updated_at",e.sinceUpdatedAt),e.cursor&&t.set("cursor",e.cursor),typeof e.limit=="number"&&t.set("limit",String(e.limit));let s=t.toString();return this.http.get(`/v1/public/platform-issues${s?`?${s}`:""}`)}};function We(){return typeof __SDK_VERSION__<"u"&&__SDK_VERSION__?__SDK_VERSION__:"unknown"}function Ke(){return typeof window<"u"&&typeof document<"u"?"web":"node"}function xe(a){let e=a;e=e.replace(/(\(|\s|^)(?:file:\/\/)?\/[^\s)]+\/([^\s/)]+)(:\d+:\d+)?/g,"$1$2$3"),e=e.replace(/(https?:\/\/[^\s)?]+)\?[^\s)]*/g,"$1");let t=32*1024;return e.length>t&&(e=`${e.slice(0,t)}
|
|
5
5
|
\u2026[truncated]`),e}var $e=5*1024*1024,L=class extends Error{constructor(e,t){super(e),this.name="VideoProcessingError",this.video=t}},oe=class{constructor(e,t){this.http=e;this.storage={create:async e=>this.http.post("/v1/public/storages/videos",e),list:async()=>this.http.get("/v1/public/storages/videos"),get:async e=>this.http.get(`/v1/public/storages/videos/${e}`),update:async(e,t)=>this.http.put(`/v1/public/storages/videos/${e}`,t),delete:async e=>{await this.http.delete(`/v1/public/storages/videos/${e}`)},upload:async(e,t,s)=>{let r=await this.http.post(`/v1/public/storages/videos/${e}/uploads/init`,{filename:t.name,file_size:t.size,mime_type:t.type}),i=r.chunk_size||$e,n=Math.ceil(t.size/i),o=0,c=Date.now(),l=0;for(let p=0;p<n;p++){let u=p*i,f=Math.min(u+i,t.size),y=t.slice(u,f),h=new FormData;h.append("chunk",y),await this.http.put(`/v1/public/storages/videos/${e}/uploads/${r.upload_id}/chunk?chunk_index=${p}`,h),o++;let w=Date.now(),R=(w-c)/1e3,v=f,S=v-l,m=R>0?S/R:0;c=w,l=v,s.onProgress&&s.onProgress({phase:"uploading",uploadedChunks:o,totalChunks:n,percentage:Math.round(o/n*100),currentSpeed:m})}let d=await this.http.post(`/v1/public/storages/videos/${e}/uploads/${r.upload_id}/complete`,{title:s.title,description:s.description,visibility:s.visibility||"private",tags:s.tags});return this.storage.getVideo(e,d.video_id)},listVideos:async(e,t)=>{let s=new URLSearchParams;t?.status&&s.set("status",t.status),t?.visibility&&s.set("visibility",t.visibility),t?.search&&s.set("search",t.search),t?.page&&s.set("page",String(t.page)),t?.limit&&s.set("limit",String(t.limit));let r=s.toString();return this.http.get(`/v1/public/storages/videos/${e}/videos${r?`?${r}`:""}`)},getVideo:async(e,t)=>this.http.get(`/v1/public/storages/videos/${e}/videos/${t}`),deleteVideo:async(e,t)=>{await this.http.delete(`/v1/public/storages/videos/${e}/videos/${t}`)},getStreamUrl:async(e,t)=>this.http.get(`/v1/public/storages/videos/${e}/videos/${t}/stream`),getTranscodeStatus:async(e,t)=>this.http.get(`/v1/public/storages/videos/${e}/videos/${t}/transcode`)};this.videoBaseUrl=t||this.getDefaultVideoUrl()}getDefaultVideoUrl(){if(typeof window<"u"){let e=window.location.hostname;if(e==="localhost"||e==="127.0.0.1")return"http://localhost:8089"}return"https://video.connectbase.world"}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async videoFetch(e,t,s){let r={},i=this.http.getPublicKey();i&&(r["X-Public-Key"]=i);let n=this.http.getAccessToken();n&&(r.Authorization=`Bearer ${n}`),s&&!(s instanceof FormData)&&(r["Content-Type"]="application/json");let{signal:o,cleanup:c}=I({timeout:3e4});try{let l=await fetch(`${this.videoBaseUrl}${t}`,{method:e,headers:r,body:s instanceof FormData?s:s?JSON.stringify(s):void 0,signal:o});if(!l.ok){let d=await l.json().catch(()=>({message:l.statusText})),p=d.error;if(p&&typeof p=="object"&&"message"in p)throw new g(l.status,p.message||"Unknown error",p.code,p.details);let u=typeof p=="string"?p:d.message||"Unknown error";throw new g(l.status,u)}return l.status===204||l.headers.get("content-length")==="0"?{}:await l.json()}finally{c()}}async upload(e,t){let s=this.getPublicPrefix(),r=await this.videoFetch("POST",`${s}/uploads/init`,{filename:e.name,file_size:e.size,mime_type:e.type}),i=r.chunk_size||$e,n=Math.ceil(e.size/i),o=0,l=Date.now(),d=0;for(let u=0;u<n;u++){let f=u*i,y=Math.min(f+i,e.size),h=e.slice(f,y),w=new FormData;w.append("chunk",h),await this.videoFetch("PUT",`${s}/uploads/${r.upload_id}/chunk?chunk_index=${u}`,w),o++;let R=Date.now(),v=(R-l)/1e3,S=y,m=S-d,P=v>0?m/v:0;l=R,d=S,t.onProgress&&t.onProgress({phase:"uploading",uploadedChunks:o,totalChunks:n,percentage:Math.round(o/n*100),currentSpeed:P})}let p=await this.videoFetch("POST",`${s}/uploads/${r.upload_id}/complete`,{title:t.title,description:t.description,visibility:t.visibility||"private",tags:t.tags});return this.get(p.video_id)}async waitForReady(e,t){let s=t?.timeout||18e5,r=t?.interval||5e3,i=Date.now(),n=this.getPublicPrefix();for(;Date.now()-i<s;){let o=await this.videoFetch("GET",`${n}/videos/${e}`);if(o.status==="ready")return o;if(o.status==="failed")throw new L("Video processing failed",o);if(t?.onProgress){let c=o.qualities.filter(d=>d.status==="ready").length,l=o.qualities.length||1;t.onProgress({phase:"processing",uploadedChunks:0,totalChunks:0,percentage:Math.round(c/l*100)})}await new Promise(c=>setTimeout(c,r))}throw new L("Timeout waiting for video to be ready")}async list(e){let t=this.getPublicPrefix(),s=new URLSearchParams;e?.status&&s.set("status",e.status),e?.visibility&&s.set("visibility",e.visibility),e?.search&&s.set("search",e.search),e?.channel_id&&s.set("channel_id",e.channel_id),e?.page&&s.set("page",String(e.page)),e?.limit&&s.set("limit",String(e.limit));let r=s.toString();return this.videoFetch("GET",`${t}/videos${r?`?${r}`:""}`)}async get(e){let t=this.getPublicPrefix();return this.videoFetch("GET",`${t}/videos/${e}`)}async update(e,t){let s=this.getPublicPrefix();return this.videoFetch("PATCH",`${s}/videos/${e}`,t)}async delete(e){let t=this.getPublicPrefix();await this.videoFetch("DELETE",`${t}/videos/${e}`)}async getStreamUrl(e,t){let s=this.getPublicPrefix(),r=t?`?quality=${t}`:"";return this.videoFetch("GET",`${s}/videos/${e}/stream-url${r}`)}async getThumbnails(e){let t=this.getPublicPrefix(),s=await this.videoFetch("GET",`${t}/videos/${e}/thumbnail`),r=new Set;s.thumbnail_url&&r.add(s.thumbnail_url);for(let i of s.thumbnail_urls??[])i&&r.add(i);return[...r]}async getTranscodeStatus(e){let t=this.getPublicPrefix();return this.videoFetch("GET",`${t}/videos/${e}/transcode/status`)}async retryTranscode(e){let t=this.getPublicPrefix();await this.videoFetch("POST",`${t}/videos/${e}/transcode/retry`,{})}async createChannel(e){let t=this.getPublicPrefix();return this.videoFetch("POST",`${t}/channels`,e)}async getChannel(e){let t=this.getPublicPrefix();return this.videoFetch("GET",`${t}/channels/${e}`)}async getChannelByHandle(e){let t=this.getPublicPrefix();return this.videoFetch("GET",`${t}/channels/handle/${e}`)}async updateChannel(e,t){let s=this.getPublicPrefix();return this.videoFetch("PATCH",`${s}/channels/${e}`,t)}async subscribeChannel(e){let t=this.getPublicPrefix();await this.videoFetch("POST",`${t}/channels/${e}/subscribe`,{})}async unsubscribeChannel(e){let t=this.getPublicPrefix();await this.videoFetch("DELETE",`${t}/channels/${e}/subscribe`)}async createPlaylist(e,t){let s=this.getPublicPrefix();return this.videoFetch("POST",`${s}/playlists`,{...t,channel_id:e})}async getPlaylists(e){let t=this.getPublicPrefix();return(await this.videoFetch("GET",`${t}/playlists/public?channel_id=${encodeURIComponent(e)}`)).playlists}async getPlaylistItems(e){let t=this.getPublicPrefix();return(await this.videoFetch("GET",`${t}/playlists/${e}`)).items}async addToPlaylist(e,t,s){let r=this.getPublicPrefix();return this.videoFetch("POST",`${r}/playlists/${e}/items`,{video_id:t,position:s})}async removeFromPlaylist(e,t){let s=this.getPublicPrefix();await this.videoFetch("DELETE",`${s}/playlists/${e}/items/${t}`)}async getShortsFeed(e){let t=this.getPublicPrefix(),s=new URLSearchParams;e?.cursor&&s.set("cursor",e.cursor),e?.limit&&s.set("limit",String(e.limit));let r=s.toString();return this.videoFetch("GET",`${t}/shorts${r?`?${r}`:""}`)}async getTrendingShorts(e){let t=this.getPublicPrefix(),s=e?`?limit=${e}`:"";return this.videoFetch("GET",`${t}/shorts/trending${s}`)}async getShorts(e){let t=this.getPublicPrefix();return this.videoFetch("GET",`${t}/shorts/${e}`)}async getComments(e,t){let s=this.getPublicPrefix(),r=new URLSearchParams;t?.cursor&&r.set("cursor",t.cursor),t?.limit&&r.set("limit",String(t.limit)),t?.sort&&r.set("sort",t.sort);let i=r.toString();return this.videoFetch("GET",`${s}/videos/${e}/comments${i?`?${i}`:""}`)}async postComment(e,t,s){let r=this.getPublicPrefix();return this.videoFetch("POST",`${r}/comments`,{video_id:e,content:t,parent_id:s})}async deleteComment(e){let t=this.getPublicPrefix();await this.videoFetch("DELETE",`${t}/comments/${e}`)}async likeVideo(e){let t=this.getPublicPrefix();await this.videoFetch("POST",`${t}/videos/${e}/like`,{})}async unlikeVideo(e){let t=this.getPublicPrefix();await this.videoFetch("DELETE",`${t}/videos/${e}/like`)}async getWatchHistory(e){let t=this.getPublicPrefix(),s=new URLSearchParams;e?.cursor&&s.set("cursor",e.cursor),e?.limit&&s.set("limit",String(e.limit));let r=s.toString();return this.videoFetch("GET",`${t}/watch-history${r?`?${r}`:""}`)}async clearWatchHistory(){let e=this.getPublicPrefix();await this.videoFetch("DELETE",`${e}/watch-history`)}async reportWatchProgress(e,t,s){let r=this.getPublicPrefix();await this.videoFetch("POST",`${r}/videos/${e}/watch-progress`,{position:t,duration:s})}async getMembershipTiers(e){let t=this.getPublicPrefix();return(await this.videoFetch("GET",`${t}/channels/${e}/membership/tiers`)).tiers}async joinMembership(e,t){let s=this.getPublicPrefix();return this.videoFetch("POST",`${s}/membership/join`,{tier_id:t})}async cancelMembership(e,t){let s=this.getPublicPrefix();await this.videoFetch("POST",`${s}/membership/${t}/cancel`,{})}async sendSuperChat(e,t,s){let r=this.getPublicPrefix();return this.videoFetch("POST",`${r}/super-chats`,{channel_id:e,type:s?.type??"super_thanks",amount:t,video_id:s?.videoId,live_id:s?.liveId,currency:s?.currency??"USD",message:s?.message,sticker_id:s?.stickerId})}async getSuperChats(e){let t=this.getPublicPrefix();return(await this.videoFetch("GET",`${t}/videos/${e}/super-chats`)).super_chats}async getRecommendations(e){let t=this.getPublicPrefix(),s=e?`?limit=${e}`:"";return(await this.videoFetch("GET",`${t}/recommendations/feed${s}`)).videos}async getHomeFeed(e){let t=this.getPublicPrefix(),s=e?`?limit=${e}`:"";return(await this.videoFetch("GET",`${t}/recommendations/feed${s}`)).videos}async getRelatedVideos(e,t){let s=this.getPublicPrefix(),r=t?`?limit=${t}`:"";return(await this.videoFetch("GET",`${s}/videos/${e}/related${r}`)).videos}async getTrendingVideos(e){let t=this.getPublicPrefix(),s=e?`?limit=${e}`:"";return(await this.videoFetch("GET",`${t}/recommendations/trending${s}`)).videos}async submitFeedback(e,t){let s=this.getPublicPrefix();if(t==="not_interested"){await this.videoFetch("POST",`${s}/recommendations/not-interested/${e}`,{});return}await this.videoFetch("POST",`${s}/recommendations/interactions`,{video_id:e,type:"like"})}};var ae=class{constructor(e,t,s){this.ws=null;this.state="disconnected";this.stateListeners=[];this.errorListeners=[];this.peerJoinedListeners=[];this.peerLeftListeners=[];this.remoteStreamListeners=[];this.reconnectAttempts=0;this.maxReconnectAttempts=5;this.reconnectTimeout=null;this.currentRoomId=null;this.currentPeerId=null;this.currentUserId=null;this.isBroadcaster=!1;this.localStream=null;this.channelType="interactive";this.peerConnections=new Map;this.remoteStreams=new Map;this.iceServers=[];this.http=e,this.webrtcUrl=t,this.appId=s}async getICEServers(){if(!this.appId)throw new Error("WebRTC getICEServers \uC5D0\uB294 appId \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. ConnectBase \uCD08\uAE30\uD654 \uC2DC appId \uB97C \uC124\uC815\uD558\uC138\uC694.");let e=await this.http.get(`/v1/apps/${this.appId}/ice-servers`);return this.iceServers=e.ice_servers,e.ice_servers}async connect(e){if(this.state==="connected"||this.state==="connecting")throw new Error("\uC774\uBBF8 \uC5F0\uACB0\uB418\uC5B4 \uC788\uAC70\uB098 \uC5F0\uACB0 \uC911\uC785\uB2C8\uB2E4");if(this.setState("connecting"),this.currentRoomId=e.roomId,this.currentUserId=e.userId||null,this.isBroadcaster=e.isBroadcaster||!1,this.localStream=e.localStream||null,this.iceServers.length===0)try{await this.getICEServers()}catch{this.iceServers=[{urls:"stun:stun.l.google.com:19302"}]}return this.connectWebSocket()}connectWebSocket(){return new Promise((e,t)=>{let s=this.buildWebSocketUrl();this.ws=new WebSocket(s);let r=setTimeout(()=>{this.state==="connecting"&&(this.ws?.close(),t(new Error("\uC5F0\uACB0 \uC2DC\uAC04 \uCD08\uACFC")))},1e4);this.ws.onopen=()=>{clearTimeout(r),this.reconnectAttempts=0,this.sendSignaling({type:"join",room_id:this.currentRoomId,data:{user_id:this.currentUserId,is_broadcaster:this.isBroadcaster}})},this.ws.onmessage=async i=>{try{let n=JSON.parse(i.data);await this.handleSignalingMessage(n,e,t)}catch(n){console.error("Failed to parse signaling message:",n)}},this.ws.onerror=i=>{clearTimeout(r),console.error("WebSocket error:",i),this.emitError(new Error("WebSocket \uC5F0\uACB0 \uC624\uB958"))},this.ws.onclose=i=>{clearTimeout(r),this.state==="connecting"&&t(new Error("\uC5F0\uACB0\uC774 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4")),this.handleDisconnect(i)}})}buildWebSocketUrl(){let e=this.webrtcUrl.replace("https://","wss://").replace("http://","ws://"),t=this.http.getPublicKey(),s=this.http.getAccessToken(),r="";if(s?r=`access_token=${encodeURIComponent(s)}`:t&&(r=`public_key=${encodeURIComponent(t)}`),!this.appId)throw new Error("WebRTC \uC5F0\uACB0\uC5D0\uB294 appId\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. ConnectBase \uCD08\uAE30\uD654 \uC2DC appId\uB97C \uC124\uC815\uD558\uC138\uC694.");return`${e}/v1/apps/${this.appId}/signaling?${r}`}async handleSignalingMessage(e,t,s){switch(e.type){case"joined":if(this.setState("connected"),this.currentPeerId=e.peer_id||null,e.data&&typeof e.data=="object"){let r=e.data;r.channel_type&&(this.channelType=r.channel_type);let i=r.peers||[];for(let n of i)n.peer_id!==this.currentPeerId&&await this.createPeerConnection(n.peer_id,!0)}t?.();break;case"peer_joined":if(e.peer_id&&e.peer_id!==this.currentPeerId){let r={peer_id:e.peer_id,...typeof e.data=="object"?e.data:{}};this.emitPeerJoined(e.peer_id,r),await this.createPeerConnection(e.peer_id,!1)}break;case"peer_left":e.peer_id&&(this.closePeerConnection(e.peer_id),this.emitPeerLeft(e.peer_id));break;case"offer":e.peer_id&&e.sdp&&await this.handleOffer(e.peer_id,e.sdp);break;case"answer":e.peer_id&&e.sdp&&await this.handleAnswer(e.peer_id,e.sdp);break;case"ice_candidate":e.peer_id&&e.candidate&&await this.handleICECandidate(e.peer_id,e.candidate);break;case"error":{let r=typeof e.data=="string"?e.data:"Unknown error",i=new Error(r);this.emitError(i),s?.(i);break}}}async createPeerConnection(e,t){this.closePeerConnection(e);let s={iceServers:this.iceServers.map(i=>({urls:i.urls,username:i.username,credential:i.credential}))},r=new RTCPeerConnection(s);if(this.peerConnections.set(e,r),this.localStream&&this.localStream.getTracks().forEach(i=>{r.addTrack(i,this.localStream)}),r.onicecandidate=i=>{i.candidate&&this.sendSignaling({type:"ice_candidate",target_id:e,candidate:i.candidate.toJSON()})},r.ontrack=i=>{let[n]=i.streams;n&&(this.remoteStreams.set(e,n),this.emitRemoteStream(e,n))},r.onconnectionstatechange=()=>{r.connectionState==="failed"&&(console.warn(`Peer connection failed: ${e}`),this.closePeerConnection(e))},t){let i=await r.createOffer();await r.setLocalDescription(i),this.sendSignaling({type:"offer",target_id:e,sdp:i.sdp})}return r}async handleOffer(e,t){let s=this.peerConnections.get(e);s||(s=await this.createPeerConnection(e,!1)),await s.setRemoteDescription(new RTCSessionDescription({type:"offer",sdp:t}));let r=await s.createAnswer();await s.setLocalDescription(r),this.sendSignaling({type:"answer",target_id:e,sdp:r.sdp})}async handleAnswer(e,t){let s=this.peerConnections.get(e);s&&await s.setRemoteDescription(new RTCSessionDescription({type:"answer",sdp:t}))}async handleICECandidate(e,t){let s=this.peerConnections.get(e);if(s)try{await s.addIceCandidate(new RTCIceCandidate(t))}catch(r){console.warn("Failed to add ICE candidate:",r)}}closePeerConnection(e){let t=this.peerConnections.get(e);t&&(t.close(),this.peerConnections.delete(e)),this.remoteStreams.delete(e)}sendSignaling(e){this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify(e))}handleDisconnect(e){let t=this.state==="connected";this.setState("disconnected"),this.peerConnections.forEach((s,r)=>{s.close(),this.emitPeerLeft(r)}),this.peerConnections.clear(),this.remoteStreams.clear(),t&&e.code!==1e3&&this.reconnectAttempts<this.maxReconnectAttempts&&this.attemptReconnect()}attemptReconnect(){this.reconnectAttempts++,this.setState("reconnecting");let e=Math.min(5e3*2**(this.reconnectAttempts-1),3e4);this.reconnectTimeout=setTimeout(async()=>{try{await this.connectWebSocket()}catch{this.reconnectAttempts<this.maxReconnectAttempts?this.attemptReconnect():(this.setState("failed"),this.emitError(new Error("\uC7AC\uC5F0\uACB0 \uC2E4\uD328: \uCD5C\uB300 \uC2DC\uB3C4 \uD69F\uC218 \uCD08\uACFC")))}},e)}disconnect(){this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),this.ws&&this.ws.readyState===WebSocket.OPEN&&(this.sendSignaling({type:"leave"}),this.ws.close(1e3,"User disconnected")),this.peerConnections.forEach(e=>e.close()),this.peerConnections.clear(),this.remoteStreams.clear(),this.ws=null,this.currentRoomId=null,this.currentPeerId=null,this.localStream=null,this.setState("disconnected")}getState(){return this.state}getRoomId(){return this.currentRoomId}getPeerId(){return this.currentPeerId}getChannelType(){return this.channelType}getRemoteStream(e){return this.remoteStreams.get(e)}getAllRemoteStreams(){return new Map(this.remoteStreams)}replaceLocalStream(e){this.localStream=e,this.peerConnections.forEach(t=>{let s=t.getSenders();e.getTracks().forEach(r=>{let i=s.find(n=>n.track?.kind===r.kind);i?i.replaceTrack(r):t.addTrack(r,e)})})}setAudioEnabled(e){this.localStream&&this.localStream.getAudioTracks().forEach(t=>{t.enabled=e})}setVideoEnabled(e){this.localStream&&this.localStream.getVideoTracks().forEach(t=>{t.enabled=e})}onStateChange(e){return this.stateListeners.push(e),()=>{this.stateListeners=this.stateListeners.filter(t=>t!==e)}}onError(e){return this.errorListeners.push(e),()=>{this.errorListeners=this.errorListeners.filter(t=>t!==e)}}onPeerJoined(e){return this.peerJoinedListeners.push(e),()=>{this.peerJoinedListeners=this.peerJoinedListeners.filter(t=>t!==e)}}onPeerLeft(e){return this.peerLeftListeners.push(e),()=>{this.peerLeftListeners=this.peerLeftListeners.filter(t=>t!==e)}}onRemoteStream(e){return this.remoteStreamListeners.push(e),()=>{this.remoteStreamListeners=this.remoteStreamListeners.filter(t=>t!==e)}}setState(e){this.state!==e&&(this.state=e,this.stateListeners.forEach(t=>t(e)))}emitError(e){this.errorListeners.forEach(t=>t(e))}emitPeerJoined(e,t){this.peerJoinedListeners.forEach(s=>s(e,t))}emitPeerLeft(e){this.peerLeftListeners.forEach(t=>t(e))}emitRemoteStream(e,t){this.remoteStreamListeners.forEach(s=>s(e,t))}async validate(){if(!this.appId)throw new Error("WebRTC \uAC80\uC99D\uC5D0\uB294 appId\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. ConnectBase \uCD08\uAE30\uD654 \uC2DC appId\uB97C \uC124\uC815\uD558\uC138\uC694.");return this.http.get(`/v1/apps/${this.appId}/validate`)}async getStats(e){return this.http.get(`/v1/apps/${e}/stats`)}async getRooms(e){return this.http.get(`/v1/apps/${e}/rooms`)}};var ce=class{constructor(e=20){this.buf=[];this.capacity=Math.min(Math.max(1,e),50)}push(e){this.buf.length>=this.capacity&&this.buf.shift(),this.buf.push(e)}snapshot(){return this.buf.slice()}clear(){this.buf=[]}};function Ae(a){try{return(a.startsWith("http")?new URL(a):new URL(a,"http://x")).pathname||a}catch{let e=a.indexOf("?");return e>=0?a.slice(0,e):a}}function Ue(a){return a.split("?")[0].startsWith("/v1/public/")?"omit":"include"}var Me="cb_auth_tokens";function ve(a){try{let e=a.split(".")[1];if(!e)return null;let t=e.replace(/-/g,"+").replace(/_/g,"/"),s=t+"=".repeat((4-t.length%4)%4),r=JSON.parse(atob(s));return r&&typeof r=="object"?r:null}catch{return null}}function Oe(a){let e=ve(a)?.role;return e==="User"||e==="AdminInvite"}var le=class{constructor(e){this.isRefreshing=!1;this.refreshPromise=null;this.refreshFailureCount=0;this.refreshLockedUntil=0;this.recentCalls=new ce;this.bootRestorePromise=null;this.bootRestoreAlwaysAwait=!1;this.config={...e},this.storageKey=this.buildStorageKey(),this.warnIfUnsafePersistence(),this.restoreTokens()}setBootRestorePromise(e,t){this.bootRestorePromise=e.catch(()=>!1),this.bootRestoreAlwaysAwait=t?.alwaysAwait===!0}getRecentCalls(){return this.recentCalls.snapshot()}clearRecentCalls(){this.recentCalls.clear()}warnIfUnsafePersistence(){typeof window>"u"||(this.config.persistence==="localStorage"?console.warn(`[connect-base-client] persistence="localStorage" \uB294 XSS \uC2DC refresh token \uC601\uAD6C \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uAE30\uBCF8\uAC12('none') \uC744 \uC0AC\uC6A9\uD558\uBA74 \uC11C\uBC84\uAC00 \uBC1C\uAE09\uD55C HttpOnly cookie \uB85C\uB9CC refresh token \uC774 \uBCF4\uAD00\uB418\uC5B4 JS \uAC00 \uC811\uADFC\uD560 \uC218 \uC5C6\uACE0, \uC0C8\uB85C\uACE0\uCE68 \uD6C4\uC5D0\uB3C4 autoRestoreSession \uC73C\uB85C \uC790\uB3D9 \uBCF5\uAD6C\uB429\uB2C8\uB2E4.`):this.config.persistence==="sessionStorage"&&console.warn(`[connect-base-client] persistence="sessionStorage" \uB294 XSS \uC2DC \uD0ED \uC138\uC158 \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uAE30\uBCF8\uAC12('none') + HttpOnly cookie \uD750\uB984\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.`))}updateConfig(e){this.config={...this.config,...e}}setTokens(e,t){this.config.accessToken=e,this.config.refreshToken=t,this.persistTokens(),this.markSessionHint()}clearTokens(){this.config.accessToken=void 0,this.config.refreshToken=void 0,this.removePersistedTokens(),this.clearSessionHint()}buildSessionHintKey(){return`${this.buildStorageKey()}:has_session`}markSessionHint(){if(!(typeof window>"u"))try{localStorage.setItem(this.buildSessionHintKey(),"1")}catch{}}clearSessionHint(){if(!(typeof window>"u"))try{localStorage.removeItem(this.buildSessionHintKey())}catch{}}hasSessionHint(){if(typeof window>"u")return!1;try{return localStorage.getItem(this.buildSessionHintKey())==="1"}catch{return!0}}async bootstrapRefreshCookie(){if(!(typeof window>"u")&&this.persistence==="none"&&this.config.refreshToken)try{await this.refreshAccessToken()}catch{}}get persistence(){return this.config.persistence??"none"}getStorage(){return typeof window>"u"?null:this.persistence==="localStorage"&&typeof localStorage<"u"?localStorage:this.persistence==="sessionStorage"&&typeof sessionStorage<"u"?sessionStorage:null}getPersistenceStorage(){return this.getStorage()}buildStorageKey(){let e=this.config.publicKey??this.config.secretKey;if(!e)return Me;let t=0;for(let s=0;s<e.length;s++)t=(t<<5)-t+e.charCodeAt(s),t=t&t;return`${Me}_${Math.abs(t).toString(36)}`}persistTokens(){if(this.persistence==="none")return;let e=this.getStorage();!e||!this.config.accessToken||!this.config.refreshToken||e.setItem(this.storageKey,JSON.stringify({accessToken:this.config.accessToken,refreshToken:this.config.refreshToken}))}restoreTokens(){if(this.persistence==="none"||this.config.accessToken&&this.config.refreshToken)return;let e=this.getStorage();if(!e)return;let t=e.getItem(this.storageKey);if(t)try{let{accessToken:s,refreshToken:r}=JSON.parse(t);s&&r&&(this.config.accessToken=s,this.config.refreshToken=r)}catch{e.removeItem(this.storageKey)}}removePersistedTokens(){if(this.persistence==="none")return;let e=this.getStorage();e&&e.removeItem(this.storageKey)}hasPublicKey(){return!!this.config.publicKey}getPublicKey(){return this.config.publicKey}hasSecretKey(){return!!this.config.secretKey}getSecretKey(){return this.config.secretKey}getCredential(){return this.config.publicKey??this.config.secretKey}getAccessToken(){return this.config.accessToken}hasJWT(){return!!this.config.accessToken}getBaseUrl(){return this.config.baseUrl}getAppId(){return this.config.appId}async refreshAccessToken(){if(this.isRefreshing)return this.refreshPromise;if(Date.now()<this.refreshLockedUntil){let t=new T("Token refresh locked due to repeated failures. Please login again.");throw this.emitError(t),this.config.onAuthError?.(t),t}if(this.isRefreshing=!0,!this.config.refreshToken&&typeof window>"u"){this.isRefreshing=!1,this.config.onTokenExpired?.();let t=new T("Refresh token is missing. Please login again.");throw this.emitError(t),this.config.onAuthError?.(t),t}return this.refreshPromise=(async()=>{let{signal:t,cleanup:s}=I({timeout:this.config.requestTimeoutMs??3e4}),r="transient";try{let i={"Content-Type":"application/json"};this.config.refreshToken&&(i.Authorization=`Bearer ${this.config.refreshToken}`);let n=this.getCredential();n&&(i["X-Public-Key"]=n);let o=await fetch(`${this.config.baseUrl}/v1/auth/re-issue`,{method:"POST",headers:i,credentials:"include",signal:t});if(!o.ok){let d=o.status,p;try{let u=await o.clone().json();u&&typeof u.error=="string"?p=u.error:u&&typeof u.code=="string"&&(p=u.code)}catch{}throw d>=500?new Error(`Token refresh failed (${d})`):(d===401||d===403||d===400&&(p==="invalid_grant"||p==="invalid_token")?r="permanent":r="client_bug",new Error(`Token refresh failed (${d}${p?` ${p}`:""})`))}let c=await o.json();if(!c||typeof c.access_token!="string")throw new Error("Token refresh response missing access_token");if(Oe(c.access_token))return this.refreshFailureCount=0,this.refreshLockedUntil=0,null;if(this.config.appId){let d=ve(c.access_token)?.app_id;if(typeof d=="string"&&d!==this.config.appId)return this.clearTokens(),this.refreshFailureCount=0,this.refreshLockedUntil=0,null}let l=typeof c.refresh_token=="string"&&c.refresh_token.length>0?c.refresh_token:this.config.refreshToken??"";return l?this.setTokens(c.access_token,l):(this.config.accessToken=c.access_token,this.persistTokens()),this.config.onTokenRefresh?.({accessToken:c.access_token,refreshToken:l}),this.refreshFailureCount=0,this.refreshLockedUntil=0,c.access_token}catch(i){let n=i instanceof Error?i.message:"Token refresh failed";this.refreshFailureCount++;let o=Math.min(500*2**Math.max(0,this.refreshFailureCount-1),3e4);if(this.refreshLockedUntil=Date.now()+o,r==="permanent"){this.clearTokens(),this.config.onTokenExpired?.();let l=new T(`${n}. Please login again.`);throw this.emitError(l),this.config.onAuthError?.(l),l}if(r==="client_bug"){let l=new T(`${n}. Client request invalid; tokens preserved.`);throw this.emitError(l),this.config.onAuthError?.(l),l}let c=new T(`${n}. Transient failure; tokens preserved, will retry after backoff.`);throw this.emitError(c),this.config.onTransientRefreshFailure?.(c),this.config.onAuthError?.(c),c}finally{s(),this.isRefreshing=!1,this.refreshPromise=null}})(),this.refreshPromise}async tryRestoreSessionFromCookie(){if(typeof window>"u")return!1;if(this.config.accessToken&&!this.isTokenExpired(this.config.accessToken))return!0;try{return!!await this.refreshAccessToken()}catch{return!1}}emitError(e){try{this.config.onError?.(e)}catch{}}isTokenExpired(e){let t=ve(e);if(!t||typeof t.exp!="number")return!0;let s=Date.now()/1e3;return t.exp<s+300}async prepareHeaders(e){let t=new Headers;t.set("Content-Type","application/json");let s=this.getCredential();if(s&&t.set("X-Public-Key",s),!e?.skipAuth&&!this.config.accessToken&&this.config.publicKey&&typeof window<"u"&&this.bootRestorePromise&&(this.bootRestoreAlwaysAwait||this.hasSessionHint()))try{await this.bootRestorePromise}catch{}if(!e?.skipAuth&&this.config.accessToken){let r=this.config.accessToken;if(this.isTokenExpired(r)&&this.config.refreshToken){let i=await this.refreshAccessToken();i&&(r=i)}Oe(r)||t.set("Authorization",`Bearer ${r}`)}else!e?.skipAuth&&this.config.publicKey&&this.config.secretKey&&!t.has("Authorization")&&t.set("Authorization",`Bearer ${this.config.secretKey}`);return e?.headers&&Object.entries(e.headers).forEach(([r,i])=>{t.set(r,i)}),t}async handleResponse(e){if(!e.ok){let t=await e.json().catch(()=>({message:e.statusText})),s=e.status===429?e.headers.get("Retry-After"):null,r;if(s){let l=Number.parseInt(s,10);if(Number.isFinite(l)&&l>=0)r=l;else{let d=Date.parse(s);Number.isFinite(d)&&(r=Math.max(0,Math.round((d-Date.now())/1e3)))}}let i=t.error;if(i&&typeof i=="object"&&"message"in i){let l={...i.details&&typeof i.details=="object"?i.details:{}};r!==void 0&&(l.retry_after_seconds=r);let d=new g(e.status,i.message||"Unknown error",i.code,Object.keys(l).length>0?l:i.details);throw this.emitError(d),d}let n=typeof i=="string"?i:t.message||"Unknown error",o=r!==void 0?{retry_after_seconds:r}:void 0,c=new g(e.status,n,void 0,o);throw this.emitError(c),c}return e.status===204||e.headers.get("content-length")==="0"?{}:e.json()}async doFetch(e,t,s){let r=Date.now(),i=0;try{let n=await this.tryFetchOnce(e,t,s);if(i=n.status,!n.ok&&n.status===401&&!s?.skipAuth&&this.config.publicKey&&typeof window<"u"&&await this.tryRestoreSessionFromCookie()){let c=await this.prepareHeaders(s);t.body instanceof FormData&&c.delete("Content-Type");let l=await this.tryFetchOnce(e,{...t,headers:c},s);return i=l.status,await this.handleResponse(l.response)}return await this.handleResponse(n.response)}finally{this.recentCalls.push({method:(t.method||"GET").toUpperCase(),path:Ae(e),status:i,duration_ms:Date.now()-r,timestamp:new Date().toISOString()})}}async tryFetchOnce(e,t,s){let{signal:r,cleanup:i}=I({timeout:s?.timeout??this.config.requestTimeoutMs??3e4,signal:s?.signal});try{let n=await fetch(`${this.config.baseUrl}${e}`,{...t,credentials:Ue(e),signal:r});return{response:n,ok:n.ok,status:n.status}}finally{i()}}async get(e,t){let s=await this.prepareHeaders(t);return this.doFetch(e,{method:"GET",headers:s},t)}async post(e,t,s){let r=await this.prepareHeaders(s);return t instanceof FormData&&r.delete("Content-Type"),this.doFetch(e,{method:"POST",headers:r,body:t instanceof FormData?t:JSON.stringify(t)},s)}async put(e,t,s){let r=await this.prepareHeaders(s);return t instanceof FormData&&r.delete("Content-Type"),this.doFetch(e,{method:"PUT",headers:r,body:t instanceof FormData?t:JSON.stringify(t)},s)}async patch(e,t,s){let r=await this.prepareHeaders(s);return this.doFetch(e,{method:"PATCH",headers:r,body:JSON.stringify(t)},s)}async delete(e,t){let s=await this.prepareHeaders(t);return this.doFetch(e,{method:"DELETE",headers:s},t)}async fetchRaw(e,t){let s=await this.prepareHeaders(),r=new Headers(s);return t?.headers&&new Headers(t.headers).forEach((n,o)=>r.set(o,n)),fetch(`${this.config.baseUrl}${e}`,{...t,credentials:Ue(e),headers:r})}};var we=class{constructor(e,t,s,r){this.type="webtransport";this.transport=null;this.writer=null;this.config=e,this.onMessage=t,this.onClose=s,this.onError=r}async connect(){let e=this.buildUrl();this.transport=new WebTransport(e),await this.transport.ready,this.config.useUnreliableDatagrams!==!1&&this.readDatagrams();let t=await this.transport.createBidirectionalStream();this.writer=t.writable.getWriter(),this.readStream(t.readable),this.transport.closed.then(()=>{this.onClose()}).catch(s=>{this.onError(s)})}buildUrl(){let t=(this.config.gameServerUrl||"https://game.connectbase.world").replace(/^ws/,"http").replace(/^http:/,"https:"),s=new URLSearchParams;return s.set("client_id",this.config.clientId),this.config.publicKey&&s.set("public_key",this.config.publicKey),this.config.accessToken&&s.set("token",this.config.accessToken),`${t}/v1/game/webtransport?${s.toString()}`}async readDatagrams(){if(!this.transport)return;let e=this.transport.datagrams.readable.getReader();try{for(;;){let{value:t,done:s}=await e.read();if(s)break;this.onMessage(t)}}catch{}}async readStream(e){let t=e.getReader(),s=new Uint8Array(0);try{for(;;){let{value:r,done:i}=await t.read();if(i)break;let n=new Uint8Array(s.length+r.length);for(n.set(s),n.set(r,s.length),s=n;s.length>=4;){let o=new DataView(s.buffer).getUint32(0,!0);if(s.length<4+o)break;let c=s.slice(4,4+o);s=s.slice(4+o),this.onMessage(c)}}}catch{}}disconnect(){this.transport&&(this.transport.close(),this.transport=null,this.writer=null)}send(e,t=!0){if(!this.transport)throw new b({code:"NOT_CONNECTED",message:"Not connected"});let s=typeof e=="string"?new TextEncoder().encode(e):e;if(t){if(this.writer){let r=new Uint8Array(4);new DataView(r.buffer).setUint32(0,s.length,!0);let i=new Uint8Array(4+s.length);i.set(r),i.set(s,4),this.writer.write(i)}}else{let r=this.config.maxDatagramSize||1200;s.length<=r?this.transport.datagrams.writable.getWriter().write(s):(console.warn("Datagram too large, falling back to reliable stream"),this.send(e,!0))}}isConnected(){return this.transport!==null}},de=class{constructor(e,t,s,r){this.type="websocket";this.ws=null;this.config=e,this.onMessage=t,this.onClose=s,this.onError=r}connect(){return new Promise((e,t)=>{let s=this.buildUrl();try{this.ws=new WebSocket(s),this.ws.binaryType="arraybuffer"}catch(c){t(c);return}let r=()=>{e()},i=()=>{this.onClose()},n=c=>{let l=new Error("WebSocket error");this.onError(l),t(l)},o=c=>{c.data instanceof ArrayBuffer?this.onMessage(new Uint8Array(c.data)):typeof c.data=="string"&&this.onMessage(new TextEncoder().encode(c.data))};this.ws.addEventListener("open",r,{once:!0}),this.ws.addEventListener("close",i),this.ws.addEventListener("error",n,{once:!0}),this.ws.addEventListener("message",o)})}buildUrl(){let t=(this.config.gameServerUrl||"wss://game.connectbase.world").replace(/^http/,"ws"),s=new URLSearchParams;s.set("client_id",this.config.clientId),this.config.publicKey&&s.set("public_key",this.config.publicKey),this.config.accessToken&&s.set("token",this.config.accessToken);let r=this.config.appId||"";return`${t}/v1/game/${r}/ws?${s.toString()}`}disconnect(){this.ws&&(this.ws.close(1e3,"Client disconnected"),this.ws=null)}send(e,t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new b({code:"NOT_CONNECTED",message:"Not connected"});typeof e=="string"?this.ws.send(e):this.ws.send(e)}isConnected(){return this.ws!==null&&this.ws.readyState===WebSocket.OPEN}};function Pe(){return typeof WebTransport<"u"}var pe=class{constructor(e){this.transport=null;this.handlers={};this.reconnectAttempts=0;this.reconnectTimer=null;this.pingInterval=null;this.actionSequence=0;this._roomId=null;this._state=null;this._scriptName=null;this._scriptVersion=null;this._isConnected=!1;this._connectionStatus="disconnected";this._lastError=null;this._latency=0;this._transportType="websocket";this.decoder=new TextDecoder;this.pendingHandlers=new Map;this.messageId=0;this.config={gameServerUrl:this.getDefaultGameServerUrl(),autoReconnect:!0,maxReconnectAttempts:5,reconnectInterval:1e3,connectionTimeout:1e4,transport:"auto",useUnreliableDatagrams:!0,...e}}getDefaultGameServerUrl(){if(typeof window<"u"){let e=window.location.hostname;if(e==="localhost"||e==="127.0.0.1")return"ws://localhost:8087"}return"wss://game.connectbase.world"}get transportType(){return this._transportType}get roomId(){return this._roomId}get state(){return this._state}get isConnected(){return this._isConnected}get scriptName(){return this._scriptName}get scriptVersion(){return this._scriptVersion}get connectionState(){return{status:this._connectionStatus,transport:this._transportType==="auto"?null:this._transportType,roomId:this._roomId,latency:this._latency,reconnectAttempt:this.reconnectAttempts,lastError:this._lastError||void 0}}get latency(){return this._latency}on(e,t){return this.handlers[e]=t,this}async connect(e){if(this.transport?.isConnected())return;this._connectionStatus=this.reconnectAttempts>0?"reconnecting":"connecting";let t=this.config.transport||"auto",s=(t==="webtransport"||t==="auto")&&Pe(),r=o=>{this.handleMessage(this.decoder.decode(o))},i=()=>{this._isConnected=!1,this._connectionStatus="disconnected",this.stopPingInterval(),this.handlers.onDisconnect?.(new CloseEvent("close")),this.config.autoReconnect&&(this._connectionStatus="reconnecting",this.scheduleReconnect(e))},n=o=>{this._connectionStatus="error",this._lastError=o,this.handlers.onError?.(new b({code:"CONNECTION_ERROR",message:o.message}))};if(s)try{this.transport=new we(this.config,r,i,n),await this.transport.connect(),this._transportType="webtransport"}catch{console.log("WebTransport failed, falling back to WebSocket"),this.transport=new de(this.config,r,i,n),await this.transport.connect(),this._transportType="websocket"}else this.transport=new de(this.config,r,i,n),await this.transport.connect(),this._transportType="websocket";this._isConnected=!0,this._connectionStatus="connected",this._lastError=null,this.reconnectAttempts=0,this.startPingInterval(),this.handlers.onConnect?.(),e&&await this.joinRoom(e)}disconnect(){this.stopPingInterval(),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.transport&&(this.transport.disconnect(),this.transport=null),this._isConnected=!1,this._connectionStatus="disconnected",this._roomId=null,this._state=null}async createRoom(e={}){return(await this.createRoomDetailed(e)).state}async createRoomDetailed(e={}){return new Promise((t,s)=>{let r=i=>{if(i.type==="room_created"){let n=i.data;this._roomId=n.room_id,this._state=n.initial_state,this._scriptName=n.script_name??null,this._scriptVersion=n.script_version??null,t({roomId:n.room_id,state:n.initial_state,scriptName:n.script_name,scriptVersion:n.script_version})}else i.type==="error"&&s(_(i))};this.sendWithHandler("create_room",F(e),r)})}async joinRoom(e,t){return new Promise((s,r)=>{let i=n=>{if(n.type==="room_joined"){let o=n.data;this._roomId=o.room_id,this._state=o.initial_state,this._scriptName=null,this._scriptVersion=null,s(o.initial_state)}else n.type==="error"&&r(_(n))};this.sendWithHandler("join_room",{room_id:e,metadata:t},i)})}async leaveRoom(){return new Promise((e,t)=>{if(!this._roomId){t(new b({code:"NOT_IN_ROOM",message:"Not in a room"}));return}let s=r=>{r.type==="room_left"?(this._roomId=null,this._state=null,this._scriptName=null,this._scriptVersion=null,e()):r.type==="error"&&t(_(r))};this.sendWithHandler("leave_room",{},s)})}sendAction(e,t=!1){if(!this._roomId)throw new b({code:"NOT_IN_ROOM",message:"Not in a room"});let s=JSON.stringify({type:"action",data:{type:e.type,data:e.data,client_timestamp:e.clientTimestamp??Date.now(),sequence:this.actionSequence++}}),r=t||this._transportType!=="webtransport";this.transport?.send(s,r)}sendChat(e){if(!this._roomId)throw new b({code:"NOT_IN_ROOM",message:"Not in a room"});this.send("chat",{message:e})}async requestState(){return new Promise((e,t)=>{if(!this._roomId){t(new b({code:"NOT_IN_ROOM",message:"Not in a room"}));return}let s=r=>{if(r.type==="state"){let i=r.data;this._state=i,e(i)}else r.type==="error"&&t(_(r))};this.sendWithHandler("get_state",{},s)})}async ping(){return new Promise((e,t)=>{let s=Date.now(),r=i=>{if(i.type==="pong"){let n=i.data,o=Date.now()-n.clientTimestamp;this._latency=o,this.handlers.onPong?.(n),e(o)}else i.type==="error"&&t(_(i))};this.sendWithHandler("ping",{timestamp:s},r)})}send(e,t,s){if(!this.transport?.isConnected())throw new b({code:"NOT_CONNECTED",message:"Not connected"});let r=JSON.stringify({type:e,data:t,msg_id:s});this.transport.send(r,!0)}sendWithHandler(e,t,s){let r=`msg_${this.messageId++}`;this.pendingHandlers.set(r,s),setTimeout(()=>{this.pendingHandlers.delete(r)},1e4),this.send(e,t,r)}handleMessage(e){try{let t=JSON.parse(e);if(t.msg_id&&this.pendingHandlers.has(t.msg_id)){let s=this.pendingHandlers.get(t.msg_id);this.pendingHandlers.delete(t.msg_id),s(t);return}switch(t.type){case"delta":this.handleDelta(t);break;case"state":this._state=t.data,this.handlers.onStateUpdate?.(this._state);break;case"player_event":this.handlePlayerEvent(t);break;case"chat":this.handlers.onChat?.({roomId:t.room_id||"",clientId:t.client_id||"",userId:t.user_id,message:t.message||"",serverTime:t.server_time||0});break;case"error":this.handlers.onError?.(_(t));break;case"room_stale":this.handlers.onRoomStale?this.handlers.onRoomStale({reason:t.reason||"unknown",roomId:t.room_id||"",scriptId:t.script_id,scriptVersion:t.script_version,serverTime:t.server_time||0}):console.warn("[connect-base game] room_stale received but onRoomStale handler not set:",{reason:t.reason,roomId:t.room_id,scriptVersion:t.script_version});break}}catch{console.error("Failed to parse game message:",e)}}handleDelta(e){let t=e.delta;if(!t)return;let s={fromVersion:t.from_version,toVersion:t.to_version,changes:t.changes.map(r=>({path:r.path,operation:r.operation,value:r.value,oldValue:r.old_value})),tick:t.tick};if(this._state){for(let r of s.changes)this.applyChange(r);this._state.version=s.toVersion}if(this.handlers.onAction){for(let r of s.changes)if(r.path.startsWith("actions.")&&r.operation==="set"&&r.value){let i=r.value;this.handlers.onAction({type:i.type||"",clientId:i.client_id||"",data:i.data,timestamp:i.timestamp||0})}}this.handlers.onDelta?.(s)}applyChange(e){if(!this._state)return;let t=e.path.split("."),s=this._state.state;for(let i=0;i<t.length-1;i++){let n=t[i];n in s||(s[n]={}),s=s[n]}let r=t[t.length-1];e.operation==="delete"?delete s[r]:s[r]=e.value}handlePlayerEvent(e){let t={clientId:e.player?.client_id||"",userId:e.player?.user_id,joinedAt:e.player?.joined_at||0,metadata:e.player?.metadata};e.event==="joined"?this.handlers.onPlayerJoined?.(t):e.event==="left"&&this.handlers.onPlayerLeft?.(t)}scheduleReconnect(e){if(this.reconnectAttempts>=(this.config.maxReconnectAttempts??5)){console.error("Max reconnect attempts reached");return}let t=Math.min((this.config.reconnectInterval??1e3)*2**this.reconnectAttempts,3e4);this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{console.log(`Reconnecting... (attempt ${this.reconnectAttempts})`),this.connect(e||this._roomId||void 0).catch(()=>{})},t)}startPingInterval(){this.pingInterval=setInterval(()=>{this.ping().catch(()=>{})},3e4)}stopPingInterval(){this.pingInterval&&(clearInterval(this.pingInterval),this.pingInterval=null)}};var ze="$auth.member_id";var je="https://api.connectbase.world",Je="https://socket.connectbase.world",Qe="https://webrtc.connectbase.world",Xe="https://video.connectbase.world",Ye="https://game.connectbase.world",ue=class{constructor(e={}){let t=i=>{if(!(typeof process>"u"||!process.env))return process.env[i]},s={baseUrl:e.baseUrl||t("CB_BASE_URL")||je,publicKey:e.publicKey,secretKey:e.secretKey,appId:e.appId,persistence:e.persistence,autoRestoreSession:e.autoRestoreSession,requestTimeoutMs:e.requestTimeoutMs,onError:e.onError,onTokenRefresh:e.onTokenRefresh,onAuthError:e.onAuthError,onTokenExpired:e.onTokenExpired,onTransientRefreshFailure:e.onTransientRefreshFailure};this.http=new le(s),this.auth=new W(this.http),this.database=new K(this.http),this.storage=new re(this.http),this.publicKey=new Z(this.http),this.functions=new j(this.http),this.realtime=new se(this.http,e.socketUrl||t("CB_SOCKET_URL")||Je),this.webrtc=new ae(this.http,e.webrtcUrl||t("CB_WEBRTC_URL")||Qe,e.appId),this.errorTracker=new z(this.http,e.errorTracker),this.oauth=new X(this.http),this.payment=new Y(this.http),this.subscription=new ie(this.http),this.push=new ee(this.http),this.roles=new O(this.http),this.video=new oe(this.http,e.videoUrl||t("CB_VIDEO_URL")||Xe),this.game=new U(this.http,e.gameUrl||t("CB_GAME_URL")||Ye,e.appId),this.ads=new E(this.http),this.native=new M,this.knowledge=new J(this.http),this.ai=new x(this.http),this.queue=new te(this.http),this.analytics=new V(this.http),this.endpoint=new $(this.http),this.support=new ne(this.http),this.auth._attachAnalytics(this.analytics),(e.autoRestoreSession??!0)&&typeof window<"u"&&(this.isOAuthRedirectCallbackUrl()?this.http.setBootRestorePromise(this.oauth.consumeRedirectCallbackOnBoot(),{alwaysAwait:!0}):this.isOAuthCallbackUrl()||this.http.setBootRestorePromise(this.http.tryRestoreSessionFromCookie()))}isOAuthCallbackUrl(){if(typeof window>"u"||!window.location)return!1;let e=new URLSearchParams(window.location.search);return!!(e.has("access_token")&&e.has("refresh_token")||e.has("code")&&e.has("state")||e.has("error")&&e.has("state"))}isOAuthRedirectCallbackUrl(){if(typeof window>"u"||!window.location||window.opener)return!1;let e=new URLSearchParams(window.location.search);return e.has("access_token")&&e.has("refresh_token")}async restoreSession(){return this.http.tryRestoreSessionFromCookie()}setTokens(e,t){this.http.setTokens(e,t)}clearTokens(){this.http.clearTokens()}updateConfig(e){this.http.updateConfig(e)}},Ze=ue;return Fe(et);})();
|
|
6
6
|
var ConnectBase = ConnectBaseModule.default || ConnectBaseModule.ConnectBase;
|
package/dist/index.d.mts
CHANGED
|
@@ -5173,7 +5173,7 @@ declare class OAuthAPI {
|
|
|
5173
5173
|
} | null>;
|
|
5174
5174
|
}
|
|
5175
5175
|
|
|
5176
|
-
type PaymentProvider = "toss" | "stripe" | "payapp" | "paypal" | "paddle";
|
|
5176
|
+
type PaymentProvider = "toss" | "stripe" | "payapp" | "paypal" | "paddle" | "dodo";
|
|
5177
5177
|
type PaymentStatus = "pending" | "ready" | "in_progress" | "done" | "canceled" | "partial_canceled" | "aborted" | "expired" | "failed";
|
|
5178
5178
|
interface PreparePaymentRequest {
|
|
5179
5179
|
amount: number;
|
|
@@ -5200,10 +5200,11 @@ interface PreparePaymentResponse {
|
|
|
5200
5200
|
payapp_mul_no?: string;
|
|
5201
5201
|
paypal_approve_url?: string;
|
|
5202
5202
|
paypal_order_id?: string;
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5203
|
+
provider_ref?: string;
|
|
5204
|
+
client_token?: string;
|
|
5205
|
+
environment?: string;
|
|
5206
|
+
checkout_url?: string;
|
|
5207
|
+
launch_mode?: "overlay" | "redirect";
|
|
5207
5208
|
}
|
|
5208
5209
|
interface CreateCheckoutSessionRequest {
|
|
5209
5210
|
amount: number;
|
|
@@ -5230,7 +5231,7 @@ interface ConfirmPaymentRequest {
|
|
|
5230
5231
|
amount: number;
|
|
5231
5232
|
stripe_payment_intent_id?: string;
|
|
5232
5233
|
paypal_order_id?: string;
|
|
5233
|
-
|
|
5234
|
+
provider_ref?: string;
|
|
5234
5235
|
}
|
|
5235
5236
|
interface ConfirmPaymentResponse {
|
|
5236
5237
|
payment_id: string;
|
|
@@ -7290,7 +7291,7 @@ interface CreateSubscriptionRequest {
|
|
|
7290
7291
|
/** payapp 정기결제 만료일 yyyy-mm-dd (미입력시 +10년) */
|
|
7291
7292
|
expire_date?: string;
|
|
7292
7293
|
/** paddle 필수: Paddle 카탈로그의 recurring price ID (pri_*) */
|
|
7293
|
-
|
|
7294
|
+
provider_price_ref?: string;
|
|
7294
7295
|
/** 메타데이터 */
|
|
7295
7296
|
metadata?: Record<string, unknown>;
|
|
7296
7297
|
}
|
|
@@ -7300,19 +7301,22 @@ interface SubscriptionResponse {
|
|
|
7300
7301
|
/** 구독 ID (문자열) */
|
|
7301
7302
|
subscription_id: string;
|
|
7302
7303
|
/** 결제 프로바이더 */
|
|
7303
|
-
provider?: "toss" | "stripe" | "payapp" | "paypal" | "paddle";
|
|
7304
|
+
provider?: "toss" | "stripe" | "payapp" | "paypal" | "paddle" | "dodo";
|
|
7304
7305
|
/** payapp: 카드등록+1회차 결제창 URL. 이 URL로 사용자를 redirect 시킨다 (위젯 불필요) */
|
|
7305
7306
|
payapp_pay_url?: string;
|
|
7306
7307
|
/** paypal: 구독 승인+결제수단 등록 URL. 이 URL로 사용자를 redirect 시킨다 (위젯 불필요) */
|
|
7307
7308
|
paypal_approve_url?: string;
|
|
7308
7309
|
/** paddle: Paddle.js Checkout.open({transactionId})로 오버레이. 확정은 웹훅이 SoT */
|
|
7309
|
-
|
|
7310
|
+
provider_ref?: string;
|
|
7310
7311
|
/** paddle: Paddle.js 초기화용 client-side token */
|
|
7311
|
-
|
|
7312
|
+
client_token?: string;
|
|
7312
7313
|
/** paddle: sandbox | production */
|
|
7313
|
-
|
|
7314
|
+
environment?: string;
|
|
7314
7315
|
/** paddle: 호스티드 결제 페이지 URL (fallback) */
|
|
7315
|
-
|
|
7316
|
+
checkout_url?: string;
|
|
7317
|
+
launch_mode?: "overlay" | "redirect";
|
|
7318
|
+
/** paddle: 현재 구독이 물린 recurring price ID (pri_*) */
|
|
7319
|
+
provider_price_ref?: string;
|
|
7316
7320
|
/** 플랜 이름 */
|
|
7317
7321
|
plan_name: string;
|
|
7318
7322
|
/** 플랜 설명 */
|
|
@@ -7380,6 +7384,67 @@ interface CancelSubscriptionRequest {
|
|
|
7380
7384
|
/** 즉시 취소 여부 (false면 현재 기간 종료 후 취소) */
|
|
7381
7385
|
immediate?: boolean;
|
|
7382
7386
|
}
|
|
7387
|
+
/**
|
|
7388
|
+
* 요금제 스왑 시 남은 금액 정산 방식 (Paddle proration_billing_mode).
|
|
7389
|
+
*
|
|
7390
|
+
* - `prorated_immediately`: 신·구 플랜 차액을 지금 일할 청구하고 즉시 전환 (업그레이드 기본).
|
|
7391
|
+
* - `prorated_next_billing_period`: 차액을 계산하되 다음 청구일에 부과 (전환은 즉시). 다운그레이드에 적합.
|
|
7392
|
+
* - `full_immediately`: 새 플랜 전액을 지금 청구.
|
|
7393
|
+
* - `full_next_billing_period`: 새 플랜 전액을 다음 청구일에 부과.
|
|
7394
|
+
* - `do_not_bill`: 전환만 하고 청구하지 않음.
|
|
7395
|
+
*/
|
|
7396
|
+
type ProrationMode = "prorated_immediately" | "prorated_next_billing_period" | "full_immediately" | "full_next_billing_period" | "do_not_bill";
|
|
7397
|
+
/**
|
|
7398
|
+
* 즉시청구(prorated/full_immediately) 실패 시 플랜 변경을 적용할지.
|
|
7399
|
+
*
|
|
7400
|
+
* - `prevent_change`(기본): 청구 실패 시 플랜 변경 미적용 (옛 플랜 유지). 셀프서비스 안전 기본.
|
|
7401
|
+
* - `apply_change`: 청구 실패해도 플랜 변경 적용 (차액은 past_due 로 나중 수금). CS/관리자 흐름용.
|
|
7402
|
+
*/
|
|
7403
|
+
type OnPaymentFailure = "prevent_change" | "apply_change";
|
|
7404
|
+
/**
|
|
7405
|
+
* 요금제 스왑(플랜 변경) 요청 — Paddle(MoR) 구독 전용.
|
|
7406
|
+
*
|
|
7407
|
+
* 하나의 구독을 유지한 채(id 동일) 다른 recurring price 로 갈아타고 남은 금액을 Paddle proration 으로
|
|
7408
|
+
* 정산한다. 예) 혼자→가족 in-place 업그레이드.
|
|
7409
|
+
*/
|
|
7410
|
+
interface ChangePlanRequest {
|
|
7411
|
+
/** 새 recurring price ID (pri_*) — 필수 */
|
|
7412
|
+
provider_price_ref: string;
|
|
7413
|
+
/** 우리 표시용 플랜명 (선택) */
|
|
7414
|
+
plan_name?: string;
|
|
7415
|
+
/** 우리 표시용 플랜 설명 (선택) */
|
|
7416
|
+
plan_description?: string;
|
|
7417
|
+
/** 우리 기록용 정기 금액 (선택 — 미지정 시 Paddle 값으로 자동 수렴) */
|
|
7418
|
+
amount?: number;
|
|
7419
|
+
/** 정산 방식 (기본: prorated_immediately) */
|
|
7420
|
+
proration_mode?: ProrationMode;
|
|
7421
|
+
/** 즉시청구 실패 시 처리 (기본: prevent_change) */
|
|
7422
|
+
on_payment_failure?: OnPaymentFailure;
|
|
7423
|
+
/** 메타데이터 병합 (선택) */
|
|
7424
|
+
metadata?: Record<string, unknown>;
|
|
7425
|
+
}
|
|
7426
|
+
/**
|
|
7427
|
+
* 요금제 스왑 미리보기 응답 — 실제 적용 없이 정산 결과만 계산.
|
|
7428
|
+
*
|
|
7429
|
+
* `immediate_charge_amount` 는 세금·기존 크레딧 잔액까지 반영된 Paddle 계산값(권위)이다.
|
|
7430
|
+
* 확정 화면에 "지금 ₩X 청구, 이후 ₩Y/주기" 로 그대로 노출한다.
|
|
7431
|
+
*/
|
|
7432
|
+
interface ChangePlanPreview {
|
|
7433
|
+
/** 적용된 정산 모드 */
|
|
7434
|
+
proration_mode: ProrationMode;
|
|
7435
|
+
/** 지금 발생: charge(청구) | credit(크레딧) | 없음 */
|
|
7436
|
+
immediate_charge_action?: "charge" | "credit";
|
|
7437
|
+
/** 지금 순액(minor). 청구=양수, 크레딧=음수 */
|
|
7438
|
+
immediate_charge_amount: number;
|
|
7439
|
+
/** 새 플랜의 다음 정기 청구액(minor) */
|
|
7440
|
+
recurring_amount: number;
|
|
7441
|
+
/** 통화 (ISO 4217) */
|
|
7442
|
+
currency: string;
|
|
7443
|
+
/** 다음 청구 예정일 */
|
|
7444
|
+
next_billed_at?: string | null;
|
|
7445
|
+
/** 미리보기가 반영한 새 price ID */
|
|
7446
|
+
new_provider_price_ref?: string;
|
|
7447
|
+
}
|
|
7383
7448
|
interface ListSubscriptionsRequest {
|
|
7384
7449
|
/** 상태 필터 */
|
|
7385
7450
|
status?: SubscriptionStatus;
|
|
@@ -7675,6 +7740,51 @@ declare class SubscriptionAPI {
|
|
|
7675
7740
|
* ```
|
|
7676
7741
|
*/
|
|
7677
7742
|
cancel(subscriptionId: string, data?: CancelSubscriptionRequest): Promise<SubscriptionResponse>;
|
|
7743
|
+
/**
|
|
7744
|
+
* 요금제 스왑 (플랜 변경) — Paddle(MoR) 구독 전용
|
|
7745
|
+
*
|
|
7746
|
+
* 하나의 구독을 유지한 채(id 동일) 다른 recurring price 로 갈아타고 남은 금액을 Paddle proration 으로
|
|
7747
|
+
* 즉시 정산합니다. 예) 혼자→가족 in-place 업그레이드 — 이미 낸 혼자 미사용분을 Paddle 이 자동 크레딧해
|
|
7748
|
+
* 첫 가족 청구에서 차감합니다. 즉시청구 차액의 결제 확정은 웹훅(SoT)으로 수렴합니다.
|
|
7749
|
+
*
|
|
7750
|
+
* `proration_mode` 미지정 시 `prorated_immediately`(업그레이드 기본)가 적용됩니다.
|
|
7751
|
+
* 확정 전 정산액을 보여주려면 {@link previewChangePlan} 을 먼저 호출하세요.
|
|
7752
|
+
*
|
|
7753
|
+
* @param subscriptionId - 구독 ID
|
|
7754
|
+
* @param data - 새 price + 정산 옵션
|
|
7755
|
+
* @returns 갱신된 구독 정보
|
|
7756
|
+
*
|
|
7757
|
+
* @example
|
|
7758
|
+
* ```typescript
|
|
7759
|
+
* await client.subscription.changePlan(subscriptionId, {
|
|
7760
|
+
* provider_price_ref: 'pri_family_monthly',
|
|
7761
|
+
* plan_name: '가족 플랜',
|
|
7762
|
+
* amount: 24900,
|
|
7763
|
+
* proration_mode: 'prorated_immediately',
|
|
7764
|
+
* })
|
|
7765
|
+
* ```
|
|
7766
|
+
*/
|
|
7767
|
+
changePlan(subscriptionId: string, data: ChangePlanRequest): Promise<SubscriptionResponse>;
|
|
7768
|
+
/**
|
|
7769
|
+
* 요금제 스왑 정산 미리보기 — 실제 적용 없이 청구/크레딧 금액만 계산 (Paddle 전용)
|
|
7770
|
+
*
|
|
7771
|
+
* 세금·기존 크레딧 잔액까지 반영된 Paddle 계산값을 반환하므로, 확정 화면에
|
|
7772
|
+
* "지금 ₩X 청구, 이후 ₩Y/주기 (다음 청구일 …)" 를 그대로 노출하면 됩니다.
|
|
7773
|
+
* 직접 차액을 계산하지 마세요 — 이 값이 권위입니다.
|
|
7774
|
+
*
|
|
7775
|
+
* @param subscriptionId - 구독 ID
|
|
7776
|
+
* @param data - 새 price + 정산 옵션 ({@link changePlan} 과 동일 body)
|
|
7777
|
+
* @returns 정산 미리보기 (지금 청구/크레딧 · 새 정기 청구액 · 다음 청구일)
|
|
7778
|
+
*
|
|
7779
|
+
* @example
|
|
7780
|
+
* ```typescript
|
|
7781
|
+
* const preview = await client.subscription.previewChangePlan(subscriptionId, {
|
|
7782
|
+
* provider_price_ref: 'pri_family_monthly',
|
|
7783
|
+
* })
|
|
7784
|
+
* // preview.immediate_charge_amount, preview.recurring_amount, preview.next_billed_at
|
|
7785
|
+
* ```
|
|
7786
|
+
*/
|
|
7787
|
+
previewChangePlan(subscriptionId: string, data: ChangePlanRequest): Promise<ChangePlanPreview>;
|
|
7678
7788
|
/**
|
|
7679
7789
|
* 구독 결제 이력 조회
|
|
7680
7790
|
*
|
|
@@ -9408,4 +9518,4 @@ declare class ConnectBase {
|
|
|
9408
9518
|
updateConfig(config: Partial<ConnectBaseConfig>): void;
|
|
9409
9519
|
}
|
|
9410
9520
|
|
|
9411
|
-
export { AIAPI, type AIChatRequest, type AIChatResponse, type AIChatStreamCallbacks, type AIChatStreamOptions, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, AUTH_MEMBER_ID_TOKEN, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AgenticSearchProgress, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchOperationResult, type BatchSetPageMetaRequest, type BatchWriteResult, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChannelStats, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateRolePayload, type CreateRoleResult, type CreateRoomResult, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type DatabaseRealtimeConnectOptions, type DatabaseRealtimeFilter, type DatabaseRealtimeHandlers, type DatabaseRealtimeSubscription, type DatabaseSnapshot, type DatabaseSnapshotMessage, type DatabaseSubscribeOptions, type DeleteWhereResponse, type DeviceInfo, type DocumentResponse, type EnabledProviderInfo, type EnabledProvidersResponse, EndpointAPI, type EndpointCallInit, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type ExportDataRequest, type ExportDataResponse, type FetchDataResponse, type FetchFilesResponse, type FetchPublicKeysResponse, type FileItem, type FileStats, GameAPI, type GameAction, type GameClientConfig, type GameConfig, GameConfigAPI, type GameConfigPatch, type GameConnectionState, type GameConnectionStatus, type GameDelta, GameError, type GameErrorCode, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GenerateUploadURLByPathRequest, type GenerateUploadURLRequest, type GenerateUploadURLResponse, type GeoBoundingBox, type GeoIndex, type GeoNear, type GeoPoint, type GeoPolygon, type GeoQuery, type GeoResponse, type GeoResult, type GetAuthorizationURLResponse, type GetFileByPathResponse, type GoogleConnectionStatus, type HistoryResponse, type ICEServer, type ICEServersResponse, type ImageResult, type ImportDataRequest, type ImportDataResponse, type IndexAnalysis, type IndexRecommendation, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinQueueRequest, type JoinRoomRequest, type JoinRoomResponse, type KnowledgeSearchRequest, type KnowledgeSearchResponse, type KnowledgeSearchResult, type LeaderboardEntry, type LeaderboardListResponse, type LeaderboardScoreEntry, type LifecyclePolicy, type ListBillingKeysResponse, type ListDocumentsResponse, type ListPageMetasOptions, type ListPageMetasResponse, type ListPaymentsOptions, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type LobbyInfo, type LobbyInvite, type LobbyMember, type LobbyVisibility, type MatchResult, type MatchmakingTicket, type MatchqueueListResponse, type MatchqueueTicket, type MemberInfoResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MemoryInfo, type MessageHandler, type MigrateDataRequest, type MigrateDataResponse, type MoveFileRequest, type NackMessageRequest, NativeAPI, type OAuthCallbackResponse, type OAuthProvider, type OpenDialogOptions, type OpenDialogResult, type PageMetaResponse, type PartyInfo, type PartyInvite, type PartyMember, type PauseSubscriptionRequest, type PaymentDetail, type PaymentListItem, type PaymentListResult, type PaymentProvider, type PaymentStatus, type PeerInfo, type Platform, type PlayerEvent, type PlayerStats, type Playlist, type PlaylistItem, type PollUntilOptions, type PongMessage, type PopulateOption, type Position, type PreparePaymentRequest, type PreparePaymentResponse, type PresenceChangeHandler, type PresenceInfo, type PresenceSetOptions, type PresenceStatus, type PresenceStatusResult, type PublicKeyItem, type PublishBatchRequest, type PublishBatchResponse, type PublishMessageRequest, type PublishMessageResponse, type PushPlatform, type PushStatsResult, type QualityProgress, type QueryOptions, type QueueInfoResponse, type QueueMessage, type ReadReceiptHandler, type ReadReceiptInfo, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RelationType, type RenameFileRequest, type RenameFileResponse, type ReplayHighlight, type ReplayInfo, type ReplayPlayerInfo, type RestoreBackupRequest, type RestoreBackupResponse, type RetentionPolicy, type RoleDetail, type RoleList, type RoleListItem, type RolePermissionItem, type RoleUserItem, RolesAPI, type RoomInfo, type RoomStaleMessage, type RoomSummary, type RoomsResponse, type SaveDialogOptions, type SaveDialogResult, type ScriptDetailResponse, type ScriptListResponse, type ScriptMeta, type ScriptVersion, type ScriptVersionListResponse, type SearchIndex, type SearchOptions, type SearchResponse, type SearchResult, type SecurityRule, type SendOptions, type SendSuperChatOptions, type SendSuperChatResponse, type ServerMessage, SessionManager, type SetPageMetaRequest, type Shorts, type ShortsListResponse, type SignalingMessage, type SignalingMessageType, type SlowQueryInfo, type SpectatorInfo, type SpectatorPlayerState, type SpectatorState, type SpeechRecognizeOptions, type SpeechResult, type StateChange, type StateChangeHandler, type StorageUploadOptions, type StorageUploadProgress, type StreamContentPart, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamImageURLPart, type StreamMessage, type StreamOptions, type StreamSession, type StreamTextPart, type StreamTokenCallback, type StreamToolCallCallback, type StreamToolResultCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type SuperChatType, type SystemInfo, type TTLConfig, type TableAccessLevel, type TableColumnDef, type TableIndex, type TableRelation, type TableSchema, type TableSchemaDefinition, type TokenPersistence, type TransactionRead, type TransactionResult, type TransactionWrite, type TransactionWriteResult, type TranscodeStatus, type TransportType, type Trigger, type TriggerEvent, type TriggerHandlerType, type TypingChangeHandler, type TypingInfo, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateCustomDataRequest, type UpdateCustomDataResponse, type UpdateDataRequest, type UpdateDocumentRequest, type UpdateLobbyRequest, type UpdatePublicKeyRequest, type UpdatePublicKeyResponse, type UpdateRolePayload, type UpdateSecurityRuleRequest, type UpdateSubscriptionRequest, type UpdateTriggerRequest, type UpdateVideoRequest, type UpdateVideoStorageRequest, type UploadByPathOptions, type UploadFileOptions, type UploadFileResponse, type UploadOptions, type UploadProgress, type VAPIDPublicKeyResponse, type ValidateResponse, type ValidationSchema, type ValidationSchemaField, type ValidationStateTransitions, type Video, type VideoComment, type VideoCompleteUploadResponse, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoStorage, type VideoStorageListResponse, type VideoVisibility, type VoiceChannel, type VoiceMember, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, detectInAppBrowser, escapeToExternalBrowser, isWebTransportSupported, toCreateRoomWire };
|
|
9521
|
+
export { AIAPI, type AIChatRequest, type AIChatResponse, type AIChatStreamCallbacks, type AIChatStreamOptions, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, AUTH_MEMBER_ID_TOKEN, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AgenticSearchProgress, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchOperationResult, type BatchSetPageMetaRequest, type BatchWriteResult, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type ChangePlanPreview, type ChangePlanRequest, type Channel, type ChannelMembership, type ChannelStats, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateRolePayload, type CreateRoleResult, type CreateRoomResult, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type DatabaseRealtimeConnectOptions, type DatabaseRealtimeFilter, type DatabaseRealtimeHandlers, type DatabaseRealtimeSubscription, type DatabaseSnapshot, type DatabaseSnapshotMessage, type DatabaseSubscribeOptions, type DeleteWhereResponse, type DeviceInfo, type DocumentResponse, type EnabledProviderInfo, type EnabledProvidersResponse, EndpointAPI, type EndpointCallInit, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type ExportDataRequest, type ExportDataResponse, type FetchDataResponse, type FetchFilesResponse, type FetchPublicKeysResponse, type FileItem, type FileStats, GameAPI, type GameAction, type GameClientConfig, type GameConfig, GameConfigAPI, type GameConfigPatch, type GameConnectionState, type GameConnectionStatus, type GameDelta, GameError, type GameErrorCode, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GenerateUploadURLByPathRequest, type GenerateUploadURLRequest, type GenerateUploadURLResponse, type GeoBoundingBox, type GeoIndex, type GeoNear, type GeoPoint, type GeoPolygon, type GeoQuery, type GeoResponse, type GeoResult, type GetAuthorizationURLResponse, type GetFileByPathResponse, type GoogleConnectionStatus, type HistoryResponse, type ICEServer, type ICEServersResponse, type ImageResult, type ImportDataRequest, type ImportDataResponse, type IndexAnalysis, type IndexRecommendation, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinQueueRequest, type JoinRoomRequest, type JoinRoomResponse, type KnowledgeSearchRequest, type KnowledgeSearchResponse, type KnowledgeSearchResult, type LeaderboardEntry, type LeaderboardListResponse, type LeaderboardScoreEntry, type LifecyclePolicy, type ListBillingKeysResponse, type ListDocumentsResponse, type ListPageMetasOptions, type ListPageMetasResponse, type ListPaymentsOptions, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type LobbyInfo, type LobbyInvite, type LobbyMember, type LobbyVisibility, type MatchResult, type MatchmakingTicket, type MatchqueueListResponse, type MatchqueueTicket, type MemberInfoResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MemoryInfo, type MessageHandler, type MigrateDataRequest, type MigrateDataResponse, type MoveFileRequest, type NackMessageRequest, NativeAPI, type OAuthCallbackResponse, type OAuthProvider, type OnPaymentFailure, type OpenDialogOptions, type OpenDialogResult, type PageMetaResponse, type PartyInfo, type PartyInvite, type PartyMember, type PauseSubscriptionRequest, type PaymentDetail, type PaymentListItem, type PaymentListResult, type PaymentProvider, type PaymentStatus, type PeerInfo, type Platform, type PlayerEvent, type PlayerStats, type Playlist, type PlaylistItem, type PollUntilOptions, type PongMessage, type PopulateOption, type Position, type PreparePaymentRequest, type PreparePaymentResponse, type PresenceChangeHandler, type PresenceInfo, type PresenceSetOptions, type PresenceStatus, type PresenceStatusResult, type ProrationMode, type PublicKeyItem, type PublishBatchRequest, type PublishBatchResponse, type PublishMessageRequest, type PublishMessageResponse, type PushPlatform, type PushStatsResult, type QualityProgress, type QueryOptions, type QueueInfoResponse, type QueueMessage, type ReadReceiptHandler, type ReadReceiptInfo, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RelationType, type RenameFileRequest, type RenameFileResponse, type ReplayHighlight, type ReplayInfo, type ReplayPlayerInfo, type RestoreBackupRequest, type RestoreBackupResponse, type RetentionPolicy, type RoleDetail, type RoleList, type RoleListItem, type RolePermissionItem, type RoleUserItem, RolesAPI, type RoomInfo, type RoomStaleMessage, type RoomSummary, type RoomsResponse, type SaveDialogOptions, type SaveDialogResult, type ScriptDetailResponse, type ScriptListResponse, type ScriptMeta, type ScriptVersion, type ScriptVersionListResponse, type SearchIndex, type SearchOptions, type SearchResponse, type SearchResult, type SecurityRule, type SendOptions, type SendSuperChatOptions, type SendSuperChatResponse, type ServerMessage, SessionManager, type SetPageMetaRequest, type Shorts, type ShortsListResponse, type SignalingMessage, type SignalingMessageType, type SlowQueryInfo, type SpectatorInfo, type SpectatorPlayerState, type SpectatorState, type SpeechRecognizeOptions, type SpeechResult, type StateChange, type StateChangeHandler, type StorageUploadOptions, type StorageUploadProgress, type StreamContentPart, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamImageURLPart, type StreamMessage, type StreamOptions, type StreamSession, type StreamTextPart, type StreamTokenCallback, type StreamToolCallCallback, type StreamToolResultCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type SuperChatType, type SystemInfo, type TTLConfig, type TableAccessLevel, type TableColumnDef, type TableIndex, type TableRelation, type TableSchema, type TableSchemaDefinition, type TokenPersistence, type TransactionRead, type TransactionResult, type TransactionWrite, type TransactionWriteResult, type TranscodeStatus, type TransportType, type Trigger, type TriggerEvent, type TriggerHandlerType, type TypingChangeHandler, type TypingInfo, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateCustomDataRequest, type UpdateCustomDataResponse, type UpdateDataRequest, type UpdateDocumentRequest, type UpdateLobbyRequest, type UpdatePublicKeyRequest, type UpdatePublicKeyResponse, type UpdateRolePayload, type UpdateSecurityRuleRequest, type UpdateSubscriptionRequest, type UpdateTriggerRequest, type UpdateVideoRequest, type UpdateVideoStorageRequest, type UploadByPathOptions, type UploadFileOptions, type UploadFileResponse, type UploadOptions, type UploadProgress, type VAPIDPublicKeyResponse, type ValidateResponse, type ValidationSchema, type ValidationSchemaField, type ValidationStateTransitions, type Video, type VideoComment, type VideoCompleteUploadResponse, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoStorage, type VideoStorageListResponse, type VideoVisibility, type VoiceChannel, type VoiceMember, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, detectInAppBrowser, escapeToExternalBrowser, isWebTransportSupported, toCreateRoomWire };
|
package/dist/index.d.ts
CHANGED
|
@@ -5173,7 +5173,7 @@ declare class OAuthAPI {
|
|
|
5173
5173
|
} | null>;
|
|
5174
5174
|
}
|
|
5175
5175
|
|
|
5176
|
-
type PaymentProvider = "toss" | "stripe" | "payapp" | "paypal" | "paddle";
|
|
5176
|
+
type PaymentProvider = "toss" | "stripe" | "payapp" | "paypal" | "paddle" | "dodo";
|
|
5177
5177
|
type PaymentStatus = "pending" | "ready" | "in_progress" | "done" | "canceled" | "partial_canceled" | "aborted" | "expired" | "failed";
|
|
5178
5178
|
interface PreparePaymentRequest {
|
|
5179
5179
|
amount: number;
|
|
@@ -5200,10 +5200,11 @@ interface PreparePaymentResponse {
|
|
|
5200
5200
|
payapp_mul_no?: string;
|
|
5201
5201
|
paypal_approve_url?: string;
|
|
5202
5202
|
paypal_order_id?: string;
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5203
|
+
provider_ref?: string;
|
|
5204
|
+
client_token?: string;
|
|
5205
|
+
environment?: string;
|
|
5206
|
+
checkout_url?: string;
|
|
5207
|
+
launch_mode?: "overlay" | "redirect";
|
|
5207
5208
|
}
|
|
5208
5209
|
interface CreateCheckoutSessionRequest {
|
|
5209
5210
|
amount: number;
|
|
@@ -5230,7 +5231,7 @@ interface ConfirmPaymentRequest {
|
|
|
5230
5231
|
amount: number;
|
|
5231
5232
|
stripe_payment_intent_id?: string;
|
|
5232
5233
|
paypal_order_id?: string;
|
|
5233
|
-
|
|
5234
|
+
provider_ref?: string;
|
|
5234
5235
|
}
|
|
5235
5236
|
interface ConfirmPaymentResponse {
|
|
5236
5237
|
payment_id: string;
|
|
@@ -7290,7 +7291,7 @@ interface CreateSubscriptionRequest {
|
|
|
7290
7291
|
/** payapp 정기결제 만료일 yyyy-mm-dd (미입력시 +10년) */
|
|
7291
7292
|
expire_date?: string;
|
|
7292
7293
|
/** paddle 필수: Paddle 카탈로그의 recurring price ID (pri_*) */
|
|
7293
|
-
|
|
7294
|
+
provider_price_ref?: string;
|
|
7294
7295
|
/** 메타데이터 */
|
|
7295
7296
|
metadata?: Record<string, unknown>;
|
|
7296
7297
|
}
|
|
@@ -7300,19 +7301,22 @@ interface SubscriptionResponse {
|
|
|
7300
7301
|
/** 구독 ID (문자열) */
|
|
7301
7302
|
subscription_id: string;
|
|
7302
7303
|
/** 결제 프로바이더 */
|
|
7303
|
-
provider?: "toss" | "stripe" | "payapp" | "paypal" | "paddle";
|
|
7304
|
+
provider?: "toss" | "stripe" | "payapp" | "paypal" | "paddle" | "dodo";
|
|
7304
7305
|
/** payapp: 카드등록+1회차 결제창 URL. 이 URL로 사용자를 redirect 시킨다 (위젯 불필요) */
|
|
7305
7306
|
payapp_pay_url?: string;
|
|
7306
7307
|
/** paypal: 구독 승인+결제수단 등록 URL. 이 URL로 사용자를 redirect 시킨다 (위젯 불필요) */
|
|
7307
7308
|
paypal_approve_url?: string;
|
|
7308
7309
|
/** paddle: Paddle.js Checkout.open({transactionId})로 오버레이. 확정은 웹훅이 SoT */
|
|
7309
|
-
|
|
7310
|
+
provider_ref?: string;
|
|
7310
7311
|
/** paddle: Paddle.js 초기화용 client-side token */
|
|
7311
|
-
|
|
7312
|
+
client_token?: string;
|
|
7312
7313
|
/** paddle: sandbox | production */
|
|
7313
|
-
|
|
7314
|
+
environment?: string;
|
|
7314
7315
|
/** paddle: 호스티드 결제 페이지 URL (fallback) */
|
|
7315
|
-
|
|
7316
|
+
checkout_url?: string;
|
|
7317
|
+
launch_mode?: "overlay" | "redirect";
|
|
7318
|
+
/** paddle: 현재 구독이 물린 recurring price ID (pri_*) */
|
|
7319
|
+
provider_price_ref?: string;
|
|
7316
7320
|
/** 플랜 이름 */
|
|
7317
7321
|
plan_name: string;
|
|
7318
7322
|
/** 플랜 설명 */
|
|
@@ -7380,6 +7384,67 @@ interface CancelSubscriptionRequest {
|
|
|
7380
7384
|
/** 즉시 취소 여부 (false면 현재 기간 종료 후 취소) */
|
|
7381
7385
|
immediate?: boolean;
|
|
7382
7386
|
}
|
|
7387
|
+
/**
|
|
7388
|
+
* 요금제 스왑 시 남은 금액 정산 방식 (Paddle proration_billing_mode).
|
|
7389
|
+
*
|
|
7390
|
+
* - `prorated_immediately`: 신·구 플랜 차액을 지금 일할 청구하고 즉시 전환 (업그레이드 기본).
|
|
7391
|
+
* - `prorated_next_billing_period`: 차액을 계산하되 다음 청구일에 부과 (전환은 즉시). 다운그레이드에 적합.
|
|
7392
|
+
* - `full_immediately`: 새 플랜 전액을 지금 청구.
|
|
7393
|
+
* - `full_next_billing_period`: 새 플랜 전액을 다음 청구일에 부과.
|
|
7394
|
+
* - `do_not_bill`: 전환만 하고 청구하지 않음.
|
|
7395
|
+
*/
|
|
7396
|
+
type ProrationMode = "prorated_immediately" | "prorated_next_billing_period" | "full_immediately" | "full_next_billing_period" | "do_not_bill";
|
|
7397
|
+
/**
|
|
7398
|
+
* 즉시청구(prorated/full_immediately) 실패 시 플랜 변경을 적용할지.
|
|
7399
|
+
*
|
|
7400
|
+
* - `prevent_change`(기본): 청구 실패 시 플랜 변경 미적용 (옛 플랜 유지). 셀프서비스 안전 기본.
|
|
7401
|
+
* - `apply_change`: 청구 실패해도 플랜 변경 적용 (차액은 past_due 로 나중 수금). CS/관리자 흐름용.
|
|
7402
|
+
*/
|
|
7403
|
+
type OnPaymentFailure = "prevent_change" | "apply_change";
|
|
7404
|
+
/**
|
|
7405
|
+
* 요금제 스왑(플랜 변경) 요청 — Paddle(MoR) 구독 전용.
|
|
7406
|
+
*
|
|
7407
|
+
* 하나의 구독을 유지한 채(id 동일) 다른 recurring price 로 갈아타고 남은 금액을 Paddle proration 으로
|
|
7408
|
+
* 정산한다. 예) 혼자→가족 in-place 업그레이드.
|
|
7409
|
+
*/
|
|
7410
|
+
interface ChangePlanRequest {
|
|
7411
|
+
/** 새 recurring price ID (pri_*) — 필수 */
|
|
7412
|
+
provider_price_ref: string;
|
|
7413
|
+
/** 우리 표시용 플랜명 (선택) */
|
|
7414
|
+
plan_name?: string;
|
|
7415
|
+
/** 우리 표시용 플랜 설명 (선택) */
|
|
7416
|
+
plan_description?: string;
|
|
7417
|
+
/** 우리 기록용 정기 금액 (선택 — 미지정 시 Paddle 값으로 자동 수렴) */
|
|
7418
|
+
amount?: number;
|
|
7419
|
+
/** 정산 방식 (기본: prorated_immediately) */
|
|
7420
|
+
proration_mode?: ProrationMode;
|
|
7421
|
+
/** 즉시청구 실패 시 처리 (기본: prevent_change) */
|
|
7422
|
+
on_payment_failure?: OnPaymentFailure;
|
|
7423
|
+
/** 메타데이터 병합 (선택) */
|
|
7424
|
+
metadata?: Record<string, unknown>;
|
|
7425
|
+
}
|
|
7426
|
+
/**
|
|
7427
|
+
* 요금제 스왑 미리보기 응답 — 실제 적용 없이 정산 결과만 계산.
|
|
7428
|
+
*
|
|
7429
|
+
* `immediate_charge_amount` 는 세금·기존 크레딧 잔액까지 반영된 Paddle 계산값(권위)이다.
|
|
7430
|
+
* 확정 화면에 "지금 ₩X 청구, 이후 ₩Y/주기" 로 그대로 노출한다.
|
|
7431
|
+
*/
|
|
7432
|
+
interface ChangePlanPreview {
|
|
7433
|
+
/** 적용된 정산 모드 */
|
|
7434
|
+
proration_mode: ProrationMode;
|
|
7435
|
+
/** 지금 발생: charge(청구) | credit(크레딧) | 없음 */
|
|
7436
|
+
immediate_charge_action?: "charge" | "credit";
|
|
7437
|
+
/** 지금 순액(minor). 청구=양수, 크레딧=음수 */
|
|
7438
|
+
immediate_charge_amount: number;
|
|
7439
|
+
/** 새 플랜의 다음 정기 청구액(minor) */
|
|
7440
|
+
recurring_amount: number;
|
|
7441
|
+
/** 통화 (ISO 4217) */
|
|
7442
|
+
currency: string;
|
|
7443
|
+
/** 다음 청구 예정일 */
|
|
7444
|
+
next_billed_at?: string | null;
|
|
7445
|
+
/** 미리보기가 반영한 새 price ID */
|
|
7446
|
+
new_provider_price_ref?: string;
|
|
7447
|
+
}
|
|
7383
7448
|
interface ListSubscriptionsRequest {
|
|
7384
7449
|
/** 상태 필터 */
|
|
7385
7450
|
status?: SubscriptionStatus;
|
|
@@ -7675,6 +7740,51 @@ declare class SubscriptionAPI {
|
|
|
7675
7740
|
* ```
|
|
7676
7741
|
*/
|
|
7677
7742
|
cancel(subscriptionId: string, data?: CancelSubscriptionRequest): Promise<SubscriptionResponse>;
|
|
7743
|
+
/**
|
|
7744
|
+
* 요금제 스왑 (플랜 변경) — Paddle(MoR) 구독 전용
|
|
7745
|
+
*
|
|
7746
|
+
* 하나의 구독을 유지한 채(id 동일) 다른 recurring price 로 갈아타고 남은 금액을 Paddle proration 으로
|
|
7747
|
+
* 즉시 정산합니다. 예) 혼자→가족 in-place 업그레이드 — 이미 낸 혼자 미사용분을 Paddle 이 자동 크레딧해
|
|
7748
|
+
* 첫 가족 청구에서 차감합니다. 즉시청구 차액의 결제 확정은 웹훅(SoT)으로 수렴합니다.
|
|
7749
|
+
*
|
|
7750
|
+
* `proration_mode` 미지정 시 `prorated_immediately`(업그레이드 기본)가 적용됩니다.
|
|
7751
|
+
* 확정 전 정산액을 보여주려면 {@link previewChangePlan} 을 먼저 호출하세요.
|
|
7752
|
+
*
|
|
7753
|
+
* @param subscriptionId - 구독 ID
|
|
7754
|
+
* @param data - 새 price + 정산 옵션
|
|
7755
|
+
* @returns 갱신된 구독 정보
|
|
7756
|
+
*
|
|
7757
|
+
* @example
|
|
7758
|
+
* ```typescript
|
|
7759
|
+
* await client.subscription.changePlan(subscriptionId, {
|
|
7760
|
+
* provider_price_ref: 'pri_family_monthly',
|
|
7761
|
+
* plan_name: '가족 플랜',
|
|
7762
|
+
* amount: 24900,
|
|
7763
|
+
* proration_mode: 'prorated_immediately',
|
|
7764
|
+
* })
|
|
7765
|
+
* ```
|
|
7766
|
+
*/
|
|
7767
|
+
changePlan(subscriptionId: string, data: ChangePlanRequest): Promise<SubscriptionResponse>;
|
|
7768
|
+
/**
|
|
7769
|
+
* 요금제 스왑 정산 미리보기 — 실제 적용 없이 청구/크레딧 금액만 계산 (Paddle 전용)
|
|
7770
|
+
*
|
|
7771
|
+
* 세금·기존 크레딧 잔액까지 반영된 Paddle 계산값을 반환하므로, 확정 화면에
|
|
7772
|
+
* "지금 ₩X 청구, 이후 ₩Y/주기 (다음 청구일 …)" 를 그대로 노출하면 됩니다.
|
|
7773
|
+
* 직접 차액을 계산하지 마세요 — 이 값이 권위입니다.
|
|
7774
|
+
*
|
|
7775
|
+
* @param subscriptionId - 구독 ID
|
|
7776
|
+
* @param data - 새 price + 정산 옵션 ({@link changePlan} 과 동일 body)
|
|
7777
|
+
* @returns 정산 미리보기 (지금 청구/크레딧 · 새 정기 청구액 · 다음 청구일)
|
|
7778
|
+
*
|
|
7779
|
+
* @example
|
|
7780
|
+
* ```typescript
|
|
7781
|
+
* const preview = await client.subscription.previewChangePlan(subscriptionId, {
|
|
7782
|
+
* provider_price_ref: 'pri_family_monthly',
|
|
7783
|
+
* })
|
|
7784
|
+
* // preview.immediate_charge_amount, preview.recurring_amount, preview.next_billed_at
|
|
7785
|
+
* ```
|
|
7786
|
+
*/
|
|
7787
|
+
previewChangePlan(subscriptionId: string, data: ChangePlanRequest): Promise<ChangePlanPreview>;
|
|
7678
7788
|
/**
|
|
7679
7789
|
* 구독 결제 이력 조회
|
|
7680
7790
|
*
|
|
@@ -9408,4 +9518,4 @@ declare class ConnectBase {
|
|
|
9408
9518
|
updateConfig(config: Partial<ConnectBaseConfig>): void;
|
|
9409
9519
|
}
|
|
9410
9520
|
|
|
9411
|
-
export { AIAPI, type AIChatRequest, type AIChatResponse, type AIChatStreamCallbacks, type AIChatStreamOptions, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, AUTH_MEMBER_ID_TOKEN, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AgenticSearchProgress, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchOperationResult, type BatchSetPageMetaRequest, type BatchWriteResult, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChannelStats, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateRolePayload, type CreateRoleResult, type CreateRoomResult, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type DatabaseRealtimeConnectOptions, type DatabaseRealtimeFilter, type DatabaseRealtimeHandlers, type DatabaseRealtimeSubscription, type DatabaseSnapshot, type DatabaseSnapshotMessage, type DatabaseSubscribeOptions, type DeleteWhereResponse, type DeviceInfo, type DocumentResponse, type EnabledProviderInfo, type EnabledProvidersResponse, EndpointAPI, type EndpointCallInit, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type ExportDataRequest, type ExportDataResponse, type FetchDataResponse, type FetchFilesResponse, type FetchPublicKeysResponse, type FileItem, type FileStats, GameAPI, type GameAction, type GameClientConfig, type GameConfig, GameConfigAPI, type GameConfigPatch, type GameConnectionState, type GameConnectionStatus, type GameDelta, GameError, type GameErrorCode, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GenerateUploadURLByPathRequest, type GenerateUploadURLRequest, type GenerateUploadURLResponse, type GeoBoundingBox, type GeoIndex, type GeoNear, type GeoPoint, type GeoPolygon, type GeoQuery, type GeoResponse, type GeoResult, type GetAuthorizationURLResponse, type GetFileByPathResponse, type GoogleConnectionStatus, type HistoryResponse, type ICEServer, type ICEServersResponse, type ImageResult, type ImportDataRequest, type ImportDataResponse, type IndexAnalysis, type IndexRecommendation, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinQueueRequest, type JoinRoomRequest, type JoinRoomResponse, type KnowledgeSearchRequest, type KnowledgeSearchResponse, type KnowledgeSearchResult, type LeaderboardEntry, type LeaderboardListResponse, type LeaderboardScoreEntry, type LifecyclePolicy, type ListBillingKeysResponse, type ListDocumentsResponse, type ListPageMetasOptions, type ListPageMetasResponse, type ListPaymentsOptions, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type LobbyInfo, type LobbyInvite, type LobbyMember, type LobbyVisibility, type MatchResult, type MatchmakingTicket, type MatchqueueListResponse, type MatchqueueTicket, type MemberInfoResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MemoryInfo, type MessageHandler, type MigrateDataRequest, type MigrateDataResponse, type MoveFileRequest, type NackMessageRequest, NativeAPI, type OAuthCallbackResponse, type OAuthProvider, type OpenDialogOptions, type OpenDialogResult, type PageMetaResponse, type PartyInfo, type PartyInvite, type PartyMember, type PauseSubscriptionRequest, type PaymentDetail, type PaymentListItem, type PaymentListResult, type PaymentProvider, type PaymentStatus, type PeerInfo, type Platform, type PlayerEvent, type PlayerStats, type Playlist, type PlaylistItem, type PollUntilOptions, type PongMessage, type PopulateOption, type Position, type PreparePaymentRequest, type PreparePaymentResponse, type PresenceChangeHandler, type PresenceInfo, type PresenceSetOptions, type PresenceStatus, type PresenceStatusResult, type PublicKeyItem, type PublishBatchRequest, type PublishBatchResponse, type PublishMessageRequest, type PublishMessageResponse, type PushPlatform, type PushStatsResult, type QualityProgress, type QueryOptions, type QueueInfoResponse, type QueueMessage, type ReadReceiptHandler, type ReadReceiptInfo, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RelationType, type RenameFileRequest, type RenameFileResponse, type ReplayHighlight, type ReplayInfo, type ReplayPlayerInfo, type RestoreBackupRequest, type RestoreBackupResponse, type RetentionPolicy, type RoleDetail, type RoleList, type RoleListItem, type RolePermissionItem, type RoleUserItem, RolesAPI, type RoomInfo, type RoomStaleMessage, type RoomSummary, type RoomsResponse, type SaveDialogOptions, type SaveDialogResult, type ScriptDetailResponse, type ScriptListResponse, type ScriptMeta, type ScriptVersion, type ScriptVersionListResponse, type SearchIndex, type SearchOptions, type SearchResponse, type SearchResult, type SecurityRule, type SendOptions, type SendSuperChatOptions, type SendSuperChatResponse, type ServerMessage, SessionManager, type SetPageMetaRequest, type Shorts, type ShortsListResponse, type SignalingMessage, type SignalingMessageType, type SlowQueryInfo, type SpectatorInfo, type SpectatorPlayerState, type SpectatorState, type SpeechRecognizeOptions, type SpeechResult, type StateChange, type StateChangeHandler, type StorageUploadOptions, type StorageUploadProgress, type StreamContentPart, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamImageURLPart, type StreamMessage, type StreamOptions, type StreamSession, type StreamTextPart, type StreamTokenCallback, type StreamToolCallCallback, type StreamToolResultCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type SuperChatType, type SystemInfo, type TTLConfig, type TableAccessLevel, type TableColumnDef, type TableIndex, type TableRelation, type TableSchema, type TableSchemaDefinition, type TokenPersistence, type TransactionRead, type TransactionResult, type TransactionWrite, type TransactionWriteResult, type TranscodeStatus, type TransportType, type Trigger, type TriggerEvent, type TriggerHandlerType, type TypingChangeHandler, type TypingInfo, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateCustomDataRequest, type UpdateCustomDataResponse, type UpdateDataRequest, type UpdateDocumentRequest, type UpdateLobbyRequest, type UpdatePublicKeyRequest, type UpdatePublicKeyResponse, type UpdateRolePayload, type UpdateSecurityRuleRequest, type UpdateSubscriptionRequest, type UpdateTriggerRequest, type UpdateVideoRequest, type UpdateVideoStorageRequest, type UploadByPathOptions, type UploadFileOptions, type UploadFileResponse, type UploadOptions, type UploadProgress, type VAPIDPublicKeyResponse, type ValidateResponse, type ValidationSchema, type ValidationSchemaField, type ValidationStateTransitions, type Video, type VideoComment, type VideoCompleteUploadResponse, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoStorage, type VideoStorageListResponse, type VideoVisibility, type VoiceChannel, type VoiceMember, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, detectInAppBrowser, escapeToExternalBrowser, isWebTransportSupported, toCreateRoomWire };
|
|
9521
|
+
export { AIAPI, type AIChatRequest, type AIChatResponse, type AIChatStreamCallbacks, type AIChatStreamOptions, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, AUTH_MEMBER_ID_TOKEN, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AgenticSearchProgress, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchOperationResult, type BatchSetPageMetaRequest, type BatchWriteResult, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type ChangePlanPreview, type ChangePlanRequest, type Channel, type ChannelMembership, type ChannelStats, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateRolePayload, type CreateRoleResult, type CreateRoomResult, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type DatabaseRealtimeConnectOptions, type DatabaseRealtimeFilter, type DatabaseRealtimeHandlers, type DatabaseRealtimeSubscription, type DatabaseSnapshot, type DatabaseSnapshotMessage, type DatabaseSubscribeOptions, type DeleteWhereResponse, type DeviceInfo, type DocumentResponse, type EnabledProviderInfo, type EnabledProvidersResponse, EndpointAPI, type EndpointCallInit, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type ExportDataRequest, type ExportDataResponse, type FetchDataResponse, type FetchFilesResponse, type FetchPublicKeysResponse, type FileItem, type FileStats, GameAPI, type GameAction, type GameClientConfig, type GameConfig, GameConfigAPI, type GameConfigPatch, type GameConnectionState, type GameConnectionStatus, type GameDelta, GameError, type GameErrorCode, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GenerateUploadURLByPathRequest, type GenerateUploadURLRequest, type GenerateUploadURLResponse, type GeoBoundingBox, type GeoIndex, type GeoNear, type GeoPoint, type GeoPolygon, type GeoQuery, type GeoResponse, type GeoResult, type GetAuthorizationURLResponse, type GetFileByPathResponse, type GoogleConnectionStatus, type HistoryResponse, type ICEServer, type ICEServersResponse, type ImageResult, type ImportDataRequest, type ImportDataResponse, type IndexAnalysis, type IndexRecommendation, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinQueueRequest, type JoinRoomRequest, type JoinRoomResponse, type KnowledgeSearchRequest, type KnowledgeSearchResponse, type KnowledgeSearchResult, type LeaderboardEntry, type LeaderboardListResponse, type LeaderboardScoreEntry, type LifecyclePolicy, type ListBillingKeysResponse, type ListDocumentsResponse, type ListPageMetasOptions, type ListPageMetasResponse, type ListPaymentsOptions, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type LobbyInfo, type LobbyInvite, type LobbyMember, type LobbyVisibility, type MatchResult, type MatchmakingTicket, type MatchqueueListResponse, type MatchqueueTicket, type MemberInfoResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MemoryInfo, type MessageHandler, type MigrateDataRequest, type MigrateDataResponse, type MoveFileRequest, type NackMessageRequest, NativeAPI, type OAuthCallbackResponse, type OAuthProvider, type OnPaymentFailure, type OpenDialogOptions, type OpenDialogResult, type PageMetaResponse, type PartyInfo, type PartyInvite, type PartyMember, type PauseSubscriptionRequest, type PaymentDetail, type PaymentListItem, type PaymentListResult, type PaymentProvider, type PaymentStatus, type PeerInfo, type Platform, type PlayerEvent, type PlayerStats, type Playlist, type PlaylistItem, type PollUntilOptions, type PongMessage, type PopulateOption, type Position, type PreparePaymentRequest, type PreparePaymentResponse, type PresenceChangeHandler, type PresenceInfo, type PresenceSetOptions, type PresenceStatus, type PresenceStatusResult, type ProrationMode, type PublicKeyItem, type PublishBatchRequest, type PublishBatchResponse, type PublishMessageRequest, type PublishMessageResponse, type PushPlatform, type PushStatsResult, type QualityProgress, type QueryOptions, type QueueInfoResponse, type QueueMessage, type ReadReceiptHandler, type ReadReceiptInfo, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RelationType, type RenameFileRequest, type RenameFileResponse, type ReplayHighlight, type ReplayInfo, type ReplayPlayerInfo, type RestoreBackupRequest, type RestoreBackupResponse, type RetentionPolicy, type RoleDetail, type RoleList, type RoleListItem, type RolePermissionItem, type RoleUserItem, RolesAPI, type RoomInfo, type RoomStaleMessage, type RoomSummary, type RoomsResponse, type SaveDialogOptions, type SaveDialogResult, type ScriptDetailResponse, type ScriptListResponse, type ScriptMeta, type ScriptVersion, type ScriptVersionListResponse, type SearchIndex, type SearchOptions, type SearchResponse, type SearchResult, type SecurityRule, type SendOptions, type SendSuperChatOptions, type SendSuperChatResponse, type ServerMessage, SessionManager, type SetPageMetaRequest, type Shorts, type ShortsListResponse, type SignalingMessage, type SignalingMessageType, type SlowQueryInfo, type SpectatorInfo, type SpectatorPlayerState, type SpectatorState, type SpeechRecognizeOptions, type SpeechResult, type StateChange, type StateChangeHandler, type StorageUploadOptions, type StorageUploadProgress, type StreamContentPart, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamImageURLPart, type StreamMessage, type StreamOptions, type StreamSession, type StreamTextPart, type StreamTokenCallback, type StreamToolCallCallback, type StreamToolResultCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type SuperChatType, type SystemInfo, type TTLConfig, type TableAccessLevel, type TableColumnDef, type TableIndex, type TableRelation, type TableSchema, type TableSchemaDefinition, type TokenPersistence, type TransactionRead, type TransactionResult, type TransactionWrite, type TransactionWriteResult, type TranscodeStatus, type TransportType, type Trigger, type TriggerEvent, type TriggerHandlerType, type TypingChangeHandler, type TypingInfo, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateCustomDataRequest, type UpdateCustomDataResponse, type UpdateDataRequest, type UpdateDocumentRequest, type UpdateLobbyRequest, type UpdatePublicKeyRequest, type UpdatePublicKeyResponse, type UpdateRolePayload, type UpdateSecurityRuleRequest, type UpdateSubscriptionRequest, type UpdateTriggerRequest, type UpdateVideoRequest, type UpdateVideoStorageRequest, type UploadByPathOptions, type UploadFileOptions, type UploadFileResponse, type UploadOptions, type UploadProgress, type VAPIDPublicKeyResponse, type ValidateResponse, type ValidationSchema, type ValidationSchemaField, type ValidationStateTransitions, type Video, type VideoComment, type VideoCompleteUploadResponse, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoStorage, type VideoStorageListResponse, type VideoVisibility, type VoiceChannel, type VoiceMember, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, detectInAppBrowser, escapeToExternalBrowser, isWebTransportSupported, toCreateRoomWire };
|
package/dist/index.js
CHANGED
|
@@ -8856,6 +8856,63 @@ var SubscriptionAPI = class {
|
|
|
8856
8856
|
body
|
|
8857
8857
|
);
|
|
8858
8858
|
}
|
|
8859
|
+
/**
|
|
8860
|
+
* 요금제 스왑 (플랜 변경) — Paddle(MoR) 구독 전용
|
|
8861
|
+
*
|
|
8862
|
+
* 하나의 구독을 유지한 채(id 동일) 다른 recurring price 로 갈아타고 남은 금액을 Paddle proration 으로
|
|
8863
|
+
* 즉시 정산합니다. 예) 혼자→가족 in-place 업그레이드 — 이미 낸 혼자 미사용분을 Paddle 이 자동 크레딧해
|
|
8864
|
+
* 첫 가족 청구에서 차감합니다. 즉시청구 차액의 결제 확정은 웹훅(SoT)으로 수렴합니다.
|
|
8865
|
+
*
|
|
8866
|
+
* `proration_mode` 미지정 시 `prorated_immediately`(업그레이드 기본)가 적용됩니다.
|
|
8867
|
+
* 확정 전 정산액을 보여주려면 {@link previewChangePlan} 을 먼저 호출하세요.
|
|
8868
|
+
*
|
|
8869
|
+
* @param subscriptionId - 구독 ID
|
|
8870
|
+
* @param data - 새 price + 정산 옵션
|
|
8871
|
+
* @returns 갱신된 구독 정보
|
|
8872
|
+
*
|
|
8873
|
+
* @example
|
|
8874
|
+
* ```typescript
|
|
8875
|
+
* await client.subscription.changePlan(subscriptionId, {
|
|
8876
|
+
* provider_price_ref: 'pri_family_monthly',
|
|
8877
|
+
* plan_name: '가족 플랜',
|
|
8878
|
+
* amount: 24900,
|
|
8879
|
+
* proration_mode: 'prorated_immediately',
|
|
8880
|
+
* })
|
|
8881
|
+
* ```
|
|
8882
|
+
*/
|
|
8883
|
+
async changePlan(subscriptionId, data) {
|
|
8884
|
+
const prefix = this.getPublicPrefix();
|
|
8885
|
+
return this.http.post(
|
|
8886
|
+
`${prefix}/subscriptions/${subscriptionId}/change-plan`,
|
|
8887
|
+
data
|
|
8888
|
+
);
|
|
8889
|
+
}
|
|
8890
|
+
/**
|
|
8891
|
+
* 요금제 스왑 정산 미리보기 — 실제 적용 없이 청구/크레딧 금액만 계산 (Paddle 전용)
|
|
8892
|
+
*
|
|
8893
|
+
* 세금·기존 크레딧 잔액까지 반영된 Paddle 계산값을 반환하므로, 확정 화면에
|
|
8894
|
+
* "지금 ₩X 청구, 이후 ₩Y/주기 (다음 청구일 …)" 를 그대로 노출하면 됩니다.
|
|
8895
|
+
* 직접 차액을 계산하지 마세요 — 이 값이 권위입니다.
|
|
8896
|
+
*
|
|
8897
|
+
* @param subscriptionId - 구독 ID
|
|
8898
|
+
* @param data - 새 price + 정산 옵션 ({@link changePlan} 과 동일 body)
|
|
8899
|
+
* @returns 정산 미리보기 (지금 청구/크레딧 · 새 정기 청구액 · 다음 청구일)
|
|
8900
|
+
*
|
|
8901
|
+
* @example
|
|
8902
|
+
* ```typescript
|
|
8903
|
+
* const preview = await client.subscription.previewChangePlan(subscriptionId, {
|
|
8904
|
+
* provider_price_ref: 'pri_family_monthly',
|
|
8905
|
+
* })
|
|
8906
|
+
* // preview.immediate_charge_amount, preview.recurring_amount, preview.next_billed_at
|
|
8907
|
+
* ```
|
|
8908
|
+
*/
|
|
8909
|
+
async previewChangePlan(subscriptionId, data) {
|
|
8910
|
+
const prefix = this.getPublicPrefix();
|
|
8911
|
+
return this.http.post(
|
|
8912
|
+
`${prefix}/subscriptions/${subscriptionId}/change-plan/preview`,
|
|
8913
|
+
data
|
|
8914
|
+
);
|
|
8915
|
+
}
|
|
8859
8916
|
// =====================
|
|
8860
8917
|
// Subscription Payment APIs
|
|
8861
8918
|
// =====================
|
package/dist/index.mjs
CHANGED
|
@@ -8810,6 +8810,63 @@ var SubscriptionAPI = class {
|
|
|
8810
8810
|
body
|
|
8811
8811
|
);
|
|
8812
8812
|
}
|
|
8813
|
+
/**
|
|
8814
|
+
* 요금제 스왑 (플랜 변경) — Paddle(MoR) 구독 전용
|
|
8815
|
+
*
|
|
8816
|
+
* 하나의 구독을 유지한 채(id 동일) 다른 recurring price 로 갈아타고 남은 금액을 Paddle proration 으로
|
|
8817
|
+
* 즉시 정산합니다. 예) 혼자→가족 in-place 업그레이드 — 이미 낸 혼자 미사용분을 Paddle 이 자동 크레딧해
|
|
8818
|
+
* 첫 가족 청구에서 차감합니다. 즉시청구 차액의 결제 확정은 웹훅(SoT)으로 수렴합니다.
|
|
8819
|
+
*
|
|
8820
|
+
* `proration_mode` 미지정 시 `prorated_immediately`(업그레이드 기본)가 적용됩니다.
|
|
8821
|
+
* 확정 전 정산액을 보여주려면 {@link previewChangePlan} 을 먼저 호출하세요.
|
|
8822
|
+
*
|
|
8823
|
+
* @param subscriptionId - 구독 ID
|
|
8824
|
+
* @param data - 새 price + 정산 옵션
|
|
8825
|
+
* @returns 갱신된 구독 정보
|
|
8826
|
+
*
|
|
8827
|
+
* @example
|
|
8828
|
+
* ```typescript
|
|
8829
|
+
* await client.subscription.changePlan(subscriptionId, {
|
|
8830
|
+
* provider_price_ref: 'pri_family_monthly',
|
|
8831
|
+
* plan_name: '가족 플랜',
|
|
8832
|
+
* amount: 24900,
|
|
8833
|
+
* proration_mode: 'prorated_immediately',
|
|
8834
|
+
* })
|
|
8835
|
+
* ```
|
|
8836
|
+
*/
|
|
8837
|
+
async changePlan(subscriptionId, data) {
|
|
8838
|
+
const prefix = this.getPublicPrefix();
|
|
8839
|
+
return this.http.post(
|
|
8840
|
+
`${prefix}/subscriptions/${subscriptionId}/change-plan`,
|
|
8841
|
+
data
|
|
8842
|
+
);
|
|
8843
|
+
}
|
|
8844
|
+
/**
|
|
8845
|
+
* 요금제 스왑 정산 미리보기 — 실제 적용 없이 청구/크레딧 금액만 계산 (Paddle 전용)
|
|
8846
|
+
*
|
|
8847
|
+
* 세금·기존 크레딧 잔액까지 반영된 Paddle 계산값을 반환하므로, 확정 화면에
|
|
8848
|
+
* "지금 ₩X 청구, 이후 ₩Y/주기 (다음 청구일 …)" 를 그대로 노출하면 됩니다.
|
|
8849
|
+
* 직접 차액을 계산하지 마세요 — 이 값이 권위입니다.
|
|
8850
|
+
*
|
|
8851
|
+
* @param subscriptionId - 구독 ID
|
|
8852
|
+
* @param data - 새 price + 정산 옵션 ({@link changePlan} 과 동일 body)
|
|
8853
|
+
* @returns 정산 미리보기 (지금 청구/크레딧 · 새 정기 청구액 · 다음 청구일)
|
|
8854
|
+
*
|
|
8855
|
+
* @example
|
|
8856
|
+
* ```typescript
|
|
8857
|
+
* const preview = await client.subscription.previewChangePlan(subscriptionId, {
|
|
8858
|
+
* provider_price_ref: 'pri_family_monthly',
|
|
8859
|
+
* })
|
|
8860
|
+
* // preview.immediate_charge_amount, preview.recurring_amount, preview.next_billed_at
|
|
8861
|
+
* ```
|
|
8862
|
+
*/
|
|
8863
|
+
async previewChangePlan(subscriptionId, data) {
|
|
8864
|
+
const prefix = this.getPublicPrefix();
|
|
8865
|
+
return this.http.post(
|
|
8866
|
+
`${prefix}/subscriptions/${subscriptionId}/change-plan/preview`,
|
|
8867
|
+
data
|
|
8868
|
+
);
|
|
8869
|
+
}
|
|
8813
8870
|
// =====================
|
|
8814
8871
|
// Subscription Payment APIs
|
|
8815
8872
|
// =====================
|