connectbase-client 0.1.15 → 0.1.17
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/README.md +78 -11
- package/dist/cli.js +13 -1
- package/dist/connect-base.umd.js +2 -2
- package/dist/index.d.mts +88 -3
- package/dist/index.d.ts +88 -3
- package/dist/index.js +122 -0
- package/dist/index.mjs +122 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -51,6 +51,7 @@ const state = await gameClient.createRoom({
|
|
|
51
51
|
- **Push Notifications**: Cross-platform push notification support
|
|
52
52
|
- **WebRTC**: Real-time audio/video communication
|
|
53
53
|
- **Payments**: Subscription and one-time payment support
|
|
54
|
+
- **AI Streaming**: Real-time AI text generation via WebSocket (Gemini)
|
|
54
55
|
- **CLI**: Command-line tool for deploying web storage
|
|
55
56
|
|
|
56
57
|
## CLI
|
|
@@ -68,10 +69,11 @@ npm run deploy
|
|
|
68
69
|
```
|
|
69
70
|
|
|
70
71
|
The `init` command will:
|
|
71
|
-
- Ask for your API Key
|
|
72
|
+
- Ask for your API Key
|
|
73
|
+
- List existing web storages or create a new one automatically
|
|
72
74
|
- Create a `.connectbaserc` config file
|
|
73
75
|
- Add `.connectbaserc` to `.gitignore`
|
|
74
|
-
- Add a `deploy` script to `package.json`
|
|
76
|
+
- Add a `deploy` script to `package.json` (includes `build` if available)
|
|
75
77
|
|
|
76
78
|
### Commands
|
|
77
79
|
|
|
@@ -400,22 +402,87 @@ const files = await cb.storage.list('avatars/')
|
|
|
400
402
|
### Realtime
|
|
401
403
|
|
|
402
404
|
```typescript
|
|
403
|
-
//
|
|
404
|
-
|
|
405
|
+
// Connect to WebSocket
|
|
406
|
+
await cb.realtime.connect()
|
|
405
407
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
})
|
|
408
|
+
// Subscribe to a category
|
|
409
|
+
const subscription = await cb.realtime.subscribe('chat-room')
|
|
409
410
|
|
|
410
|
-
|
|
411
|
+
// Listen for messages
|
|
412
|
+
subscription.onMessage((message) => {
|
|
413
|
+
console.log('New message:', message.data)
|
|
414
|
+
})
|
|
411
415
|
|
|
412
|
-
//
|
|
413
|
-
|
|
416
|
+
// Send message
|
|
417
|
+
await subscription.send({ text: 'Hello!' })
|
|
414
418
|
|
|
415
419
|
// Unsubscribe
|
|
416
|
-
await
|
|
420
|
+
await subscription.unsubscribe()
|
|
421
|
+
|
|
422
|
+
// Disconnect
|
|
423
|
+
await cb.realtime.disconnect()
|
|
417
424
|
```
|
|
418
425
|
|
|
426
|
+
#### AI Streaming
|
|
427
|
+
|
|
428
|
+
Real-time AI text generation using Gemini API through WebSocket.
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
// Connect first
|
|
432
|
+
await cb.realtime.connect()
|
|
433
|
+
|
|
434
|
+
// Start AI streaming
|
|
435
|
+
const session = await cb.realtime.stream(
|
|
436
|
+
[
|
|
437
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
438
|
+
{ role: 'user', content: 'Explain quantum computing in simple terms.' }
|
|
439
|
+
],
|
|
440
|
+
{
|
|
441
|
+
onToken: (token, index) => {
|
|
442
|
+
// Called for each generated token
|
|
443
|
+
process.stdout.write(token)
|
|
444
|
+
},
|
|
445
|
+
onDone: (result) => {
|
|
446
|
+
// Called when generation completes
|
|
447
|
+
console.log('\n\nFull text:', result.fullText)
|
|
448
|
+
console.log('Total tokens:', result.totalTokens)
|
|
449
|
+
console.log('Duration:', result.duration, 'ms')
|
|
450
|
+
},
|
|
451
|
+
onError: (error) => {
|
|
452
|
+
console.error('Stream error:', error.message)
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
model: 'gemini-2.0-flash', // Optional: default is gemini-2.0-flash
|
|
457
|
+
temperature: 0.7, // Optional: 0.0-2.0, default 0.7
|
|
458
|
+
maxTokens: 1000 // Optional: max output tokens
|
|
459
|
+
}
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
// Stop streaming early if needed
|
|
463
|
+
await session.stop()
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
**Stream Options:**
|
|
467
|
+
| Option | Type | Default | Description |
|
|
468
|
+
|--------|------|---------|-------------|
|
|
469
|
+
| `provider` | `'gemini'` | `'gemini'` | AI provider |
|
|
470
|
+
| `model` | `string` | `'gemini-2.0-flash'` | Model name |
|
|
471
|
+
| `system` | `string` | - | System prompt |
|
|
472
|
+
| `temperature` | `number` | `0.7` | Creativity (0.0-2.0) |
|
|
473
|
+
| `maxTokens` | `number` | - | Max output tokens |
|
|
474
|
+
| `sessionId` | `string` | auto | Session tracking ID |
|
|
475
|
+
| `metadata` | `object` | - | Custom metadata |
|
|
476
|
+
|
|
477
|
+
**Stream Result (onDone):**
|
|
478
|
+
| Field | Type | Description |
|
|
479
|
+
|-------|------|-------------|
|
|
480
|
+
| `sessionId` | `string` | Session ID |
|
|
481
|
+
| `fullText` | `string` | Complete generated text |
|
|
482
|
+
| `totalTokens` | `number` | Total tokens generated |
|
|
483
|
+
| `promptTokens` | `number` | Input prompt tokens |
|
|
484
|
+
| `duration` | `number` | Generation time in ms |
|
|
485
|
+
|
|
419
486
|
### Push Notifications
|
|
420
487
|
|
|
421
488
|
```typescript
|
package/dist/cli.js
CHANGED
|
@@ -214,6 +214,17 @@ async function deploy(directory, config) {
|
|
|
214
214
|
error(`\uB514\uB809\uD1A0\uB9AC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${dir}`);
|
|
215
215
|
process.exit(1);
|
|
216
216
|
}
|
|
217
|
+
const pkgPath = path.join(process.cwd(), "package.json");
|
|
218
|
+
if (fs.existsSync(pkgPath)) {
|
|
219
|
+
try {
|
|
220
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
221
|
+
if (pkg.scripts?.build) {
|
|
222
|
+
warn(`\uBC30\uD3EC \uC804 \uBE4C\uB4DC\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694: ${colors.cyan}npm run build && npx connectbase-client deploy${colors.reset}`);
|
|
223
|
+
warn(`\uB610\uB294 package.json\uC5D0 deploy \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uB4F1\uB85D\uD558\uC138\uC694: ${colors.cyan}npx connectbase-client init${colors.reset}`);
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
226
|
+
}
|
|
227
|
+
}
|
|
217
228
|
if (!fs.statSync(dir).isDirectory()) {
|
|
218
229
|
error(`${dir}\uB294 \uB514\uB809\uD1A0\uB9AC\uAC00 \uC544\uB2D9\uB2C8\uB2E4`);
|
|
219
230
|
process.exit(1);
|
|
@@ -412,7 +423,8 @@ function addDeployScript(deployDir) {
|
|
|
412
423
|
info(`package.json\uC5D0 \uC774\uBBF8 deploy \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uC788\uC2B5\uB2C8\uB2E4: "${pkg.scripts.deploy}"`);
|
|
413
424
|
return;
|
|
414
425
|
}
|
|
415
|
-
|
|
426
|
+
const deployCmd = `connectbase-client deploy ${deployDir}`;
|
|
427
|
+
pkg.scripts.deploy = pkg.scripts.build ? `${pkg.scripts.build} && ${deployCmd}` : deployCmd;
|
|
416
428
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
417
429
|
success(`package.json\uC5D0 deploy \uC2A4\uD06C\uB9BD\uD2B8 \uCD94\uAC00 \uC644\uB8CC`);
|
|
418
430
|
} catch {
|
package/dist/connect-base.umd.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var ConnectBaseModule=(()=>{var F=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var Z=Object.prototype.hasOwnProperty;var ee=(a,e)=>{for(var t in e)F(a,t,{get:e[t],enumerable:!0})},te=(a,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Y(e))!Z.call(a,r)&&r!==t&&F(a,r,{get:()=>e[r],enumerable:!(s=X(e,r))||s.enumerable});return a};var se=a=>te(F({},"__esModule",{value:!0}),a);var le={};ee(le,{ApiError:()=>p,AuthError:()=>d,ConnectBase:()=>D,GameAPI:()=>f,GameRoom:()=>v,GameRoomTransport:()=>G,VideoProcessingError:()=>g,default:()=>he,isWebTransportSupported:()=>H});var p=class extends Error{constructor(t,s){super(s);this.statusCode=t;this.name="ApiError"}},d=class extends Error{constructor(e){super(e),this.name="AuthError"}};var P=class{constructor(e){this.isRefreshing=!1;this.refreshPromise=null;this.config=e}updateConfig(e){this.config={...this.config,...e}}setTokens(e,t){this.config.accessToken=e,this.config.refreshToken=t}clearTokens(){this.config.accessToken=void 0,this.config.refreshToken=void 0}hasApiKey(){return!!this.config.apiKey}getApiKey(){return this.config.apiKey}getAccessToken(){return this.config.accessToken}getBaseUrl(){return this.config.baseUrl}async refreshAccessToken(){if(this.isRefreshing)return this.refreshPromise;if(this.isRefreshing=!0,!this.config.refreshToken){this.isRefreshing=!1;let e=new d("Refresh token is missing. Please login again.");throw this.config.onAuthError?.(e),e}return this.refreshPromise=(async()=>{try{let e=await fetch(`${this.config.baseUrl}/v1/auth/re-issue`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.refreshToken}`}});if(!e.ok)throw new Error("Token refresh failed");let t=await e.json();return this.config.accessToken=t.access_token,this.config.refreshToken=t.refresh_token,this.config.onTokenRefresh?.({accessToken:t.access_token,refreshToken:t.refresh_token}),t.access_token}catch{this.clearTokens();let e=new d("Token refresh failed. Please login again.");throw this.config.onAuthError?.(e),e}finally{this.isRefreshing=!1,this.refreshPromise=null}})(),this.refreshPromise}isTokenExpired(e){try{let t=JSON.parse(atob(e.split(".")[1])),s=Date.now()/1e3;return t.exp<s+300}catch{return!0}}async prepareHeaders(e){let t=new Headers;if(t.set("Content-Type","application/json"),this.config.apiKey&&t.set("X-API-Key",this.config.apiKey),!e?.skipAuth&&this.config.accessToken){let s=this.config.accessToken;if(this.isTokenExpired(s)&&this.config.refreshToken){let r=await this.refreshAccessToken();r&&(s=r)}t.set("Authorization",`Bearer ${s}`)}return e?.headers&&Object.entries(e.headers).forEach(([s,r])=>{t.set(s,r)}),t}async handleResponse(e){if(!e.ok){let t=await e.json().catch(()=>({message:e.statusText}));throw new p(e.status,t.message||t.error||"Unknown error")}return e.status===204||e.headers.get("content-length")==="0"?{}:e.json()}async get(e,t){let s=await this.prepareHeaders(t),r=await fetch(`${this.config.baseUrl}${e}`,{method:"GET",headers:s});return this.handleResponse(r)}async post(e,t,s){let r=await this.prepareHeaders(s);t instanceof FormData&&r.delete("Content-Type");let i=await fetch(`${this.config.baseUrl}${e}`,{method:"POST",headers:r,body:t instanceof FormData?t:JSON.stringify(t)});return this.handleResponse(i)}async put(e,t,s){let r=await this.prepareHeaders(s),i=await fetch(`${this.config.baseUrl}${e}`,{method:"PUT",headers:r,body:JSON.stringify(t)});return this.handleResponse(i)}async patch(e,t,s){let r=await this.prepareHeaders(s),i=await fetch(`${this.config.baseUrl}${e}`,{method:"PATCH",headers:r,body:JSON.stringify(t)});return this.handleResponse(i)}async delete(e,t){let s=await this.prepareHeaders(t),r=await fetch(`${this.config.baseUrl}${e}`,{method:"DELETE",headers:s});return this.handleResponse(r)}};var V="cb_anon_",N="cb_guest_";function j(a){let e=0;for(let t=0;t<a.length;t++){let s=a.charCodeAt(t);e=(e<<5)-e+s,e=e&e}return Math.abs(e).toString(36)}var w=class{constructor(e){this.http=e;this.anonymousLoginPromise=null;this.guestMemberLoginPromise=null;this.cachedAnonymousUIDKey=null;this.cachedGuestMemberTokenKey=null}async getAuthSettings(){return this.http.get("/v1/public/auth-settings",{skipAuth:!0})}getAnonymousUIDKey(){if(this.cachedAnonymousUIDKey)return this.cachedAnonymousUIDKey;let e=this.http.getApiKey();if(!e)this.cachedAnonymousUIDKey=`${V}default`;else{let t=j(e);this.cachedAnonymousUIDKey=`${V}${t}`}return this.cachedAnonymousUIDKey}async signUp(e){let t=await this.http.post("/v1/auth/signup",e,{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),t}async signIn(e){let t=await this.http.post("/v1/auth/signin",e,{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),t}async signInAnonymously(){if(this.anonymousLoginPromise)return this.anonymousLoginPromise;this.anonymousLoginPromise=this.executeAnonymousLogin();try{return await this.anonymousLoginPromise}finally{this.anonymousLoginPromise=null}}async executeAnonymousLogin(){let e=this.getStoredAnonymousUID(),t=await this.http.post("/v1/auth/signin/anonymous",e?{anonymous_uid:e}:{},{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),t.anonymous_uid&&this.storeAnonymousUID(t.anonymous_uid),t}getStoredAnonymousUID(){return typeof localStorage>"u"?null:localStorage.getItem(this.getAnonymousUIDKey())}storeAnonymousUID(e){typeof localStorage>"u"||localStorage.setItem(this.getAnonymousUIDKey(),e)}clearAnonymousUID(){typeof localStorage>"u"||localStorage.removeItem(this.getAnonymousUIDKey())}async signOut(){try{await this.http.post("/v1/auth/logout")}finally{this.http.clearTokens()}}async getCurrentUser(e=!1){try{return await this.http.get("/v1/auth")}catch(t){let s=t instanceof p&&(t.statusCode===401||t.statusCode===403)||t instanceof d;if(e&&s)return await this.signInAnonymously(),{...await this.http.get("/v1/auth"),is_anonymous:!0};throw t}}async resendVerificationEmail(){await this.http.post("/v1/auth/re-send-activate-email")}async signInAsGuestMember(){if(this.guestMemberLoginPromise)return this.guestMemberLoginPromise;this.guestMemberLoginPromise=this.executeGuestMemberLogin();try{return await this.guestMemberLoginPromise}finally{this.guestMemberLoginPromise=null}}async executeGuestMemberLogin(){let e=this.getStoredGuestMemberTokens();if(e){if(!this.isTokenExpired(e.accessToken))try{this.http.setTokens(e.accessToken,e.refreshToken);let s=await this.http.get("/v1/public/app-members/me");if(s.is_active)return{member_id:s.member_id,access_token:e.accessToken,refresh_token:e.refreshToken};this.clearGuestMemberTokens()}catch{this.http.clearTokens()}if(e.refreshToken&&!this.isTokenExpired(e.refreshToken))try{let s=await this.http.post("/v1/auth/re-issue",{},{headers:{Authorization:`Bearer ${e.refreshToken}`},skipAuth:!0});return this.http.setTokens(s.access_token,s.refresh_token),this.storeGuestMemberTokens(s.access_token,s.refresh_token,e.memberId),{member_id:e.memberId,access_token:s.access_token,refresh_token:s.refresh_token}}catch{this.clearGuestMemberTokens()}else this.clearGuestMemberTokens()}let t=await this.http.post("/v1/public/app-members",{},{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),this.storeGuestMemberTokens(t.access_token,t.refresh_token,t.member_id),t}isTokenExpired(e){try{let t=JSON.parse(atob(e.split(".")[1])),s=Date.now()/1e3;return t.exp<s}catch{return!0}}getGuestMemberTokenKey(){if(this.cachedGuestMemberTokenKey)return this.cachedGuestMemberTokenKey;let e=this.http.getApiKey();if(!e)this.cachedGuestMemberTokenKey=`${N}default`;else{let t=j(e);this.cachedGuestMemberTokenKey=`${N}${t}`}return this.cachedGuestMemberTokenKey}getStoredGuestMemberTokens(){if(typeof localStorage>"u")return null;let e=localStorage.getItem(this.getGuestMemberTokenKey());if(!e)return null;try{return JSON.parse(e)}catch{return null}}storeGuestMemberTokens(e,t,s){typeof localStorage>"u"||localStorage.setItem(this.getGuestMemberTokenKey(),JSON.stringify({accessToken:e,refreshToken:t,memberId:s}))}clearGuestMemberTokens(){typeof localStorage>"u"||localStorage.removeItem(this.getGuestMemberTokenKey())}async signUpMember(e){let t=await this.http.post("/v1/public/app-members/signup",e,{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),t}async signInMember(e){let t=await this.http.post("/v1/public/app-members/signin",e,{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),t}};var S=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/v1/public":"/v1"}async getTables(e){return(await this.http.get(`/v1/databases/json-databases/${e}/tables`)).tables}async createTable(e,t){return this.http.post(`/v1/databases/json-databases/${e}/tables`,t)}async deleteTable(e,t){await this.http.delete(`/v1/databases/json-databases/${e}/tables/${t}`)}async getColumns(e){return(await this.http.get(`/v1/tables/${e}/columns`)).columns}async createColumn(e,t){return this.http.post(`/v1/tables/${e}/columns`,t)}async updateColumn(e,t,s){return this.http.patch(`/v1/tables/${e}/columns/${t}`,s)}async deleteColumn(e,t){await this.http.delete(`/v1/tables/${e}/columns/${t}`)}async getData(e,t){let s=this.getPublicPrefix();if(t?.where)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());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})}async getDataById(e,t){let s=this.getPublicPrefix();return this.http.get(`${s}/tables/${e}/data/${t}`)}async createData(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data`,t)}async updateData(e,t,s){let r=this.getPublicPrefix();return this.http.put(`${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})}};var R=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/v1/public":"/v1"}async getFiles(e){let t=this.getPublicPrefix();return(await this.http.get(`${t}/storages/files/${e}/items`)).files}async uploadFile(e,t,s){let r=this.getPublicPrefix(),i=new FormData;return i.append("file",t),s&&i.append("parent_id",s),this.http.post(`${r}/storages/files/${e}/upload`,i)}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){let r=this.getPublicPrefix();await this.http.post(`${r}/storages/files/${e}/items/${t}/move`,s)}async renameFile(e,t,s){let r=this.getPublicPrefix();return this.http.patch(`${r}/storages/files/${e}/items/${t}/rename`,s)}getFileUrl(e){return e.url||null}isImageFile(e){return e.mime_type?.startsWith("image/")||!1}};var T=class{constructor(e){this.http=e}async getApiKeys(e){return this.http.get(`/v1/apps/${e}/api-keys`)}async createApiKey(e,t){return this.http.post(`/v1/apps/${e}/api-keys`,t)}async updateApiKey(e,t,s){return this.http.patch(`/v1/apps/${e}/api-keys/${t}`,s)}async deleteApiKey(e,t){await this.http.delete(`/v1/apps/${e}/api-keys/${t}`)}};var C=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/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 call(e,t){let s=await this.invoke(e,t);if(!s.success)throw new Error(s.error||"Function execution failed");return s.result}};var k=class{constructor(e,t){this.ws=null;this.state="disconnected";this._connectionId=null;this._appId=null;this.options={maxRetries:5,retryInterval:1e3,userId:"",accessToken:""};this.retryCount=0;this.pendingRequests=new Map;this.subscriptions=new Map;this.stateHandlers=[];this.errorHandlers=[];this.http=e,this.socketUrl=t,this.clientId=this.generateClientId()}get connectionId(){return this._connectionId}get appId(){return this._appId}async connect(e={}){if(!(this.state==="connected"||this.state==="connecting"))return this.options={...this.options,...e},e.userId&&(this.userId=e.userId),this.doConnect()}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()}async subscribe(e,t={}){if(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,h)=>{await this.sendMessage(e,c,h)},getHistory:async c=>this.getHistory(e,c??t.historyLimit),unsubscribe:async()=>{await this.unsubscribe(e)},onMessage:c=>{n.push(c)}}}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.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.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}}getState(){return this.state}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 doConnect(){return new Promise((e,t)=>{this.state="connecting",this.notifyStateChange();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}`;else{let i=this.http.getApiKey();if(!i){t(new Error("API Key or accessToken is required for realtime connection"));return}r=`${s}/v1/realtime/auth?api_key=${encodeURIComponent(i)}&client_id=${this.clientId}`}this.userId&&(r+=`&user_id=${encodeURIComponent(this.userId)}`);try{this.ws=new WebSocket(r),this.ws.onopen=()=>{},this.ws.onmessage=i=>{let n=i.data.split(`
|
|
2
|
-
`).filter(o=>o.trim());for(let o of n)try{let c=JSON.parse(o);this.handleServerMessage(c,e)}catch(c){console.error("[Realtime] Failed to parse message:",o,c)}},this.ws.onclose=()=>{(this.state==="connected"||this.state==="connecting")&&this.handleDisconnect()},this.ws.onerror=i=>{console.error("[Realtime] WebSocket error:",i),this.notifyError(new Error("WebSocket connection error")),this.state==="connecting"&&t(new Error("Failed to connect"))}}catch(i){t(i)}})}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.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}}handleDisconnect(){this.ws=null,this._connectionId=null,this.retryCount<this.options.maxRetries?(this.state="reconnecting",this.notifyStateChange(),this.retryCount++,setTimeout(()=>{this.doConnect().catch(e=>{console.error("[Realtime] Reconnect failed:",e)})},this.options.retryInterval*this.retryCount)):(this.state="disconnected",this.notifyStateChange(),this.notifyError(new Error("Connection lost. Max retries exceeded.")))}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"))},3e4);this.pendingRequests.set(e.request_id,{resolve:t,reject:s,timeout:r}),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 _=class{constructor(e,t){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}async getICEServers(){let e=await this.http.get("/v1/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.getApiKey(),s=this.http.getAccessToken(),r="";return s?r=`access_token=${encodeURIComponent(s)}`:t&&(r=`api_key=${encodeURIComponent(t)}`),`${e}/v1/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 n=e.data;n.channel_type&&(this.channelType=n.channel_type);let o=n.peers||[];for(let c of o)c.peer_id!==this.currentPeerId&&await this.createPeerConnection(c.peer_id,!0)}t?.();break;case"peer_joined":if(e.peer_id&&e.peer_id!==this.currentPeerId){let n={peer_id:e.peer_id,...typeof e.data=="object"?e.data:{}};this.emitPeerJoined(e.peer_id,n),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*Math.pow(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 getStats(e){return this.http.get(`/v1/apps/${e}/webrtc/stats`)}async getRooms(e){return this.http.get(`/v1/apps/${e}/webrtc/rooms`)}};var E=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 x=class{constructor(e){this.http=e}async getEnabledProviders(){return this.http.get("/v1/public/oauth/providers")}async signIn(e,t,s){let r=new URLSearchParams({app_callback:t});s&&r.append("state",s);let i=await this.http.get(`/v1/public/oauth/${e}/authorize/central?${r.toString()}`);window.location.href=i.authorization_url}async signInWithPopup(e,t){let s=new URLSearchParams({app_callback:t}),r=await this.http.get(`/v1/public/oauth/${e}/authorize/central?${s.toString()}`),i=500,n=600,o=window.screenX+(window.outerWidth-i)/2,c=window.screenY+(window.outerHeight-n)/2,h=window.open(r.authorization_url,"oauth-popup",`width=${i},height=${n},left=${o},top=${c}`);if(!h)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((m,q)=>{let u=async l=>{if(l.data?.type!=="oauth-callback")return;if(window.removeEventListener("message",u),l.data.error){q(new Error(l.data.error));return}let y={member_id:l.data.member_id,access_token:l.data.access_token,refresh_token:l.data.refresh_token,is_new_member:l.data.is_new_member==="true"||l.data.is_new_member===!0};this.http.setTokens(y.access_token,y.refresh_token),m(y)};window.addEventListener("message",u);let b=setInterval(()=>{h.closed&&(clearInterval(b),window.removeEventListener("message",u),q(new Error("\uB85C\uADF8\uC778\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.")))},500)})}getCallbackResult(){let e=new URLSearchParams(window.location.search),t=e.get("error");if(t){let o={error:t};return window.opener&&(window.opener.postMessage({type:"oauth-callback",...o},"*"),window.close()),o}let s=e.get("access_token"),r=e.get("refresh_token"),i=e.get("member_id");if(!s||!r||!i)return null;let n={access_token:s,refresh_token:r,member_id:i,is_new_member:e.get("is_new_member")==="true",state:e.get("state")||void 0};return window.opener?(window.opener.postMessage({type:"oauth-callback",...n},"*"),window.close(),n):(this.http.setTokens(s,r),n)}};var I=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/v1/public":"/v1"}async prepare(e){let t=this.getPublicPrefix();return this.http.post(`${t}/payments/prepare`,e)}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/${e}/cancel`,t)}async getByOrderId(e){let t=this.getPublicPrefix();return this.http.get(`${t}/payments/orders/${e}`)}};var $=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/v1/public":"/v1"}async issueBillingKey(){let e=this.getPublicPrefix();return this.http.post(`${e}/subscriptions/billing-keys/issue`,{})}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();return this.http.post(`${t}/subscriptions`,e)}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();return this.http.post(`${s}/subscriptions/${e}/cancel`,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 A=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/v1/public":"/v1"}async registerDevice(e){let t=this.getPublicPrefix();return this.http.post(`${t}/push/devices`,e)}async unregisterDevice(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/push/devices/${e}`)}async getDevices(){let e=this.getPublicPrefix();return(await this.http.get(`${e}/push/devices`)).devices||[]}async subscribeTopic(e){let t=this.getPublicPrefix(),s={topic_name:e};await this.http.post(`${t}/push/topics/subscribe`,s)}async unsubscribeTopic(e){let t=this.getPublicPrefix(),s={topic_name:e};await this.http.post(`${t}/push/topics/unsubscribe`,s)}async getSubscribedTopics(){let e=this.getPublicPrefix();return(await this.http.get(`${e}/push/topics/subscribed`)).topics||[]}async getVAPIDPublicKey(){let e=this.getPublicPrefix();return this.http.get(`${e}/push/vapid-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/web`,{...r,web_push_subscription:s})}async unregisterWebPush(){let e=this.getPublicPrefix();await this.http.delete(`${e}/push/devices/web`)}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 re=5*1024*1024,g=class extends Error{constructor(e,t){super(e),this.name="VideoProcessingError",this.video=t}},U=class{constructor(e,t){this.http=e;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.hasApiKey()?"/v1/public":"/v1"}async videoFetch(e,t,s){let r={},i=this.http.getApiKey();i&&(r["X-API-Key"]=i);let n=this.http.getAccessToken();n&&(r.Authorization=`Bearer ${n}`),s&&!(s instanceof FormData)&&(r["Content-Type"]="application/json");let o=await fetch(`${this.videoBaseUrl}${t}`,{method:e,headers:r,body:s instanceof FormData?s:s?JSON.stringify(s):void 0});if(!o.ok){let c=await o.json().catch(()=>({message:o.statusText}));throw new p(o.status,c.message||"Unknown error")}return o.status===204||o.headers.get("content-length")==="0"?{}:o.json()}async upload(e,t){let s=this.getPublicPrefix(),r=await this.videoFetch("POST",`${s}/uploads`,{filename:e.name,size:e.size,mime_type:e.type,title:t.title,description:t.description,visibility:t.visibility||"private",tags:t.tags,channel_id:t.channel_id}),i=r.chunk_size||re,n=Math.ceil(e.size/i),o=0,h=Date.now(),m=0;for(let u=0;u<n;u++){let b=u*i,l=Math.min(b+i,e.size),y=e.slice(b,l),L=new FormData;L.append("chunk",y),L.append("chunk_index",String(u)),await this.videoFetch("POST",`${s}/uploads/${r.session_id}/chunks`,L),o++;let O=Date.now(),W=(O-h)/1e3,B=l,J=B-m,Q=W>0?J/W:0;h=O,m=B,t.onProgress&&t.onProgress({phase:"uploading",uploadedChunks:o,totalChunks:n,percentage:Math.round(o/n*100),currentSpeed:Q})}return(await this.videoFetch("POST",`${s}/uploads/${r.session_id}/complete`,{})).video}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 g("Video processing failed",o);if(t?.onProgress){let c=o.qualities.filter(m=>m.status==="ready").length,h=o.qualities.length||1;t.onProgress({phase:"processing",uploadedChunks:0,totalChunks:0,percentage:Math.round(c/h*100)})}await new Promise(c=>setTimeout(c,r))}throw new g("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();return(await this.videoFetch("GET",`${t}/videos/${e}/thumbnails`)).thumbnails}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}/channels/${e}/playlists`,t)}async getPlaylists(e){let t=this.getPublicPrefix();return(await this.videoFetch("GET",`${t}/channels/${e}/playlists`)).playlists}async getPlaylistItems(e){let t=this.getPublicPrefix();return(await this.videoFetch("GET",`${t}/playlists/${e}/items`)).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}/videos/${e}/comments`,{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}/memberships/tiers`)).tiers}async joinMembership(e,t){let s=this.getPublicPrefix();return this.videoFetch("POST",`${s}/channels/${e}/memberships/${t}/join`,{})}async cancelMembership(e,t){let s=this.getPublicPrefix();await this.videoFetch("POST",`${s}/channels/${e}/memberships/${t}/cancel`,{})}async sendSuperChat(e,t,s,r){let i=this.getPublicPrefix();return this.videoFetch("POST",`${i}/videos/${e}/super-chats`,{amount:t,message:s,currency:r||"USD"})}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${s}`)).videos}async getHomeFeed(e){let t=this.getPublicPrefix(),s=e?`?limit=${e}`:"";return(await this.videoFetch("GET",`${t}/recommendations/home${s}`)).videos}async getRelatedVideos(e,t){let s=this.getPublicPrefix(),r=t?`?limit=${t}`:"";return(await this.videoFetch("GET",`${s}/recommendations/related/${e}${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();await this.videoFetch("POST",`${s}/recommendations/feedback`,{video_id:e,feedback:t})}};var z=()=>{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"},v=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._isConnected=!1;this.config={gameServerUrl:z(),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}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=h=>{this._isConnected=!1,this.stopPingInterval(),this.handlers.onDisconnect?.(h),this.config.autoReconnect&&h.code!==1e3&&this.scheduleReconnect(e)},o=h=>{this.handlers.onError?.(h),s(new Error("WebSocket connection failed"))},c=h=>{this.handleMessage(h.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}createRoom(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,t(n.initial_state)}else i.type==="error"&&s(new Error(i.data.message))};this.sendWithHandler("create_room",e,r)})}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,s(o.initial_state)}else n.type==="error"&&r(new Error(n.data.message))};this.sendWithHandler("join_room",{room_id:e,metadata:t},i)})}leaveRoom(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("Not in a room"));return}let s=r=>{r.type==="room_left"?(this._roomId=null,this._state=null,e()):r.type==="error"&&t(new Error(r.data.message))};this.sendWithHandler("leave_room",{},s)})}sendAction(e){if(!this._roomId)throw new Error("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 Error("Not in a room");this.send("chat",{message:e})}requestState(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("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(new Error(r.data.message))};this.sendWithHandler("get_state",{},s)})}listRooms(){return new Promise((e,t)=>{let s=r=>{if(r.type==="room_list"){let i=r.data;e(i.rooms)}else r.type==="error"&&t(new Error(r.data.message))};this.sendWithHandler("list_rooms",{},s)})}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.handlers.onPong?.(n),e(o)}else i.type==="error"&&t(new Error(i.data.message))};this.sendWithHandler("ping",{timestamp:s},r)})}buildConnectionUrl(e){let s=this.config.gameServerUrl.replace(/^http/,"ws"),r=new URLSearchParams;return r.set("client_id",this.config.clientId),e&&r.set("room_id",e),this.config.apiKey&&r.set("api_key",this.config.apiKey),this.config.accessToken&&r.set("token",this.config.accessToken),`${s}/v1/game/ws?${r.toString()}`}send(e,t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("WebSocket is not connected");this.ws.send(JSON.stringify({type:e,data:t}))}sendWithHandler(e,t,s){let r=i=>{try{let n=JSON.parse(i.data);s(n),this.ws?.removeEventListener("message",r)}catch{}};this.ws?.addEventListener("message",r),this.send(e,t)}handleMessage(e){try{let t=JSON.parse(e);switch(t.type){case"delta":this.handleDelta(t.data);break;case"state":this._state=t.data,this.handlers.onStateUpdate?.(this._state);break;case"player_event":this.handlePlayerEvent(t.data);break;case"chat":this.handlers.onChat?.(t.data);break;case"error":this.handlers.onError?.(t.data);break;default:break}}catch{console.error("Failed to parse game message:",e)}}handleDelta(e){let t=e.delta;if(this._state){for(let s of t.changes)this.applyChange(s);this._state.version=t.toVersion}this.handlers.onDelta?.(t)}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){e.event==="joined"?this.handlers.onPlayerJoined?.(e.player):e.event==="left"&&this.handlers.onPlayerLeft?.(e.player)}scheduleReconnect(e){if(this.reconnectAttempts>=(this.config.maxReconnectAttempts??5)){console.error("Max reconnect attempts reached");return}let t=Math.min((this.config.reconnectInterval??1e3)*Math.pow(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)}},f=class{constructor(e,t){this.http=e,this.gameServerUrl=t||z().replace(/^ws/,"http")}createClient(e){return new v({...e,gameServerUrl:this.gameServerUrl.replace(/^http/,"ws"),apiKey:this.http.getApiKey(),accessToken:this.http.getAccessToken()})}async listRooms(e){let t=await fetch(`${this.gameServerUrl}/v1/game/rooms${e?`?app_id=${e}`:""}`,{headers:this.getHeaders()});if(!t.ok)throw new Error(`Failed to list rooms: ${t.statusText}`);return(await t.json()).rooms}async getRoom(e){let t=await fetch(`${this.gameServerUrl}/v1/game/rooms/${e}`,{headers:this.getHeaders()});if(!t.ok)throw new Error(`Failed to get room: ${t.statusText}`);return t.json()}async createRoom(e,t={}){let s=await fetch(`${this.gameServerUrl}/v1/game/rooms`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({app_id:e,category_id:t.categoryId,room_id:t.roomId,tick_rate:t.tickRate,max_players:t.maxPlayers,metadata:t.metadata})});if(!s.ok)throw new Error(`Failed to create room: ${s.statusText}`);return s.json()}async deleteRoom(e){let t=await fetch(`${this.gameServerUrl}/v1/game/rooms/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!t.ok)throw new Error(`Failed to delete room: ${t.statusText}`)}getHeaders(){let e={},t=this.http.getApiKey();t&&(e["X-API-Key"]=t);let s=this.http.getAccessToken();return s&&(e.Authorization=`Bearer ${s}`),e}};var K=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.apiKey&&s.set("api_key",this.config.apiKey),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 Error("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}},M=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 h=new Error("WebSocket error");this.onError(h),t(h)},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;return s.set("client_id",this.config.clientId),this.config.apiKey&&s.set("api_key",this.config.apiKey),this.config.accessToken&&s.set("token",this.config.accessToken),`${t}/v1/game/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 Error("Not connected");typeof e=="string"?this.ws.send(e):this.ws.send(e)}isConnected(){return this.ws!==null&&this.ws.readyState===WebSocket.OPEN}};function H(){return typeof WebTransport<"u"}var G=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._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 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")&&H(),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?.(o)};if(s)try{this.transport=new K(this.config,r,i,n),await this.transport.connect(),this._transportType="webtransport"}catch{console.log("WebTransport failed, falling back to WebSocket"),this.transport=new M(this.config,r,i,n),await this.transport.connect(),this._transportType="websocket"}else this.transport=new M(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 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,t(n.initial_state)}else i.type==="error"&&s(new Error(i.data.message))};this.sendWithHandler("create_room",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,s(o.initial_state)}else n.type==="error"&&r(new Error(n.data.message))};this.sendWithHandler("join_room",{room_id:e,metadata:t},i)})}async leaveRoom(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("Not in a room"));return}let s=r=>{r.type==="room_left"?(this._roomId=null,this._state=null,e()):r.type==="error"&&t(new Error(r.data.message))};this.sendWithHandler("leave_room",{},s)})}sendAction(e,t=!1){if(!this._roomId)throw new Error("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 Error("Not in a room");this.send("chat",{message:e})}async requestState(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("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(new Error(r.data.message))};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(new Error(i.data.message))};this.sendWithHandler("ping",{timestamp:s},r)})}send(e,t){if(!this.transport?.isConnected())throw new Error("Not connected");let s=JSON.stringify({type:e,data:t});this.transport.send(s,!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,_msg_id: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.data);break;case"state":this._state=t.data,this.handlers.onStateUpdate?.(this._state);break;case"player_event":this.handlePlayerEvent(t.data);break;case"chat":this.handlers.onChat?.(t.data);break;case"error":this.handlers.onError?.(t.data);break}}catch{console.error("Failed to parse game message:",e)}}handleDelta(e){let t=e.delta;if(this._state){for(let s of t.changes)this.applyChange(s);this._state.version=t.toVersion}this.handlers.onDelta?.(t)}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){e.event==="joined"?this.handlers.onPlayerJoined?.(e.player):e.event==="left"&&this.handlers.onPlayerLeft?.(e.player)}scheduleReconnect(e){if(this.reconnectAttempts>=(this.config.maxReconnectAttempts??5)){console.error("Max reconnect attempts reached");return}let t=Math.min((this.config.reconnectInterval??1e3)*Math.pow(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 ie="https://api.connectbase.world",ne="https://socket.connectbase.world",oe="https://webrtc.connectbase.world",ae="https://video.connectbase.world",ce="https://game.connectbase.world",D=class{constructor(e={}){let t={baseUrl:e.baseUrl||ie,apiKey:e.apiKey,onTokenRefresh:e.onTokenRefresh,onAuthError:e.onAuthError};this.http=new P(t),this.auth=new w(this.http),this.database=new S(this.http),this.storage=new R(this.http),this.apiKey=new T(this.http),this.functions=new C(this.http),this.realtime=new k(this.http,e.socketUrl||ne),this.webrtc=new _(this.http,e.webrtcUrl||oe),this.errorTracker=new E(this.http,e.errorTracker),this.oauth=new x(this.http),this.payment=new I(this.http),this.subscription=new $(this.http),this.push=new A(this.http),this.video=new U(this.http,e.videoUrl||ae),this.game=new f(this.http,e.gameUrl||ce)}setTokens(e,t){this.http.setTokens(e,t)}clearTokens(){this.http.clearTokens()}updateConfig(e){this.http.updateConfig(e)}},he=D;return se(le);})();
|
|
1
|
+
"use strict";var ConnectBaseModule=(()=>{var F=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var Z=Object.prototype.hasOwnProperty;var ee=(a,e)=>{for(var t in e)F(a,t,{get:e[t],enumerable:!0})},te=(a,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Y(e))!Z.call(a,r)&&r!==t&&F(a,r,{get:()=>e[r],enumerable:!(s=X(e,r))||s.enumerable});return a};var se=a=>te(F({},"__esModule",{value:!0}),a);var le={};ee(le,{ApiError:()=>p,AuthError:()=>d,ConnectBase:()=>D,GameAPI:()=>f,GameRoom:()=>v,GameRoomTransport:()=>q,VideoProcessingError:()=>g,default:()=>he,isWebTransportSupported:()=>H});var p=class extends Error{constructor(t,s){super(s);this.statusCode=t;this.name="ApiError"}},d=class extends Error{constructor(e){super(e),this.name="AuthError"}};var P=class{constructor(e){this.isRefreshing=!1;this.refreshPromise=null;this.config=e}updateConfig(e){this.config={...this.config,...e}}setTokens(e,t){this.config.accessToken=e,this.config.refreshToken=t}clearTokens(){this.config.accessToken=void 0,this.config.refreshToken=void 0}hasApiKey(){return!!this.config.apiKey}getApiKey(){return this.config.apiKey}getAccessToken(){return this.config.accessToken}getBaseUrl(){return this.config.baseUrl}async refreshAccessToken(){if(this.isRefreshing)return this.refreshPromise;if(this.isRefreshing=!0,!this.config.refreshToken){this.isRefreshing=!1;let e=new d("Refresh token is missing. Please login again.");throw this.config.onAuthError?.(e),e}return this.refreshPromise=(async()=>{try{let e=await fetch(`${this.config.baseUrl}/v1/auth/re-issue`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.refreshToken}`}});if(!e.ok)throw new Error("Token refresh failed");let t=await e.json();return this.config.accessToken=t.access_token,this.config.refreshToken=t.refresh_token,this.config.onTokenRefresh?.({accessToken:t.access_token,refreshToken:t.refresh_token}),t.access_token}catch{this.clearTokens();let e=new d("Token refresh failed. Please login again.");throw this.config.onAuthError?.(e),e}finally{this.isRefreshing=!1,this.refreshPromise=null}})(),this.refreshPromise}isTokenExpired(e){try{let t=JSON.parse(atob(e.split(".")[1])),s=Date.now()/1e3;return t.exp<s+300}catch{return!0}}async prepareHeaders(e){let t=new Headers;if(t.set("Content-Type","application/json"),this.config.apiKey&&t.set("X-API-Key",this.config.apiKey),!e?.skipAuth&&this.config.accessToken){let s=this.config.accessToken;if(this.isTokenExpired(s)&&this.config.refreshToken){let r=await this.refreshAccessToken();r&&(s=r)}t.set("Authorization",`Bearer ${s}`)}return e?.headers&&Object.entries(e.headers).forEach(([s,r])=>{t.set(s,r)}),t}async handleResponse(e){if(!e.ok){let t=await e.json().catch(()=>({message:e.statusText}));throw new p(e.status,t.message||t.error||"Unknown error")}return e.status===204||e.headers.get("content-length")==="0"?{}:e.json()}async get(e,t){let s=await this.prepareHeaders(t),r=await fetch(`${this.config.baseUrl}${e}`,{method:"GET",headers:s});return this.handleResponse(r)}async post(e,t,s){let r=await this.prepareHeaders(s);t instanceof FormData&&r.delete("Content-Type");let i=await fetch(`${this.config.baseUrl}${e}`,{method:"POST",headers:r,body:t instanceof FormData?t:JSON.stringify(t)});return this.handleResponse(i)}async put(e,t,s){let r=await this.prepareHeaders(s),i=await fetch(`${this.config.baseUrl}${e}`,{method:"PUT",headers:r,body:JSON.stringify(t)});return this.handleResponse(i)}async patch(e,t,s){let r=await this.prepareHeaders(s),i=await fetch(`${this.config.baseUrl}${e}`,{method:"PATCH",headers:r,body:JSON.stringify(t)});return this.handleResponse(i)}async delete(e,t){let s=await this.prepareHeaders(t),r=await fetch(`${this.config.baseUrl}${e}`,{method:"DELETE",headers:s});return this.handleResponse(r)}};var N="cb_anon_",V="cb_guest_";function j(a){let e=0;for(let t=0;t<a.length;t++){let s=a.charCodeAt(t);e=(e<<5)-e+s,e=e&e}return Math.abs(e).toString(36)}var w=class{constructor(e){this.http=e;this.anonymousLoginPromise=null;this.guestMemberLoginPromise=null;this.cachedAnonymousUIDKey=null;this.cachedGuestMemberTokenKey=null}async getAuthSettings(){return this.http.get("/v1/public/auth-settings",{skipAuth:!0})}getAnonymousUIDKey(){if(this.cachedAnonymousUIDKey)return this.cachedAnonymousUIDKey;let e=this.http.getApiKey();if(!e)this.cachedAnonymousUIDKey=`${N}default`;else{let t=j(e);this.cachedAnonymousUIDKey=`${N}${t}`}return this.cachedAnonymousUIDKey}async signUp(e){let t=await this.http.post("/v1/auth/signup",e,{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),t}async signIn(e){let t=await this.http.post("/v1/auth/signin",e,{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),t}async signInAnonymously(){if(this.anonymousLoginPromise)return this.anonymousLoginPromise;this.anonymousLoginPromise=this.executeAnonymousLogin();try{return await this.anonymousLoginPromise}finally{this.anonymousLoginPromise=null}}async executeAnonymousLogin(){let e=this.getStoredAnonymousUID(),t=await this.http.post("/v1/auth/signin/anonymous",e?{anonymous_uid:e}:{},{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),t.anonymous_uid&&this.storeAnonymousUID(t.anonymous_uid),t}getStoredAnonymousUID(){return typeof localStorage>"u"?null:localStorage.getItem(this.getAnonymousUIDKey())}storeAnonymousUID(e){typeof localStorage>"u"||localStorage.setItem(this.getAnonymousUIDKey(),e)}clearAnonymousUID(){typeof localStorage>"u"||localStorage.removeItem(this.getAnonymousUIDKey())}async signOut(){try{await this.http.post("/v1/auth/logout")}finally{this.http.clearTokens()}}async getCurrentUser(e=!1){try{return await this.http.get("/v1/auth")}catch(t){let s=t instanceof p&&(t.statusCode===401||t.statusCode===403)||t instanceof d;if(e&&s)return await this.signInAnonymously(),{...await this.http.get("/v1/auth"),is_anonymous:!0};throw t}}async resendVerificationEmail(){await this.http.post("/v1/auth/re-send-activate-email")}async signInAsGuestMember(){if(this.guestMemberLoginPromise)return this.guestMemberLoginPromise;this.guestMemberLoginPromise=this.executeGuestMemberLogin();try{return await this.guestMemberLoginPromise}finally{this.guestMemberLoginPromise=null}}async executeGuestMemberLogin(){let e=this.getStoredGuestMemberTokens();if(e){if(!this.isTokenExpired(e.accessToken))try{this.http.setTokens(e.accessToken,e.refreshToken);let s=await this.http.get("/v1/public/app-members/me");if(s.is_active)return{member_id:s.member_id,access_token:e.accessToken,refresh_token:e.refreshToken};this.clearGuestMemberTokens()}catch{this.http.clearTokens()}if(e.refreshToken&&!this.isTokenExpired(e.refreshToken))try{let s=await this.http.post("/v1/auth/re-issue",{},{headers:{Authorization:`Bearer ${e.refreshToken}`},skipAuth:!0});return this.http.setTokens(s.access_token,s.refresh_token),this.storeGuestMemberTokens(s.access_token,s.refresh_token,e.memberId),{member_id:e.memberId,access_token:s.access_token,refresh_token:s.refresh_token}}catch{this.clearGuestMemberTokens()}else this.clearGuestMemberTokens()}let t=await this.http.post("/v1/public/app-members",{},{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),this.storeGuestMemberTokens(t.access_token,t.refresh_token,t.member_id),t}isTokenExpired(e){try{let t=JSON.parse(atob(e.split(".")[1])),s=Date.now()/1e3;return t.exp<s}catch{return!0}}getGuestMemberTokenKey(){if(this.cachedGuestMemberTokenKey)return this.cachedGuestMemberTokenKey;let e=this.http.getApiKey();if(!e)this.cachedGuestMemberTokenKey=`${V}default`;else{let t=j(e);this.cachedGuestMemberTokenKey=`${V}${t}`}return this.cachedGuestMemberTokenKey}getStoredGuestMemberTokens(){if(typeof localStorage>"u")return null;let e=localStorage.getItem(this.getGuestMemberTokenKey());if(!e)return null;try{return JSON.parse(e)}catch{return null}}storeGuestMemberTokens(e,t,s){typeof localStorage>"u"||localStorage.setItem(this.getGuestMemberTokenKey(),JSON.stringify({accessToken:e,refreshToken:t,memberId:s}))}clearGuestMemberTokens(){typeof localStorage>"u"||localStorage.removeItem(this.getGuestMemberTokenKey())}async signUpMember(e){let t=await this.http.post("/v1/public/app-members/signup",e,{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),t}async signInMember(e){let t=await this.http.post("/v1/public/app-members/signin",e,{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),t}};var S=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/v1/public":"/v1"}async getTables(e){return(await this.http.get(`/v1/databases/json-databases/${e}/tables`)).tables}async createTable(e,t){return this.http.post(`/v1/databases/json-databases/${e}/tables`,t)}async deleteTable(e,t){await this.http.delete(`/v1/databases/json-databases/${e}/tables/${t}`)}async getColumns(e){return(await this.http.get(`/v1/tables/${e}/columns`)).columns}async createColumn(e,t){return this.http.post(`/v1/tables/${e}/columns`,t)}async updateColumn(e,t,s){return this.http.patch(`/v1/tables/${e}/columns/${t}`,s)}async deleteColumn(e,t){await this.http.delete(`/v1/tables/${e}/columns/${t}`)}async getData(e,t){let s=this.getPublicPrefix();if(t?.where)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());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})}async getDataById(e,t){let s=this.getPublicPrefix();return this.http.get(`${s}/tables/${e}/data/${t}`)}async createData(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data`,t)}async updateData(e,t,s){let r=this.getPublicPrefix();return this.http.put(`${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})}};var R=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/v1/public":"/v1"}async getFiles(e){let t=this.getPublicPrefix();return(await this.http.get(`${t}/storages/files/${e}/items`)).files}async uploadFile(e,t,s){let r=this.getPublicPrefix(),i=new FormData;return i.append("file",t),s&&i.append("parent_id",s),this.http.post(`${r}/storages/files/${e}/upload`,i)}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){let r=this.getPublicPrefix();await this.http.post(`${r}/storages/files/${e}/items/${t}/move`,s)}async renameFile(e,t,s){let r=this.getPublicPrefix();return this.http.patch(`${r}/storages/files/${e}/items/${t}/rename`,s)}getFileUrl(e){return e.url||null}isImageFile(e){return e.mime_type?.startsWith("image/")||!1}};var T=class{constructor(e){this.http=e}async getApiKeys(e){return this.http.get(`/v1/apps/${e}/api-keys`)}async createApiKey(e,t){return this.http.post(`/v1/apps/${e}/api-keys`,t)}async updateApiKey(e,t,s){return this.http.patch(`/v1/apps/${e}/api-keys/${t}`,s)}async deleteApiKey(e,t){await this.http.delete(`/v1/apps/${e}/api-keys/${t}`)}};var C=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/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 call(e,t){let s=await this.invoke(e,t);if(!s.success)throw new Error(s.error||"Function execution failed");return s.result}};var k=class{constructor(e,t){this.ws=null;this.state="disconnected";this._connectionId=null;this._appId=null;this.options={maxRetries:5,retryInterval:1e3,userId:"",accessToken:""};this.retryCount=0;this.pendingRequests=new Map;this.subscriptions=new Map;this.streamSessions=new Map;this.stateHandlers=[];this.errorHandlers=[];this.http=e,this.socketUrl=t,this.clientId=this.generateClientId()}get connectionId(){return this._connectionId}get appId(){return this._appId}async connect(e={}){if(!(this.state==="connected"||this.state==="connecting"))return this.options={...this.options,...e},e.userId&&(this.userId=e.userId),this.doConnect()}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()}async subscribe(e,t={}){if(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,h)=>{await this.sendMessage(e,c,h)},getHistory:async c=>this.getHistory(e,c??t.historyLimit),unsubscribe:async()=>{await this.unsubscribe(e)},onMessage:c=>{n.push(c)}}}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.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.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")throw new Error("Not connected. Call connect() first.");let r=this.generateRequestId(),i=s.sessionId||this.generateRequestId();return this.streamSessions.set(i,{handlers:t,requestId:r}),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},request_id:r}),{sessionId:i,stop:async()=>{await this.stopStream(i)}}}async stopStream(e){if(this.state!=="connected")return;let t=this.generateRequestId();this.sendRaw({category:"",action:"stream_stop",data:{session_id:e},request_id:t}),this.streamSessions.delete(e)}getState(){return this.state}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 doConnect(){return new Promise((e,t)=>{this.state="connecting",this.notifyStateChange();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}`;else{let i=this.http.getApiKey();if(!i){t(new Error("API Key or accessToken is required for realtime connection"));return}r=`${s}/v1/realtime/auth?api_key=${encodeURIComponent(i)}&client_id=${this.clientId}`}this.userId&&(r+=`&user_id=${encodeURIComponent(this.userId)}`);try{this.ws=new WebSocket(r),this.ws.onopen=()=>{},this.ws.onmessage=i=>{let n=i.data.split(`
|
|
2
|
+
`).filter(o=>o.trim());for(let o of n)try{let c=JSON.parse(o);this.handleServerMessage(c,e)}catch(c){console.error("[Realtime] Failed to parse message:",o,c)}},this.ws.onclose=()=>{(this.state==="connected"||this.state==="connecting")&&this.handleDisconnect()},this.ws.onerror=i=>{console.error("[Realtime] WebSocket error:",i),this.notifyError(new Error("WebSocket connection error")),this.state==="connecting"&&t(new Error("Failed to connect"))}}catch(i){t(i)}})}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.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_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}}}handleDisconnect(){this.ws=null,this._connectionId=null,this.retryCount<this.options.maxRetries?(this.state="reconnecting",this.notifyStateChange(),this.retryCount++,setTimeout(()=>{this.doConnect().catch(e=>{console.error("[Realtime] Reconnect failed:",e)})},this.options.retryInterval*this.retryCount)):(this.state="disconnected",this.notifyStateChange(),this.notifyError(new Error("Connection lost. Max retries exceeded.")))}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"))},3e4);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 _=class{constructor(e,t){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}async getICEServers(){let e=await this.http.get("/v1/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.getApiKey(),s=this.http.getAccessToken(),r="";return s?r=`access_token=${encodeURIComponent(s)}`:t&&(r=`api_key=${encodeURIComponent(t)}`),`${e}/v1/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 n=e.data;n.channel_type&&(this.channelType=n.channel_type);let o=n.peers||[];for(let c of o)c.peer_id!==this.currentPeerId&&await this.createPeerConnection(c.peer_id,!0)}t?.();break;case"peer_joined":if(e.peer_id&&e.peer_id!==this.currentPeerId){let n={peer_id:e.peer_id,...typeof e.data=="object"?e.data:{}};this.emitPeerJoined(e.peer_id,n),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*Math.pow(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 getStats(e){return this.http.get(`/v1/apps/${e}/webrtc/stats`)}async getRooms(e){return this.http.get(`/v1/apps/${e}/webrtc/rooms`)}};var E=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 I=class{constructor(e){this.http=e}async getEnabledProviders(){return this.http.get("/v1/public/oauth/providers")}async signIn(e,t,s){let r=new URLSearchParams({app_callback:t});s&&r.append("state",s);let i=await this.http.get(`/v1/public/oauth/${e}/authorize/central?${r.toString()}`);window.location.href=i.authorization_url}async signInWithPopup(e,t){let s=new URLSearchParams({app_callback:t}),r=await this.http.get(`/v1/public/oauth/${e}/authorize/central?${s.toString()}`),i=500,n=600,o=window.screenX+(window.outerWidth-i)/2,c=window.screenY+(window.outerHeight-n)/2,h=window.open(r.authorization_url,"oauth-popup",`width=${i},height=${n},left=${o},top=${c}`);if(!h)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((m,G)=>{let u=async l=>{if(l.data?.type!=="oauth-callback")return;if(window.removeEventListener("message",u),l.data.error){G(new Error(l.data.error));return}let y={member_id:l.data.member_id,access_token:l.data.access_token,refresh_token:l.data.refresh_token,is_new_member:l.data.is_new_member==="true"||l.data.is_new_member===!0};this.http.setTokens(y.access_token,y.refresh_token),m(y)};window.addEventListener("message",u);let b=setInterval(()=>{h.closed&&(clearInterval(b),window.removeEventListener("message",u),G(new Error("\uB85C\uADF8\uC778\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.")))},500)})}getCallbackResult(){let e=new URLSearchParams(window.location.search),t=e.get("error");if(t){let o={error:t};return window.opener&&(window.opener.postMessage({type:"oauth-callback",...o},"*"),window.close()),o}let s=e.get("access_token"),r=e.get("refresh_token"),i=e.get("member_id");if(!s||!r||!i)return null;let n={access_token:s,refresh_token:r,member_id:i,is_new_member:e.get("is_new_member")==="true",state:e.get("state")||void 0};return window.opener?(window.opener.postMessage({type:"oauth-callback",...n},"*"),window.close(),n):(this.http.setTokens(s,r),n)}};var x=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/v1/public":"/v1"}async prepare(e){let t=this.getPublicPrefix();return this.http.post(`${t}/payments/prepare`,e)}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/${e}/cancel`,t)}async getByOrderId(e){let t=this.getPublicPrefix();return this.http.get(`${t}/payments/orders/${e}`)}};var $=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/v1/public":"/v1"}async issueBillingKey(){let e=this.getPublicPrefix();return this.http.post(`${e}/subscriptions/billing-keys/issue`,{})}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();return this.http.post(`${t}/subscriptions`,e)}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();return this.http.post(`${s}/subscriptions/${e}/cancel`,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 A=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasApiKey()?"/v1/public":"/v1"}async registerDevice(e){let t=this.getPublicPrefix();return this.http.post(`${t}/push/devices`,e)}async unregisterDevice(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/push/devices/${e}`)}async getDevices(){let e=this.getPublicPrefix();return(await this.http.get(`${e}/push/devices`)).devices||[]}async subscribeTopic(e){let t=this.getPublicPrefix(),s={topic_name:e};await this.http.post(`${t}/push/topics/subscribe`,s)}async unsubscribeTopic(e){let t=this.getPublicPrefix(),s={topic_name:e};await this.http.post(`${t}/push/topics/unsubscribe`,s)}async getSubscribedTopics(){let e=this.getPublicPrefix();return(await this.http.get(`${e}/push/topics/subscribed`)).topics||[]}async getVAPIDPublicKey(){let e=this.getPublicPrefix();return this.http.get(`${e}/push/vapid-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/web`,{...r,web_push_subscription:s})}async unregisterWebPush(){let e=this.getPublicPrefix();await this.http.delete(`${e}/push/devices/web`)}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 re=5*1024*1024,g=class extends Error{constructor(e,t){super(e),this.name="VideoProcessingError",this.video=t}},U=class{constructor(e,t){this.http=e;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.hasApiKey()?"/v1/public":"/v1"}async videoFetch(e,t,s){let r={},i=this.http.getApiKey();i&&(r["X-API-Key"]=i);let n=this.http.getAccessToken();n&&(r.Authorization=`Bearer ${n}`),s&&!(s instanceof FormData)&&(r["Content-Type"]="application/json");let o=await fetch(`${this.videoBaseUrl}${t}`,{method:e,headers:r,body:s instanceof FormData?s:s?JSON.stringify(s):void 0});if(!o.ok){let c=await o.json().catch(()=>({message:o.statusText}));throw new p(o.status,c.message||"Unknown error")}return o.status===204||o.headers.get("content-length")==="0"?{}:o.json()}async upload(e,t){let s=this.getPublicPrefix(),r=await this.videoFetch("POST",`${s}/uploads`,{filename:e.name,size:e.size,mime_type:e.type,title:t.title,description:t.description,visibility:t.visibility||"private",tags:t.tags,channel_id:t.channel_id}),i=r.chunk_size||re,n=Math.ceil(e.size/i),o=0,h=Date.now(),m=0;for(let u=0;u<n;u++){let b=u*i,l=Math.min(b+i,e.size),y=e.slice(b,l),L=new FormData;L.append("chunk",y),L.append("chunk_index",String(u)),await this.videoFetch("POST",`${s}/uploads/${r.session_id}/chunks`,L),o++;let O=Date.now(),W=(O-h)/1e3,B=l,z=B-m,Q=W>0?z/W:0;h=O,m=B,t.onProgress&&t.onProgress({phase:"uploading",uploadedChunks:o,totalChunks:n,percentage:Math.round(o/n*100),currentSpeed:Q})}return(await this.videoFetch("POST",`${s}/uploads/${r.session_id}/complete`,{})).video}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 g("Video processing failed",o);if(t?.onProgress){let c=o.qualities.filter(m=>m.status==="ready").length,h=o.qualities.length||1;t.onProgress({phase:"processing",uploadedChunks:0,totalChunks:0,percentage:Math.round(c/h*100)})}await new Promise(c=>setTimeout(c,r))}throw new g("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();return(await this.videoFetch("GET",`${t}/videos/${e}/thumbnails`)).thumbnails}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}/channels/${e}/playlists`,t)}async getPlaylists(e){let t=this.getPublicPrefix();return(await this.videoFetch("GET",`${t}/channels/${e}/playlists`)).playlists}async getPlaylistItems(e){let t=this.getPublicPrefix();return(await this.videoFetch("GET",`${t}/playlists/${e}/items`)).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}/videos/${e}/comments`,{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}/memberships/tiers`)).tiers}async joinMembership(e,t){let s=this.getPublicPrefix();return this.videoFetch("POST",`${s}/channels/${e}/memberships/${t}/join`,{})}async cancelMembership(e,t){let s=this.getPublicPrefix();await this.videoFetch("POST",`${s}/channels/${e}/memberships/${t}/cancel`,{})}async sendSuperChat(e,t,s,r){let i=this.getPublicPrefix();return this.videoFetch("POST",`${i}/videos/${e}/super-chats`,{amount:t,message:s,currency:r||"USD"})}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${s}`)).videos}async getHomeFeed(e){let t=this.getPublicPrefix(),s=e?`?limit=${e}`:"";return(await this.videoFetch("GET",`${t}/recommendations/home${s}`)).videos}async getRelatedVideos(e,t){let s=this.getPublicPrefix(),r=t?`?limit=${t}`:"";return(await this.videoFetch("GET",`${s}/recommendations/related/${e}${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();await this.videoFetch("POST",`${s}/recommendations/feedback`,{video_id:e,feedback:t})}};var J=()=>{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"},v=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._isConnected=!1;this.config={gameServerUrl:J(),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}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=h=>{this._isConnected=!1,this.stopPingInterval(),this.handlers.onDisconnect?.(h),this.config.autoReconnect&&h.code!==1e3&&this.scheduleReconnect(e)},o=h=>{this.handlers.onError?.(h),s(new Error("WebSocket connection failed"))},c=h=>{this.handleMessage(h.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}createRoom(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,t(n.initial_state)}else i.type==="error"&&s(new Error(i.data.message))};this.sendWithHandler("create_room",e,r)})}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,s(o.initial_state)}else n.type==="error"&&r(new Error(n.data.message))};this.sendWithHandler("join_room",{room_id:e,metadata:t},i)})}leaveRoom(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("Not in a room"));return}let s=r=>{r.type==="room_left"?(this._roomId=null,this._state=null,e()):r.type==="error"&&t(new Error(r.data.message))};this.sendWithHandler("leave_room",{},s)})}sendAction(e){if(!this._roomId)throw new Error("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 Error("Not in a room");this.send("chat",{message:e})}requestState(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("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(new Error(r.data.message))};this.sendWithHandler("get_state",{},s)})}listRooms(){return new Promise((e,t)=>{let s=r=>{if(r.type==="room_list"){let i=r.data;e(i.rooms)}else r.type==="error"&&t(new Error(r.data.message))};this.sendWithHandler("list_rooms",{},s)})}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.handlers.onPong?.(n),e(o)}else i.type==="error"&&t(new Error(i.data.message))};this.sendWithHandler("ping",{timestamp:s},r)})}buildConnectionUrl(e){let s=this.config.gameServerUrl.replace(/^http/,"ws"),r=new URLSearchParams;return r.set("client_id",this.config.clientId),e&&r.set("room_id",e),this.config.apiKey&&r.set("api_key",this.config.apiKey),this.config.accessToken&&r.set("token",this.config.accessToken),`${s}/v1/game/ws?${r.toString()}`}send(e,t){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("WebSocket is not connected");this.ws.send(JSON.stringify({type:e,data:t}))}sendWithHandler(e,t,s){let r=i=>{try{let n=JSON.parse(i.data);s(n),this.ws?.removeEventListener("message",r)}catch{}};this.ws?.addEventListener("message",r),this.send(e,t)}handleMessage(e){try{let t=JSON.parse(e);switch(t.type){case"delta":this.handleDelta(t.data);break;case"state":this._state=t.data,this.handlers.onStateUpdate?.(this._state);break;case"player_event":this.handlePlayerEvent(t.data);break;case"chat":this.handlers.onChat?.(t.data);break;case"error":this.handlers.onError?.(t.data);break;default:break}}catch{console.error("Failed to parse game message:",e)}}handleDelta(e){let t=e.delta;if(this._state){for(let s of t.changes)this.applyChange(s);this._state.version=t.toVersion}this.handlers.onDelta?.(t)}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){e.event==="joined"?this.handlers.onPlayerJoined?.(e.player):e.event==="left"&&this.handlers.onPlayerLeft?.(e.player)}scheduleReconnect(e){if(this.reconnectAttempts>=(this.config.maxReconnectAttempts??5)){console.error("Max reconnect attempts reached");return}let t=Math.min((this.config.reconnectInterval??1e3)*Math.pow(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)}},f=class{constructor(e,t){this.http=e,this.gameServerUrl=t||J().replace(/^ws/,"http")}createClient(e){return new v({...e,gameServerUrl:this.gameServerUrl.replace(/^http/,"ws"),apiKey:this.http.getApiKey(),accessToken:this.http.getAccessToken()})}async listRooms(e){let t=await fetch(`${this.gameServerUrl}/v1/game/rooms${e?`?app_id=${e}`:""}`,{headers:this.getHeaders()});if(!t.ok)throw new Error(`Failed to list rooms: ${t.statusText}`);return(await t.json()).rooms}async getRoom(e){let t=await fetch(`${this.gameServerUrl}/v1/game/rooms/${e}`,{headers:this.getHeaders()});if(!t.ok)throw new Error(`Failed to get room: ${t.statusText}`);return t.json()}async createRoom(e,t={}){let s=await fetch(`${this.gameServerUrl}/v1/game/rooms`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({app_id:e,category_id:t.categoryId,room_id:t.roomId,tick_rate:t.tickRate,max_players:t.maxPlayers,metadata:t.metadata})});if(!s.ok)throw new Error(`Failed to create room: ${s.statusText}`);return s.json()}async deleteRoom(e){let t=await fetch(`${this.gameServerUrl}/v1/game/rooms/${e}`,{method:"DELETE",headers:this.getHeaders()});if(!t.ok)throw new Error(`Failed to delete room: ${t.statusText}`)}getHeaders(){let e={},t=this.http.getApiKey();t&&(e["X-API-Key"]=t);let s=this.http.getAccessToken();return s&&(e.Authorization=`Bearer ${s}`),e}};var K=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.apiKey&&s.set("api_key",this.config.apiKey),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 Error("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}},M=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 h=new Error("WebSocket error");this.onError(h),t(h)},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;return s.set("client_id",this.config.clientId),this.config.apiKey&&s.set("api_key",this.config.apiKey),this.config.accessToken&&s.set("token",this.config.accessToken),`${t}/v1/game/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 Error("Not connected");typeof e=="string"?this.ws.send(e):this.ws.send(e)}isConnected(){return this.ws!==null&&this.ws.readyState===WebSocket.OPEN}};function H(){return typeof WebTransport<"u"}var q=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._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 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")&&H(),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?.(o)};if(s)try{this.transport=new K(this.config,r,i,n),await this.transport.connect(),this._transportType="webtransport"}catch{console.log("WebTransport failed, falling back to WebSocket"),this.transport=new M(this.config,r,i,n),await this.transport.connect(),this._transportType="websocket"}else this.transport=new M(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 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,t(n.initial_state)}else i.type==="error"&&s(new Error(i.data.message))};this.sendWithHandler("create_room",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,s(o.initial_state)}else n.type==="error"&&r(new Error(n.data.message))};this.sendWithHandler("join_room",{room_id:e,metadata:t},i)})}async leaveRoom(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("Not in a room"));return}let s=r=>{r.type==="room_left"?(this._roomId=null,this._state=null,e()):r.type==="error"&&t(new Error(r.data.message))};this.sendWithHandler("leave_room",{},s)})}sendAction(e,t=!1){if(!this._roomId)throw new Error("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 Error("Not in a room");this.send("chat",{message:e})}async requestState(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("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(new Error(r.data.message))};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(new Error(i.data.message))};this.sendWithHandler("ping",{timestamp:s},r)})}send(e,t){if(!this.transport?.isConnected())throw new Error("Not connected");let s=JSON.stringify({type:e,data:t});this.transport.send(s,!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,_msg_id: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.data);break;case"state":this._state=t.data,this.handlers.onStateUpdate?.(this._state);break;case"player_event":this.handlePlayerEvent(t.data);break;case"chat":this.handlers.onChat?.(t.data);break;case"error":this.handlers.onError?.(t.data);break}}catch{console.error("Failed to parse game message:",e)}}handleDelta(e){let t=e.delta;if(this._state){for(let s of t.changes)this.applyChange(s);this._state.version=t.toVersion}this.handlers.onDelta?.(t)}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){e.event==="joined"?this.handlers.onPlayerJoined?.(e.player):e.event==="left"&&this.handlers.onPlayerLeft?.(e.player)}scheduleReconnect(e){if(this.reconnectAttempts>=(this.config.maxReconnectAttempts??5)){console.error("Max reconnect attempts reached");return}let t=Math.min((this.config.reconnectInterval??1e3)*Math.pow(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 ie="https://api.connectbase.world",ne="https://socket.connectbase.world",oe="https://webrtc.connectbase.world",ae="https://video.connectbase.world",ce="https://game.connectbase.world",D=class{constructor(e={}){let t={baseUrl:e.baseUrl||ie,apiKey:e.apiKey,onTokenRefresh:e.onTokenRefresh,onAuthError:e.onAuthError};this.http=new P(t),this.auth=new w(this.http),this.database=new S(this.http),this.storage=new R(this.http),this.apiKey=new T(this.http),this.functions=new C(this.http),this.realtime=new k(this.http,e.socketUrl||ne),this.webrtc=new _(this.http,e.webrtcUrl||oe),this.errorTracker=new E(this.http,e.errorTracker),this.oauth=new I(this.http),this.payment=new x(this.http),this.subscription=new $(this.http),this.push=new A(this.http),this.video=new U(this.http,e.videoUrl||ae),this.game=new f(this.http,e.gameUrl||ce)}setTokens(e,t){this.http.setTokens(e,t)}clearTokens(){this.http.clearTokens()}updateConfig(e){this.http.updateConfig(e)}},he=D;return se(le);})();
|
|
3
3
|
var ConnectBase = ConnectBaseModule.default || ConnectBaseModule.ConnectBase;
|
package/dist/index.d.mts
CHANGED
|
@@ -811,19 +811,68 @@ interface Subscription {
|
|
|
811
811
|
/** 클라이언트 -> 서버 메시지 */
|
|
812
812
|
interface ClientMessage {
|
|
813
813
|
category: string;
|
|
814
|
-
action: 'subscribe' | 'unsubscribe' | 'send' | 'invoke' | 'ping' | 'history';
|
|
814
|
+
action: 'subscribe' | 'unsubscribe' | 'send' | 'invoke' | 'ping' | 'history' | 'stream' | 'stream_stop';
|
|
815
815
|
data?: unknown;
|
|
816
816
|
request_id: string;
|
|
817
817
|
}
|
|
818
818
|
/** 서버 -> 클라이언트 메시지 */
|
|
819
819
|
interface ServerMessage<T = unknown> {
|
|
820
820
|
category?: string;
|
|
821
|
-
event: 'connected' | 'subscribed' | 'unsubscribed' | 'message' | 'sent' | 'result' | 'error' | 'pong' | 'history';
|
|
821
|
+
event: 'connected' | 'subscribed' | 'unsubscribed' | 'message' | 'sent' | 'result' | 'error' | 'pong' | 'history' | 'stream_token' | 'stream_done' | 'stream_error';
|
|
822
822
|
data?: T;
|
|
823
823
|
request_id?: string;
|
|
824
824
|
error?: string;
|
|
825
825
|
timestamp: number;
|
|
826
826
|
}
|
|
827
|
+
/** AI 스트리밍 메시지 */
|
|
828
|
+
interface StreamMessage {
|
|
829
|
+
role: 'user' | 'assistant' | 'system';
|
|
830
|
+
content: string;
|
|
831
|
+
}
|
|
832
|
+
/** AI 스트리밍 요청 옵션 */
|
|
833
|
+
interface StreamOptions {
|
|
834
|
+
/** AI 프로바이더 (기본: gemini) */
|
|
835
|
+
provider?: 'gemini' | 'openai' | 'anthropic';
|
|
836
|
+
/** 모델명 (기본: gemini-2.0-flash) */
|
|
837
|
+
model?: string;
|
|
838
|
+
/** 시스템 프롬프트 */
|
|
839
|
+
system?: string;
|
|
840
|
+
/** 온도 (0.0-2.0, 기본: 0.7) */
|
|
841
|
+
temperature?: number;
|
|
842
|
+
/** 최대 토큰 수 */
|
|
843
|
+
maxTokens?: number;
|
|
844
|
+
/** 세션 ID (대화 추적용) */
|
|
845
|
+
sessionId?: string;
|
|
846
|
+
/** 사용자 정의 메타데이터 */
|
|
847
|
+
metadata?: Record<string, unknown>;
|
|
848
|
+
}
|
|
849
|
+
/** AI 스트리밍 토큰 콜백 */
|
|
850
|
+
type StreamTokenCallback = (token: string, index: number) => void;
|
|
851
|
+
/** AI 스트리밍 완료 데이터 */
|
|
852
|
+
interface StreamDoneData {
|
|
853
|
+
sessionId: string;
|
|
854
|
+
fullText: string;
|
|
855
|
+
totalTokens: number;
|
|
856
|
+
promptTokens: number;
|
|
857
|
+
duration: number;
|
|
858
|
+
}
|
|
859
|
+
/** AI 스트리밍 완료 콜백 */
|
|
860
|
+
type StreamDoneCallback = (result: StreamDoneData) => void;
|
|
861
|
+
/** AI 스트리밍 에러 콜백 */
|
|
862
|
+
type StreamErrorCallback = (error: Error) => void;
|
|
863
|
+
/** AI 스트리밍 핸들러 */
|
|
864
|
+
interface StreamHandlers {
|
|
865
|
+
onToken?: StreamTokenCallback;
|
|
866
|
+
onDone?: StreamDoneCallback;
|
|
867
|
+
onError?: StreamErrorCallback;
|
|
868
|
+
}
|
|
869
|
+
/** AI 스트리밍 세션 */
|
|
870
|
+
interface StreamSession {
|
|
871
|
+
/** 세션 ID */
|
|
872
|
+
sessionId: string;
|
|
873
|
+
/** 스트리밍 중지 */
|
|
874
|
+
stop(): Promise<void>;
|
|
875
|
+
}
|
|
827
876
|
/** 연결 성공 데이터 */
|
|
828
877
|
interface ConnectedData {
|
|
829
878
|
connection_id: string;
|
|
@@ -889,6 +938,7 @@ declare class RealtimeAPI {
|
|
|
889
938
|
private retryCount;
|
|
890
939
|
private pendingRequests;
|
|
891
940
|
private subscriptions;
|
|
941
|
+
private streamSessions;
|
|
892
942
|
private stateHandlers;
|
|
893
943
|
private errorHandlers;
|
|
894
944
|
constructor(http: HttpClient, socketUrl: string);
|
|
@@ -922,6 +972,40 @@ declare class RealtimeAPI {
|
|
|
922
972
|
* 히스토리 조회
|
|
923
973
|
*/
|
|
924
974
|
getHistory<T = unknown>(category: string, limit?: number): Promise<HistoryResponse<T>>;
|
|
975
|
+
/**
|
|
976
|
+
* AI 스트리밍
|
|
977
|
+
* @param messages 대화 메시지 목록
|
|
978
|
+
* @param handlers 스트리밍 핸들러 (onToken, onDone, onError)
|
|
979
|
+
* @param options 스트리밍 옵션
|
|
980
|
+
* @returns 스트리밍 세션 (중지 가능)
|
|
981
|
+
*
|
|
982
|
+
* @example
|
|
983
|
+
* ```typescript
|
|
984
|
+
* const session = await client.realtime.stream(
|
|
985
|
+
* [{ role: 'user', content: '안녕하세요' }],
|
|
986
|
+
* {
|
|
987
|
+
* onToken: (token) => {
|
|
988
|
+
* process.stdout.write(token) // 실시간 출력
|
|
989
|
+
* },
|
|
990
|
+
* onDone: (result) => {
|
|
991
|
+
* console.log('완료:', result.fullText)
|
|
992
|
+
* },
|
|
993
|
+
* onError: (error) => {
|
|
994
|
+
* console.error('에러:', error)
|
|
995
|
+
* }
|
|
996
|
+
* },
|
|
997
|
+
* { model: 'gemini-2.0-flash', temperature: 0.7 }
|
|
998
|
+
* )
|
|
999
|
+
*
|
|
1000
|
+
* // 필요 시 중지
|
|
1001
|
+
* await session.stop()
|
|
1002
|
+
* ```
|
|
1003
|
+
*/
|
|
1004
|
+
stream(messages: StreamMessage[], handlers: StreamHandlers, options?: StreamOptions): Promise<StreamSession>;
|
|
1005
|
+
/**
|
|
1006
|
+
* 스트리밍 중지
|
|
1007
|
+
*/
|
|
1008
|
+
stopStream(sessionId: string): Promise<void>;
|
|
925
1009
|
/**
|
|
926
1010
|
* 연결 상태
|
|
927
1011
|
*/
|
|
@@ -938,6 +1022,7 @@ declare class RealtimeAPI {
|
|
|
938
1022
|
private handleServerMessage;
|
|
939
1023
|
private handleDisconnect;
|
|
940
1024
|
private sendRequest;
|
|
1025
|
+
private sendRaw;
|
|
941
1026
|
private notifyStateChange;
|
|
942
1027
|
private notifyError;
|
|
943
1028
|
private generateClientId;
|
|
@@ -3303,4 +3388,4 @@ declare class ConnectBase {
|
|
|
3303
3388
|
updateConfig(config: Partial<ConnectBaseConfig>): void;
|
|
3304
3389
|
}
|
|
3305
3390
|
|
|
3306
|
-
export { type AnonymousSignInRequest, type AnonymousSignInResponse, type AnonymousSignUpResponse, ApiError, type ApiKeyItem, type AppStatsResponse, AuthError, type AuthSettingsResponse, type BillingCycle, type BillingKeyResponse, type BulkCreateResponse, type BulkError, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateChannelRequest, type CreateColumnRequest, type CreateDataRequest, type CreateFolderRequest, type CreateFolderResponse, type CreatePlaylistRequest, type CreateSubscriptionRequest, type CreateTableRequest, type DataItem, type DataType, type DeleteWhereResponse, type DeviceInfo, type EnabledProviderInfo, type EnabledProvidersResponse, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type FetchApiKeysResponse, type FetchDataResponse, type FetchFilesResponse, type FileItem, GameAPI, type GameAction, type GameClientConfig, type GameConnectionState, type GameConnectionStatus, type GameDelta, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GetAuthorizationURLResponse, type GuestMemberSignInResponse, type HistoryResponse, type ICEServer, type ICEServersResponse, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinRoomRequest, type JoinRoomResponse, type ListBillingKeysResponse, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MessageHandler, type MoveFileRequest, type OAuthCallbackResponse, type OAuthProvider, type PauseSubscriptionRequest, type PaymentDetail, type PaymentStatus, type PeerInfo, type PlayerEvent, type Playlist, type PlaylistItem, type PongMessage, type PreparePaymentRequest, type PreparePaymentResponse, type PushPlatform, type QualityProgress, type QueryOptions, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RenameFileRequest, type RenameFileResponse, type RoomInfo, type RoomStats, type RoomsResponse, type SendOptions, type ServerMessage, type Shorts, type ShortsListResponse, type SignInRequest, type SignInResponse, type SignUpRequest, type SignUpResponse, type SignalingMessage, type SignalingMessageType, type StateChange, type StateChangeHandler, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type TableSchema, type TranscodeStatus, type TransportType, type UpdateApiKeyRequest, type UpdateApiKeyResponse, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateDataRequest, type UpdateSubscriptionRequest, type UpdateVideoRequest, type UploadFileResponse, type UploadOptions, type UploadProgress, type UserInfo, type VAPIDPublicKeyResponse, type Video, type VideoComment, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoVisibility, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, isWebTransportSupported };
|
|
3391
|
+
export { type AnonymousSignInRequest, type AnonymousSignInResponse, type AnonymousSignUpResponse, ApiError, type ApiKeyItem, type AppStatsResponse, AuthError, type AuthSettingsResponse, type BillingCycle, type BillingKeyResponse, type BulkCreateResponse, type BulkError, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateChannelRequest, type CreateColumnRequest, type CreateDataRequest, type CreateFolderRequest, type CreateFolderResponse, type CreatePlaylistRequest, type CreateSubscriptionRequest, type CreateTableRequest, type DataItem, type DataType, type DeleteWhereResponse, type DeviceInfo, type EnabledProviderInfo, type EnabledProvidersResponse, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type FetchApiKeysResponse, type FetchDataResponse, type FetchFilesResponse, type FileItem, GameAPI, type GameAction, type GameClientConfig, type GameConnectionState, type GameConnectionStatus, type GameDelta, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GetAuthorizationURLResponse, type GuestMemberSignInResponse, type HistoryResponse, type ICEServer, type ICEServersResponse, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinRoomRequest, type JoinRoomResponse, type ListBillingKeysResponse, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MessageHandler, type MoveFileRequest, type OAuthCallbackResponse, type OAuthProvider, type PauseSubscriptionRequest, type PaymentDetail, type PaymentStatus, type PeerInfo, type PlayerEvent, type Playlist, type PlaylistItem, type PongMessage, type PreparePaymentRequest, type PreparePaymentResponse, type PushPlatform, type QualityProgress, type QueryOptions, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RenameFileRequest, type RenameFileResponse, type RoomInfo, type RoomStats, type RoomsResponse, type SendOptions, type ServerMessage, type Shorts, type ShortsListResponse, type SignInRequest, type SignInResponse, type SignUpRequest, type SignUpResponse, type SignalingMessage, type SignalingMessageType, type StateChange, type StateChangeHandler, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamMessage, type StreamOptions, type StreamSession, type StreamTokenCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type TableSchema, type TranscodeStatus, type TransportType, type UpdateApiKeyRequest, type UpdateApiKeyResponse, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateDataRequest, type UpdateSubscriptionRequest, type UpdateVideoRequest, type UploadFileResponse, type UploadOptions, type UploadProgress, type UserInfo, type VAPIDPublicKeyResponse, type Video, type VideoComment, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoVisibility, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, isWebTransportSupported };
|
package/dist/index.d.ts
CHANGED
|
@@ -811,19 +811,68 @@ interface Subscription {
|
|
|
811
811
|
/** 클라이언트 -> 서버 메시지 */
|
|
812
812
|
interface ClientMessage {
|
|
813
813
|
category: string;
|
|
814
|
-
action: 'subscribe' | 'unsubscribe' | 'send' | 'invoke' | 'ping' | 'history';
|
|
814
|
+
action: 'subscribe' | 'unsubscribe' | 'send' | 'invoke' | 'ping' | 'history' | 'stream' | 'stream_stop';
|
|
815
815
|
data?: unknown;
|
|
816
816
|
request_id: string;
|
|
817
817
|
}
|
|
818
818
|
/** 서버 -> 클라이언트 메시지 */
|
|
819
819
|
interface ServerMessage<T = unknown> {
|
|
820
820
|
category?: string;
|
|
821
|
-
event: 'connected' | 'subscribed' | 'unsubscribed' | 'message' | 'sent' | 'result' | 'error' | 'pong' | 'history';
|
|
821
|
+
event: 'connected' | 'subscribed' | 'unsubscribed' | 'message' | 'sent' | 'result' | 'error' | 'pong' | 'history' | 'stream_token' | 'stream_done' | 'stream_error';
|
|
822
822
|
data?: T;
|
|
823
823
|
request_id?: string;
|
|
824
824
|
error?: string;
|
|
825
825
|
timestamp: number;
|
|
826
826
|
}
|
|
827
|
+
/** AI 스트리밍 메시지 */
|
|
828
|
+
interface StreamMessage {
|
|
829
|
+
role: 'user' | 'assistant' | 'system';
|
|
830
|
+
content: string;
|
|
831
|
+
}
|
|
832
|
+
/** AI 스트리밍 요청 옵션 */
|
|
833
|
+
interface StreamOptions {
|
|
834
|
+
/** AI 프로바이더 (기본: gemini) */
|
|
835
|
+
provider?: 'gemini' | 'openai' | 'anthropic';
|
|
836
|
+
/** 모델명 (기본: gemini-2.0-flash) */
|
|
837
|
+
model?: string;
|
|
838
|
+
/** 시스템 프롬프트 */
|
|
839
|
+
system?: string;
|
|
840
|
+
/** 온도 (0.0-2.0, 기본: 0.7) */
|
|
841
|
+
temperature?: number;
|
|
842
|
+
/** 최대 토큰 수 */
|
|
843
|
+
maxTokens?: number;
|
|
844
|
+
/** 세션 ID (대화 추적용) */
|
|
845
|
+
sessionId?: string;
|
|
846
|
+
/** 사용자 정의 메타데이터 */
|
|
847
|
+
metadata?: Record<string, unknown>;
|
|
848
|
+
}
|
|
849
|
+
/** AI 스트리밍 토큰 콜백 */
|
|
850
|
+
type StreamTokenCallback = (token: string, index: number) => void;
|
|
851
|
+
/** AI 스트리밍 완료 데이터 */
|
|
852
|
+
interface StreamDoneData {
|
|
853
|
+
sessionId: string;
|
|
854
|
+
fullText: string;
|
|
855
|
+
totalTokens: number;
|
|
856
|
+
promptTokens: number;
|
|
857
|
+
duration: number;
|
|
858
|
+
}
|
|
859
|
+
/** AI 스트리밍 완료 콜백 */
|
|
860
|
+
type StreamDoneCallback = (result: StreamDoneData) => void;
|
|
861
|
+
/** AI 스트리밍 에러 콜백 */
|
|
862
|
+
type StreamErrorCallback = (error: Error) => void;
|
|
863
|
+
/** AI 스트리밍 핸들러 */
|
|
864
|
+
interface StreamHandlers {
|
|
865
|
+
onToken?: StreamTokenCallback;
|
|
866
|
+
onDone?: StreamDoneCallback;
|
|
867
|
+
onError?: StreamErrorCallback;
|
|
868
|
+
}
|
|
869
|
+
/** AI 스트리밍 세션 */
|
|
870
|
+
interface StreamSession {
|
|
871
|
+
/** 세션 ID */
|
|
872
|
+
sessionId: string;
|
|
873
|
+
/** 스트리밍 중지 */
|
|
874
|
+
stop(): Promise<void>;
|
|
875
|
+
}
|
|
827
876
|
/** 연결 성공 데이터 */
|
|
828
877
|
interface ConnectedData {
|
|
829
878
|
connection_id: string;
|
|
@@ -889,6 +938,7 @@ declare class RealtimeAPI {
|
|
|
889
938
|
private retryCount;
|
|
890
939
|
private pendingRequests;
|
|
891
940
|
private subscriptions;
|
|
941
|
+
private streamSessions;
|
|
892
942
|
private stateHandlers;
|
|
893
943
|
private errorHandlers;
|
|
894
944
|
constructor(http: HttpClient, socketUrl: string);
|
|
@@ -922,6 +972,40 @@ declare class RealtimeAPI {
|
|
|
922
972
|
* 히스토리 조회
|
|
923
973
|
*/
|
|
924
974
|
getHistory<T = unknown>(category: string, limit?: number): Promise<HistoryResponse<T>>;
|
|
975
|
+
/**
|
|
976
|
+
* AI 스트리밍
|
|
977
|
+
* @param messages 대화 메시지 목록
|
|
978
|
+
* @param handlers 스트리밍 핸들러 (onToken, onDone, onError)
|
|
979
|
+
* @param options 스트리밍 옵션
|
|
980
|
+
* @returns 스트리밍 세션 (중지 가능)
|
|
981
|
+
*
|
|
982
|
+
* @example
|
|
983
|
+
* ```typescript
|
|
984
|
+
* const session = await client.realtime.stream(
|
|
985
|
+
* [{ role: 'user', content: '안녕하세요' }],
|
|
986
|
+
* {
|
|
987
|
+
* onToken: (token) => {
|
|
988
|
+
* process.stdout.write(token) // 실시간 출력
|
|
989
|
+
* },
|
|
990
|
+
* onDone: (result) => {
|
|
991
|
+
* console.log('완료:', result.fullText)
|
|
992
|
+
* },
|
|
993
|
+
* onError: (error) => {
|
|
994
|
+
* console.error('에러:', error)
|
|
995
|
+
* }
|
|
996
|
+
* },
|
|
997
|
+
* { model: 'gemini-2.0-flash', temperature: 0.7 }
|
|
998
|
+
* )
|
|
999
|
+
*
|
|
1000
|
+
* // 필요 시 중지
|
|
1001
|
+
* await session.stop()
|
|
1002
|
+
* ```
|
|
1003
|
+
*/
|
|
1004
|
+
stream(messages: StreamMessage[], handlers: StreamHandlers, options?: StreamOptions): Promise<StreamSession>;
|
|
1005
|
+
/**
|
|
1006
|
+
* 스트리밍 중지
|
|
1007
|
+
*/
|
|
1008
|
+
stopStream(sessionId: string): Promise<void>;
|
|
925
1009
|
/**
|
|
926
1010
|
* 연결 상태
|
|
927
1011
|
*/
|
|
@@ -938,6 +1022,7 @@ declare class RealtimeAPI {
|
|
|
938
1022
|
private handleServerMessage;
|
|
939
1023
|
private handleDisconnect;
|
|
940
1024
|
private sendRequest;
|
|
1025
|
+
private sendRaw;
|
|
941
1026
|
private notifyStateChange;
|
|
942
1027
|
private notifyError;
|
|
943
1028
|
private generateClientId;
|
|
@@ -3303,4 +3388,4 @@ declare class ConnectBase {
|
|
|
3303
3388
|
updateConfig(config: Partial<ConnectBaseConfig>): void;
|
|
3304
3389
|
}
|
|
3305
3390
|
|
|
3306
|
-
export { type AnonymousSignInRequest, type AnonymousSignInResponse, type AnonymousSignUpResponse, ApiError, type ApiKeyItem, type AppStatsResponse, AuthError, type AuthSettingsResponse, type BillingCycle, type BillingKeyResponse, type BulkCreateResponse, type BulkError, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateChannelRequest, type CreateColumnRequest, type CreateDataRequest, type CreateFolderRequest, type CreateFolderResponse, type CreatePlaylistRequest, type CreateSubscriptionRequest, type CreateTableRequest, type DataItem, type DataType, type DeleteWhereResponse, type DeviceInfo, type EnabledProviderInfo, type EnabledProvidersResponse, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type FetchApiKeysResponse, type FetchDataResponse, type FetchFilesResponse, type FileItem, GameAPI, type GameAction, type GameClientConfig, type GameConnectionState, type GameConnectionStatus, type GameDelta, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GetAuthorizationURLResponse, type GuestMemberSignInResponse, type HistoryResponse, type ICEServer, type ICEServersResponse, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinRoomRequest, type JoinRoomResponse, type ListBillingKeysResponse, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MessageHandler, type MoveFileRequest, type OAuthCallbackResponse, type OAuthProvider, type PauseSubscriptionRequest, type PaymentDetail, type PaymentStatus, type PeerInfo, type PlayerEvent, type Playlist, type PlaylistItem, type PongMessage, type PreparePaymentRequest, type PreparePaymentResponse, type PushPlatform, type QualityProgress, type QueryOptions, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RenameFileRequest, type RenameFileResponse, type RoomInfo, type RoomStats, type RoomsResponse, type SendOptions, type ServerMessage, type Shorts, type ShortsListResponse, type SignInRequest, type SignInResponse, type SignUpRequest, type SignUpResponse, type SignalingMessage, type SignalingMessageType, type StateChange, type StateChangeHandler, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type TableSchema, type TranscodeStatus, type TransportType, type UpdateApiKeyRequest, type UpdateApiKeyResponse, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateDataRequest, type UpdateSubscriptionRequest, type UpdateVideoRequest, type UploadFileResponse, type UploadOptions, type UploadProgress, type UserInfo, type VAPIDPublicKeyResponse, type Video, type VideoComment, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoVisibility, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, isWebTransportSupported };
|
|
3391
|
+
export { type AnonymousSignInRequest, type AnonymousSignInResponse, type AnonymousSignUpResponse, ApiError, type ApiKeyItem, type AppStatsResponse, AuthError, type AuthSettingsResponse, type BillingCycle, type BillingKeyResponse, type BulkCreateResponse, type BulkError, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateChannelRequest, type CreateColumnRequest, type CreateDataRequest, type CreateFolderRequest, type CreateFolderResponse, type CreatePlaylistRequest, type CreateSubscriptionRequest, type CreateTableRequest, type DataItem, type DataType, type DeleteWhereResponse, type DeviceInfo, type EnabledProviderInfo, type EnabledProvidersResponse, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type FetchApiKeysResponse, type FetchDataResponse, type FetchFilesResponse, type FileItem, GameAPI, type GameAction, type GameClientConfig, type GameConnectionState, type GameConnectionStatus, type GameDelta, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GetAuthorizationURLResponse, type GuestMemberSignInResponse, type HistoryResponse, type ICEServer, type ICEServersResponse, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinRoomRequest, type JoinRoomResponse, type ListBillingKeysResponse, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MessageHandler, type MoveFileRequest, type OAuthCallbackResponse, type OAuthProvider, type PauseSubscriptionRequest, type PaymentDetail, type PaymentStatus, type PeerInfo, type PlayerEvent, type Playlist, type PlaylistItem, type PongMessage, type PreparePaymentRequest, type PreparePaymentResponse, type PushPlatform, type QualityProgress, type QueryOptions, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RenameFileRequest, type RenameFileResponse, type RoomInfo, type RoomStats, type RoomsResponse, type SendOptions, type ServerMessage, type Shorts, type ShortsListResponse, type SignInRequest, type SignInResponse, type SignUpRequest, type SignUpResponse, type SignalingMessage, type SignalingMessageType, type StateChange, type StateChangeHandler, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamMessage, type StreamOptions, type StreamSession, type StreamTokenCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type TableSchema, type TranscodeStatus, type TransportType, type UpdateApiKeyRequest, type UpdateApiKeyResponse, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateDataRequest, type UpdateSubscriptionRequest, type UpdateVideoRequest, type UploadFileResponse, type UploadOptions, type UploadProgress, type UserInfo, type VAPIDPublicKeyResponse, type Video, type VideoComment, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoVisibility, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, isWebTransportSupported };
|
package/dist/index.js
CHANGED
|
@@ -959,6 +959,7 @@ var RealtimeAPI = class {
|
|
|
959
959
|
this.retryCount = 0;
|
|
960
960
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
961
961
|
this.subscriptions = /* @__PURE__ */ new Map();
|
|
962
|
+
this.streamSessions = /* @__PURE__ */ new Map();
|
|
962
963
|
this.stateHandlers = [];
|
|
963
964
|
this.errorHandlers = [];
|
|
964
965
|
this.http = http;
|
|
@@ -1105,6 +1106,83 @@ var RealtimeAPI = class {
|
|
|
1105
1106
|
total: response.total
|
|
1106
1107
|
};
|
|
1107
1108
|
}
|
|
1109
|
+
/**
|
|
1110
|
+
* AI 스트리밍
|
|
1111
|
+
* @param messages 대화 메시지 목록
|
|
1112
|
+
* @param handlers 스트리밍 핸들러 (onToken, onDone, onError)
|
|
1113
|
+
* @param options 스트리밍 옵션
|
|
1114
|
+
* @returns 스트리밍 세션 (중지 가능)
|
|
1115
|
+
*
|
|
1116
|
+
* @example
|
|
1117
|
+
* ```typescript
|
|
1118
|
+
* const session = await client.realtime.stream(
|
|
1119
|
+
* [{ role: 'user', content: '안녕하세요' }],
|
|
1120
|
+
* {
|
|
1121
|
+
* onToken: (token) => {
|
|
1122
|
+
* process.stdout.write(token) // 실시간 출력
|
|
1123
|
+
* },
|
|
1124
|
+
* onDone: (result) => {
|
|
1125
|
+
* console.log('완료:', result.fullText)
|
|
1126
|
+
* },
|
|
1127
|
+
* onError: (error) => {
|
|
1128
|
+
* console.error('에러:', error)
|
|
1129
|
+
* }
|
|
1130
|
+
* },
|
|
1131
|
+
* { model: 'gemini-2.0-flash', temperature: 0.7 }
|
|
1132
|
+
* )
|
|
1133
|
+
*
|
|
1134
|
+
* // 필요 시 중지
|
|
1135
|
+
* await session.stop()
|
|
1136
|
+
* ```
|
|
1137
|
+
*/
|
|
1138
|
+
async stream(messages, handlers, options = {}) {
|
|
1139
|
+
if (this.state !== "connected") {
|
|
1140
|
+
throw new Error("Not connected. Call connect() first.");
|
|
1141
|
+
}
|
|
1142
|
+
const requestId = this.generateRequestId();
|
|
1143
|
+
const sessionId = options.sessionId || this.generateRequestId();
|
|
1144
|
+
this.streamSessions.set(sessionId, {
|
|
1145
|
+
handlers,
|
|
1146
|
+
requestId
|
|
1147
|
+
});
|
|
1148
|
+
this.sendRaw({
|
|
1149
|
+
category: "",
|
|
1150
|
+
action: "stream",
|
|
1151
|
+
data: {
|
|
1152
|
+
provider: options.provider,
|
|
1153
|
+
model: options.model,
|
|
1154
|
+
messages,
|
|
1155
|
+
system: options.system,
|
|
1156
|
+
temperature: options.temperature,
|
|
1157
|
+
max_tokens: options.maxTokens,
|
|
1158
|
+
session_id: sessionId,
|
|
1159
|
+
metadata: options.metadata
|
|
1160
|
+
},
|
|
1161
|
+
request_id: requestId
|
|
1162
|
+
});
|
|
1163
|
+
return {
|
|
1164
|
+
sessionId,
|
|
1165
|
+
stop: async () => {
|
|
1166
|
+
await this.stopStream(sessionId);
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* 스트리밍 중지
|
|
1172
|
+
*/
|
|
1173
|
+
async stopStream(sessionId) {
|
|
1174
|
+
if (this.state !== "connected") {
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
const requestId = this.generateRequestId();
|
|
1178
|
+
this.sendRaw({
|
|
1179
|
+
category: "",
|
|
1180
|
+
action: "stream_stop",
|
|
1181
|
+
data: { session_id: sessionId },
|
|
1182
|
+
request_id: requestId
|
|
1183
|
+
});
|
|
1184
|
+
this.streamSessions.delete(sessionId);
|
|
1185
|
+
}
|
|
1108
1186
|
/**
|
|
1109
1187
|
* 연결 상태
|
|
1110
1188
|
*/
|
|
@@ -1240,6 +1318,44 @@ var RealtimeAPI = class {
|
|
|
1240
1318
|
}
|
|
1241
1319
|
case "pong":
|
|
1242
1320
|
break;
|
|
1321
|
+
case "stream_token": {
|
|
1322
|
+
const data = msg.data;
|
|
1323
|
+
const session = this.streamSessions.get(data.session_id);
|
|
1324
|
+
if (session?.handlers.onToken) {
|
|
1325
|
+
session.handlers.onToken(data.token, data.index);
|
|
1326
|
+
}
|
|
1327
|
+
break;
|
|
1328
|
+
}
|
|
1329
|
+
case "stream_done": {
|
|
1330
|
+
const data = msg.data;
|
|
1331
|
+
const session = this.streamSessions.get(data.session_id);
|
|
1332
|
+
if (session?.handlers.onDone) {
|
|
1333
|
+
session.handlers.onDone({
|
|
1334
|
+
sessionId: data.session_id,
|
|
1335
|
+
fullText: data.full_text,
|
|
1336
|
+
totalTokens: data.total_tokens,
|
|
1337
|
+
promptTokens: data.prompt_tokens,
|
|
1338
|
+
duration: data.duration_ms
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
this.streamSessions.delete(data.session_id);
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
case "stream_error": {
|
|
1345
|
+
const data = msg.data;
|
|
1346
|
+
if (msg.request_id) {
|
|
1347
|
+
for (const [sessionId, session] of this.streamSessions) {
|
|
1348
|
+
if (session.requestId === msg.request_id) {
|
|
1349
|
+
if (session.handlers.onError) {
|
|
1350
|
+
session.handlers.onError(new Error(data.message));
|
|
1351
|
+
}
|
|
1352
|
+
this.streamSessions.delete(sessionId);
|
|
1353
|
+
break;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1243
1359
|
}
|
|
1244
1360
|
}
|
|
1245
1361
|
handleDisconnect() {
|
|
@@ -1278,6 +1394,12 @@ var RealtimeAPI = class {
|
|
|
1278
1394
|
this.ws.send(JSON.stringify(message));
|
|
1279
1395
|
});
|
|
1280
1396
|
}
|
|
1397
|
+
sendRaw(message) {
|
|
1398
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
1399
|
+
throw new Error("Not connected");
|
|
1400
|
+
}
|
|
1401
|
+
this.ws.send(JSON.stringify(message));
|
|
1402
|
+
}
|
|
1281
1403
|
notifyStateChange() {
|
|
1282
1404
|
this.stateHandlers.forEach((h) => h(this.state));
|
|
1283
1405
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -925,6 +925,7 @@ var RealtimeAPI = class {
|
|
|
925
925
|
this.retryCount = 0;
|
|
926
926
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
927
927
|
this.subscriptions = /* @__PURE__ */ new Map();
|
|
928
|
+
this.streamSessions = /* @__PURE__ */ new Map();
|
|
928
929
|
this.stateHandlers = [];
|
|
929
930
|
this.errorHandlers = [];
|
|
930
931
|
this.http = http;
|
|
@@ -1071,6 +1072,83 @@ var RealtimeAPI = class {
|
|
|
1071
1072
|
total: response.total
|
|
1072
1073
|
};
|
|
1073
1074
|
}
|
|
1075
|
+
/**
|
|
1076
|
+
* AI 스트리밍
|
|
1077
|
+
* @param messages 대화 메시지 목록
|
|
1078
|
+
* @param handlers 스트리밍 핸들러 (onToken, onDone, onError)
|
|
1079
|
+
* @param options 스트리밍 옵션
|
|
1080
|
+
* @returns 스트리밍 세션 (중지 가능)
|
|
1081
|
+
*
|
|
1082
|
+
* @example
|
|
1083
|
+
* ```typescript
|
|
1084
|
+
* const session = await client.realtime.stream(
|
|
1085
|
+
* [{ role: 'user', content: '안녕하세요' }],
|
|
1086
|
+
* {
|
|
1087
|
+
* onToken: (token) => {
|
|
1088
|
+
* process.stdout.write(token) // 실시간 출력
|
|
1089
|
+
* },
|
|
1090
|
+
* onDone: (result) => {
|
|
1091
|
+
* console.log('완료:', result.fullText)
|
|
1092
|
+
* },
|
|
1093
|
+
* onError: (error) => {
|
|
1094
|
+
* console.error('에러:', error)
|
|
1095
|
+
* }
|
|
1096
|
+
* },
|
|
1097
|
+
* { model: 'gemini-2.0-flash', temperature: 0.7 }
|
|
1098
|
+
* )
|
|
1099
|
+
*
|
|
1100
|
+
* // 필요 시 중지
|
|
1101
|
+
* await session.stop()
|
|
1102
|
+
* ```
|
|
1103
|
+
*/
|
|
1104
|
+
async stream(messages, handlers, options = {}) {
|
|
1105
|
+
if (this.state !== "connected") {
|
|
1106
|
+
throw new Error("Not connected. Call connect() first.");
|
|
1107
|
+
}
|
|
1108
|
+
const requestId = this.generateRequestId();
|
|
1109
|
+
const sessionId = options.sessionId || this.generateRequestId();
|
|
1110
|
+
this.streamSessions.set(sessionId, {
|
|
1111
|
+
handlers,
|
|
1112
|
+
requestId
|
|
1113
|
+
});
|
|
1114
|
+
this.sendRaw({
|
|
1115
|
+
category: "",
|
|
1116
|
+
action: "stream",
|
|
1117
|
+
data: {
|
|
1118
|
+
provider: options.provider,
|
|
1119
|
+
model: options.model,
|
|
1120
|
+
messages,
|
|
1121
|
+
system: options.system,
|
|
1122
|
+
temperature: options.temperature,
|
|
1123
|
+
max_tokens: options.maxTokens,
|
|
1124
|
+
session_id: sessionId,
|
|
1125
|
+
metadata: options.metadata
|
|
1126
|
+
},
|
|
1127
|
+
request_id: requestId
|
|
1128
|
+
});
|
|
1129
|
+
return {
|
|
1130
|
+
sessionId,
|
|
1131
|
+
stop: async () => {
|
|
1132
|
+
await this.stopStream(sessionId);
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* 스트리밍 중지
|
|
1138
|
+
*/
|
|
1139
|
+
async stopStream(sessionId) {
|
|
1140
|
+
if (this.state !== "connected") {
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
const requestId = this.generateRequestId();
|
|
1144
|
+
this.sendRaw({
|
|
1145
|
+
category: "",
|
|
1146
|
+
action: "stream_stop",
|
|
1147
|
+
data: { session_id: sessionId },
|
|
1148
|
+
request_id: requestId
|
|
1149
|
+
});
|
|
1150
|
+
this.streamSessions.delete(sessionId);
|
|
1151
|
+
}
|
|
1074
1152
|
/**
|
|
1075
1153
|
* 연결 상태
|
|
1076
1154
|
*/
|
|
@@ -1206,6 +1284,44 @@ var RealtimeAPI = class {
|
|
|
1206
1284
|
}
|
|
1207
1285
|
case "pong":
|
|
1208
1286
|
break;
|
|
1287
|
+
case "stream_token": {
|
|
1288
|
+
const data = msg.data;
|
|
1289
|
+
const session = this.streamSessions.get(data.session_id);
|
|
1290
|
+
if (session?.handlers.onToken) {
|
|
1291
|
+
session.handlers.onToken(data.token, data.index);
|
|
1292
|
+
}
|
|
1293
|
+
break;
|
|
1294
|
+
}
|
|
1295
|
+
case "stream_done": {
|
|
1296
|
+
const data = msg.data;
|
|
1297
|
+
const session = this.streamSessions.get(data.session_id);
|
|
1298
|
+
if (session?.handlers.onDone) {
|
|
1299
|
+
session.handlers.onDone({
|
|
1300
|
+
sessionId: data.session_id,
|
|
1301
|
+
fullText: data.full_text,
|
|
1302
|
+
totalTokens: data.total_tokens,
|
|
1303
|
+
promptTokens: data.prompt_tokens,
|
|
1304
|
+
duration: data.duration_ms
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
this.streamSessions.delete(data.session_id);
|
|
1308
|
+
break;
|
|
1309
|
+
}
|
|
1310
|
+
case "stream_error": {
|
|
1311
|
+
const data = msg.data;
|
|
1312
|
+
if (msg.request_id) {
|
|
1313
|
+
for (const [sessionId, session] of this.streamSessions) {
|
|
1314
|
+
if (session.requestId === msg.request_id) {
|
|
1315
|
+
if (session.handlers.onError) {
|
|
1316
|
+
session.handlers.onError(new Error(data.message));
|
|
1317
|
+
}
|
|
1318
|
+
this.streamSessions.delete(sessionId);
|
|
1319
|
+
break;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
break;
|
|
1324
|
+
}
|
|
1209
1325
|
}
|
|
1210
1326
|
}
|
|
1211
1327
|
handleDisconnect() {
|
|
@@ -1244,6 +1360,12 @@ var RealtimeAPI = class {
|
|
|
1244
1360
|
this.ws.send(JSON.stringify(message));
|
|
1245
1361
|
});
|
|
1246
1362
|
}
|
|
1363
|
+
sendRaw(message) {
|
|
1364
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
1365
|
+
throw new Error("Not connected");
|
|
1366
|
+
}
|
|
1367
|
+
this.ws.send(JSON.stringify(message));
|
|
1368
|
+
}
|
|
1247
1369
|
notifyStateChange() {
|
|
1248
1370
|
this.stateHandlers.forEach((h) => h(this.state));
|
|
1249
1371
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "connectbase-client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -42,7 +42,10 @@
|
|
|
42
42
|
"storage",
|
|
43
43
|
"sdk",
|
|
44
44
|
"cli",
|
|
45
|
-
"deploy"
|
|
45
|
+
"deploy",
|
|
46
|
+
"ai",
|
|
47
|
+
"streaming",
|
|
48
|
+
"realtime"
|
|
46
49
|
],
|
|
47
50
|
"author": "",
|
|
48
51
|
"license": "MIT",
|