connectbase-client 1.12.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +77 -0
- package/MIGRATION-v2.md +149 -0
- package/README.md +23 -0
- package/dist/connect-base.umd.js +2 -2
- package/dist/index.d.mts +1 -17
- package/dist/index.d.ts +1 -17
- package/dist/index.js +1 -36
- package/dist/index.mjs +1 -36
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,83 @@
|
|
|
3
3
|
본 SDK 의 모든 주요 변경사항을 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/) 형식으로 기록합니다.
|
|
4
4
|
버전은 [Semantic Versioning](https://semver.org/lang/ko/) 을 따릅니다.
|
|
5
5
|
|
|
6
|
+
## [2.0.0] - 2026-04-27 — BREAKING
|
|
7
|
+
|
|
8
|
+
Realtime presence/typing 단일화의 최종 단계. 1.13 deprecation 단계를 건너뛰고 즉시
|
|
9
|
+
deprecated 코드를 제거. presence/typing 의 단일 SoT 는 `cb.realtime.*`.
|
|
10
|
+
|
|
11
|
+
### Removed (BREAKING)
|
|
12
|
+
|
|
13
|
+
- **`cb.database.setPresence(status, device, metadata)`** — 코드 자체 제거. 호출 시 `TypeError`.
|
|
14
|
+
- **`cb.database.subscribePresence(userIds, callback)`** — 코드 자체 제거.
|
|
15
|
+
- **`DatabasePresenceState`** type — `PresenceInfo` (from `'./realtime'`) 사용.
|
|
16
|
+
- 1.13 에서 추가됐던 deprecation `console.warn` 헬퍼도 함께 제거 (필요 없음).
|
|
17
|
+
|
|
18
|
+
### Migration
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// Before
|
|
22
|
+
cb.database.setPresence('online', 'web', { nickname: '홍길동' })
|
|
23
|
+
cb.database.subscribePresence(['user1', 'user2'], (states) => {
|
|
24
|
+
console.log(states['user1']?.last_seen)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// After
|
|
28
|
+
await cb.realtime.setPresence('online', { device: 'web', metadata: { nickname: '홍길동' } })
|
|
29
|
+
const unsub = await cb.realtime.subscribePresence('user1', (info) => {
|
|
30
|
+
console.log(info.lastSeen)
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
자세한 가이드: [MIGRATION-v2.md](./MIGRATION-v2.md). 필드 차이는 `user_id`→`userId`,
|
|
35
|
+
`last_seen` (ISO) → `lastSeen` (epoch ms), `'busy'` 상태 추가.
|
|
36
|
+
|
|
37
|
+
### Server-side
|
|
38
|
+
|
|
39
|
+
데이터 서버 측 presence/typing 코드도 함께 제거되었습니다. `presence_set` / `presence_subscribe`
|
|
40
|
+
/ `typing_*` 메시지를 데이터 서버 WebSocket 으로 보내면 `USE_SOCKET_SERVER` 에러가 반환됩니다.
|
|
41
|
+
|
|
42
|
+
## [1.13.0] - 2026-04-27
|
|
43
|
+
|
|
44
|
+
Realtime presence/typing 단일화 — `cb.database.setPresence` / `cb.database.subscribePresence`
|
|
45
|
+
는 v2.0.0 에서 제거됩니다. 신규 코드는 `cb.realtime.*` 를 사용하세요. 자세한 마이그레이션은
|
|
46
|
+
[MIGRATION-v2.md](./MIGRATION-v2.md) 참고.
|
|
47
|
+
|
|
48
|
+
### Deprecated
|
|
49
|
+
|
|
50
|
+
- **`cb.database.setPresence(status, device, metadata)`** → `cb.realtime.setPresence(status, { device, metadata })`
|
|
51
|
+
- **`cb.database.subscribePresence(userIds, onPresence)`** → `cb.realtime.subscribePresence(userId, handler)` 또는
|
|
52
|
+
`cb.realtime.onPresenceChange(handler)` (다중 사용자 일괄 수신).
|
|
53
|
+
- **`DatabasePresenceState`** type → `PresenceInfo` (from `'./realtime'`).
|
|
54
|
+
- 필드 네이밍: `user_id` → `userId`, `last_seen` (ISO) → `lastSeen` (epoch ms 숫자).
|
|
55
|
+
- 상태값: `'busy'` 추가 (PresenceInfo 만 지원).
|
|
56
|
+
|
|
57
|
+
호출 시 개발 환경(`process.env.NODE_ENV !== 'production'`)에서 `console.warn` 이 1회 출력됩니다.
|
|
58
|
+
런타임 동작은 1.12.x 와 동일합니다 (이번 버전은 deprecate 만, 코드 제거는 v2.0.0).
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- **WebSocket URL 기본값** `cb.database.connectRealtime` 의 기본 endpoint 가
|
|
63
|
+
`/v1/realtime/ws` → `/v1/database/realtime/ws` 로 변경. 기존 경로는 30일간 alias 로
|
|
64
|
+
유지되며 응답에 `Sunset` (RFC 8594) + `X-Deprecated-Endpoint` 헤더가 부착됩니다.
|
|
65
|
+
명시적으로 `dataServerUrl` 을 지정한 경우 자동 변환은 하지 않습니다 (Hyrum's Law 준수).
|
|
66
|
+
|
|
67
|
+
### Migration
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
// Before (1.12.x)
|
|
71
|
+
cb.database.setPresence('online', 'web', { nickname: '홍길동' })
|
|
72
|
+
cb.database.subscribePresence(['user1', 'user2'], (states) => {
|
|
73
|
+
console.log(states['user1']?.last_seen) // ISO string
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// After (1.13+, 권장 — v2 호환)
|
|
77
|
+
cb.realtime.setPresence('online', { device: 'web', metadata: { nickname: '홍길동' } })
|
|
78
|
+
const unsubscribe = await cb.realtime.subscribePresence('user1', (info) => {
|
|
79
|
+
console.log(info.lastSeen) // epoch ms number
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
6
83
|
## [1.12.0] - 2026-04-26
|
|
7
84
|
|
|
8
85
|
Analytics 조회 정렬 옵션 정합성 수정 — `VisitorListOptions.sort_by` 의 TypeScript
|
package/MIGRATION-v2.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Migration to v2.0.0 — Presence/Typing 단일화
|
|
2
|
+
|
|
3
|
+
> 작성일: 2026-04-27
|
|
4
|
+
> 대상 버전: 1.12.x → 2.0.0 (1.13 deprecation 단계 생략, 즉시 제거)
|
|
5
|
+
> 영향: `cb.database.setPresence`, `cb.database.subscribePresence`, `DatabasePresenceState`, WebSocket URL
|
|
6
|
+
|
|
7
|
+
본 문서는 v1.12.x 이전에서 v2.0.0 으로 업그레이드할 때 필요한 변경 사항을 안내합니다.
|
|
8
|
+
v2.0.0 에서 데이터베이스 측 presence/typing API 가 코드에서 완전히 제거되었습니다
|
|
9
|
+
(호출 시 `TypeError`). presence/typing 의 단일 SoT 는 `cb.realtime.*` 입니다.
|
|
10
|
+
|
|
11
|
+
## 1. 책임 경계 변경
|
|
12
|
+
|
|
13
|
+
| 기능 | v1.12.x | v2.0.0 |
|
|
14
|
+
|------|---------|--------|
|
|
15
|
+
| presence 상태 설정/조회/구독 | `cb.database` / `cb.realtime` 양쪽 | `cb.realtime` 만 |
|
|
16
|
+
| typing 상태 | `cb.database` | `cb.realtime` 만 |
|
|
17
|
+
| DB 변경 구독 | `cb.database.connectRealtime` | (변경 없음) |
|
|
18
|
+
| WebSocket URL (DB) | `/v1/realtime/ws` | `/v1/database/realtime/ws` |
|
|
19
|
+
|
|
20
|
+
## 2. setPresence 마이그레이션
|
|
21
|
+
|
|
22
|
+
### Before (v1.12.x)
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
cb.database.setPresence('online', 'web', { nickname: '홍길동' })
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### After (v1.13+ / v2)
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
await cb.realtime.setPresence('online', {
|
|
32
|
+
device: 'web',
|
|
33
|
+
metadata: { nickname: '홍길동' }
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**차이점**
|
|
38
|
+
|
|
39
|
+
- `cb.realtime.setPresence` 는 `Promise<void>` 를 반환합니다 (기존은 동기 void).
|
|
40
|
+
- `device` 와 `metadata` 가 두 번째 객체 파라미터의 옵션 필드입니다.
|
|
41
|
+
- 상태값에 `'busy'` 가 추가되어 있습니다 (`'online' | 'away' | 'busy' | 'offline'`).
|
|
42
|
+
|
|
43
|
+
## 3. subscribePresence 마이그레이션
|
|
44
|
+
|
|
45
|
+
### Before (v1.12.x)
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
cb.database.subscribePresence(['user1', 'user2'], (states) => {
|
|
49
|
+
console.log(states['user1']?.last_seen) // string ISO
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### After (옵션 1 — 단일 사용자별 구독)
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const unsub1 = await cb.realtime.subscribePresence('user1', (info) => {
|
|
57
|
+
console.log(info.lastSeen) // number (epoch ms)
|
|
58
|
+
})
|
|
59
|
+
const unsub2 = await cb.realtime.subscribePresence('user2', (info) => {
|
|
60
|
+
console.log(info.userId, info.status, info.eventType) // join/leave/update
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// 정리
|
|
64
|
+
unsub1()
|
|
65
|
+
unsub2()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### After (옵션 2 — 앱 전체 변경 일괄 수신)
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const unsub = cb.realtime.onPresenceChange((info) => {
|
|
72
|
+
// 모든 사용자의 변경 이벤트가 한 콜백으로 전달됨
|
|
73
|
+
if (['user1', 'user2'].includes(info.userId)) {
|
|
74
|
+
console.log(info.userId, info.status)
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**차이점**
|
|
80
|
+
|
|
81
|
+
| 필드 | v1.12 (`DatabasePresenceState`) | v1.13+ (`PresenceInfo`) |
|
|
82
|
+
|------|-------------------------------|-----------------------|
|
|
83
|
+
| ID | `user_id` (snake) | `userId` (camel) |
|
|
84
|
+
| 마지막 활동 | `last_seen: string` (ISO) | `lastSeen: number` (epoch ms) |
|
|
85
|
+
| 상태 | `'online' \| 'away' \| 'offline'` | `'online' \| 'away' \| 'busy' \| 'offline'` |
|
|
86
|
+
| 이벤트 타입 | (없음) | `eventType?: 'join' \| 'leave' \| 'update'` |
|
|
87
|
+
|
|
88
|
+
### 변환 헬퍼 (마이그레이션 보조)
|
|
89
|
+
|
|
90
|
+
기존 `DatabasePresenceState` 모양으로 데이터를 가공하던 코드를 점진적으로 옮길 때 도움이
|
|
91
|
+
되도록, v1 호환 형태(snake_case · ISO 문자열) 로 변환하는 헬퍼 예제. v2.0.0 에서는
|
|
92
|
+
`DatabasePresenceState` type 이 제거되었으므로 inline 타입으로 정의한다.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import type { PresenceInfo } from 'connectbase-client'
|
|
96
|
+
|
|
97
|
+
interface LegacyPresenceState {
|
|
98
|
+
user_id: string
|
|
99
|
+
status: 'online' | 'away' | 'offline'
|
|
100
|
+
last_seen: string
|
|
101
|
+
device?: string
|
|
102
|
+
metadata?: Record<string, unknown>
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function fromPresenceInfo(info: PresenceInfo): LegacyPresenceState {
|
|
106
|
+
return {
|
|
107
|
+
user_id: info.userId,
|
|
108
|
+
status: info.status === 'busy' ? 'away' : info.status,
|
|
109
|
+
last_seen: new Date(info.lastSeen).toISOString(),
|
|
110
|
+
device: info.device,
|
|
111
|
+
metadata: info.metadata,
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 4. typing 마이그레이션 (있는 경우)
|
|
117
|
+
|
|
118
|
+
`cb.database` 에는 공개된 typing 메서드가 없으므로 추가 마이그레이션은 불필요합니다.
|
|
119
|
+
`cb.realtime.startTyping(roomId)` / `cb.realtime.stopTyping(roomId)` /
|
|
120
|
+
`cb.realtime.onTypingChange(roomId, handler)` 를 그대로 사용하세요.
|
|
121
|
+
|
|
122
|
+
## 5. WebSocket URL 변경
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
await cb.database.connectRealtime({
|
|
126
|
+
accessToken: 'xxx',
|
|
127
|
+
dataServerUrl: 'https://data.connectbase.world',
|
|
128
|
+
})
|
|
129
|
+
// SDK 는 자동으로 /v1/database/realtime/ws 로 연결 (v1.13+).
|
|
130
|
+
// 사용자가 dataServerUrl 의 host:port 만 지정하면 path 는 SDK 가 결정.
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
옛 경로(`/v1/realtime/ws`) 는 v2.0.0 과 함께 서버에서도 제거되었습니다. 사용자 측
|
|
134
|
+
firewall/proxy 에 `/v1/database/realtime/` 경로 허용 규칙이 있는지 확인하세요.
|
|
135
|
+
|
|
136
|
+
## 6. 런타임 동작 차이
|
|
137
|
+
|
|
138
|
+
- **v1.12.x**: 두 API 모두 동작.
|
|
139
|
+
- **v2.0.0**: `cb.database.setPresence` / `subscribePresence` 호출 시 `TypeError` (메서드 미존재).
|
|
140
|
+
|
|
141
|
+
## 7. 자동 마이그레이션 (codemod)
|
|
142
|
+
|
|
143
|
+
향후 별도 codemod 스크립트가 제공될 예정입니다. 우선은 위 가이드에 따라 수동 변경을
|
|
144
|
+
권장합니다 (필드 네이밍/시그니처 차이가 있어 수동 검토가 안전).
|
|
145
|
+
|
|
146
|
+
## 8. 문의
|
|
147
|
+
|
|
148
|
+
- 이슈: GitHub Issues
|
|
149
|
+
- 디스코드: 공식 채널의 `#sdk` 룸
|
package/README.md
CHANGED
|
@@ -708,6 +708,29 @@ await subscription.unsubscribe()
|
|
|
708
708
|
await cb.realtime.disconnect()
|
|
709
709
|
```
|
|
710
710
|
|
|
711
|
+
#### Presence / Typing
|
|
712
|
+
|
|
713
|
+
Presence(온라인 상태) 와 typing(입력 중 표시) 은 `cb.realtime.*` 가 단일 SoT 입니다.
|
|
714
|
+
v2.0.0 에서 `cb.database.setPresence` / `subscribePresence` 는 제거되었습니다.
|
|
715
|
+
v1.x 에서 마이그레이션은 [MIGRATION-v2.md](./MIGRATION-v2.md) 참고.
|
|
716
|
+
|
|
717
|
+
```typescript
|
|
718
|
+
// 본인 온라인 상태 publish
|
|
719
|
+
await cb.realtime.setPresence('online', { device: 'web', metadata: { nickname: '홍길동' } })
|
|
720
|
+
|
|
721
|
+
// 다른 사용자 상태 구독
|
|
722
|
+
const unsub = await cb.realtime.subscribePresence('user-id', (info) => {
|
|
723
|
+
console.log(info.userId, info.status, info.eventType) // join | leave | update
|
|
724
|
+
})
|
|
725
|
+
|
|
726
|
+
// 룸 단위 typing indicator
|
|
727
|
+
await cb.realtime.startTyping('room-id')
|
|
728
|
+
await cb.realtime.stopTyping('room-id')
|
|
729
|
+
const unsubTyping = await cb.realtime.onTypingChange('room-id', (typing) => {
|
|
730
|
+
console.log(typing.users) // 현재 입력 중인 사용자 ID 목록
|
|
731
|
+
})
|
|
732
|
+
```
|
|
733
|
+
|
|
711
734
|
#### AI Streaming
|
|
712
735
|
|
|
713
736
|
Real-time AI text generation using Gemini API through WebSocket.
|
package/dist/connect-base.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var ConnectBaseModule=(()=>{var ee=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var be=Object.getOwnPropertyNames;var ye=Object.prototype.hasOwnProperty;var ve=(a,e)=>{for(var t in e)ee(a,t,{get:e[t],enumerable:!0})},we=(a,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of be(e))!ye.call(a,i)&&i!==t&&ee(a,i,{get:()=>e[i],enumerable:!(s=fe(e,i))||s.enumerable});return a};var Pe=a=>we(ee({},"__esModule",{value:!0}),a);var Me={};ve(Me,{AIAPI:()=>$,AdsAPI:()=>k,ApiError:()=>u,AuthError:()=>P,ConnectBase:()=>Z,GameAPI:()=>I,GameRoom:()=>x,GameRoomTransport:()=>Y,NativeAPI:()=>C,SessionManager:()=>E,VideoProcessingError:()=>T,default:()=>Ee,isWebTransportSupported:()=>ne});var u=class extends Error{constructor(t,s,i,r){super(s);this.statusCode=t;this.name="ApiError",this.code=i,this.details=r}},P=class extends Error{constructor(e){super(e),this.name="AuthError"}};function R(a={}){let e=new AbortController,t=a.timeout??3e4,s=a.signal,i=null,r=null;return s&&(s.aborted?e.abort(s.reason):(r=()=>e.abort(s.reason),s.addEventListener("abort",r,{once:!0}))),t>0&&Number.isFinite(t)&&(i=setTimeout(()=>{e.abort(new DOMException(`Request timed out after ${t}ms`,"TimeoutError"))},t)),{signal:e.signal,cleanup:()=>{i!==null&&clearTimeout(i),s&&r&&s.removeEventListener("abort",r)}}}var oe="cb_auth_tokens",A=class{constructor(e){this.isRefreshing=!1;this.refreshPromise=null;this.refreshFailureCount=0;this.refreshLockedUntil=0;this.config={...e},this.storageKey=this.buildStorageKey(),this.warnIfUnsafePersistence(),this.restoreTokens()}warnIfUnsafePersistence(){typeof window>"u"||(this.config.persistence==="localStorage"?console.warn(`[connect-base-client] persistence="localStorage" \uB294 XSS \uC2DC \uD1A0\uD070 \uC601\uAD6C \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4. refresh token \uC740 \uC11C\uBC84 HttpOnly \uCFE0\uD0A4\uB85C \uBC1C\uAE09\uBC1B\uACE0 \uAE30\uBCF8\uAC12('none')\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`):this.config.persistence==="sessionStorage"&&console.warn('[connect-base-client] persistence="sessionStorage" \uB294 XSS \uC2DC \uD604\uC7AC \uD0ED \uC138\uC158 \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4.'))}updateConfig(e){this.config={...this.config,...e}}setTokens(e,t){this.config.accessToken=e,this.config.refreshToken=t,this.persistTokens()}clearTokens(){this.config.accessToken=void 0,this.config.refreshToken=void 0,this.removePersistedTokens()}get persistence(){return this.config.persistence??"none"}getStorage(){return typeof window>"u"?null:this.persistence==="localStorage"&&typeof localStorage<"u"?localStorage:this.persistence==="sessionStorage"&&typeof sessionStorage<"u"?sessionStorage:null}getPersistenceStorage(){return this.getStorage()}buildStorageKey(){let e=this.config.publicKey??this.config.secretKey;if(!e)return oe;let t=0;for(let s=0;s<e.length;s++)t=(t<<5)-t+e.charCodeAt(s),t=t&t;return`${oe}_${Math.abs(t).toString(36)}`}persistTokens(){if(this.persistence==="none")return;let e=this.getStorage();!e||!this.config.accessToken||!this.config.refreshToken||e.setItem(this.storageKey,JSON.stringify({accessToken:this.config.accessToken,refreshToken:this.config.refreshToken}))}restoreTokens(){if(this.persistence==="none"||this.config.accessToken&&this.config.refreshToken)return;let e=this.getStorage();if(!e)return;let t=e.getItem(this.storageKey);if(t)try{let{accessToken:s,refreshToken:i}=JSON.parse(t);s&&i&&(this.config.accessToken=s,this.config.refreshToken=i)}catch{e.removeItem(this.storageKey)}}removePersistedTokens(){if(this.persistence==="none")return;let e=this.getStorage();e&&e.removeItem(this.storageKey)}hasPublicKey(){return!!this.config.publicKey}getPublicKey(){return this.config.publicKey}hasSecretKey(){return!!this.config.secretKey}getSecretKey(){return this.config.secretKey}getCredential(){return this.config.publicKey??this.config.secretKey}getAccessToken(){return this.config.accessToken}hasJWT(){return!!this.config.accessToken}getBaseUrl(){return this.config.baseUrl}async refreshAccessToken(){if(this.isRefreshing)return this.refreshPromise;if(Date.now()<this.refreshLockedUntil){let t=new P("Token refresh locked due to repeated failures. Please login again.");throw this.emitError(t),this.config.onAuthError?.(t),t}if(this.isRefreshing=!0,!this.config.refreshToken){this.isRefreshing=!1,this.config.onTokenExpired?.();let t=new P("Refresh token is missing. Please login again.");throw this.emitError(t),this.config.onAuthError?.(t),t}return this.refreshPromise=(async()=>{let{signal:t,cleanup:s}=R({timeout:this.config.requestTimeoutMs??3e4});try{let i=await fetch(`${this.config.baseUrl}/v1/auth/re-issue`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.refreshToken}`},signal:t});if(!i.ok)throw new Error("Token refresh failed");let r=await i.json();return this.setTokens(r.access_token,r.refresh_token),this.config.onTokenRefresh?.({accessToken:r.access_token,refreshToken:r.refresh_token}),this.refreshFailureCount=0,this.refreshLockedUntil=0,r.access_token}catch{this.refreshFailureCount++;let i=Math.min(500*2**Math.max(0,this.refreshFailureCount-1),3e4);this.refreshLockedUntil=Date.now()+i,this.clearTokens(),this.config.onTokenExpired?.();let r=new P("Token refresh failed. Please login again.");throw this.emitError(r),this.config.onAuthError?.(r),r}finally{s(),this.isRefreshing=!1,this.refreshPromise=null}})(),this.refreshPromise}emitError(e){try{this.config.onError?.(e)}catch{}}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;t.set("Content-Type","application/json");let s=this.getCredential();if(s&&t.set("X-Public-Key",s),!e?.skipAuth&&this.config.accessToken){let i=this.config.accessToken;if(this.isTokenExpired(i)&&this.config.refreshToken){let r=await this.refreshAccessToken();r&&(i=r)}t.set("Authorization",`Bearer ${i}`)}return e?.headers&&Object.entries(e.headers).forEach(([i,r])=>{t.set(i,r)}),t}async handleResponse(e){if(!e.ok){let t=await e.json().catch(()=>({message:e.statusText})),s=e.status===429?e.headers.get("Retry-After"):null,i;if(s){let l=Number.parseInt(s,10);if(Number.isFinite(l)&&l>=0)i=l;else{let d=Date.parse(s);Number.isFinite(d)&&(i=Math.max(0,Math.round((d-Date.now())/1e3)))}}let r=t.error;if(r&&typeof r=="object"&&"message"in r){let l={...r.details&&typeof r.details=="object"?r.details:{}};i!==void 0&&(l.retry_after_seconds=i);let d=new u(e.status,r.message||"Unknown error",r.code,Object.keys(l).length>0?l:r.details);throw this.emitError(d),d}let n=typeof r=="string"?r:t.message||"Unknown error",o=i!==void 0?{retry_after_seconds:i}:void 0,c=new u(e.status,n,void 0,o);throw this.emitError(c),c}return e.status===204||e.headers.get("content-length")==="0"?{}:e.json()}async doFetch(e,t,s){let{signal:i,cleanup:r}=R({timeout:s?.timeout??this.config.requestTimeoutMs??3e4,signal:s?.signal});try{let n=await fetch(`${this.config.baseUrl}${e}`,{...t,signal:i});return await this.handleResponse(n)}finally{r()}}async get(e,t){let s=await this.prepareHeaders(t);return this.doFetch(e,{method:"GET",headers:s},t)}async post(e,t,s){let i=await this.prepareHeaders(s);return t instanceof FormData&&i.delete("Content-Type"),this.doFetch(e,{method:"POST",headers:i,body:t instanceof FormData?t:JSON.stringify(t)},s)}async put(e,t,s){let i=await this.prepareHeaders(s);return this.doFetch(e,{method:"PUT",headers:i,body:JSON.stringify(t)},s)}async patch(e,t,s){let i=await this.prepareHeaders(s);return this.doFetch(e,{method:"PATCH",headers:i,body:JSON.stringify(t)},s)}async delete(e,t){let s=await this.prepareHeaders(t);return this.doFetch(e,{method:"DELETE",headers:s},t)}async fetchRaw(e,t){let s=await this.prepareHeaders(),i=new Headers(s);return t?.headers&&new Headers(t.headers).forEach((n,o)=>i.set(o,n)),fetch(`${this.config.baseUrl}${e}`,{...t,headers:i})}};function Se(a,e){switch(e){case"string":return typeof a=="string";case"number":return typeof a=="number"&&Number.isFinite(a);case"boolean":return typeof a=="boolean";case"array":return Array.isArray(a);case"object":return typeof a=="object"&&a!==null&&!Array.isArray(a);case"string-or-number":return typeof a=="string"||typeof a=="number"&&Number.isFinite(a)}}function f(a,e,t){if(typeof a!="object"||a===null||Array.isArray(a))throw new u(502,`[${t}] expected object, got ${typeof a}`,"SCHEMA_MISMATCH");let s=a;for(let[i,r]of Object.entries(e)){if(!(i in s)){if(r.optional)continue;throw new u(502,`[${t}] missing required field "${i}"`,"SCHEMA_MISMATCH")}let o=s[i];if(!(r.optional&&o==null)&&!Se(o,r.type))throw new u(502,`[${t}] field "${i}" expected ${r.type}, got ${typeof o}`,"SCHEMA_MISMATCH")}return a}var ae="cb_guest_";function _e(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 U=class{constructor(e){this.http=e;this.guestMemberLoginPromise=null;this.cachedGuestMemberTokenKey=null;this.analytics=null}_attachAnalytics(e){this.analytics=e}notifyVisitorTracker(e){if(this.analytics){this.analytics.setMemberId(e);return}typeof window>"u"||(e?typeof window.__cbSetMember=="function"&&window.__cbSetMember(e):typeof window.__cbClearMember=="function"&&window.__cbClearMember())}async getAuthSettings(){return this.http.get("/v1/public/auth-settings",{skipAuth:!0})}async signUpMember(e){let t=await this.http.post("/v1/public/app-members/signup",e,{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),this.notifyVisitorTracker(t.member_id),t}async signInMember(e){let t=await this.http.post("/v1/public/app-members/signin",e,{skipAuth:!0});return f(t,{access_token:{type:"string"},refresh_token:{type:"string"},member_id:{type:"string-or-number"}},"auth.signInMember"),this.http.setTokens(t.access_token,t.refresh_token),this.notifyVisitorTracker(t.member_id),t}async signInAsGuestMember(){if(this.guestMemberLoginPromise)return this.guestMemberLoginPromise;this.guestMemberLoginPromise=this.executeGuestMemberLogin();try{return await this.guestMemberLoginPromise}finally{this.guestMemberLoginPromise=null}}async getMe(){let e=await this.http.get("/v1/public/app-members/me");return f(e,{member_id:{type:"string-or-number"}},"auth.getMe"),e}async updateCustomData(e){return this.http.patch("/v1/public/app-members/me/custom-data",e)}async signOut(){try{await this.http.post("/v1/auth/logout")}finally{this.http.clearTokens(),this.notifyVisitorTracker(null)}}clearGuestMemberTokens(){let e=this.http.getPersistenceStorage();e&&e.removeItem(this.getGuestMemberTokenKey())}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 this.notifyVisitorTracker(s.member_id),{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),this.notifyVisitorTracker(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),this.notifyVisitorTracker(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.getCredential();if(!e)this.cachedGuestMemberTokenKey=`${ae}default`;else{let t=_e(e);this.cachedGuestMemberTokenKey=`${ae}${t}`}return this.cachedGuestMemberTokenKey}getStoredGuestMemberTokens(){let e=this.http.getPersistenceStorage();if(!e)return null;let t=e.getItem(this.getGuestMemberTokenKey());if(!t)return null;try{return JSON.parse(t)}catch{return null}}storeGuestMemberTokens(e,t,s){let i=this.http.getPersistenceStorage();i&&i.setItem(this.getGuestMemberTokenKey(),JSON.stringify({accessToken:e,refreshToken:t,memberId:s}))}};var q=class{constructor(e){this.realtimeWs=null;this.realtimeState="disconnected";this.realtimeHandlers=new Map;this.realtimeRetryCount=0;this.realtimeOptions=null;this.pendingRequests=new Map;this.pingInterval=null;this.realtimeOnStateChange=null;this.realtimeOnError=null;this.activeSubscriptions=new Map;this.http=e}getPublicPrefix(){return"/v1/public"}async getTables(){let e=this.getPublicPrefix();return(await this.http.get(`${e}/tables`)).tables}async getTable(e){let t=this.getPublicPrefix();return this.http.get(`${t}/tables/${e}`)}async createTable(e){let t=this.getPublicPrefix(),s={title:e.name,access_level:e.accessLevel??"Creator"};e.schema&&Object.keys(e.schema).length>0&&(s.schema=e.schema),await this.http.post(`${t}/tables`,s)}async updateTable(e,t){let s=this.getPublicPrefix(),i={};t.name!==void 0&&(i.title=t.name),t.schema!==void 0&&(i.schema=t.schema),t.accessLevel!==void 0&&(i.access_level=t.accessLevel),t.description!==void 0&&(i.description=t.description),await this.http.patch(`${s}/tables/${e}`,i)}async deleteTable(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/tables/${e}`)}async getValidationSchema(e){let t=this.getPublicPrefix();return(await this.http.get(`${t}/tables/${e}/validation-schema`)).validation_schema}async setValidationSchema(e,t){await this.http.put(`/v1/apps/${this.requireAppId()}/databases/tables/${e}/validation-schema`,{validation_schema:t})}async deleteValidationSchema(e){await this.http.delete(`/v1/apps/${this.requireAppId()}/databases/tables/${e}/validation-schema`)}requireAppId(){let e=this.http.config?.appId;if(!e)throw new Error("setValidationSchema/deleteValidationSchema \uB294 \uCF58\uC194 (JWT) \uC778\uC99D\uC774 \uD544\uC694\uD558\uBA70 ConnectBase config \uC5D0 appId \uAC00 \uC124\uC815\uB418\uC5B4\uC57C \uD569\uB2C8\uB2E4.");return e}async getColumns(e){let t=await this.getTable(e),s=t.schema??{},i=new Set(Array.isArray(s.$required)?s.$required:[]),r=[],n=0;for(let[o,c]of Object.entries(s)){if(o.startsWith("$")||c===void 0||Array.isArray(c))continue;let l="string",d=i.has(o),p,h,m;typeof c=="string"?l=c:(l=c.type,c.required===!0&&(d=!0),c.default!==void 0&&(p=c.default),c.description!==void 0&&(h=c.description),c.encrypted!==void 0&&(m=c.encrypted)),r.push({id:o,name:o,data_type:l,is_required:d,default_value:p,description:h,encrypted:m,order:n++,created_at:t.created_at})}return r}async createColumn(e,t){let s=this.getPublicPrefix();await this.http.post(`${s}/tables/${e}/columns`,t)}async updateColumn(e,t,s){let i=this.getPublicPrefix();await this.http.patch(`${i}/tables/${e}/columns/${t}`,s)}async deleteColumn(e,t){let s=this.getPublicPrefix();await this.http.delete(`${s}/tables/${e}/columns/${t}`)}async getData(e,t){let s=this.getPublicPrefix();if(t?.where||t?.select||t?.exclude)return this.queryData(e,t);let i=new URLSearchParams;t?.limit&&i.append("limit",t.limit.toString()),t?.offset&&i.append("offset",t.offset.toString());let r=i.toString(),n=r?`${s}/tables/${e}/data?${r}`:`${s}/tables/${e}/data`;return this.http.get(n)}async queryData(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data/query`,{where:t.where,order_by:t.orderBy,order_direction:t.orderDirection,limit:t.limit,offset:t.offset,select:t.select,exclude:t.exclude})}async getDataById(e,t){let s=this.getPublicPrefix();return this.http.get(`${s}/tables/${e}/data/${t}`)}async createData(e,t,s){let i=this.getPublicPrefix();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return this.http.post(`${i}/tables/${e}/data`,t);let n=s?.autoCreate?"?auto_create=true":"";return this.http.post(`${i}/tables/name/${encodeURIComponent(e)}/data${n}`,t)}async updateData(e,t,s){let i=this.getPublicPrefix();return this.http.patch(`${i}/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(i=>i.data)})}async deleteWhere(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data/delete-where`,{where:t})}async aggregate(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/aggregate`,{table_id:e,pipeline:t})}async search(e,t,s,i){let r=this.getPublicPrefix();return this.http.post(`${r}/search`,{table_id:e,query:t,fields:s,options:i})}async autocomplete(e,t,s,i){let r=this.getPublicPrefix();return this.http.post(`${r}/autocomplete`,{table_id:e,query:t,field:s,...i})}async geoQuery(e,t,s,i){let r=this.getPublicPrefix();return this.http.post(`${r}/geo`,{table_id:e,field:t,query:s,...i})}async batch(e){let t=this.getPublicPrefix();return this.http.post(`${t}/batch`,{operations:e})}async transaction(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/transactions`,{reads:e,writes:t})}async getDataWithPopulate(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data/query`,{where:t.where,order_by:t.orderBy,order_direction:t.orderDirection,limit:t.limit,offset:t.offset,select:t.select,exclude:t.exclude,populate:t.populate})}async listSecurityRules(e){return(await this.http.get(`/v1/apps/${e}/databases/security/rules`)).rules}async createSecurityRule(e,t){return this.http.post(`/v1/apps/${e}/databases/security/rules`,t)}async updateSecurityRule(e,t,s){return this.http.put(`/v1/apps/${e}/databases/security/rules/${t}`,s)}async deleteSecurityRule(e,t){await this.http.delete(`/v1/apps/${e}/databases/security/rules/${t}`)}async listIndexes(e,t){return(await this.http.get(`/v1/apps/${e}/databases/tables/${t}/indexes`)).indexes}async createIndex(e,t,s){return this.http.post(`/v1/apps/${e}/databases/tables/${t}/indexes`,s)}async deleteIndex(e,t,s){await this.http.delete(`/v1/apps/${e}/databases/tables/${t}/indexes/${s}`)}async analyzeIndexes(e,t){return this.http.get(`/v1/apps/${e}/databases/tables/${t}/indexes/analyze`)}async listSearchIndexes(e,t){return(await this.http.get(`/v1/apps/${e}/databases/tables/${t}/search-indexes`)).indexes}async createSearchIndex(e,t,s){return this.http.post(`/v1/apps/${e}/databases/tables/${t}/search-indexes`,s)}async deleteSearchIndex(e,t,s){await this.http.delete(`/v1/apps/${e}/databases/tables/${t}/search-indexes/${s}`)}async listGeoIndexes(e,t){return(await this.http.get(`/v1/apps/${e}/databases/tables/${t}/geo-indexes`)).indexes}async createGeoIndex(e,t,s){return this.http.post(`/v1/apps/${e}/databases/tables/${t}/geo-indexes`,s)}async deleteGeoIndex(e,t,s){await this.http.delete(`/v1/apps/${e}/databases/tables/${t}/geo-indexes/${s}`)}async listRelations(e,t){let s=t?`?source_table=${encodeURIComponent(t)}`:"";return(await this.http.get(`/v1/apps/${e}/databases/relations${s}`)).relations}async createRelation(e,t){return this.http.post(`/v1/apps/${e}/databases/relations`,t)}async deleteRelation(e,t){await this.http.delete(`/v1/apps/${e}/databases/relations/${t}`)}async listTriggers(e){return(await this.http.get(`/v1/apps/${e}/databases/triggers`)).triggers}async createTrigger(e,t){return this.http.post(`/v1/apps/${e}/databases/triggers`,t)}async updateTrigger(e,t,s){return this.http.put(`/v1/apps/${e}/databases/triggers/${t}`,s)}async deleteTrigger(e,t){await this.http.delete(`/v1/apps/${e}/databases/triggers/${t}`)}async setTTL(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/ttl`,t)}async getTTL(e,t){return this.http.get(`/v1/apps/${e}/lifecycle/ttl/${t}`)}async setRetentionPolicy(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/retention`,t)}async getRetentionPolicy(e,t){return this.http.get(`/v1/apps/${e}/lifecycle/retention/${t}`)}async setArchivePolicy(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/archive`,t)}async getArchivePolicy(e,t){return this.http.get(`/v1/apps/${e}/lifecycle/archive/${t}`)}async executeTTL(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/ttl/${t}/execute`,{})}async executeArchive(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/archive/${t}/execute`,{})}async executeRetention(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/retention/${t}/execute`,{})}async listPolicies(e){return(await this.http.get(`/v1/apps/${e}/lifecycle`)).policies}async deletePolicy(e,t){await this.http.delete(`/v1/apps/${e}/lifecycle/${t}`)}async generateTypes(e){return this.http.get(`/v1/apps/${e}/types`)}async listBackups(e){return this.http.get(`/v1/apps/${e}/backups`)}async createBackup(e,t){return this.http.post(`/v1/apps/${e}/backups`,t)}async getBackup(e,t){return this.http.get(`/v1/apps/${e}/backups/${t}`)}async deleteBackup(e,t){await this.http.delete(`/v1/apps/${e}/backups/${t}`)}async restoreBackup(e,t,s){return this.http.post(`/v1/apps/${e}/backups/${t}/restore`,s||{backup_id:t})}async exportData(e,t){return this.http.post(`/v1/apps/${e}/data/export`,t||{format:"json"})}async importData(e,t){return this.http.post(`/v1/apps/${e}/data/import`,t)}async copyTable(e,t){return this.http.post(`/v1/apps/${e}/tables/copy`,t)}async migrateData(e,t){return this.http.post(`/v1/apps/${e}/tables/migrate`,t)}connectRealtime(e){return this.realtimeState==="connected"?Promise.resolve():this.realtimeState==="connecting"?Promise.reject(new Error("Already connecting")):(this.realtimeOptions=e,this.realtimeRetryCount=0,this.doRealtimeConnect())}disconnectRealtime(){this.realtimeOptions=null,this.setRealtimeState("disconnected"),this.realtimeRetryCount=0,this.stopRealtimePing(),this.realtimeWs&&(this.realtimeWs.close(),this.realtimeWs=null),this.pendingRequests.forEach(e=>{clearTimeout(e.timeout),e.reject(new Error("Connection closed"))}),this.pendingRequests.clear(),this.realtimeHandlers.clear(),this.activeSubscriptions.clear()}subscribe(e,t,s){if(this.realtimeState!=="connected")throw new Error("Not connected. Call connectRealtime() first.");let i=`csub_${Date.now()}_${Math.random().toString(36).substring(2,9)}`;this.activeSubscriptions.set(i,{tableId:e,options:s,handlers:t});let r=this.sendSubscribeRequest(e,t,s);return r.catch(n=>{this.activeSubscriptions.delete(i),t.onError?.(n instanceof Error?n:new Error(String(n)))}),{subscriptionId:i,unsubscribe:()=>{this.activeSubscriptions.delete(i),r.then(n=>{this.realtimeHandlers.delete(n),this.realtimeState==="connected"&&this.sendRealtimeMessage({type:"unsubscribe",request_id:this.generateRequestId(),subscription_id:n})}).catch(()=>{})},loadMore:(n,o)=>{this.realtimeState==="connected"&&r.then(c=>{let l={type:"snapshot_more",request_id:this.generateRequestId(),subscription_id:c,offset:n};o!==void 0&&(l.limit=o),this.sendRealtimeMessage(l)}).catch(()=>{})}}}setPresence(e,t,s){this.realtimeState==="connected"&&this.sendRealtimeMessage({type:"presence_set",request_id:this.generateRequestId(),status:e,device:t,metadata:s})}subscribePresence(e,t){this.realtimeState==="connected"&&(this.realtimeHandlers.set("__presence__",{onSnapshot:s=>{let i={};for(let r of s)r.data&&(i[r.id]=r.data);t(i)}}),this.sendRealtimeMessage({type:"presence_subscribe",request_id:this.generateRequestId(),user_ids:e}))}isRealtimeConnected(){return this.realtimeState==="connected"}getRealtimeState(){return this.realtimeState}onRealtimeStateChange(e){return this.realtimeOnStateChange=e,()=>{this.realtimeOnStateChange=null}}onRealtimeError(e){return this.realtimeOnError=e,()=>{this.realtimeOnError=null}}setRealtimeState(e){this.realtimeState!==e&&(this.realtimeState=e,this.realtimeOnStateChange?.(e))}doRealtimeConnect(){if(!this.realtimeOptions)return Promise.reject(new Error("No realtime options"));this.setRealtimeState("connecting");let s=`${(this.realtimeOptions.dataServerUrl||this.http.getBaseUrl()).replace(/^http/,"ws")}/v1/realtime/ws?access_token=${encodeURIComponent(this.realtimeOptions.accessToken)}`;return new Promise((i,r)=>{try{this.realtimeWs=new WebSocket(s);let n=!1,o=setTimeout(()=>{n||(n=!0,this.realtimeWs&&(this.realtimeWs.close(),this.realtimeWs=null),this.setRealtimeState("disconnected"),r(new Error("Connection timeout")))},15e3);this.realtimeWs.onopen=()=>{n||(n=!0,clearTimeout(o)),this.setRealtimeState("connected"),this.realtimeRetryCount=0,this.startRealtimePing(),this.debugLog("Database realtime connected"),this.resubscribeAll(),i()},this.realtimeWs.onmessage=c=>{try{let l=JSON.parse(c.data);this.handleRealtimeMessage(l)}catch{this.debugLog("Failed to parse realtime message")}},this.realtimeWs.onclose=()=>{this.debugLog("Database realtime disconnected"),this.realtimeWs=null,this.stopRealtimePing(),n||(n=!0,clearTimeout(o),r(new Error("Connection closed during handshake"))),this.realtimeOptions&&this.realtimeState!=="disconnected"&&this.attemptRealtimeReconnect()},this.realtimeWs.onerror=()=>{this.debugLog("Database realtime error"),this.realtimeOnError?.(new Error("WebSocket connection error"))}}catch(n){this.setRealtimeState("disconnected"),r(n)}})}sendSubscribeRequest(e,t,s){let i=this.generateRequestId();this.realtimeHandlers.set(i,t);let r=s?.where?{filters:s.where.map(n=>({field:n.field,operator:n.operator,value:n.value}))}:void 0;return this.sendRealtimeMessage({type:"subscribe",request_id:i,table_id:e,doc_id:s?.docId,query:r,options:{include_self:s?.includeSelf??!1,include_metadata_changes:s?.includeMetadataChanges??!1}}),new Promise((n,o)=>{let c=setTimeout(()=>{this.pendingRequests.delete(i),this.realtimeHandlers.delete(i),o(new Error("Subscribe request timeout"))},3e4);this.pendingRequests.set(i,{resolve:l=>{let d=l,p=this.realtimeHandlers.get(i);p&&(this.realtimeHandlers.delete(i),this.realtimeHandlers.set(d,p)),n(d)},reject:o,timeout:c})})}resubscribeAll(){if(this.activeSubscriptions.size!==0){this.realtimeHandlers.clear(),this.pendingRequests.forEach(e=>clearTimeout(e.timeout)),this.pendingRequests.clear(),this.debugLog(`Resubscribing ${this.activeSubscriptions.size} subscriptions`);for(let[,e]of this.activeSubscriptions)this.sendSubscribeRequest(e.tableId,e.handlers,e.options).catch(t=>{e.handlers.onError?.(t instanceof Error?t:new Error(String(t)))})}}handleRealtimeMessage(e){switch(e.type){case"subscribed":{let s=e.request_id,i=e.subscription_id,r=this.pendingRequests.get(s);r&&(clearTimeout(r.timeout),r.resolve(i),this.pendingRequests.delete(s));break}case"snapshot":{let s=e.subscription_id,i=this.realtimeHandlers.get(s);if(i?.onSnapshot){let r=e.docs||[],n=e.has_more||!1,o=n?e.next_offset:void 0;i.onSnapshot(r,{totalCount:e.total_count||0,hasMore:n,nextOffset:o})}break}case"change":{let s=e.subscription_id,i=this.realtimeHandlers.get(s);if(i?.onChange){let r=e.changes||[];i.onChange(r)}break}case"presence":{let s=this.realtimeHandlers.get("__presence__");if(s?.onSnapshot){let i=e.states,r=Object.entries(i||{}).map(([n,o])=>({id:n,data:o,exists:!0}));s.onSnapshot(r,{totalCount:r.length,hasMore:!1})}break}case"error":{let s=e.request_id,i=e.message||"Unknown error";if(s){let r=this.pendingRequests.get(s);r&&(clearTimeout(r.timeout),r.reject(new Error(i)),this.pendingRequests.delete(s));let n=this.realtimeHandlers.get(s);n?.onError&&n.onError(new Error(i))}else this.realtimeOnError?.(new Error(i));break}case"pong":break;case"unsubscribed":case"presence_set_ack":case"presence_subscribed":case"typing_subscribed":break}}attemptRealtimeReconnect(){let e=this.realtimeOptions?.maxRetries??5,t=this.realtimeOptions?.retryInterval??1e3;if(this.realtimeRetryCount>=e){this.setRealtimeState("disconnected"),this.realtimeOnError?.(new Error("Realtime connection lost. Max retries exceeded."));return}this.setRealtimeState("connecting"),this.realtimeRetryCount++;let s=Math.min(t*Math.pow(2,this.realtimeRetryCount-1),3e4);this.debugLog(`Reconnecting in ${s}ms (attempt ${this.realtimeRetryCount}/${e})`),setTimeout(()=>{this.realtimeOptions&&this.doRealtimeConnect().catch(i=>{this.debugLog(`Reconnect failed: ${i}`)})},s)}startRealtimePing(){this.stopRealtimePing(),this.pingInterval=setInterval(()=>{this.realtimeState==="connected"&&this.realtimeWs?.readyState===WebSocket.OPEN&&this.sendRealtimeMessage({type:"ping",timestamp:Date.now()})},3e4)}stopRealtimePing(){this.pingInterval&&(clearInterval(this.pingInterval),this.pingInterval=null)}sendRealtimeMessage(e){this.realtimeWs?.readyState===WebSocket.OPEN&&this.realtimeWs.send(JSON.stringify(e))}generateRequestId(){return"req_"+Date.now()+"_"+Math.random().toString(36).substring(2,9)}debugLog(e){this.realtimeOptions?.debug&&console.log(`[DatabaseRealtime] ${e}`)}};var te=new Set(["localhost","127.0.0.1","::1"]),ce=2048;function le(a,e={}){let t=e.context??"external URL";if(typeof a!="string"||a.length===0)throw new u(400,`${t}: empty URL`,"INVALID_PRESIGNED_URL");if(a.length>ce)throw new u(400,`${t}: URL exceeds ${ce} chars`,"INVALID_PRESIGNED_URL");let s;try{s=new URL(a)}catch{throw new u(400,`${t}: cannot parse URL`,"INVALID_PRESIGNED_URL")}let i=e.allowLocalhost&&te.has(s.hostname);if(s.protocol!=="https:"&&!(i&&s.protocol==="http:"))throw new u(400,`${t}: scheme must be https (got ${s.protocol})`,"INVALID_PRESIGNED_URL");if(!e.allowLocalhost&&te.has(s.hostname))throw new u(400,`${t}: localhost is not allowed in production`,"INVALID_PRESIGNED_URL");if(e.allowedHosts&&e.allowedHosts.length>0){let r=s.hostname;if(!e.allowedHosts.some(o=>o===r?!0:r.endsWith(`.${o}`)))throw new u(400,`${t}: host ${r} is not in allowlist`,"INVALID_PRESIGNED_URL")}return s}function de(){if(typeof window>"u")return!1;let a=window.location.hostname;return te.has(a)}var H=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async uploadToPresigned(e,t,s){let i=le(e,{allowLocalhost:de(),context:"storage.presigned-url"}),{signal:r,cleanup:n}=R({timeout:s?.timeout,signal:s?.signal});try{let o=await fetch(i.toString(),{method:"PUT",body:t,headers:{"Content-Type":t.type||"application/octet-stream"},signal:r});if(!o.ok)throw new u(o.status,`Upload failed: ${o.statusText}`,"PRESIGNED_UPLOAD_FAILED")}finally{n()}}async getFiles(e,t){let s=this.getPublicPrefix(),i=t?`?parent_id=${encodeURIComponent(t)}`:"";return(await this.http.get(`${s}/storages/files/${e}/items${i}`)).files}async uploadFile(e,t,s){let i=this.getPublicPrefix(),r=await this.http.post(`${i}/storages/files/${e}/presigned-url`,{file_name:t.name,file_size:t.size,mime_type:t.type||"application/octet-stream",parent_id:s});await this.uploadToPresigned(r.upload_url,t);let n=await this.http.post(`${i}/storages/files/${e}/complete-upload`,{file_id:r.file_id});return{id:n.id,name:n.name,path:n.path,type:n.type,mime_type:n.mime_type,size:n.size,url:n.url,parent_id:n.parent_id,created_at:n.created_at}}async uploadFiles(e,t,s){let i=[];for(let r of t){let n=await this.uploadFile(e,r,s);i.push(n)}return i}async createFolder(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/storages/files/${e}/folders`,t)}async deleteFile(e,t){let s=this.getPublicPrefix();await this.http.delete(`${s}/storages/files/${e}/items/${t}`)}async moveFile(e,t,s){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error("storage.moveFile requires JWT (console token) auth; not available with Public Key alone.");await this.http.post(`/v1/storages/files/${e}/items/${t}/move`,s)}async renameFile(e,t,s){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error("storage.renameFile requires JWT (console token) auth; not available with Public Key alone.");return this.http.patch(`/v1/storages/files/${e}/items/${t}/rename`,s)}getFileUrl(e){return e.url||null}isImageFile(e){return e.mime_type?.startsWith("image/")||!1}async uploadByPath(e,t,s,i){let r=this.getPublicPrefix(),n=t.startsWith("/")?t.slice(1):t,o=i?.overwrite!==!1,c=await this.http.post(`${r}/storages/files/${e}/presigned-url/path/${n}`,{file_name:s.name,file_size:s.size,mime_type:s.type||"application/octet-stream",overwrite:o});await this.uploadToPresigned(c.upload_url,s);let l=await this.http.post(`${r}/storages/files/${e}/complete-upload`,{file_id:c.file_id});return{id:l.id,name:l.name,path:l.path,type:l.type,mime_type:l.mime_type,size:l.size,url:l.url,parent_id:l.parent_id,created_at:l.created_at}}async getByPath(e,t){let s=this.getPublicPrefix(),i=t.startsWith("/")?t.slice(1):t;return this.http.get(`${s}/storages/files/${e}/path/${i}`)}async getUrlByPath(e,t){try{return(await this.getByPath(e,t)).url||null}catch{return null}}async setPageMeta(e,t){let s=this.getPublicPrefix();return this.http.put(`${s}/storages/webs/${e}/page-metas`,t)}async batchSetPageMeta(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/storages/webs/${e}/page-metas/batch`,t)}async listPageMetas(e,t){let s=this.getPublicPrefix(),i=new URLSearchParams;t?.limit!=null&&i.set("limit",String(t.limit)),t?.offset!=null&&i.set("offset",String(t.offset));let r=i.toString();return this.http.get(`${s}/storages/webs/${e}/page-metas${r?`?${r}`:""}`)}async getPageMeta(e,t){let s=this.getPublicPrefix(),i=encodeURIComponent(t);return this.http.get(`${s}/storages/webs/${e}/page-metas/get?path=${i}`)}async deletePageMeta(e,t){let s=this.getPublicPrefix(),i=encodeURIComponent(t);await this.http.delete(`${s}/storages/webs/${e}/page-metas?path=${i}`)}async deleteAllPageMetas(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/storages/webs/${e}/page-metas/all`)}};var O=class{constructor(e){this.http=e}async getPublicKeys(e){return this.http.get(`/v1/apps/${e}/public-keys`)}async createPublicKey(e,t){return this.http.post(`/v1/apps/${e}/public-keys`,t)}async updatePublicKey(e,t,s){return this.http.patch(`/v1/apps/${e}/public-keys/${t}`,s)}async deletePublicKey(e,t){await this.http.delete(`/v1/apps/${e}/public-keys/${t}`)}};var D=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async invoke(e,t,s){let i=this.getPublicPrefix(),r={};return t!==void 0&&(r.payload=t),s!==void 0&&(r.timeout=s),this.http.post(`${i}/functions/${e}/invoke`,r)}async call(e,t){let s=await this.invoke(e,t);if(!s.success)throw new u(500,s.error||"Function execution failed","FUNCTION_EXECUTION_FAILED");return s.result}};var L=class{constructor(e,t){this.ws=null;this.state="disconnected";this._connectionId=null;this._appId=null;this.options={maxRetries:5,retryInterval:1e3,userId:"",accessToken:"",timeout:3e4,debug:!1};this.retryCount=0;this.pendingRequests=new Map;this.subscriptions=new Map;this.streamSessions=new Map;this.stateHandlers=[];this.errorHandlers=[];this.presenceHandlers=[];this.presenceSubscriptions=new Map;this.typingHandlers=new Map;this.readReceiptHandlers=new Map;this.connectPromise=null;this.http=e,this.socketUrl=t,this.clientId=this.generateClientId()}get connectionId(){return this._connectionId}get appId(){return this._appId}async connect(e={}){if(this.state!=="connected"){if(this.state==="connecting"&&this.connectPromise)return this.connectPromise;this.options={...this.options,...e},e.userId&&(this.userId=e.userId),this.connectPromise=this.doConnect();try{await this.connectPromise}finally{this.connectPromise=null}}}disconnect(){this.state="disconnected",this.notifyStateChange(),this.ws&&(this.ws.close(),this.ws=null),this.pendingRequests.forEach(e=>{clearTimeout(e.timeout),e.reject(new Error("Connection closed"))}),this.pendingRequests.clear(),this.subscriptions.clear(),this.streamSessions.forEach(e=>{e.handlers.onError&&e.handlers.onError(new Error("Connection closed"))}),this.streamSessions.clear(),this.presenceHandlers=[],this.presenceSubscriptions.clear(),this.typingHandlers.clear(),this.readReceiptHandlers.clear(),this._connectionId=null,this._appId=null,this.retryCount=0,this.connectPromise=null}async subscribe(e,t={}){if(this.state!=="connected")throw new Error("Not connected. Call connect() first.");let s=this.generateRequestId(),i=await this.sendRequest({category:e,action:"subscribe",request_id:s}),r={category:i.category,persist:i.persist,historyCount:i.history_count,readReceipt:i.read_receipt},n=[];return this.subscriptions.set(e,{info:r,handlers:n}),{info:r,send:async(c,l)=>{await this.sendMessage(e,c,l)},getHistory:async c=>this.getHistory(e,c??t.historyLimit),unsubscribe:async()=>{await this.unsubscribe(e)},onMessage:c=>(n.push(c),()=>{let l=n.indexOf(c);l>-1&&n.splice(l,1)})}}async unsubscribe(e){if(this.state!=="connected")return;let t=this.generateRequestId();await this.sendRequest({category:e,action:"unsubscribe",request_id:t}),this.subscriptions.delete(e)}async sendMessage(e,t,s={}){if(this.state!=="connected")throw new Error("Not connected");let r=s.includeSelf!==!1,n=this.generateRequestId();await this.sendRequest({category:e,action:"send",data:{data:t,broadcast:r},request_id:n})}async getHistory(e,t){if(this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId(),i=await this.sendRequest({category:e,action:"history",data:t?{limit:t}:void 0,request_id:s});return{category:i.category,messages:i.messages.map(r=>({id:r.id,category:r.category,from:r.from,data:r.data,sentAt:r.sent_at})),total:i.total}}async stream(e,t,s={}){if(this.state!=="connected")throw new Error("Not connected. Call connect() first.");let i=this.generateRequestId(),r=s.sessionId||this.generateRequestId();this.streamSessions.set(r,{handlers:t,requestId:i});try{this.sendRaw({category:"",action:"stream",data:{provider:s.provider,model:s.model,messages:e,system:s.system,temperature:s.temperature,max_tokens:s.maxTokens,session_id:r,metadata:s.metadata,mcp_group:s.mcpGroup},request_id:i})}catch(n){throw this.streamSessions.delete(r),n}return{sessionId:r,stop:async()=>{await this.stopStream(r)}}}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}isConnected(){return this.state==="connected"}onStateChange(e){return this.stateHandlers.push(e),()=>{let t=this.stateHandlers.indexOf(e);t>-1&&this.stateHandlers.splice(t,1)}}onError(e){return this.errorHandlers.push(e),()=>{let t=this.errorHandlers.indexOf(e);t>-1&&this.errorHandlers.splice(t,1)}}async setPresence(e,t={}){if(this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId();await this.sendRequest({category:"",action:"presence_set",data:{status:e,device:t.device,metadata:t.metadata},request_id:s})}async setPresenceOnDisconnect(e,t){if(this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId();await this.sendRequest({category:"",action:"presence_on_disconnect",data:{status:e,metadata:t},request_id:s})}async getPresence(e){if(this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId(),s=await this.sendRequest({category:"",action:"presence_get",data:{user_id:e},request_id:t});return{userId:s.user_id,status:s.status,lastSeen:s.last_seen,device:s.device,metadata:s.metadata}}async getPresenceMany(e){if(this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId(),s=await this.sendRequest({category:"",action:"presence_get_many",data:{user_ids:e},request_id:t}),i={};for(let[r,n]of Object.entries(s.users))i[r]={userId:n.user_id,status:n.status,lastSeen:n.last_seen,device:n.device,metadata:n.metadata};return{users:i}}async subscribePresence(e,t){if(this.state!=="connected")throw new Error("Not connected");if(!this.presenceSubscriptions.has(e)){let i=this.generateRequestId();await this.sendRequest({category:"",action:"presence_subscribe",data:{user_id:e},request_id:i}),this.presenceSubscriptions.set(e,[])}let s=this.presenceSubscriptions.get(e);return s.push(t),()=>{let i=s.indexOf(t);if(i>-1&&s.splice(i,1),s.length===0&&(this.presenceSubscriptions.delete(e),this.state==="connected")){let r=this.generateRequestId();this.sendRequest({category:"",action:"presence_unsubscribe",data:{user_id:e},request_id:r}).catch(n=>{this.log(`Failed to unsubscribe presence for ${e}: ${n}`)})}}}onPresenceChange(e){return this.presenceHandlers.push(e),()=>{let t=this.presenceHandlers.indexOf(e);t>-1&&this.presenceHandlers.splice(t,1)}}async startTyping(e){if(this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId();await this.sendRequest({category:"",action:"typing_start",data:{room_id:e},request_id:t})}async stopTyping(e){if(this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId();await this.sendRequest({category:"",action:"typing_stop",data:{room_id:e},request_id:t})}async onTypingChange(e,t){if(this.state!=="connected")throw new Error("Not connected");if(!this.typingHandlers.has(e)){let i=this.generateRequestId();await this.sendRequest({category:"",action:"typing_subscribe",data:{room_id:e},request_id:i}),this.typingHandlers.set(e,[])}let s=this.typingHandlers.get(e);return s.push(t),()=>{let i=s.indexOf(t);if(i>-1&&s.splice(i,1),s.length===0&&(this.typingHandlers.delete(e),this.state==="connected")){let r=this.generateRequestId();this.sendRequest({category:"",action:"typing_unsubscribe",data:{room_id:e},request_id:r}).catch(n=>{this.log(`Failed to unsubscribe typing for ${e}: ${n}`)})}}}async markRead(e,t){if(this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId();await this.sendRequest({category:e,action:"mark_read",data:{message_ids:t},request_id:s})}onReadReceipt(e,t){this.readReceiptHandlers.has(e)||this.readReceiptHandlers.set(e,[]);let s=this.readReceiptHandlers.get(e);return s.push(t),()=>{let i=s.indexOf(t);i>-1&&s.splice(i,1)}}async doConnect(){return new Promise((e,t)=>{this.state="connecting",this.notifyStateChange(),this.log("Connecting...");let s=this.socketUrl.replace(/^http/,"ws"),i;if(this.options.accessToken)i=`${s}/v1/realtime/auth?access_token=${encodeURIComponent(this.options.accessToken)}&client_id=${this.clientId}`,this.log("Using accessToken authentication");else{let o=this.http.getPublicKey();if(!o){let c=new Error("API Key or accessToken is required for realtime connection");this.log("Connection failed: no API Key or accessToken"),t(c);return}i=`${s}/v1/realtime/auth?public_key=${encodeURIComponent(o)}&client_id=${this.clientId}`,this.log("Using API Key authentication")}this.userId&&(i+=`&user_id=${encodeURIComponent(this.userId)}`);let r=!1,n=setTimeout(()=>{r||(r=!0,this.log(`Connection timeout after ${this.options.timeout}ms`),this.ws&&(this.ws.close(),this.ws=null),this.state="disconnected",this.notifyStateChange(),t(new Error(`Connection timeout after ${this.options.timeout}ms`)))},this.options.timeout);try{this.log(`Connecting to ${s}`),this.ws=new WebSocket(i),this.ws.onopen=()=>{this.log("WebSocket opened, waiting for connected event...")},this.ws.onmessage=o=>{let c=o.data.split(`
|
|
1
|
+
"use strict";var ConnectBaseModule=(()=>{var ee=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var be=Object.getOwnPropertyNames;var ye=Object.prototype.hasOwnProperty;var ve=(a,e)=>{for(var t in e)ee(a,t,{get:e[t],enumerable:!0})},we=(a,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of be(e))!ye.call(a,i)&&i!==t&&ee(a,i,{get:()=>e[i],enumerable:!(s=fe(e,i))||s.enumerable});return a};var Pe=a=>we(ee({},"__esModule",{value:!0}),a);var Me={};ve(Me,{AIAPI:()=>$,AdsAPI:()=>k,ApiError:()=>u,AuthError:()=>P,ConnectBase:()=>Z,GameAPI:()=>I,GameRoom:()=>x,GameRoomTransport:()=>Y,NativeAPI:()=>C,SessionManager:()=>E,VideoProcessingError:()=>T,default:()=>Ee,isWebTransportSupported:()=>ne});var u=class extends Error{constructor(t,s,i,r){super(s);this.statusCode=t;this.name="ApiError",this.code=i,this.details=r}},P=class extends Error{constructor(e){super(e),this.name="AuthError"}};function R(a={}){let e=new AbortController,t=a.timeout??3e4,s=a.signal,i=null,r=null;return s&&(s.aborted?e.abort(s.reason):(r=()=>e.abort(s.reason),s.addEventListener("abort",r,{once:!0}))),t>0&&Number.isFinite(t)&&(i=setTimeout(()=>{e.abort(new DOMException(`Request timed out after ${t}ms`,"TimeoutError"))},t)),{signal:e.signal,cleanup:()=>{i!==null&&clearTimeout(i),s&&r&&s.removeEventListener("abort",r)}}}var oe="cb_auth_tokens",A=class{constructor(e){this.isRefreshing=!1;this.refreshPromise=null;this.refreshFailureCount=0;this.refreshLockedUntil=0;this.config={...e},this.storageKey=this.buildStorageKey(),this.warnIfUnsafePersistence(),this.restoreTokens()}warnIfUnsafePersistence(){typeof window>"u"||(this.config.persistence==="localStorage"?console.warn(`[connect-base-client] persistence="localStorage" \uB294 XSS \uC2DC \uD1A0\uD070 \uC601\uAD6C \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4. refresh token \uC740 \uC11C\uBC84 HttpOnly \uCFE0\uD0A4\uB85C \uBC1C\uAE09\uBC1B\uACE0 \uAE30\uBCF8\uAC12('none')\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`):this.config.persistence==="sessionStorage"&&console.warn('[connect-base-client] persistence="sessionStorage" \uB294 XSS \uC2DC \uD604\uC7AC \uD0ED \uC138\uC158 \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4.'))}updateConfig(e){this.config={...this.config,...e}}setTokens(e,t){this.config.accessToken=e,this.config.refreshToken=t,this.persistTokens()}clearTokens(){this.config.accessToken=void 0,this.config.refreshToken=void 0,this.removePersistedTokens()}get persistence(){return this.config.persistence??"none"}getStorage(){return typeof window>"u"?null:this.persistence==="localStorage"&&typeof localStorage<"u"?localStorage:this.persistence==="sessionStorage"&&typeof sessionStorage<"u"?sessionStorage:null}getPersistenceStorage(){return this.getStorage()}buildStorageKey(){let e=this.config.publicKey??this.config.secretKey;if(!e)return oe;let t=0;for(let s=0;s<e.length;s++)t=(t<<5)-t+e.charCodeAt(s),t=t&t;return`${oe}_${Math.abs(t).toString(36)}`}persistTokens(){if(this.persistence==="none")return;let e=this.getStorage();!e||!this.config.accessToken||!this.config.refreshToken||e.setItem(this.storageKey,JSON.stringify({accessToken:this.config.accessToken,refreshToken:this.config.refreshToken}))}restoreTokens(){if(this.persistence==="none"||this.config.accessToken&&this.config.refreshToken)return;let e=this.getStorage();if(!e)return;let t=e.getItem(this.storageKey);if(t)try{let{accessToken:s,refreshToken:i}=JSON.parse(t);s&&i&&(this.config.accessToken=s,this.config.refreshToken=i)}catch{e.removeItem(this.storageKey)}}removePersistedTokens(){if(this.persistence==="none")return;let e=this.getStorage();e&&e.removeItem(this.storageKey)}hasPublicKey(){return!!this.config.publicKey}getPublicKey(){return this.config.publicKey}hasSecretKey(){return!!this.config.secretKey}getSecretKey(){return this.config.secretKey}getCredential(){return this.config.publicKey??this.config.secretKey}getAccessToken(){return this.config.accessToken}hasJWT(){return!!this.config.accessToken}getBaseUrl(){return this.config.baseUrl}async refreshAccessToken(){if(this.isRefreshing)return this.refreshPromise;if(Date.now()<this.refreshLockedUntil){let t=new P("Token refresh locked due to repeated failures. Please login again.");throw this.emitError(t),this.config.onAuthError?.(t),t}if(this.isRefreshing=!0,!this.config.refreshToken){this.isRefreshing=!1,this.config.onTokenExpired?.();let t=new P("Refresh token is missing. Please login again.");throw this.emitError(t),this.config.onAuthError?.(t),t}return this.refreshPromise=(async()=>{let{signal:t,cleanup:s}=R({timeout:this.config.requestTimeoutMs??3e4});try{let i=await fetch(`${this.config.baseUrl}/v1/auth/re-issue`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.refreshToken}`},signal:t});if(!i.ok)throw new Error("Token refresh failed");let r=await i.json();return this.setTokens(r.access_token,r.refresh_token),this.config.onTokenRefresh?.({accessToken:r.access_token,refreshToken:r.refresh_token}),this.refreshFailureCount=0,this.refreshLockedUntil=0,r.access_token}catch{this.refreshFailureCount++;let i=Math.min(500*2**Math.max(0,this.refreshFailureCount-1),3e4);this.refreshLockedUntil=Date.now()+i,this.clearTokens(),this.config.onTokenExpired?.();let r=new P("Token refresh failed. Please login again.");throw this.emitError(r),this.config.onAuthError?.(r),r}finally{s(),this.isRefreshing=!1,this.refreshPromise=null}})(),this.refreshPromise}emitError(e){try{this.config.onError?.(e)}catch{}}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;t.set("Content-Type","application/json");let s=this.getCredential();if(s&&t.set("X-Public-Key",s),!e?.skipAuth&&this.config.accessToken){let i=this.config.accessToken;if(this.isTokenExpired(i)&&this.config.refreshToken){let r=await this.refreshAccessToken();r&&(i=r)}t.set("Authorization",`Bearer ${i}`)}return e?.headers&&Object.entries(e.headers).forEach(([i,r])=>{t.set(i,r)}),t}async handleResponse(e){if(!e.ok){let t=await e.json().catch(()=>({message:e.statusText})),s=e.status===429?e.headers.get("Retry-After"):null,i;if(s){let l=Number.parseInt(s,10);if(Number.isFinite(l)&&l>=0)i=l;else{let d=Date.parse(s);Number.isFinite(d)&&(i=Math.max(0,Math.round((d-Date.now())/1e3)))}}let r=t.error;if(r&&typeof r=="object"&&"message"in r){let l={...r.details&&typeof r.details=="object"?r.details:{}};i!==void 0&&(l.retry_after_seconds=i);let d=new u(e.status,r.message||"Unknown error",r.code,Object.keys(l).length>0?l:r.details);throw this.emitError(d),d}let n=typeof r=="string"?r:t.message||"Unknown error",o=i!==void 0?{retry_after_seconds:i}:void 0,c=new u(e.status,n,void 0,o);throw this.emitError(c),c}return e.status===204||e.headers.get("content-length")==="0"?{}:e.json()}async doFetch(e,t,s){let{signal:i,cleanup:r}=R({timeout:s?.timeout??this.config.requestTimeoutMs??3e4,signal:s?.signal});try{let n=await fetch(`${this.config.baseUrl}${e}`,{...t,signal:i});return await this.handleResponse(n)}finally{r()}}async get(e,t){let s=await this.prepareHeaders(t);return this.doFetch(e,{method:"GET",headers:s},t)}async post(e,t,s){let i=await this.prepareHeaders(s);return t instanceof FormData&&i.delete("Content-Type"),this.doFetch(e,{method:"POST",headers:i,body:t instanceof FormData?t:JSON.stringify(t)},s)}async put(e,t,s){let i=await this.prepareHeaders(s);return this.doFetch(e,{method:"PUT",headers:i,body:JSON.stringify(t)},s)}async patch(e,t,s){let i=await this.prepareHeaders(s);return this.doFetch(e,{method:"PATCH",headers:i,body:JSON.stringify(t)},s)}async delete(e,t){let s=await this.prepareHeaders(t);return this.doFetch(e,{method:"DELETE",headers:s},t)}async fetchRaw(e,t){let s=await this.prepareHeaders(),i=new Headers(s);return t?.headers&&new Headers(t.headers).forEach((n,o)=>i.set(o,n)),fetch(`${this.config.baseUrl}${e}`,{...t,headers:i})}};function Se(a,e){switch(e){case"string":return typeof a=="string";case"number":return typeof a=="number"&&Number.isFinite(a);case"boolean":return typeof a=="boolean";case"array":return Array.isArray(a);case"object":return typeof a=="object"&&a!==null&&!Array.isArray(a);case"string-or-number":return typeof a=="string"||typeof a=="number"&&Number.isFinite(a)}}function f(a,e,t){if(typeof a!="object"||a===null||Array.isArray(a))throw new u(502,`[${t}] expected object, got ${typeof a}`,"SCHEMA_MISMATCH");let s=a;for(let[i,r]of Object.entries(e)){if(!(i in s)){if(r.optional)continue;throw new u(502,`[${t}] missing required field "${i}"`,"SCHEMA_MISMATCH")}let o=s[i];if(!(r.optional&&o==null)&&!Se(o,r.type))throw new u(502,`[${t}] field "${i}" expected ${r.type}, got ${typeof o}`,"SCHEMA_MISMATCH")}return a}var ae="cb_guest_";function _e(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 U=class{constructor(e){this.http=e;this.guestMemberLoginPromise=null;this.cachedGuestMemberTokenKey=null;this.analytics=null}_attachAnalytics(e){this.analytics=e}notifyVisitorTracker(e){if(this.analytics){this.analytics.setMemberId(e);return}typeof window>"u"||(e?typeof window.__cbSetMember=="function"&&window.__cbSetMember(e):typeof window.__cbClearMember=="function"&&window.__cbClearMember())}async getAuthSettings(){return this.http.get("/v1/public/auth-settings",{skipAuth:!0})}async signUpMember(e){let t=await this.http.post("/v1/public/app-members/signup",e,{skipAuth:!0});return this.http.setTokens(t.access_token,t.refresh_token),this.notifyVisitorTracker(t.member_id),t}async signInMember(e){let t=await this.http.post("/v1/public/app-members/signin",e,{skipAuth:!0});return f(t,{access_token:{type:"string"},refresh_token:{type:"string"},member_id:{type:"string-or-number"}},"auth.signInMember"),this.http.setTokens(t.access_token,t.refresh_token),this.notifyVisitorTracker(t.member_id),t}async signInAsGuestMember(){if(this.guestMemberLoginPromise)return this.guestMemberLoginPromise;this.guestMemberLoginPromise=this.executeGuestMemberLogin();try{return await this.guestMemberLoginPromise}finally{this.guestMemberLoginPromise=null}}async getMe(){let e=await this.http.get("/v1/public/app-members/me");return f(e,{member_id:{type:"string-or-number"}},"auth.getMe"),e}async updateCustomData(e){return this.http.patch("/v1/public/app-members/me/custom-data",e)}async signOut(){try{await this.http.post("/v1/auth/logout")}finally{this.http.clearTokens(),this.notifyVisitorTracker(null)}}clearGuestMemberTokens(){let e=this.http.getPersistenceStorage();e&&e.removeItem(this.getGuestMemberTokenKey())}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 this.notifyVisitorTracker(s.member_id),{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),this.notifyVisitorTracker(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),this.notifyVisitorTracker(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.getCredential();if(!e)this.cachedGuestMemberTokenKey=`${ae}default`;else{let t=_e(e);this.cachedGuestMemberTokenKey=`${ae}${t}`}return this.cachedGuestMemberTokenKey}getStoredGuestMemberTokens(){let e=this.http.getPersistenceStorage();if(!e)return null;let t=e.getItem(this.getGuestMemberTokenKey());if(!t)return null;try{return JSON.parse(t)}catch{return null}}storeGuestMemberTokens(e,t,s){let i=this.http.getPersistenceStorage();i&&i.setItem(this.getGuestMemberTokenKey(),JSON.stringify({accessToken:e,refreshToken:t,memberId:s}))}};var q=class{constructor(e){this.realtimeWs=null;this.realtimeState="disconnected";this.realtimeHandlers=new Map;this.realtimeRetryCount=0;this.realtimeOptions=null;this.pendingRequests=new Map;this.pingInterval=null;this.realtimeOnStateChange=null;this.realtimeOnError=null;this.activeSubscriptions=new Map;this.http=e}getPublicPrefix(){return"/v1/public"}async getTables(){let e=this.getPublicPrefix();return(await this.http.get(`${e}/tables`)).tables}async getTable(e){let t=this.getPublicPrefix();return this.http.get(`${t}/tables/${e}`)}async createTable(e){let t=this.getPublicPrefix(),s={title:e.name,access_level:e.accessLevel??"Creator"};e.schema&&Object.keys(e.schema).length>0&&(s.schema=e.schema),await this.http.post(`${t}/tables`,s)}async updateTable(e,t){let s=this.getPublicPrefix(),i={};t.name!==void 0&&(i.title=t.name),t.schema!==void 0&&(i.schema=t.schema),t.accessLevel!==void 0&&(i.access_level=t.accessLevel),t.description!==void 0&&(i.description=t.description),await this.http.patch(`${s}/tables/${e}`,i)}async deleteTable(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/tables/${e}`)}async getValidationSchema(e){let t=this.getPublicPrefix();return(await this.http.get(`${t}/tables/${e}/validation-schema`)).validation_schema}async setValidationSchema(e,t){await this.http.put(`/v1/apps/${this.requireAppId()}/databases/tables/${e}/validation-schema`,{validation_schema:t})}async deleteValidationSchema(e){await this.http.delete(`/v1/apps/${this.requireAppId()}/databases/tables/${e}/validation-schema`)}requireAppId(){let e=this.http.config?.appId;if(!e)throw new Error("setValidationSchema/deleteValidationSchema \uB294 \uCF58\uC194 (JWT) \uC778\uC99D\uC774 \uD544\uC694\uD558\uBA70 ConnectBase config \uC5D0 appId \uAC00 \uC124\uC815\uB418\uC5B4\uC57C \uD569\uB2C8\uB2E4.");return e}async getColumns(e){let t=await this.getTable(e),s=t.schema??{},i=new Set(Array.isArray(s.$required)?s.$required:[]),r=[],n=0;for(let[o,c]of Object.entries(s)){if(o.startsWith("$")||c===void 0||Array.isArray(c))continue;let l="string",d=i.has(o),p,h,m;typeof c=="string"?l=c:(l=c.type,c.required===!0&&(d=!0),c.default!==void 0&&(p=c.default),c.description!==void 0&&(h=c.description),c.encrypted!==void 0&&(m=c.encrypted)),r.push({id:o,name:o,data_type:l,is_required:d,default_value:p,description:h,encrypted:m,order:n++,created_at:t.created_at})}return r}async createColumn(e,t){let s=this.getPublicPrefix();await this.http.post(`${s}/tables/${e}/columns`,t)}async updateColumn(e,t,s){let i=this.getPublicPrefix();await this.http.patch(`${i}/tables/${e}/columns/${t}`,s)}async deleteColumn(e,t){let s=this.getPublicPrefix();await this.http.delete(`${s}/tables/${e}/columns/${t}`)}async getData(e,t){let s=this.getPublicPrefix();if(t?.where||t?.select||t?.exclude)return this.queryData(e,t);let i=new URLSearchParams;t?.limit&&i.append("limit",t.limit.toString()),t?.offset&&i.append("offset",t.offset.toString());let r=i.toString(),n=r?`${s}/tables/${e}/data?${r}`:`${s}/tables/${e}/data`;return this.http.get(n)}async queryData(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data/query`,{where:t.where,order_by:t.orderBy,order_direction:t.orderDirection,limit:t.limit,offset:t.offset,select:t.select,exclude:t.exclude})}async getDataById(e,t){let s=this.getPublicPrefix();return this.http.get(`${s}/tables/${e}/data/${t}`)}async createData(e,t,s){let i=this.getPublicPrefix();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return this.http.post(`${i}/tables/${e}/data`,t);let n=s?.autoCreate?"?auto_create=true":"";return this.http.post(`${i}/tables/name/${encodeURIComponent(e)}/data${n}`,t)}async updateData(e,t,s){let i=this.getPublicPrefix();return this.http.patch(`${i}/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(i=>i.data)})}async deleteWhere(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data/delete-where`,{where:t})}async aggregate(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/aggregate`,{table_id:e,pipeline:t})}async search(e,t,s,i){let r=this.getPublicPrefix();return this.http.post(`${r}/search`,{table_id:e,query:t,fields:s,options:i})}async autocomplete(e,t,s,i){let r=this.getPublicPrefix();return this.http.post(`${r}/autocomplete`,{table_id:e,query:t,field:s,...i})}async geoQuery(e,t,s,i){let r=this.getPublicPrefix();return this.http.post(`${r}/geo`,{table_id:e,field:t,query:s,...i})}async batch(e){let t=this.getPublicPrefix();return this.http.post(`${t}/batch`,{operations:e})}async transaction(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/transactions`,{reads:e,writes:t})}async getDataWithPopulate(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/tables/${e}/data/query`,{where:t.where,order_by:t.orderBy,order_direction:t.orderDirection,limit:t.limit,offset:t.offset,select:t.select,exclude:t.exclude,populate:t.populate})}async listSecurityRules(e){return(await this.http.get(`/v1/apps/${e}/databases/security/rules`)).rules}async createSecurityRule(e,t){return this.http.post(`/v1/apps/${e}/databases/security/rules`,t)}async updateSecurityRule(e,t,s){return this.http.put(`/v1/apps/${e}/databases/security/rules/${t}`,s)}async deleteSecurityRule(e,t){await this.http.delete(`/v1/apps/${e}/databases/security/rules/${t}`)}async listIndexes(e,t){return(await this.http.get(`/v1/apps/${e}/databases/tables/${t}/indexes`)).indexes}async createIndex(e,t,s){return this.http.post(`/v1/apps/${e}/databases/tables/${t}/indexes`,s)}async deleteIndex(e,t,s){await this.http.delete(`/v1/apps/${e}/databases/tables/${t}/indexes/${s}`)}async analyzeIndexes(e,t){return this.http.get(`/v1/apps/${e}/databases/tables/${t}/indexes/analyze`)}async listSearchIndexes(e,t){return(await this.http.get(`/v1/apps/${e}/databases/tables/${t}/search-indexes`)).indexes}async createSearchIndex(e,t,s){return this.http.post(`/v1/apps/${e}/databases/tables/${t}/search-indexes`,s)}async deleteSearchIndex(e,t,s){await this.http.delete(`/v1/apps/${e}/databases/tables/${t}/search-indexes/${s}`)}async listGeoIndexes(e,t){return(await this.http.get(`/v1/apps/${e}/databases/tables/${t}/geo-indexes`)).indexes}async createGeoIndex(e,t,s){return this.http.post(`/v1/apps/${e}/databases/tables/${t}/geo-indexes`,s)}async deleteGeoIndex(e,t,s){await this.http.delete(`/v1/apps/${e}/databases/tables/${t}/geo-indexes/${s}`)}async listRelations(e,t){let s=t?`?source_table=${encodeURIComponent(t)}`:"";return(await this.http.get(`/v1/apps/${e}/databases/relations${s}`)).relations}async createRelation(e,t){return this.http.post(`/v1/apps/${e}/databases/relations`,t)}async deleteRelation(e,t){await this.http.delete(`/v1/apps/${e}/databases/relations/${t}`)}async listTriggers(e){return(await this.http.get(`/v1/apps/${e}/databases/triggers`)).triggers}async createTrigger(e,t){return this.http.post(`/v1/apps/${e}/databases/triggers`,t)}async updateTrigger(e,t,s){return this.http.put(`/v1/apps/${e}/databases/triggers/${t}`,s)}async deleteTrigger(e,t){await this.http.delete(`/v1/apps/${e}/databases/triggers/${t}`)}async setTTL(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/ttl`,t)}async getTTL(e,t){return this.http.get(`/v1/apps/${e}/lifecycle/ttl/${t}`)}async setRetentionPolicy(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/retention`,t)}async getRetentionPolicy(e,t){return this.http.get(`/v1/apps/${e}/lifecycle/retention/${t}`)}async setArchivePolicy(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/archive`,t)}async getArchivePolicy(e,t){return this.http.get(`/v1/apps/${e}/lifecycle/archive/${t}`)}async executeTTL(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/ttl/${t}/execute`,{})}async executeArchive(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/archive/${t}/execute`,{})}async executeRetention(e,t){return this.http.post(`/v1/apps/${e}/lifecycle/retention/${t}/execute`,{})}async listPolicies(e){return(await this.http.get(`/v1/apps/${e}/lifecycle`)).policies}async deletePolicy(e,t){await this.http.delete(`/v1/apps/${e}/lifecycle/${t}`)}async generateTypes(e){return this.http.get(`/v1/apps/${e}/types`)}async listBackups(e){return this.http.get(`/v1/apps/${e}/backups`)}async createBackup(e,t){return this.http.post(`/v1/apps/${e}/backups`,t)}async getBackup(e,t){return this.http.get(`/v1/apps/${e}/backups/${t}`)}async deleteBackup(e,t){await this.http.delete(`/v1/apps/${e}/backups/${t}`)}async restoreBackup(e,t,s){return this.http.post(`/v1/apps/${e}/backups/${t}/restore`,s||{backup_id:t})}async exportData(e,t){return this.http.post(`/v1/apps/${e}/data/export`,t||{format:"json"})}async importData(e,t){return this.http.post(`/v1/apps/${e}/data/import`,t)}async copyTable(e,t){return this.http.post(`/v1/apps/${e}/tables/copy`,t)}async migrateData(e,t){return this.http.post(`/v1/apps/${e}/tables/migrate`,t)}connectRealtime(e){return this.realtimeState==="connected"?Promise.resolve():this.realtimeState==="connecting"?Promise.reject(new Error("Already connecting")):(this.realtimeOptions=e,this.realtimeRetryCount=0,this.doRealtimeConnect())}disconnectRealtime(){this.realtimeOptions=null,this.setRealtimeState("disconnected"),this.realtimeRetryCount=0,this.stopRealtimePing(),this.realtimeWs&&(this.realtimeWs.close(),this.realtimeWs=null),this.pendingRequests.forEach(e=>{clearTimeout(e.timeout),e.reject(new Error("Connection closed"))}),this.pendingRequests.clear(),this.realtimeHandlers.clear(),this.activeSubscriptions.clear()}subscribe(e,t,s){if(this.realtimeState!=="connected")throw new Error("Not connected. Call connectRealtime() first.");let i=`csub_${Date.now()}_${Math.random().toString(36).substring(2,9)}`;this.activeSubscriptions.set(i,{tableId:e,options:s,handlers:t});let r=this.sendSubscribeRequest(e,t,s);return r.catch(n=>{this.activeSubscriptions.delete(i),t.onError?.(n instanceof Error?n:new Error(String(n)))}),{subscriptionId:i,unsubscribe:()=>{this.activeSubscriptions.delete(i),r.then(n=>{this.realtimeHandlers.delete(n),this.realtimeState==="connected"&&this.sendRealtimeMessage({type:"unsubscribe",request_id:this.generateRequestId(),subscription_id:n})}).catch(()=>{})},loadMore:(n,o)=>{this.realtimeState==="connected"&&r.then(c=>{let l={type:"snapshot_more",request_id:this.generateRequestId(),subscription_id:c,offset:n};o!==void 0&&(l.limit=o),this.sendRealtimeMessage(l)}).catch(()=>{})}}}isRealtimeConnected(){return this.realtimeState==="connected"}getRealtimeState(){return this.realtimeState}onRealtimeStateChange(e){return this.realtimeOnStateChange=e,()=>{this.realtimeOnStateChange=null}}onRealtimeError(e){return this.realtimeOnError=e,()=>{this.realtimeOnError=null}}setRealtimeState(e){this.realtimeState!==e&&(this.realtimeState=e,this.realtimeOnStateChange?.(e))}doRealtimeConnect(){if(!this.realtimeOptions)return Promise.reject(new Error("No realtime options"));this.setRealtimeState("connecting");let s=`${(this.realtimeOptions.dataServerUrl||this.http.getBaseUrl()).replace(/^http/,"ws")}/v1/database/realtime/ws?access_token=${encodeURIComponent(this.realtimeOptions.accessToken)}`;return new Promise((i,r)=>{try{this.realtimeWs=new WebSocket(s);let n=!1,o=setTimeout(()=>{n||(n=!0,this.realtimeWs&&(this.realtimeWs.close(),this.realtimeWs=null),this.setRealtimeState("disconnected"),r(new Error("Connection timeout")))},15e3);this.realtimeWs.onopen=()=>{n||(n=!0,clearTimeout(o)),this.setRealtimeState("connected"),this.realtimeRetryCount=0,this.startRealtimePing(),this.debugLog("Database realtime connected"),this.resubscribeAll(),i()},this.realtimeWs.onmessage=c=>{try{let l=JSON.parse(c.data);this.handleRealtimeMessage(l)}catch{this.debugLog("Failed to parse realtime message")}},this.realtimeWs.onclose=()=>{this.debugLog("Database realtime disconnected"),this.realtimeWs=null,this.stopRealtimePing(),n||(n=!0,clearTimeout(o),r(new Error("Connection closed during handshake"))),this.realtimeOptions&&this.realtimeState!=="disconnected"&&this.attemptRealtimeReconnect()},this.realtimeWs.onerror=()=>{this.debugLog("Database realtime error"),this.realtimeOnError?.(new Error("WebSocket connection error"))}}catch(n){this.setRealtimeState("disconnected"),r(n)}})}sendSubscribeRequest(e,t,s){let i=this.generateRequestId();this.realtimeHandlers.set(i,t);let r=s?.where?{filters:s.where.map(n=>({field:n.field,operator:n.operator,value:n.value}))}:void 0;return this.sendRealtimeMessage({type:"subscribe",request_id:i,table_id:e,doc_id:s?.docId,query:r,options:{include_self:s?.includeSelf??!1,include_metadata_changes:s?.includeMetadataChanges??!1}}),new Promise((n,o)=>{let c=setTimeout(()=>{this.pendingRequests.delete(i),this.realtimeHandlers.delete(i),o(new Error("Subscribe request timeout"))},3e4);this.pendingRequests.set(i,{resolve:l=>{let d=l,p=this.realtimeHandlers.get(i);p&&(this.realtimeHandlers.delete(i),this.realtimeHandlers.set(d,p)),n(d)},reject:o,timeout:c})})}resubscribeAll(){if(this.activeSubscriptions.size!==0){this.realtimeHandlers.clear(),this.pendingRequests.forEach(e=>clearTimeout(e.timeout)),this.pendingRequests.clear(),this.debugLog(`Resubscribing ${this.activeSubscriptions.size} subscriptions`);for(let[,e]of this.activeSubscriptions)this.sendSubscribeRequest(e.tableId,e.handlers,e.options).catch(t=>{e.handlers.onError?.(t instanceof Error?t:new Error(String(t)))})}}handleRealtimeMessage(e){switch(e.type){case"subscribed":{let s=e.request_id,i=e.subscription_id,r=this.pendingRequests.get(s);r&&(clearTimeout(r.timeout),r.resolve(i),this.pendingRequests.delete(s));break}case"snapshot":{let s=e.subscription_id,i=this.realtimeHandlers.get(s);if(i?.onSnapshot){let r=e.docs||[],n=e.has_more||!1,o=n?e.next_offset:void 0;i.onSnapshot(r,{totalCount:e.total_count||0,hasMore:n,nextOffset:o})}break}case"change":{let s=e.subscription_id,i=this.realtimeHandlers.get(s);if(i?.onChange){let r=e.changes||[];i.onChange(r)}break}case"presence":{let s=this.realtimeHandlers.get("__presence__");if(s?.onSnapshot){let i=e.states,r=Object.entries(i||{}).map(([n,o])=>({id:n,data:o,exists:!0}));s.onSnapshot(r,{totalCount:r.length,hasMore:!1})}break}case"error":{let s=e.request_id,i=e.message||"Unknown error";if(s){let r=this.pendingRequests.get(s);r&&(clearTimeout(r.timeout),r.reject(new Error(i)),this.pendingRequests.delete(s));let n=this.realtimeHandlers.get(s);n?.onError&&n.onError(new Error(i))}else this.realtimeOnError?.(new Error(i));break}case"pong":break;case"unsubscribed":case"presence_set_ack":case"presence_subscribed":case"typing_subscribed":break}}attemptRealtimeReconnect(){let e=this.realtimeOptions?.maxRetries??5,t=this.realtimeOptions?.retryInterval??1e3;if(this.realtimeRetryCount>=e){this.setRealtimeState("disconnected"),this.realtimeOnError?.(new Error("Realtime connection lost. Max retries exceeded."));return}this.setRealtimeState("connecting"),this.realtimeRetryCount++;let s=Math.min(t*Math.pow(2,this.realtimeRetryCount-1),3e4);this.debugLog(`Reconnecting in ${s}ms (attempt ${this.realtimeRetryCount}/${e})`),setTimeout(()=>{this.realtimeOptions&&this.doRealtimeConnect().catch(i=>{this.debugLog(`Reconnect failed: ${i}`)})},s)}startRealtimePing(){this.stopRealtimePing(),this.pingInterval=setInterval(()=>{this.realtimeState==="connected"&&this.realtimeWs?.readyState===WebSocket.OPEN&&this.sendRealtimeMessage({type:"ping",timestamp:Date.now()})},3e4)}stopRealtimePing(){this.pingInterval&&(clearInterval(this.pingInterval),this.pingInterval=null)}sendRealtimeMessage(e){this.realtimeWs?.readyState===WebSocket.OPEN&&this.realtimeWs.send(JSON.stringify(e))}generateRequestId(){return"req_"+Date.now()+"_"+Math.random().toString(36).substring(2,9)}debugLog(e){this.realtimeOptions?.debug&&console.log(`[DatabaseRealtime] ${e}`)}};var te=new Set(["localhost","127.0.0.1","::1"]),ce=2048;function le(a,e={}){let t=e.context??"external URL";if(typeof a!="string"||a.length===0)throw new u(400,`${t}: empty URL`,"INVALID_PRESIGNED_URL");if(a.length>ce)throw new u(400,`${t}: URL exceeds ${ce} chars`,"INVALID_PRESIGNED_URL");let s;try{s=new URL(a)}catch{throw new u(400,`${t}: cannot parse URL`,"INVALID_PRESIGNED_URL")}let i=e.allowLocalhost&&te.has(s.hostname);if(s.protocol!=="https:"&&!(i&&s.protocol==="http:"))throw new u(400,`${t}: scheme must be https (got ${s.protocol})`,"INVALID_PRESIGNED_URL");if(!e.allowLocalhost&&te.has(s.hostname))throw new u(400,`${t}: localhost is not allowed in production`,"INVALID_PRESIGNED_URL");if(e.allowedHosts&&e.allowedHosts.length>0){let r=s.hostname;if(!e.allowedHosts.some(o=>o===r?!0:r.endsWith(`.${o}`)))throw new u(400,`${t}: host ${r} is not in allowlist`,"INVALID_PRESIGNED_URL")}return s}function de(){if(typeof window>"u")return!1;let a=window.location.hostname;return te.has(a)}var H=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async uploadToPresigned(e,t,s){let i=le(e,{allowLocalhost:de(),context:"storage.presigned-url"}),{signal:r,cleanup:n}=R({timeout:s?.timeout,signal:s?.signal});try{let o=await fetch(i.toString(),{method:"PUT",body:t,headers:{"Content-Type":t.type||"application/octet-stream"},signal:r});if(!o.ok)throw new u(o.status,`Upload failed: ${o.statusText}`,"PRESIGNED_UPLOAD_FAILED")}finally{n()}}async getFiles(e,t){let s=this.getPublicPrefix(),i=t?`?parent_id=${encodeURIComponent(t)}`:"";return(await this.http.get(`${s}/storages/files/${e}/items${i}`)).files}async uploadFile(e,t,s){let i=this.getPublicPrefix(),r=await this.http.post(`${i}/storages/files/${e}/presigned-url`,{file_name:t.name,file_size:t.size,mime_type:t.type||"application/octet-stream",parent_id:s});await this.uploadToPresigned(r.upload_url,t);let n=await this.http.post(`${i}/storages/files/${e}/complete-upload`,{file_id:r.file_id});return{id:n.id,name:n.name,path:n.path,type:n.type,mime_type:n.mime_type,size:n.size,url:n.url,parent_id:n.parent_id,created_at:n.created_at}}async uploadFiles(e,t,s){let i=[];for(let r of t){let n=await this.uploadFile(e,r,s);i.push(n)}return i}async createFolder(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/storages/files/${e}/folders`,t)}async deleteFile(e,t){let s=this.getPublicPrefix();await this.http.delete(`${s}/storages/files/${e}/items/${t}`)}async moveFile(e,t,s){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error("storage.moveFile requires JWT (console token) auth; not available with Public Key alone.");await this.http.post(`/v1/storages/files/${e}/items/${t}/move`,s)}async renameFile(e,t,s){if(this.http.hasPublicKey()&&!this.http.hasJWT())throw new Error("storage.renameFile requires JWT (console token) auth; not available with Public Key alone.");return this.http.patch(`/v1/storages/files/${e}/items/${t}/rename`,s)}getFileUrl(e){return e.url||null}isImageFile(e){return e.mime_type?.startsWith("image/")||!1}async uploadByPath(e,t,s,i){let r=this.getPublicPrefix(),n=t.startsWith("/")?t.slice(1):t,o=i?.overwrite!==!1,c=await this.http.post(`${r}/storages/files/${e}/presigned-url/path/${n}`,{file_name:s.name,file_size:s.size,mime_type:s.type||"application/octet-stream",overwrite:o});await this.uploadToPresigned(c.upload_url,s);let l=await this.http.post(`${r}/storages/files/${e}/complete-upload`,{file_id:c.file_id});return{id:l.id,name:l.name,path:l.path,type:l.type,mime_type:l.mime_type,size:l.size,url:l.url,parent_id:l.parent_id,created_at:l.created_at}}async getByPath(e,t){let s=this.getPublicPrefix(),i=t.startsWith("/")?t.slice(1):t;return this.http.get(`${s}/storages/files/${e}/path/${i}`)}async getUrlByPath(e,t){try{return(await this.getByPath(e,t)).url||null}catch{return null}}async setPageMeta(e,t){let s=this.getPublicPrefix();return this.http.put(`${s}/storages/webs/${e}/page-metas`,t)}async batchSetPageMeta(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/storages/webs/${e}/page-metas/batch`,t)}async listPageMetas(e,t){let s=this.getPublicPrefix(),i=new URLSearchParams;t?.limit!=null&&i.set("limit",String(t.limit)),t?.offset!=null&&i.set("offset",String(t.offset));let r=i.toString();return this.http.get(`${s}/storages/webs/${e}/page-metas${r?`?${r}`:""}`)}async getPageMeta(e,t){let s=this.getPublicPrefix(),i=encodeURIComponent(t);return this.http.get(`${s}/storages/webs/${e}/page-metas/get?path=${i}`)}async deletePageMeta(e,t){let s=this.getPublicPrefix(),i=encodeURIComponent(t);await this.http.delete(`${s}/storages/webs/${e}/page-metas?path=${i}`)}async deleteAllPageMetas(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/storages/webs/${e}/page-metas/all`)}};var O=class{constructor(e){this.http=e}async getPublicKeys(e){return this.http.get(`/v1/apps/${e}/public-keys`)}async createPublicKey(e,t){return this.http.post(`/v1/apps/${e}/public-keys`,t)}async updatePublicKey(e,t,s){return this.http.patch(`/v1/apps/${e}/public-keys/${t}`,s)}async deletePublicKey(e,t){await this.http.delete(`/v1/apps/${e}/public-keys/${t}`)}};var L=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async invoke(e,t,s){let i=this.getPublicPrefix(),r={};return t!==void 0&&(r.payload=t),s!==void 0&&(r.timeout=s),this.http.post(`${i}/functions/${e}/invoke`,r)}async call(e,t){let s=await this.invoke(e,t);if(!s.success)throw new u(500,s.error||"Function execution failed","FUNCTION_EXECUTION_FAILED");return s.result}};var D=class{constructor(e,t){this.ws=null;this.state="disconnected";this._connectionId=null;this._appId=null;this.options={maxRetries:5,retryInterval:1e3,userId:"",accessToken:"",timeout:3e4,debug:!1};this.retryCount=0;this.pendingRequests=new Map;this.subscriptions=new Map;this.streamSessions=new Map;this.stateHandlers=[];this.errorHandlers=[];this.presenceHandlers=[];this.presenceSubscriptions=new Map;this.typingHandlers=new Map;this.readReceiptHandlers=new Map;this.connectPromise=null;this.http=e,this.socketUrl=t,this.clientId=this.generateClientId()}get connectionId(){return this._connectionId}get appId(){return this._appId}async connect(e={}){if(this.state!=="connected"){if(this.state==="connecting"&&this.connectPromise)return this.connectPromise;this.options={...this.options,...e},e.userId&&(this.userId=e.userId),this.connectPromise=this.doConnect();try{await this.connectPromise}finally{this.connectPromise=null}}}disconnect(){this.state="disconnected",this.notifyStateChange(),this.ws&&(this.ws.close(),this.ws=null),this.pendingRequests.forEach(e=>{clearTimeout(e.timeout),e.reject(new Error("Connection closed"))}),this.pendingRequests.clear(),this.subscriptions.clear(),this.streamSessions.forEach(e=>{e.handlers.onError&&e.handlers.onError(new Error("Connection closed"))}),this.streamSessions.clear(),this.presenceHandlers=[],this.presenceSubscriptions.clear(),this.typingHandlers.clear(),this.readReceiptHandlers.clear(),this._connectionId=null,this._appId=null,this.retryCount=0,this.connectPromise=null}async subscribe(e,t={}){if(this.state!=="connected")throw new Error("Not connected. Call connect() first.");let s=this.generateRequestId(),i=await this.sendRequest({category:e,action:"subscribe",request_id:s}),r={category:i.category,persist:i.persist,historyCount:i.history_count,readReceipt:i.read_receipt},n=[];return this.subscriptions.set(e,{info:r,handlers:n}),{info:r,send:async(c,l)=>{await this.sendMessage(e,c,l)},getHistory:async c=>this.getHistory(e,c??t.historyLimit),unsubscribe:async()=>{await this.unsubscribe(e)},onMessage:c=>(n.push(c),()=>{let l=n.indexOf(c);l>-1&&n.splice(l,1)})}}async unsubscribe(e){if(this.state!=="connected")return;let t=this.generateRequestId();await this.sendRequest({category:e,action:"unsubscribe",request_id:t}),this.subscriptions.delete(e)}async sendMessage(e,t,s={}){if(this.state!=="connected")throw new Error("Not connected");let r=s.includeSelf!==!1,n=this.generateRequestId();await this.sendRequest({category:e,action:"send",data:{data:t,broadcast:r},request_id:n})}async getHistory(e,t){if(this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId(),i=await this.sendRequest({category:e,action:"history",data:t?{limit:t}:void 0,request_id:s});return{category:i.category,messages:i.messages.map(r=>({id:r.id,category:r.category,from:r.from,data:r.data,sentAt:r.sent_at})),total:i.total}}async stream(e,t,s={}){if(this.state!=="connected")throw new Error("Not connected. Call connect() first.");let i=this.generateRequestId(),r=s.sessionId||this.generateRequestId();this.streamSessions.set(r,{handlers:t,requestId:i});try{this.sendRaw({category:"",action:"stream",data:{provider:s.provider,model:s.model,messages:e,system:s.system,temperature:s.temperature,max_tokens:s.maxTokens,session_id:r,metadata:s.metadata,mcp_group:s.mcpGroup},request_id:i})}catch(n){throw this.streamSessions.delete(r),n}return{sessionId:r,stop:async()=>{await this.stopStream(r)}}}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}isConnected(){return this.state==="connected"}onStateChange(e){return this.stateHandlers.push(e),()=>{let t=this.stateHandlers.indexOf(e);t>-1&&this.stateHandlers.splice(t,1)}}onError(e){return this.errorHandlers.push(e),()=>{let t=this.errorHandlers.indexOf(e);t>-1&&this.errorHandlers.splice(t,1)}}async setPresence(e,t={}){if(this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId();await this.sendRequest({category:"",action:"presence_set",data:{status:e,device:t.device,metadata:t.metadata},request_id:s})}async setPresenceOnDisconnect(e,t){if(this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId();await this.sendRequest({category:"",action:"presence_on_disconnect",data:{status:e,metadata:t},request_id:s})}async getPresence(e){if(this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId(),s=await this.sendRequest({category:"",action:"presence_get",data:{user_id:e},request_id:t});return{userId:s.user_id,status:s.status,lastSeen:s.last_seen,device:s.device,metadata:s.metadata}}async getPresenceMany(e){if(this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId(),s=await this.sendRequest({category:"",action:"presence_get_many",data:{user_ids:e},request_id:t}),i={};for(let[r,n]of Object.entries(s.users))i[r]={userId:n.user_id,status:n.status,lastSeen:n.last_seen,device:n.device,metadata:n.metadata};return{users:i}}async subscribePresence(e,t){if(this.state!=="connected")throw new Error("Not connected");if(!this.presenceSubscriptions.has(e)){let i=this.generateRequestId();await this.sendRequest({category:"",action:"presence_subscribe",data:{user_id:e},request_id:i}),this.presenceSubscriptions.set(e,[])}let s=this.presenceSubscriptions.get(e);return s.push(t),()=>{let i=s.indexOf(t);if(i>-1&&s.splice(i,1),s.length===0&&(this.presenceSubscriptions.delete(e),this.state==="connected")){let r=this.generateRequestId();this.sendRequest({category:"",action:"presence_unsubscribe",data:{user_id:e},request_id:r}).catch(n=>{this.log(`Failed to unsubscribe presence for ${e}: ${n}`)})}}}onPresenceChange(e){return this.presenceHandlers.push(e),()=>{let t=this.presenceHandlers.indexOf(e);t>-1&&this.presenceHandlers.splice(t,1)}}async startTyping(e){if(this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId();await this.sendRequest({category:"",action:"typing_start",data:{room_id:e},request_id:t})}async stopTyping(e){if(this.state!=="connected")throw new Error("Not connected");let t=this.generateRequestId();await this.sendRequest({category:"",action:"typing_stop",data:{room_id:e},request_id:t})}async onTypingChange(e,t){if(this.state!=="connected")throw new Error("Not connected");if(!this.typingHandlers.has(e)){let i=this.generateRequestId();await this.sendRequest({category:"",action:"typing_subscribe",data:{room_id:e},request_id:i}),this.typingHandlers.set(e,[])}let s=this.typingHandlers.get(e);return s.push(t),()=>{let i=s.indexOf(t);if(i>-1&&s.splice(i,1),s.length===0&&(this.typingHandlers.delete(e),this.state==="connected")){let r=this.generateRequestId();this.sendRequest({category:"",action:"typing_unsubscribe",data:{room_id:e},request_id:r}).catch(n=>{this.log(`Failed to unsubscribe typing for ${e}: ${n}`)})}}}async markRead(e,t){if(this.state!=="connected")throw new Error("Not connected");let s=this.generateRequestId();await this.sendRequest({category:e,action:"mark_read",data:{message_ids:t},request_id:s})}onReadReceipt(e,t){this.readReceiptHandlers.has(e)||this.readReceiptHandlers.set(e,[]);let s=this.readReceiptHandlers.get(e);return s.push(t),()=>{let i=s.indexOf(t);i>-1&&s.splice(i,1)}}async doConnect(){return new Promise((e,t)=>{this.state="connecting",this.notifyStateChange(),this.log("Connecting...");let s=this.socketUrl.replace(/^http/,"ws"),i;if(this.options.accessToken)i=`${s}/v1/realtime/auth?access_token=${encodeURIComponent(this.options.accessToken)}&client_id=${this.clientId}`,this.log("Using accessToken authentication");else{let o=this.http.getPublicKey();if(!o){let c=new Error("API Key or accessToken is required for realtime connection");this.log("Connection failed: no API Key or accessToken"),t(c);return}i=`${s}/v1/realtime/auth?public_key=${encodeURIComponent(o)}&client_id=${this.clientId}`,this.log("Using API Key authentication")}this.userId&&(i+=`&user_id=${encodeURIComponent(this.userId)}`);let r=!1,n=setTimeout(()=>{r||(r=!0,this.log(`Connection timeout after ${this.options.timeout}ms`),this.ws&&(this.ws.close(),this.ws=null),this.state="disconnected",this.notifyStateChange(),t(new Error(`Connection timeout after ${this.options.timeout}ms`)))},this.options.timeout);try{this.log(`Connecting to ${s}`),this.ws=new WebSocket(i),this.ws.onopen=()=>{this.log("WebSocket opened, waiting for connected event...")},this.ws.onmessage=o=>{let c=o.data.split(`
|
|
2
2
|
`).filter(l=>l.trim());for(let l of c)try{let d=JSON.parse(l);this.handleServerMessage(d,()=>{r||(r=!0,clearTimeout(n),this.log("Connected successfully"),e())})}catch(d){this.logError("Failed to parse message",{line:l,error:d})}},this.ws.onclose=o=>{this.log(`WebSocket closed: code=${o.code}, reason=${o.reason}`),!r&&this.state==="connecting"&&(r=!0,clearTimeout(n),t(new Error(`Connection closed: ${o.reason||"unknown reason"}`))),(this.state==="connected"||this.state==="connecting")&&this.handleDisconnect()},this.ws.onerror=o=>{this.log("WebSocket error occurred"),this.logError("WebSocket error",o),this.notifyError(new Error("WebSocket connection error")),!r&&this.state==="connecting"&&(r=!0,clearTimeout(n),t(new Error("Failed to connect")))}}catch(o){r=!0,clearTimeout(n),t(o)}})}log(e){this.options.debug&&console.log(`[Realtime] ${e}`)}logError(e,t){this.options.debug&&console.error(`[Realtime] ${e}`,t)}handleServerMessage(e,t){switch(e.event){case"connected":{let s=e.data;this._connectionId=s.connection_id,this._appId=s.app_id,this.state="connected",this.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,i=this.subscriptions.get(s.category);if(i){let r={id:s.id,category:s.category,from:s.from,data:s.data,sentAt:s.sent_at};i.handlers.forEach(n=>n(r))}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,i=this.streamSessions.get(s.session_id);i?.handlers.onToken&&i.handlers.onToken(s.token,s.index);break}case"stream_done":{let s=e.data,i=this.streamSessions.get(s.session_id);i?.handlers.onDone&&i.handlers.onDone({sessionId:s.session_id,fullText:s.full_text,totalTokens:s.total_tokens,promptTokens:s.prompt_tokens,duration:s.duration_ms}),this.streamSessions.delete(s.session_id);break}case"stream_tool_call":{let s=e.data,i=this.streamSessions.get(s.session_id);i?.handlers.onToolCall&&i.handlers.onToolCall(s.tool_name,s.arguments||{},s.index);break}case"stream_tool_result":{let s=e.data,i=this.streamSessions.get(s.session_id);i?.handlers.onToolResult&&i.handlers.onToolResult(s.tool_name,s.success,s.duration_ms,s.index);break}case"stream_error":{let s=e.data;if(e.request_id){for(let[i,r]of this.streamSessions)if(r.requestId===e.request_id){r.handlers.onError&&r.handlers.onError(new Error(s.message)),this.streamSessions.delete(i);break}}break}case"presence":case"presence_status":{let s=e.data,i={userId:s.user_id,status:s.status,lastSeen:s.last_seen,device:s.device,metadata:s.metadata,eventType:s.event_type};this.presenceHandlers.forEach(n=>n(i));let r=this.presenceSubscriptions.get(s.user_id);if(r&&r.forEach(n=>n(i)),e.request_id){let n=this.pendingRequests.get(e.request_id);n&&(clearTimeout(n.timeout),n.resolve(e.data),this.pendingRequests.delete(e.request_id))}break}case"typing":{let s=e.data,i={roomId:s.room_id,users:s.users},r=this.typingHandlers.get(s.room_id);r&&r.forEach(n=>n(i));break}case"read_receipt":{let s=e.data,i={category:s.category,messageIds:s.message_ids,readerId:s.reader_id,readAt:s.read_at},r=this.readReceiptHandlers.get(s.category);r&&r.forEach(n=>n(i));break}}}handleDisconnect(){this.ws=null,this._connectionId=null,this.streamSessions.forEach(i=>{i.handlers.onError&&i.handlers.onError(new Error("Connection lost"))}),this.streamSessions.clear();let e=new Map;for(let[i,r]of this.subscriptions)e.set(i,[...r.handlers]);this.subscriptions.clear();let t=new Map;for(let[i,r]of this.presenceSubscriptions)t.set(i,[...r]);this.presenceSubscriptions.clear();let s=new Map;for(let[i,r]of this.typingHandlers)s.set(i,[...r]);if(this.typingHandlers.clear(),this.retryCount<this.options.maxRetries){this.state="reconnecting",this.notifyStateChange(),this.retryCount++;let i=Math.min(this.options.retryInterval*Math.pow(2,this.retryCount-1),3e4);setTimeout(async()=>{try{await this.doConnect(),this.log("Reconnected successfully, restoring subscriptions..."),await this.restoreSubscriptions(e,t,s)}catch(r){this.logError("Reconnect failed",r)}},i)}else this.state="disconnected",this.notifyStateChange(),this.notifyError(new Error("Connection lost. Max retries exceeded."))}async restoreSubscriptions(e,t,s){for(let[i,r]of e)try{this.log(`Restoring subscription: ${i}`);let n=this.generateRequestId(),o=await this.sendRequest({category:i,action:"subscribe",request_id:n}),c={category:o.category,persist:o.persist,historyCount:o.history_count,readReceipt:o.read_receipt};this.subscriptions.set(i,{info:c,handlers:r}),this.log(`Restored subscription: ${i}`)}catch(n){this.logError(`Failed to restore subscription for ${i}`,n),this.notifyError(new Error(`Failed to restore subscription: ${i}`))}for(let[i,r]of t)try{this.log(`Restoring presence subscription: ${i}`);let n=this.generateRequestId();await this.sendRequest({category:"",action:"presence_subscribe",data:{user_id:i},request_id:n}),this.presenceSubscriptions.set(i,r),this.log(`Restored presence subscription: ${i}`)}catch(n){this.logError(`Failed to restore presence subscription for ${i}`,n)}for(let[i,r]of s)try{this.log(`Restoring typing subscription: ${i}`);let n=this.generateRequestId();await this.sendRequest({category:"",action:"typing_subscribe",data:{room_id:i},request_id:n}),this.typingHandlers.set(i,r),this.log(`Restored typing subscription: ${i}`)}catch(n){this.logError(`Failed to restore typing subscription for ${i}`,n)}}sendRequest(e){return new Promise((t,s)=>{if(!this.ws||this.ws.readyState!==WebSocket.OPEN){s(new Error("Not connected"));return}let i=setTimeout(()=>{this.pendingRequests.delete(e.request_id),s(new Error("Request timeout"))},this.options.timeout);this.pendingRequests.set(e.request_id,{resolve:t,reject:s,timeout:i}),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 F=class{constructor(e,t,s){this.ws=null;this.state="disconnected";this.stateListeners=[];this.errorListeners=[];this.peerJoinedListeners=[];this.peerLeftListeners=[];this.remoteStreamListeners=[];this.reconnectAttempts=0;this.maxReconnectAttempts=5;this.reconnectTimeout=null;this.currentRoomId=null;this.currentPeerId=null;this.currentUserId=null;this.isBroadcaster=!1;this.localStream=null;this.channelType="interactive";this.peerConnections=new Map;this.remoteStreams=new Map;this.iceServers=[];this.http=e,this.webrtcUrl=t,this.appId=s}async getICEServers(){if(!this.appId)throw new Error("WebRTC getICEServers \uC5D0\uB294 appId \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. ConnectBase \uCD08\uAE30\uD654 \uC2DC appId \uB97C \uC124\uC815\uD558\uC138\uC694.");let e=await this.http.get(`/v1/apps/${this.appId}/ice-servers`);return this.iceServers=e.ice_servers,e.ice_servers}async connect(e){if(this.state==="connected"||this.state==="connecting")throw new Error("\uC774\uBBF8 \uC5F0\uACB0\uB418\uC5B4 \uC788\uAC70\uB098 \uC5F0\uACB0 \uC911\uC785\uB2C8\uB2E4");if(this.setState("connecting"),this.currentRoomId=e.roomId,this.currentUserId=e.userId||null,this.isBroadcaster=e.isBroadcaster||!1,this.localStream=e.localStream||null,this.iceServers.length===0)try{await this.getICEServers()}catch{this.iceServers=[{urls:"stun:stun.l.google.com:19302"}]}return this.connectWebSocket()}connectWebSocket(){return new Promise((e,t)=>{let s=this.buildWebSocketUrl();this.ws=new WebSocket(s);let i=setTimeout(()=>{this.state==="connecting"&&(this.ws?.close(),t(new Error("\uC5F0\uACB0 \uC2DC\uAC04 \uCD08\uACFC")))},1e4);this.ws.onopen=()=>{clearTimeout(i),this.reconnectAttempts=0,this.sendSignaling({type:"join",room_id:this.currentRoomId,data:{user_id:this.currentUserId,is_broadcaster:this.isBroadcaster}})},this.ws.onmessage=async r=>{try{let n=JSON.parse(r.data);await this.handleSignalingMessage(n,e,t)}catch(n){console.error("Failed to parse signaling message:",n)}},this.ws.onerror=r=>{clearTimeout(i),console.error("WebSocket error:",r),this.emitError(new Error("WebSocket \uC5F0\uACB0 \uC624\uB958"))},this.ws.onclose=r=>{clearTimeout(i),this.state==="connecting"&&t(new Error("\uC5F0\uACB0\uC774 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4")),this.handleDisconnect(r)}})}buildWebSocketUrl(){let e=this.webrtcUrl.replace("https://","wss://").replace("http://","ws://"),t=this.http.getPublicKey(),s=this.http.getAccessToken(),i="";if(s?i=`access_token=${encodeURIComponent(s)}`:t&&(i=`public_key=${encodeURIComponent(t)}`),!this.appId)throw new Error("WebRTC \uC5F0\uACB0\uC5D0\uB294 appId\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. ConnectBase \uCD08\uAE30\uD654 \uC2DC appId\uB97C \uC124\uC815\uD558\uC138\uC694.");return`${e}/v1/apps/${this.appId}/signaling?${i}`}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 i=typeof e.data=="string"?e.data:"Unknown error",r=new Error(i);this.emitError(r),s?.(r);break}}async createPeerConnection(e,t){this.closePeerConnection(e);let s={iceServers:this.iceServers.map(r=>({urls:r.urls,username:r.username,credential:r.credential}))},i=new RTCPeerConnection(s);if(this.peerConnections.set(e,i),this.localStream&&this.localStream.getTracks().forEach(r=>{i.addTrack(r,this.localStream)}),i.onicecandidate=r=>{r.candidate&&this.sendSignaling({type:"ice_candidate",target_id:e,candidate:r.candidate.toJSON()})},i.ontrack=r=>{let[n]=r.streams;n&&(this.remoteStreams.set(e,n),this.emitRemoteStream(e,n))},i.onconnectionstatechange=()=>{i.connectionState==="failed"&&(console.warn(`Peer connection failed: ${e}`),this.closePeerConnection(e))},t){let r=await i.createOffer();await i.setLocalDescription(r),this.sendSignaling({type:"offer",target_id:e,sdp:r.sdp})}return i}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 i=await s.createAnswer();await s.setLocalDescription(i),this.sendSignaling({type:"answer",target_id:e,sdp:i.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(i){console.warn("Failed to add ICE candidate:",i)}}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,i)=>{s.close(),this.emitPeerLeft(i)}),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(i=>{let r=s.find(n=>n.track?.kind===i.kind);r?r.replaceTrack(i):t.addTrack(i,e)})})}setAudioEnabled(e){this.localStream&&this.localStream.getAudioTracks().forEach(t=>{t.enabled=e})}setVideoEnabled(e){this.localStream&&this.localStream.getVideoTracks().forEach(t=>{t.enabled=e})}onStateChange(e){return this.stateListeners.push(e),()=>{this.stateListeners=this.stateListeners.filter(t=>t!==e)}}onError(e){return this.errorListeners.push(e),()=>{this.errorListeners=this.errorListeners.filter(t=>t!==e)}}onPeerJoined(e){return this.peerJoinedListeners.push(e),()=>{this.peerJoinedListeners=this.peerJoinedListeners.filter(t=>t!==e)}}onPeerLeft(e){return this.peerLeftListeners.push(e),()=>{this.peerLeftListeners=this.peerLeftListeners.filter(t=>t!==e)}}onRemoteStream(e){return this.remoteStreamListeners.push(e),()=>{this.remoteStreamListeners=this.remoteStreamListeners.filter(t=>t!==e)}}setState(e){this.state!==e&&(this.state=e,this.stateListeners.forEach(t=>t(e)))}emitError(e){this.errorListeners.forEach(t=>t(e))}emitPeerJoined(e,t){this.peerJoinedListeners.forEach(s=>s(e,t))}emitPeerLeft(e){this.peerLeftListeners.forEach(t=>t(e))}emitRemoteStream(e,t){this.remoteStreamListeners.forEach(s=>s(e,t))}async validate(){if(!this.appId)throw new Error("WebRTC \uAC80\uC99D\uC5D0\uB294 appId\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. ConnectBase \uCD08\uAE30\uD654 \uC2DC appId\uB97C \uC124\uC815\uD558\uC138\uC694.");return this.http.get(`/v1/apps/${this.appId}/validate`)}async getStats(e){return this.http.get(`/v1/apps/${e}/stats`)}async getRooms(e){return this.http.get(`/v1/apps/${e}/rooms`)}};var B=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,i,r)=>(this.handleGlobalError(e,t,s,i,r),this.originalOnError?this.originalOnError(e,t,s,i,r):!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,i,r){let n={message:typeof e=="string"?e:e.type||"Unknown error",source:t||void 0,lineno:s||void 0,colno:i||void 0,stack:r?.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",i;t instanceof Error?(s=t.message,i=t.stack):typeof t=="string"?s=t:t&&typeof t=="object"&&(s=JSON.stringify(t));let r={message:s,stack:i,error_type:"unhandledrejection",url:typeof window<"u"?window.location.href:void 0,referrer:typeof document<"u"?document.referrer:void 0};this.queueError(r)}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 i=this.config.beforeSend(s);return i===!1||i===null?(this.log("Error filtered out by beforeSend"),null):i}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 N=class{constructor(e){this.http=e}async getEnabledProviders(){return this.http.get("/v1/public/oauth/providers")}async signIn(e,t,s){let i=new URLSearchParams({app_callback:t});s&&i.append("state",s);let r=await this.http.get(`/v1/public/oauth/${e}/authorize/central?${i.toString()}`);window.location.href=r.authorization_url}async signInWithPopup(e,t){let s=new URLSearchParams;t&&s.set("app_callback",t);let i=s.toString(),r=await this.http.get(`/v1/public/oauth/${e}/authorize/central${i?"?"+i:""}`),n=500,o=600,c=window.screenX+(window.outerWidth-n)/2,l=window.screenY+(window.outerHeight-o)/2,d=window.open(r.authorization_url,"oauth-popup",`width=${n},height=${o},left=${c},top=${l}`);if(!d)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((p,h)=>{let m=!1,y=()=>{m=!0,window.removeEventListener("message",v),clearInterval(b),clearTimeout(S)},v=async g=>{if(g.data?.type!=="oauth-callback"||m)return;if(y(),g.data.error){h(new Error(g.data.error));return}let w=g.data.email,_={member_id:g.data.member_id,access_token:g.data.access_token,refresh_token:g.data.refresh_token,is_new_member:g.data.is_new_member==="true"||g.data.is_new_member===!0,...typeof w=="string"&&w.length>0?{email:w}:{}};this.http.setTokens(_.access_token,_.refresh_token),p(_)};window.addEventListener("message",v);let b=setInterval(()=>{try{d.closed&&(m||(y(),h(new Error("\uB85C\uADF8\uC778\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."))))}catch{clearInterval(b)}},500),S=setTimeout(()=>{if(!m){y();try{d.close()}catch{}h(new Error("\uB85C\uADF8\uC778 \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694."))}},18e4)})}getCallbackResult(){let e=new URLSearchParams(window.location.search),t=e.get("error");if(t){let c={error:t};return window.opener&&(window.opener.postMessage({type:"oauth-callback",...c},"*"),window.close()),c}let s=e.get("access_token"),i=e.get("refresh_token"),r=e.get("member_id");if(!s||!i||!r)return null;let n=e.get("email"),o={access_token:s,refresh_token:i,member_id:r,is_new_member:e.get("is_new_member")==="true",...n?{email:n}:{},state:e.get("state")||void 0};return window.opener?(window.opener.postMessage({type:"oauth-callback",...o},"*"),window.close(),o):(this.http.setTokens(s,i),o)}async exchangeCodeFromCallback(){let e=new URLSearchParams(window.location.search),t=e.get("code");if(!t||e.get("error"))return null;let s=await this.http.post("/v1/auth/oauth/exchange",{code:t}),i={access_token:s.access_token,refresh_token:s.refresh_token,member_id:s.member_id,is_new_member:s.is_new_member,...s.email?{email:s.email}:{},state:e.get("state")||void 0};return window.opener?(window.opener.postMessage({type:"oauth-callback",...i},"*"),window.close(),i):(this.http.setTokens(i.access_token,i.refresh_token),i)}};var G=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async prepare(e){let t=this.getPublicPrefix();return this.http.post(`${t}/payments/prepare`,e)}async createCheckoutSession(e){let t=this.getPublicPrefix(),s=await this.http.post(`${t}/payments/checkout-session`,e);return f(s,{session_id:{type:"string"},session_url:{type:"string"}},"payment.createCheckoutSession"),s}async confirm(e){let t=this.getPublicPrefix();return this.http.post(`${t}/payments/confirm`,e)}async cancel(e,t){let s=this.getPublicPrefix();return this.http.post(`${s}/payments/cancel`,{payment_id:e,...t})}async getByOrderId(e){let t=this.getPublicPrefix();return this.http.get(`${t}/payments/orders/${e}`)}};var W=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async issueBillingKey(){let e=this.getPublicPrefix();return this.http.post(`${e}/subscriptions/billing-keys`,{})}async confirmBillingKey(e){let t=this.getPublicPrefix();return this.http.post(`${t}/subscriptions/billing-keys/confirm`,e)}async listBillingKeys(e){let t=this.getPublicPrefix(),s=e?`?customer_id=${e}`:"";return this.http.get(`${t}/subscriptions/billing-keys${s}`)}async getBillingKey(e){let t=this.getPublicPrefix();return this.http.get(`${t}/subscriptions/billing-keys/${e}`)}async updateBillingKey(e,t){let s=this.getPublicPrefix();return this.http.patch(`${s}/subscriptions/billing-keys/${e}`,t)}async deleteBillingKey(e){let t=this.getPublicPrefix();return this.http.delete(`${t}/subscriptions/billing-keys/${e}`)}async create(e){let t=this.getPublicPrefix(),s=await this.http.post(`${t}/subscriptions`,e);return f(s,{id:{type:"string-or-number"},status:{type:"string"}},"subscription.create"),s}async list(e){let t=this.getPublicPrefix(),s=new URLSearchParams;e?.status&&s.set("status",e.status),e?.limit&&s.set("limit",String(e.limit)),e?.offset&&s.set("offset",String(e.offset));let i=s.toString();return this.http.get(`${t}/subscriptions${i?"?"+i:""}`)}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(),i=new URLSearchParams;t?.status&&i.set("status",t.status),t?.limit&&i.set("limit",String(t.limit)),t?.offset&&i.set("offset",String(t.offset));let r=i.toString();return this.http.get(`${s}/subscriptions/${e}/payments${r?"?"+r:""}`)}async chargeWithBillingKey(e){let t=this.getPublicPrefix();return this.http.post(`${t}/subscriptions/charge`,e)}};var K=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async registerDevice(e){let t=this.getPublicPrefix(),s=await this.http.post(`${t}/push/devices`,e);return f(s,{device_token:{type:"string"}},"push.registerDevice"),s}async unregisterDevice(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/push/devices/${e}`)}async subscribeTopic(e,t){let s=this.getPublicPrefix(),i={topic_name:t};await this.http.post(`${s}/push/devices/${e}/topics/subscribe`,i)}async unsubscribeTopic(e,t){let s=this.getPublicPrefix();await this.http.delete(`${s}/push/devices/${e}/topics/${t}/unsubscribe`)}async getVAPIDPublicKey(){let e=this.getPublicPrefix();return this.http.get(`${e}/push/vapid-public-key`)}async registerWebPush(e){let t=this.getPublicPrefix(),s;if("toJSON"in e){let r=e.toJSON();s={endpoint:r.endpoint||"",expirationTime:r.expirationTime,keys:{p256dh:r.keys?.p256dh||"",auth:r.keys?.auth||""}}}else s=e;let i={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`,{...i,web_push_subscription:s})}async unregisterWebPush(e){let t=this.getPublicPrefix();await this.http.delete(`${t}/push/devices/${encodeURIComponent(e)}`)}async sendToMembers(e,t,s){if(this.http.hasPublicKey())throw new Error("cb.push.sendToMembers() \uB294 User Secret Key(cb_sk_) \uB610\uB294 \uCF58\uC194 JWT \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. Public Key(cb_pk_) SDK \uC778\uC2A4\uD134\uC2A4\uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.");if(t.length===0)throw new Error("cb.push.sendToMembers(): memberIds \uAC00 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4.");let i={target_type:"members",target_member_ids:t,title:s.title,body:s.body,...s.imageUrl!==void 0?{image_url:s.imageUrl}:{},...s.data!==void 0?{data:s.data}:{},...s.platforms!==void 0?{platforms:s.platforms}:{},...s.ttlSeconds!==void 0?{ttl:s.ttlSeconds}:{},...s.priority!==void 0?{priority:s.priority}:{},...s.clickAction!==void 0?{click_action:s.clickAction}:{},...s.scheduledAt!==void 0?{scheduled_at:s.scheduledAt}:{}};return this.http.post(`/v1/apps/${e}/push/send`,i)}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 pe=5*1024*1024,T=class extends Error{constructor(e,t){super(e),this.name="VideoProcessingError",this.video=t}},V=class{constructor(e,t){this.http=e;this.storage={create:async e=>this.http.post("/v1/public/storages/videos",e),list:async()=>this.http.get("/v1/public/storages/videos"),get:async e=>this.http.get(`/v1/public/storages/videos/${e}`),update:async(e,t)=>this.http.put(`/v1/public/storages/videos/${e}`,t),delete:async e=>{await this.http.delete(`/v1/public/storages/videos/${e}`)},upload:async(e,t,s)=>{let i=await this.http.post(`/v1/public/storages/videos/${e}/uploads/init`,{filename:t.name,size:t.size,mime_type:t.type,title:s.title,description:s.description,visibility:s.visibility||"private",tags:s.tags}),r=i.chunk_size||pe,n=Math.ceil(t.size/r),o=0,c=Date.now(),l=0;for(let p=0;p<n;p++){let h=p*r,m=Math.min(h+r,t.size),y=t.slice(h,m),v=new FormData;v.append("chunk",y),v.append("chunk_index",String(p)),await this.http.post(`/v1/public/storages/videos/${e}/uploads/${i.session_id}/chunk`,v),o++;let b=Date.now(),S=(b-c)/1e3,g=m,w=g-l,_=S>0?w/S:0;c=b,l=g,s.onProgress&&s.onProgress({phase:"uploading",uploadedChunks:o,totalChunks:n,percentage:Math.round(o/n*100),currentSpeed:_})}return(await this.http.post(`/v1/public/storages/videos/${e}/uploads/${i.session_id}/complete`,{})).video},listVideos:async(e,t)=>{let s=new URLSearchParams;t?.status&&s.set("status",t.status),t?.visibility&&s.set("visibility",t.visibility),t?.search&&s.set("search",t.search),t?.page&&s.set("page",String(t.page)),t?.limit&&s.set("limit",String(t.limit));let i=s.toString();return this.http.get(`/v1/public/storages/videos/${e}/videos${i?`?${i}`:""}`)},getVideo:async(e,t)=>this.http.get(`/v1/public/storages/videos/${e}/videos/${t}`),deleteVideo:async(e,t)=>{await this.http.delete(`/v1/public/storages/videos/${e}/videos/${t}`)},getStreamUrl:async(e,t)=>this.http.get(`/v1/public/storages/videos/${e}/videos/${t}/stream`),getTranscodeStatus:async(e,t)=>this.http.get(`/v1/public/storages/videos/${e}/videos/${t}/transcode`)};this.videoBaseUrl=t||this.getDefaultVideoUrl()}getDefaultVideoUrl(){if(typeof window<"u"){let e=window.location.hostname;if(e==="localhost"||e==="127.0.0.1")return"http://localhost:8089"}return"https://video.connectbase.world"}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async videoFetch(e,t,s){let i={},r=this.http.getPublicKey();r&&(i["X-Public-Key"]=r);let n=this.http.getAccessToken();n&&(i.Authorization=`Bearer ${n}`),s&&!(s instanceof FormData)&&(i["Content-Type"]="application/json");let{signal:o,cleanup:c}=R({timeout:3e4});try{let l=await fetch(`${this.videoBaseUrl}${t}`,{method:e,headers:i,body:s instanceof FormData?s:s?JSON.stringify(s):void 0,signal:o});if(!l.ok){let d=await l.json().catch(()=>({message:l.statusText})),p=d.error;if(p&&typeof p=="object"&&"message"in p)throw new u(l.status,p.message||"Unknown error",p.code,p.details);let h=typeof p=="string"?p:d.message||"Unknown error";throw new u(l.status,h)}return l.status===204||l.headers.get("content-length")==="0"?{}:await l.json()}finally{c()}}async upload(e,t){let s=this.getPublicPrefix(),i=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}),r=i.chunk_size||pe,n=Math.ceil(e.size/r),o=0,l=Date.now(),d=0;for(let h=0;h<n;h++){let m=h*r,y=Math.min(m+r,e.size),v=e.slice(m,y),b=new FormData;b.append("chunk",v),b.append("chunk_index",String(h)),await this.videoFetch("POST",`${s}/uploads/${i.session_id}/chunks`,b),o++;let S=Date.now(),g=(S-l)/1e3,w=y,_=w-d,me=g>0?_/g:0;l=S,d=w,t.onProgress&&t.onProgress({phase:"uploading",uploadedChunks:o,totalChunks:n,percentage:Math.round(o/n*100),currentSpeed:me})}return(await this.videoFetch("POST",`${s}/uploads/${i.session_id}/complete`,{})).video}async waitForReady(e,t){let s=t?.timeout||18e5,i=t?.interval||5e3,r=Date.now(),n=this.getPublicPrefix();for(;Date.now()-r<s;){let o=await this.videoFetch("GET",`${n}/videos/${e}`);if(o.status==="ready")return o;if(o.status==="failed")throw new T("Video processing failed",o);if(t?.onProgress){let c=o.qualities.filter(d=>d.status==="ready").length,l=o.qualities.length||1;t.onProgress({phase:"processing",uploadedChunks:0,totalChunks:0,percentage:Math.round(c/l*100)})}await new Promise(c=>setTimeout(c,i))}throw new T("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 i=s.toString();return this.videoFetch("GET",`${t}/videos${i?`?${i}`:""}`)}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(),i=t?`?quality=${t}`:"";return this.videoFetch("GET",`${s}/videos/${e}/stream-url${i}`)}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 i=this.getPublicPrefix();return this.videoFetch("POST",`${i}/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 i=s.toString();return this.videoFetch("GET",`${t}/shorts${i?`?${i}`:""}`)}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(),i=new URLSearchParams;t?.cursor&&i.set("cursor",t.cursor),t?.limit&&i.set("limit",String(t.limit)),t?.sort&&i.set("sort",t.sort);let r=i.toString();return this.videoFetch("GET",`${s}/videos/${e}/comments${r?`?${r}`:""}`)}async postComment(e,t,s){let i=this.getPublicPrefix();return this.videoFetch("POST",`${i}/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 i=s.toString();return this.videoFetch("GET",`${t}/watch-history${i?`?${i}`:""}`)}async clearWatchHistory(){let e=this.getPublicPrefix();await this.videoFetch("DELETE",`${e}/watch-history`)}async reportWatchProgress(e,t,s){let i=this.getPublicPrefix();await this.videoFetch("POST",`${i}/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,i){let r=this.getPublicPrefix();return this.videoFetch("POST",`${r}/videos/${e}/super-chats`,{amount:t,message:s,currency:i||"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(),i=t?`?limit=${t}`:"";return(await this.videoFetch("GET",`${s}/recommendations/related/${e}${i}`)).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 he=()=>{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"},x=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.msgIdCounter=0;this.config={gameServerUrl:he(),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 i=this.buildConnectionUrl(e);this.ws=new WebSocket(i);let r=()=>{this._isConnected=!0,this.reconnectAttempts=0,this.startPingInterval(),this.handlers.onConnect?.(),t()},n=l=>{this._isConnected=!1,this.stopPingInterval(),this.handlers.onDisconnect?.(l),this.config.autoReconnect&&l.code!==1e3&&this.scheduleReconnect(e)},o=l=>{this.handlers.onError?.(l),s(new Error("WebSocket connection failed"))},c=l=>{this.handleMessage(l.data)};this.ws.addEventListener("open",r,{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 i=r=>{if(r.type==="room_created"){let n=r.data;return this._roomId=n.room_id,this._state=n.initial_state,t(n.initial_state),!0}else if(r.type==="error")return s(new Error(r.data.message)),!0;return!1};this.sendWithHandler("create_room",e,i,15e3,s)})}joinRoom(e,t){return new Promise((s,i)=>{let r=n=>{if(n.type==="room_joined"){let o=n.data;return this._roomId=o.room_id,this._state=o.initial_state,s(o.initial_state),!0}else if(n.type==="error")return i(new Error(n.data.message)),!0;return!1};this.sendWithHandler("join_room",{room_id:e,metadata:t},r,15e3,i)})}leaveRoom(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("Not in a room"));return}let s=i=>i.type==="room_left"?(this._roomId=null,this._state=null,e(),!0):i.type==="error"?(t(new Error(i.data.message)),!0):!1;this.sendWithHandler("leave_room",{},s,15e3,t)})}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=i=>{if(i.type==="state"){let r=i.data;return this._state=r,e(r),!0}else if(i.type==="error")return t(new Error(i.data.message)),!0;return!1};this.sendWithHandler("get_state",{},s,15e3,t)})}listRooms(){return new Promise((e,t)=>{let s=i=>{if(i.type==="room_list"){let r=i.data;return e(r.rooms),!0}else if(i.type==="error")return t(new Error(i.data.message)),!0;return!1};this.sendWithHandler("list_rooms",{},s,15e3,t)})}ping(){return new Promise((e,t)=>{let s=Date.now(),i=r=>{if(r.type==="pong"){let n=r.data,o=Date.now()-n.clientTimestamp;return this.handlers.onPong?.(n),e(o),!0}else if(r.type==="error")return t(new Error(r.data.message)),!0;return!1};this.sendWithHandler("ping",{timestamp:s},i,15e3,t)})}buildConnectionUrl(e){let s=this.config.gameServerUrl.replace(/^http/,"ws"),i=new URLSearchParams;i.set("client_id",this.config.clientId),e&&i.set("room_id",e),this.config.publicKey&&i.set("public_key",this.config.publicKey),this.config.accessToken&&i.set("token",this.config.accessToken);let r=this.config.appId||"";return`${s}/v1/game/${r}/ws?${i.toString()}`}send(e,t,s){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)throw new Error("WebSocket is not connected");this.ws.send(JSON.stringify({type:e,data:t,msg_id:s}))}sendWithHandler(e,t,s,i=15e3,r){let n=`${e}-${++this.msgIdCounter}`,o=null,c=()=>{this.ws?.removeEventListener("message",l),o&&(clearTimeout(o),o=null)},l=d=>{try{let p=JSON.parse(d.data);if(p.msg_id&&p.msg_id!==n)return;s(p)&&c()}catch{}};this.ws?.addEventListener("message",l),o=setTimeout(()=>{c();let d=new Error(`Request '${e}' timed out after ${i}ms`);r?.(d),this.handlers.onError?.({code:"TIMEOUT",message:d.message})},i);try{this.send(e,t,n)}catch(d){c();let p=d instanceof Error?d:new Error(String(d));r?.(p)}}handleMessage(e){try{let t=JSON.parse(e);switch(t.type){case"delta":this.handleDelta(t);break;case"state":this._state=t.data,this.handlers.onStateUpdate?.(this._state);break;case"player_event":this.handlePlayerEvent(t);break;case"chat":this.handlers.onChat?.({roomId:t.room_id||"",clientId:t.client_id||"",userId:t.user_id,message:t.message||"",serverTime:t.server_time||0});break;case"error":this.handlers.onError?.({code:t.code||"UNKNOWN",message:t.message||"Unknown error"});break;default:break}}catch{console.error("Failed to parse game message:",e)}}handleDelta(e){let t=e.delta;if(!t)return;let s={fromVersion:t.from_version,toVersion:t.to_version,changes:t.changes.map(i=>({path:i.path,operation:i.operation,value:i.value,oldValue:i.old_value})),tick:t.tick};if(this._state){for(let i of s.changes)this.applyChange(i);this._state.version=s.toVersion}if(this.handlers.onAction){for(let i of s.changes)if(i.path.startsWith("actions.")&&i.operation==="set"&&i.value){let r=i.value;this.handlers.onAction({type:r.type||"",clientId:r.client_id||"",data:r.data,timestamp:r.timestamp||0})}}this.handlers.onDelta?.(s)}applyChange(e){if(!this._state)return;let t=e.path.split("."),s=this._state.state;for(let r=0;r<t.length-1;r++){let n=t[r];n in s||(s[n]={}),s=s[n]}let i=t[t.length-1];e.operation==="delete"?delete s[i]:s[i]=e.value}handlePlayerEvent(e){let t={clientId:e.player?.client_id||"",userId:e.player?.user_id,joinedAt:e.player?.joined_at||0,metadata:e.player?.metadata};e.event==="joined"?this.handlers.onPlayerJoined?.(t):e.event==="left"&&this.handlers.onPlayerLeft?.(t)}scheduleReconnect(e){if(this.reconnectAttempts>=(this.config.maxReconnectAttempts??5)){console.error("Max reconnect attempts reached"),this.handlers.onError?.({code:"MAX_RECONNECT_ATTEMPTS",message:"Maximum reconnection attempts reached"});return}let t=Math.min((this.config.reconnectInterval??1e3)*Math.pow(2,this.reconnectAttempts),3e4);this.reconnectAttempts++;let s=e||this._roomId;this.reconnectTimer=setTimeout(async()=>{console.log(`Reconnecting... (attempt ${this.reconnectAttempts})`);try{if(await this.connect(),s){console.log(`Rejoining room ${s}...`);try{await this.joinRoom(s),console.log(`Successfully rejoined room ${s}`)}catch(i){console.error(`Failed to rejoin room ${s}:`,i),this.handlers.onError?.({code:"REJOIN_FAILED",message:`Failed to rejoin room: ${i}`})}}}catch{}},t)}startPingInterval(){this.pingInterval=setInterval(()=>{this.ping().catch(()=>{})},3e4)}stopPingInterval(){this.pingInterval&&(clearInterval(this.pingInterval),this.pingInterval=null)}},I=class{constructor(e,t,s){this.http=e,this.gameServerUrl=t||he().replace(/^ws/,"http"),this.appId=s}createClient(e){return new x({...e,gameServerUrl:this.gameServerUrl.replace(/^http/,"ws"),appId:this.appId,publicKey:this.http.getPublicKey(),accessToken:this.http.getAccessToken()})}async listRooms(e){let t=e||this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/rooms`,{headers:this.getHeaders()});if(!s.ok)throw new u(s.status,`Failed to list rooms: ${s.statusText}`,"GAME_LIST_ROOMS_FAILED");return(await s.json()).rooms}async getRoom(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/rooms/${e}`,{headers:this.getHeaders()});if(!s.ok)throw new u(s.status,`Failed to get room: ${s.statusText}`,"GAME_GET_ROOM_FAILED");return s.json()}async createRoom(e,t={}){throw new Error("cb.game.createRoom is not yet publicly available \u2014 use the admin console or request a backend public route.")}async deleteRoom(e){throw new Error("cb.game.deleteRoom is not yet publicly available \u2014 use the admin console or request a backend public route.")}async joinQueue(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/matchmaking/queue`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({game_type:e.gameType,player_id:e.playerId,rating:e.rating,region:e.region,mode:e.mode,party_members:e.partyMembers,metadata:e.metadata})});if(!s.ok){let r=await s.json().catch(()=>({}));throw new Error(r.error||`Failed to join queue: ${s.statusText}`)}let i=await s.json();return{ticketId:i.ticket_id,gameType:i.game_type,playerId:i.player_id,status:i.status,createdAt:i.created_at}}async leaveQueue(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/matchmaking/queue`,{method:"DELETE",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({ticket_id:e})});if(!s.ok){let i=await s.json().catch(()=>({}));throw new Error(i.error||`Failed to leave queue: ${s.statusText}`)}}async getMatchStatus(e){let t=this.appId||"",s=new URLSearchParams;e.ticketId&&s.set("ticket_id",e.ticketId),e.playerId&&s.set("player_id",e.playerId);let i=await fetch(`${this.gameServerUrl}/v1/game/${t}/matchmaking/status?${s}`,{headers:this.getHeaders()});if(!i.ok){let n=await i.json().catch(()=>({}));throw new Error(n.error||`Failed to get match status: ${i.statusText}`)}let r=await i.json();return{ticketId:r.ticket_id,gameType:r.game_type,playerId:"",status:r.status,createdAt:"",waitTime:r.wait_time,matchId:r.match_id,roomId:r.room_id}}async listLobbies(){let e=this.appId||"",t=await fetch(`${this.gameServerUrl}/v1/game/${e}/lobbies`,{headers:this.getHeaders()});if(!t.ok)throw new Error(`Failed to list lobbies: ${t.statusText}`);return((await t.json()).lobbies||[]).map(i=>({id:i.id,name:i.name,hostId:i.host_id,gameType:i.game_type,playerCount:i.player_count,maxPlayers:i.max_players,hasPassword:i.has_password,visibility:i.visibility,region:i.region,settings:i.settings,tags:i.tags,status:i.status,createdAt:i.created_at}))}async createLobby(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/lobbies`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:e.playerId,display_name:e.displayName,name:e.name,game_type:e.gameType,password:e.password,max_players:e.maxPlayers,visibility:e.visibility,region:e.region,settings:e.settings,tags:e.tags})});if(!s.ok){let r=await s.json().catch(()=>({}));throw new Error(r.error||`Failed to create lobby: ${s.statusText}`)}let i=await s.json();return{id:i.id,name:i.name,hostId:i.host_id,gameType:i.game_type,playerCount:1,maxPlayers:i.max_players,hasPassword:!!e.password,visibility:i.visibility,region:i.region,settings:i.settings,tags:i.tags,status:i.status,createdAt:i.created_at}}async getLobby(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/lobbies/${e}`,{headers:this.getHeaders()});if(!s.ok)throw new Error(`Failed to get lobby: ${s.statusText}`);let i=await s.json();return{id:i.id,name:i.name,hostId:i.host_id,gameType:i.game_type,playerCount:i.player_count,maxPlayers:i.max_players,hasPassword:i.has_password,visibility:i.visibility,region:i.region,settings:i.settings,tags:i.tags,status:i.status,roomId:i.room_id,members:(i.members||[]).map(r=>({playerId:r.player_id,displayName:r.display_name,role:r.role,team:r.team,ready:r.ready,slot:r.slot,joinedAt:r.joined_at})),createdAt:i.created_at}}async joinLobby(e,t,s,i){let r=this.appId||"",n=await fetch(`${this.gameServerUrl}/v1/game/${r}/lobbies/${e}/join`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t,display_name:s,password:i})});if(!n.ok){let c=await n.json().catch(()=>({}));throw new Error(c.error||`Failed to join lobby: ${n.statusText}`)}return{lobbyId:(await n.json()).lobby_id}}async leaveLobby(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/lobbies/${e}/leave`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t})});if(!i.ok){let r=await i.json().catch(()=>({}));throw new Error(r.error||`Failed to leave lobby: ${i.statusText}`)}}async toggleReady(e,t,s){let i=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${i}/lobbies/${e}/ready`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t,ready:s})});if(!r.ok){let n=await r.json().catch(()=>({}));throw new Error(n.error||`Failed to toggle ready: ${r.statusText}`)}}async startGame(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/lobbies/${e}/start`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({host_id:t})});if(!i.ok){let n=await i.json().catch(()=>({}));throw new Error(n.error||`Failed to start game: ${i.statusText}`)}return{roomId:(await i.json()).room_id}}async kickPlayer(e,t,s){let i=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${i}/lobbies/${e}/kick/${s}`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({host_id:t})});if(!r.ok){let n=await r.json().catch(()=>({}));throw new Error(n.error||`Failed to kick player: ${r.statusText}`)}}async updateLobby(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/lobbies/${e}`,{method:"PATCH",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({host_id:t.hostId,name:t.name,max_players:t.maxPlayers,visibility:t.visibility,password:t.password,settings:t.settings,tags:t.tags})});if(!i.ok){let n=await i.json().catch(()=>({}));throw new Error(n.error||`Failed to update lobby: ${i.statusText}`)}let r=await i.json();return{id:r.id,name:r.name,hostId:"",gameType:"",playerCount:0,maxPlayers:r.max_players,hasPassword:!1,visibility:r.visibility,region:"",settings:r.settings,tags:r.tags,status:"",createdAt:""}}async sendLobbyChat(e,t,s){let i=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${i}/lobbies/${e}/chat`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t,message:s})});if(!r.ok){let n=await r.json().catch(()=>({}));throw new Error(n.error||`Failed to send chat: ${r.statusText}`)}}async invitePlayer(e,t,s){let i=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${i}/lobbies/${e}/invite`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({inviter_id:t,invitee_id:s})});if(!r.ok){let o=await r.json().catch(()=>({}));throw new Error(o.error||`Failed to invite player: ${r.statusText}`)}let n=await r.json();return{inviteId:n.invite_id,lobbyId:n.lobby_id,lobbyName:n.lobby_name,inviterId:n.inviter_id,inviteeId:n.invitee_id,status:"pending",createdAt:"",expiresAt:n.expires_at}}async acceptInvite(e,t,s){let i=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${i}/lobbies/invites/${e}/accept`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t,display_name:s})});if(!r.ok){let o=await r.json().catch(()=>({}));throw new Error(o.error||`Failed to accept invite: ${r.statusText}`)}return{lobbyId:(await r.json()).lobby_id}}async declineInvite(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/lobbies/invites/${e}/decline`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t})});if(!i.ok){let r=await i.json().catch(()=>({}));throw new Error(r.error||`Failed to decline invite: ${i.statusText}`)}}async getPlayerInvites(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/lobbies/player/${e}/invites`,{headers:this.getHeaders()});if(!s.ok)throw new Error(`Failed to get invites: ${s.statusText}`);return((await s.json()).invites||[]).map(r=>({inviteId:r.invite_id,lobbyId:r.lobby_id,lobbyName:r.lobby_name,inviterId:r.inviter_id,inviteeId:"",status:r.status,createdAt:r.created_at,expiresAt:r.expires_at}))}async createParty(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/parties`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:e,max_size:t||4})});if(!i.ok)throw new Error(`Failed to create party: ${i.statusText}`);return i.json()}async joinParty(e,t){throw new Error("cb.game.joinParty is not supported \u2014 use inviteToParty + acceptPartyInvite flow.")}async acceptPartyInvite(e,t,s){let i=this.appId||"",r=new URLSearchParams({player_id:t});s&&r.set("display_name",s);let n=await fetch(`${this.gameServerUrl}/v1/game/${i}/invites/${e}/accept?${r.toString()}`,{method:"POST",headers:this.getHeaders()});if(!n.ok){let o=await n.json().catch(()=>({}));throw new Error(o.error||`Failed to accept party invite: ${n.statusText}`)}return n.json()}async declinePartyInvite(e,t){let s=this.appId||"",i=new URLSearchParams({player_id:t}),r=await fetch(`${this.gameServerUrl}/v1/game/${s}/invites/${e}/decline?${i.toString()}`,{method:"POST",headers:this.getHeaders()});if(!r.ok){let n=await r.json().catch(()=>({}));throw new Error(n.error||`Failed to decline party invite: ${r.statusText}`)}}async leaveParty(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/parties/${e}/leave`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t})});if(!i.ok)throw new Error(`Failed to leave party: ${i.statusText}`)}async kickFromParty(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/parties/${e}/kick/${t}`,{method:"POST",headers:this.getHeaders()});if(!i.ok)throw new Error(`Failed to kick from party: ${i.statusText}`)}async inviteToParty(e,t,s){let i=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${i}/parties/${e}/invite/${s}`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({inviter_id:t})});if(!r.ok)throw new Error(`Failed to invite to party: ${r.statusText}`);return r.json()}async sendPartyChat(e,t,s){let i=this.appId||"",r=await fetch(`${this.gameServerUrl}/v1/game/${i}/parties/${e}/chat`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({sender_id:t,content:s})});if(!r.ok)throw new Error(`Failed to send party chat: ${r.statusText}`)}async joinSpectator(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/rooms/${e}/spectators`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t})});if(!i.ok)throw new Error(`Failed to join spectator: ${i.statusText}`);return i.json()}async leaveSpectator(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/rooms/${e}/spectators/${t}`,{method:"DELETE",headers:this.getHeaders()});if(!i.ok)throw new Error(`Failed to leave spectator: ${i.statusText}`)}async getSpectators(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/rooms/${e}/spectators`,{headers:this.getHeaders()});if(!s.ok)throw new Error(`Failed to get spectators: ${s.statusText}`);return(await s.json()).spectators||[]}async getLeaderboard(e,t=100,s="default"){let i=this.appId||"",r=new URLSearchParams({app_id:i,game_type:e,season:s,limit:String(t)}),n=await fetch(`${this.gameServerUrl}/v1/game/${i}/ranking/leaderboard/top?${r}`,{headers:this.getHeaders()});if(!n.ok)throw new Error(`Failed to get leaderboard: ${n.statusText}`);return(await n.json()).entries||[]}async getPlayerStats(e,t,s="default"){let i=this.appId||"",r=new URLSearchParams({app_id:i,game_type:t,season:s}),n=await fetch(`${this.gameServerUrl}/v1/game/${i}/ranking/players/${e}/stats?${r}`,{headers:this.getHeaders()});if(!n.ok)throw new Error(`Failed to get player stats: ${n.statusText}`);return n.json()}async getPlayerRank(e,t,s="default"){let i=this.appId||"",r=new URLSearchParams({app_id:i,game_type:t,season:s}),n=await fetch(`${this.gameServerUrl}/v1/game/${i}/ranking/players/${e}/rank?${r}`,{headers:this.getHeaders()});if(!n.ok)throw new Error(`Failed to get player rank: ${n.statusText}`);return n.json()}async joinVoiceChannel(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/voice/rooms/${e}/join`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t})});if(!i.ok)throw new Error(`Failed to join voice channel: ${i.statusText}`);return i.json()}async leaveVoiceChannel(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/voice/rooms/${e}/leave`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:t})});if(!i.ok)throw new Error(`Failed to leave voice channel: ${i.statusText}`)}async setMute(e,t){let s=this.appId||"",i=await fetch(`${this.gameServerUrl}/v1/game/${s}/voice/mute`,{method:"POST",headers:{...this.getHeaders(),"Content-Type":"application/json"},body:JSON.stringify({player_id:e,muted:t})});if(!i.ok)throw new Error(`Failed to set mute: ${i.statusText}`)}async listReplays(e){let t=this.appId||"",s=`${this.gameServerUrl}/v1/game/${t}/replays`;e&&(s+=`?room_id=${e}`);let i=await fetch(s,{headers:this.getHeaders()});if(i.status===404)throw new Error("cb.game.listReplays: replay storage is not configured on this server (REPLAY_STORAGE_PATH unset).");if(!i.ok)throw new Error(`Failed to list replays: ${i.statusText}`);return(await i.json()).replays||[]}async getReplay(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/replays/${e}`,{headers:this.getHeaders()});if(s.status===404)throw new Error("cb.game.getReplay: replay not found or replay storage unconfigured.");if(!s.ok)throw new Error(`Failed to get replay: ${s.statusText}`);return s.json()}async downloadReplay(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/replays/${e}/download`,{headers:this.getHeaders()});if(s.status===404)throw new Error("cb.game.downloadReplay: replay not found or replay storage unconfigured.");if(!s.ok)throw new Error(`Failed to download replay: ${s.statusText}`);return s.arrayBuffer()}async getReplayHighlights(e){let t=this.appId||"",s=await fetch(`${this.gameServerUrl}/v1/game/${t}/replays/${e}/highlights`,{headers:this.getHeaders()});if(s.status===404)throw new Error("cb.game.getReplayHighlights: replay not found or replay storage unconfigured.");if(!s.ok)throw new Error(`Failed to get highlights: ${s.statusText}`);return(await s.json()).highlights||[]}getHeaders(){let e={},t=this.http.getPublicKey();t&&(e["X-Public-Key"]=t);let s=this.http.getAccessToken();return s&&(e.Authorization=`Bearer ${s}`),e}};var k=class{constructor(e){this.http=e}getPublicPrefix(){return this.http.hasPublicKey()?"/v1/public":"/v1"}async getConnectionStatus(){let e=this.getPublicPrefix();return this.http.get(`${e}/ads/connection`)}async getReport(e,t){let s=this.getPublicPrefix(),i=new URLSearchParams;e&&i.set("start",e),t&&i.set("end",t);let r=i.toString();return this.http.get(`${s}/ads/reports${r?`?${r}`:""}`)}async getReportSummary(){let e=this.getPublicPrefix();return this.http.get(`${e}/ads/reports/summary`)}async getAdMobReport(e,t){let s=this.getPublicPrefix(),i=new URLSearchParams;e&&i.set("start",e),t&&i.set("end",t);let r=i.toString();return this.http.get(`${s}/ads/admob/reports${r?`?${r}`:""}`)}async getAdMobReportSummary(){let e=this.getPublicPrefix();return this.http.get(`${e}/ads/admob/reports/summary`)}};var C=class{constructor(){this.clipboard={writeText:async e=>{this.getPlatform()==="desktop"&&window.NativeBridge?.clipboard?await window.NativeBridge.clipboard.writeText(e):await navigator.clipboard.writeText(e)},readText:async()=>this.getPlatform()==="desktop"&&window.NativeBridge?.clipboard?window.NativeBridge.clipboard.readText():navigator.clipboard.readText(),writeHTML:async e=>{window.NativeBridge?.clipboard?.writeHTML?await window.NativeBridge.clipboard.writeHTML(e):await navigator.clipboard.writeText(e)},writeImage:async e=>{if(window.NativeBridge?.clipboard?.writeImage)await window.NativeBridge.clipboard.writeImage(e);else throw new Error("Image clipboard not supported on this platform")},readImage:async()=>window.NativeBridge?.clipboard?.readImage?window.NativeBridge.clipboard.readImage():null};this.filesystem={pickFile:async e=>{if(this.getPlatform()==="desktop"&&window.NativeBridge?.filesystem?.showOpenDialog){let s=await window.NativeBridge.filesystem.showOpenDialog({properties:e?.multiple?["openFile","multiSelections"]:["openFile"],filters:e?.filters});return s.canceled||!s.filePaths.length?null:s.filePaths.map(i=>new File([],i.split("/").pop()||"file"))}return new Promise(s=>{let i=document.createElement("input");i.type="file",e?.accept&&(i.accept=e.accept),e?.multiple&&(i.multiple=!0),i.onchange=()=>{s(i.files?Array.from(i.files):null)},i.click()})},saveFile:async(e,t,s)=>{let i=this.getPlatform();if(i==="desktop"&&window.NativeBridge?.filesystem){let c=await window.NativeBridge.filesystem.showSaveDialog?.({defaultPath:t,filters:s?.filters});if(c?.canceled||!c?.filePath)return!1;let l=e instanceof Blob?await e.text():e;return(await window.NativeBridge.filesystem.writeFile(c.filePath,l)).success}if(i==="mobile"&&window.NativeBridge?.filesystem){let c=e instanceof Blob?await e.text():e;return(await window.NativeBridge.filesystem.writeFile(t,c)).success}let r=e instanceof Blob?e:new Blob([e],{type:"text/plain"}),n=URL.createObjectURL(r),o=document.createElement("a");return o.href=n,o.download=t,o.click(),URL.revokeObjectURL(n),!0},readFile:async e=>{if(window.NativeBridge?.filesystem?.readFile){let t=await window.NativeBridge.filesystem.readFile(e);return t.success?t.content??null:null}return null},exists:async e=>window.NativeBridge?.filesystem?.exists?window.NativeBridge.filesystem.exists(e):!1};this.camera={takePicture:async e=>this.getPlatform()==="mobile"&&window.NativeBridge?.camera?window.NativeBridge.camera.takePicture(e):new Promise(s=>{let i=document.createElement("input");i.type="file",i.accept="image/*",i.capture="environment",i.onchange=async()=>{let r=i.files?.[0];if(!r){s(null);return}let n=new FileReader;n.onload=()=>{let o=new Image;o.onload=()=>{s({uri:URL.createObjectURL(r),base64:e?.base64?n.result.split(",")[1]:void 0,width:o.width,height:o.height})},o.src=n.result},n.readAsDataURL(r)},i.click()}),pickImage:async e=>this.getPlatform()==="mobile"&&window.NativeBridge?.camera?window.NativeBridge.camera.pickImage(e):new Promise(s=>{let i=document.createElement("input");i.type="file",i.accept="image/*",e?.multiple&&(i.multiple=!0),i.onchange=async()=>{let r=i.files;if(!r?.length){s(null);return}let n=[];for(let o of Array.from(r)){let c=await new Promise(l=>{let d=new FileReader;d.onload=()=>{let p=new Image;p.onload=()=>{l({uri:URL.createObjectURL(o),base64:e?.base64?d.result.split(",")[1]:void 0,width:p.width,height:p.height})},p.src=d.result},d.readAsDataURL(o)});n.push(c)}s(n)},i.click()})};this.location={getCurrentPosition:async e=>this.getPlatform()==="mobile"&&window.NativeBridge?.location?window.NativeBridge.location.getCurrentPosition(e):new Promise((s,i)=>{navigator.geolocation.getCurrentPosition(r=>{s({latitude:r.coords.latitude,longitude:r.coords.longitude,altitude:r.coords.altitude,accuracy:r.coords.accuracy,timestamp:r.timestamp})},i,{enableHighAccuracy:e?.accuracy==="high",timeout:1e4,maximumAge:0})})};this.notification={show:async e=>this.getPlatform()==="desktop"&&window.NativeBridge?.notification?(await window.NativeBridge.notification.show(e)).success:!("Notification"in window)||Notification.permission!=="granted"&&await Notification.requestPermission()!=="granted"?!1:(new Notification(e.title,{body:e.body,icon:e.icon,silent:e.silent}),!0),requestPermission:async()=>this.getPlatform()==="mobile"&&window.NativeBridge?.push?(await window.NativeBridge.push.requestPermission()).granted:"Notification"in window?await Notification.requestPermission()==="granted":!1};this.shell={openExternal:async e=>this.getPlatform()==="desktop"&&window.NativeBridge?.shell?(await window.NativeBridge.shell.openExternal(e)).success:(window.open(e,"_blank"),!0)};this.window={minimize:async()=>{await window.NativeBridge?.window?.minimize()},maximize:async()=>{await window.NativeBridge?.window?.maximize()},unmaximize:async()=>{await window.NativeBridge?.window?.unmaximize()},close:async()=>{await window.NativeBridge?.window?.close()},isMaximized:async()=>await window.NativeBridge?.window?.isMaximized()??!1,setTitle:async e=>{window.NativeBridge?.window?await window.NativeBridge.window.setTitle(e):document.title=e},setFullScreen:async e=>{window.NativeBridge?.window?await window.NativeBridge.window.setFullScreen(e):document.documentElement.requestFullscreen&&(e?await document.documentElement.requestFullscreen():document.exitFullscreen&&await document.exitFullscreen())}};this.system={getInfo:async()=>window.NativeBridge?.system?window.NativeBridge.system.getInfo():null,getMemory:async()=>window.NativeBridge?.system?window.NativeBridge.system.getMemory():null};this.biometric={isAvailable:async()=>window.NativeBridge?.biometric?window.NativeBridge.biometric.isAvailable():null,authenticate:async e=>window.NativeBridge?.biometric?window.NativeBridge.biometric.authenticate(e):null};this.secureStore={setItem:async(e,t)=>window.NativeBridge?.secureStore?(await window.NativeBridge.secureStore.setItem(e,t)).success:(localStorage.setItem(e,t),!0),getItem:async e=>window.NativeBridge?.secureStore?(await window.NativeBridge.secureStore.getItem(e)).value:localStorage.getItem(e),deleteItem:async e=>window.NativeBridge?.secureStore?(await window.NativeBridge.secureStore.deleteItem(e)).success:(localStorage.removeItem(e),!0)};this.admob={showInterstitial:async()=>window.NativeBridge?.admob?(await window.NativeBridge.admob.showInterstitial()).shown:!1,showRewarded:async()=>window.NativeBridge?.admob?(await window.NativeBridge.admob.showRewarded()).rewarded:!1}}getPlatform(){if(typeof window>"u")return"web";let e=window.NativeBridge;return e?.platform==="electron"?"desktop":e?.platform==="react-native"||e?.platform==="ios"||e?.platform==="android"||e?.camera||window.ReactNativeWebView?"mobile":"web"}hasFeature(e){return typeof window>"u"?!1:!!window.NativeBridge?.[e]}get bridge(){if(!(typeof window>"u"))return window.NativeBridge}};var j=class{constructor(e){this.http=e}async addDocument(e,t){return this.http.post(`/v1/public/knowledge-bases/${e}/documents`,t)}async listDocuments(e){return this.http.get(`/v1/public/knowledge-bases/${e}/documents`)}async deleteDocument(e,t){await this.http.delete(`/v1/public/knowledge-bases/${e}/documents/${t}`)}async search(e,t){return this.http.post(`/v1/public/knowledge-bases/${e}/search`,t)}async searchGet(e,t,s){let i=new URLSearchParams({query:t});return s&&i.append("top_k",String(s)),this.http.get(`/v1/public/knowledge-bases/${e}/search?${i.toString()}`)}};var $=class{constructor(e){this.http=e}async chat(e){return this.http.post("/v1/public/ai/chat",e)}async chatStream(e,t){let s=await this.http.fetchRaw("/v1/public/ai/chat/stream",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!s.ok){let o=await s.json().catch(()=>({error:"Stream request failed"}));t.onError?.(o.error||"Stream request failed");return}let i=s.body?.getReader();if(!i){t.onError?.("ReadableStream not supported");return}let r=new TextDecoder,n="";for(;;){let{done:o,value:c}=await i.read();if(o)break;n+=r.decode(c,{stream:!0});let l=n.split(`
|
|
3
|
-
`);n=l.pop()||"";for(let d of l){if(!d.startsWith("data: "))continue;let p=d.slice(6).trim();if(p==="[DONE]"){t.onDone?.();return}try{let h=JSON.parse(p);if(h.type==="sources"&&h.sources){t.onSources?.(h.sources);continue}if(h.type==="tool_start"||h.type==="tool_end"){t.onToolEvent?.({type:h.type,name:h.name,toolCallId:h.toolCallId,arguments:h.arguments,result:h.result,success:h.success,durationMs:h.durationMs});continue}if(h.type==="heartbeat"||h.type==="searching")continue;if(h.content&&t.onToken?.(h.content),h.done){t.onDone?.();return}}catch{}}}}};var z=class{constructor(e){this.http=e}async publish(e,t){return this.http.post(`/v1/public/queues/${e}/messages`,t)}async publishBatch(e,t){return this.http.post(`/v1/public/queues/${e}/messages/batch`,t)}async consume(e,t){let s=new URLSearchParams;t?.max_messages&&s.set("max_messages",String(t.max_messages)),t?.visibility_timeout&&s.set("visibility_timeout",String(t.visibility_timeout)),t?.auto_ack!==void 0&&s.set("auto_ack",String(t.auto_ack));let i=s.toString(),r=await this.http.get(`/v1/public/queues/${e}/messages${i?`?${i}`:""}`);return f(r,{messages:{type:"array"}},"queue.consume"),r}async ack(e,t,s){let i={message_ids:t,ack_token:s};return this.http.post(`/v1/public/queues/${e}/messages/ack`,i)}async nack(e,t,s){return this.http.post(`/v1/public/queues/${e}/messages/${t}/nack`,s||{})}async getInfo(e){return this.http.get(`/v1/public/queues/${e}`)}};var Re=1800*1e3,se="__cb_session",ue="__cb_visitor_uid",J="__cb_last_activity";function ie(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,a=>{let e=Math.random()*16|0;return(a==="x"?e:e&3|8).toString(16)})}var E=class{constructor(){this._sessionId=null;this._visitorUid=null;this._lastActivity=0;this._isNewSession=!1}get sessionId(){return this.ensureSession(),this._sessionId}get visitorUid(){return this._visitorUid||(this._visitorUid=this.loadOrCreateVisitorUid()),this._visitorUid}get isNewSession(){return this._isNewSession}touch(){this._lastActivity=Date.now(),this._isNewSession=!1;try{typeof sessionStorage<"u"&&sessionStorage.setItem(J,String(this._lastActivity))}catch{}}reset(){this._sessionId=null,this._isNewSession=!1;try{typeof sessionStorage<"u"&&(sessionStorage.removeItem(se),sessionStorage.removeItem(J))}catch{}}ensureSession(){let e=Date.now();if(!this._sessionId)try{if(typeof sessionStorage<"u"){this._sessionId=sessionStorage.getItem(se);let t=sessionStorage.getItem(J);this._lastActivity=t?parseInt(t,10):0}}catch{}if(!this._sessionId||this._lastActivity>0&&e-this._lastActivity>Re){this._sessionId=ie(),this._isNewSession=!0,this._lastActivity=e;try{typeof sessionStorage<"u"&&(sessionStorage.setItem(se,this._sessionId),sessionStorage.setItem(J,String(e)))}catch{}}}loadOrCreateVisitorUid(){try{if(typeof localStorage<"u"){let e=localStorage.getItem(ue);if(e)return e;let t=ie();return localStorage.setItem(ue,t),t}}catch{}return ie()}};function ge(a){if(!a)return"";let e=new URLSearchParams;a.start_date!==void 0&&e.set("start_date",String(a.start_date)),a.end_date!==void 0&&e.set("end_date",String(a.end_date)),a.limit!==void 0&&e.set("limit",String(a.limit));let t=e.toString();return t?`?${t}`:""}function Te(){if(typeof window>"u")return{};try{let a=new URLSearchParams(window.location.search),e={};for(let t of["utm_source","utm_medium","utm_campaign","utm_content","utm_term"]){let s=a.get(t);s&&(e[t]=s)}return e}catch{return{}}}var Q=class{constructor(e){this.storageWebId=null;this.memberId=null;this.eventQueue=[];this.batchTimer=null;this.isInitialized=!1;this.heartbeatTimer=null;this.visibilityHandler=null;this.unloadHeartbeatHandler=null;this.popstateHandler=null;this.beforeUnloadHandler=null;this.origPushState=null;this.origReplaceState=null;this.heatmapClickHandler=null;this.heatmapScrollHandler=null;this.utm={};this.handleHeatmapClick=e=>{let t=e.clientX/window.innerWidth*100,s=e.clientY/window.innerHeight*100;this.recordHeatmapEvent("click",t,s)};this.heatmapQueue=[];this.http=e,this.config={trackPageViews:!0,trackEvents:!0,trackSessions:!0,heatmap:!1,recording:!1,batchSize:10,flushInterval:5e3,respectDoNotTrack:!0,debug:!1},this.consent={analytics:!0,heatmap:!1,recording:!1},this.session=new E}init(e,t){if(this.isInitialized){this.log("Analytics already initialized");return}if(typeof window>"u"){this.log("Analytics only works in browser environment");return}if(t?.respectDoNotTrack!==!1&&this.isDNT()){this.log("Do Not Track enabled, analytics disabled");return}this.storageWebId=e,Object.assign(this.config,t),this.isInitialized=!0,this.utm=Te(),this.config.trackSessions&&(this.trackSessionStart(),this.startHeartbeat()),this.config.trackPageViews&&(this.trackPageView(),this.setupAutoPageView()),this.startBatchTimer(),this.beforeUnloadHandler=()=>this.flushSync(),window.addEventListener("beforeunload",this.beforeUnloadHandler),this.log("Analytics initialized",{storageWebId:e})}destroy(){this.isInitialized&&(this.stopBatchTimer(),this.stopHeartbeat(),this.removeAutoPageView(),this.removeHeatmapListeners(),this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.flush(),this.isInitialized=!1,this.log("Analytics destroyed"))}setConsent(e){Object.assign(this.consent,e),this.log("Consent updated",e),e.analytics===!1&&this.isInitialized&&this.destroy()}getConsent(){return{...this.consent}}trackPageView(e){if(!this.canTrack())return;this.session.touch();let t=this.createBaseEvent("page_view");t.page_path=e||window.location.pathname,t.page_url=window.location.href,t.page_title=document.title,t.referrer=document.referrer||void 0,t.screen_width=window.screen.width,t.screen_height=window.screen.height,Object.assign(t,this.utm),this.enqueue(t)}trackEvent(e,t){if(!this.canTrack()||!this.config.trackEvents)return;this.session.touch();let s=this.createBaseEvent("event");s.event_name=e,s.event_properties=t,s.page_path=window.location.pathname,s.page_url=window.location.href,this.enqueue(s)}identify(e){this.setMemberId(e),this.linkMemberSilent(e)}setMemberId(e){this.memberId=e||null,typeof window<"u"&&(e?typeof window.__cbSetMember=="function"&&window.__cbSetMember(e):typeof window.__cbClearMember=="function"&&window.__cbClearMember()),this.log("Member id set",{memberId:e})}linkMemberSilent(e){if(!this.storageWebId)return;let t=this.http.hasPublicKey()?"/v1/public":"/v1";this.http.post(`${t}/storages/web/${this.storageWebId}/visitors/link-member`,{visitor_uid:this.session.visitorUid,app_member_id:e}).catch(()=>{})}getMemberId(){return this.memberId}enableHeatmap(e){if(!this.canTrack()||!this.consent.heatmap)return;let t=e?.click??!0,s=e?.scroll??!0;if(t&&(this.heatmapClickHandler=this.handleHeatmapClick,document.addEventListener("click",this.heatmapClickHandler)),s){let i=null,r=0;this.heatmapScrollHandler=()=>{let n=Math.round((window.scrollY+window.innerHeight)/document.documentElement.scrollHeight*100);r=Math.max(r,n),i&&clearTimeout(i),i=setTimeout(()=>{this.recordHeatmapEvent("scroll",50,r*(window.innerHeight/100),r)},500)},window.addEventListener("scroll",this.heatmapScrollHandler,{passive:!0})}this.log("Heatmap enabled",e)}enableHeartbeat(){if(!this.canTrack()||this.heartbeatTimer)return;let e=()=>{if(!this.canTrack())return;let t=this.http.hasPublicKey()?"/v1/public":"/v1";this.http.post(`${t}/storages/web/${this.storageWebId}/sessions/heartbeat`,{visitor_uid:this.session.visitorUid,session_id:this.session.sessionId}).catch(()=>{})};this.heartbeatTimer=setInterval(e,3e4),document.addEventListener("visibilitychange",()=>{document.hidden?this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null):this.heartbeatTimer||(this.heartbeatTimer=setInterval(e,3e4),e())}),window.addEventListener("beforeunload",()=>{if(typeof navigator<"u"&&navigator.sendBeacon&&this.storageWebId){let t=this.http.hasPublicKey()?"/v1/public":"/v1",s=`${this.http.getBaseUrl()}${t}/storages/web/${this.storageWebId}/sessions/heartbeat`;navigator.sendBeacon(s,JSON.stringify({visitor_uid:this.session.visitorUid,session_id:this.session.sessionId}))}}),e(),this.log("Heartbeat enabled (30s interval)")}async flush(){await this.flushQueue()}async getPopularPages(e,t){let s=this.requireServerSideStorageId(e,"getPopularPages"),i=ge(t);return this.http.get(`/v1/storages/web/${s}/popular-pages${i}`)}async getNavigationFlow(e,t){let s=this.requireServerSideStorageId(e,"getNavigationFlow"),i=ge(t);return this.http.get(`/v1/storages/web/${s}/navigation/flow${i}`)}async getVisitors(e,t){let s=this.requireServerSideStorageId(e,"getVisitors"),i=new URLSearchParams;t?.limit!==void 0&&i.set("limit",String(t.limit)),t?.offset!==void 0&&i.set("offset",String(t.offset)),t?.sort_by&&i.set("sort_by",t.sort_by);let r=i.toString()?`?${i.toString()}`:"";return this.http.get(`/v1/storages/web/${s}/visitors${r}`)}async getVisitorGroups(e,t){let s=this.requireServerSideStorageId(e,"getVisitorGroups"),i=new URLSearchParams;t?.limit!==void 0&&i.set("limit",String(t.limit)),t?.offset!==void 0&&i.set("offset",String(t.offset)),t?.sort_by&&i.set("sort_by",t.sort_by);let r=i.toString()?`?${i.toString()}`:"";return this.http.get(`/v1/storages/web/${s}/visitor-groups${r}`)}async getVisitorByMember(e,t){let s=this.requireServerSideStorageId(e,"getVisitorByMember");return this.http.get(`/v1/storages/web/${s}/members/${t}/visitor`)}async mergeVisitors(e,t){let s=this.requireServerSideStorageId(e,"mergeVisitors");return this.http.post(`/v1/storages/web/${s}/visitors/merge`,t)}requireServerSideStorageId(e,t){if(this.http.hasPublicKey())throw new Error(`cb.analytics.${t}() \uB294 \uCF58\uC194 JWT \uB610\uB294 User Secret Key(cb_sk_) \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uBE0C\uB77C\uC6B0\uC800 SDK \uC758 Public Key(cb_pk_) \uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.`);let s=e??this.storageWebId;if(!s)throw new Error(`cb.analytics.${t}() \uD638\uCD9C \uC2DC storageWebId \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (init \uB610\uB294 \uC778\uC790\uB85C \uC804\uB2EC).`);return s}getSession(){return this.session}canTrack(){return!(!this.isInitialized||!this.storageWebId||!this.consent.analytics)}isDNT(){return typeof navigator>"u"?!1:navigator.doNotTrack==="1"||navigator.globalPrivacyControl===!0}createBaseEvent(e){return{type:e,timestamp:new Date().toISOString(),session_id:this.session.sessionId,visitor_uid:this.session.visitorUid}}enqueue(e){this.eventQueue.push(e),this.eventQueue.length>=this.config.batchSize&&this.flushQueue()}async flushQueue(){if(this.eventQueue.length===0||!this.storageWebId)return;let e=this.eventQueue.splice(0),t=this.http.hasPublicKey()?"/v1/public":"/v1";try{await this.http.post(`${t}/storages/web/${this.storageWebId}/visitors/batch`,{visitor_uid:this.session.visitorUid,...this.memberId?{app_member_id:this.memberId}:{},events:e.map(s=>({timestamp:s.timestamp,page_path:s.page_path||"",page_url:s.page_url||"",page_title:s.page_title||"",referrer:s.referrer||"",user_agent:typeof navigator<"u"?navigator.userAgent:"",screen_width:s.screen_width||0,screen_height:s.screen_height||0,session_id:s.session_id,session_start:s.type==="session_start",is_page_view:s.type==="page_view",event_name:s.event_name,event_properties:s.event_properties,utm_source:s.utm_source,utm_medium:s.utm_medium,utm_campaign:s.utm_campaign,utm_content:s.utm_content,utm_term:s.utm_term}))}),this.log(`Flushed ${e.length} events`)}catch(s){this.eventQueue.length<this.config.batchSize*3&&this.eventQueue.unshift(...e),this.log("Flush failed",s)}}flushSync(){if(this.eventQueue.length===0||!this.storageWebId||typeof navigator>"u"||!navigator.sendBeacon)return;let e=this.eventQueue.splice(0),t=this.http.hasPublicKey()?"/v1/public":"/v1",i=`${this.http.getBaseUrl()}${t}/storages/web/${this.storageWebId}/visitors/batch`,r=JSON.stringify({visitor_uid:this.session.visitorUid,...this.memberId?{app_member_id:this.memberId}:{},events:e.map(n=>({timestamp:n.timestamp,page_path:n.page_path||"",page_url:n.page_url||"",page_title:n.page_title||"",referrer:n.referrer||"",session_id:n.session_id,session_start:n.type==="session_start",is_page_view:n.type==="page_view",event_name:n.event_name,event_properties:n.event_properties}))});try{navigator.sendBeacon(i,new Blob([r],{type:"application/json"}))}catch{}}trackSessionStart(){let e=this.createBaseEvent("session_start");e.page_path=window.location.pathname,e.page_url=window.location.href,e.referrer=document.referrer||void 0,e.screen_width=window.screen.width,e.screen_height=window.screen.height,Object.assign(e,this.utm),this.enqueue(e)}startBatchTimer(){this.batchTimer=setInterval(()=>this.flushQueue(),this.config.flushInterval)}stopBatchTimer(){this.batchTimer&&(clearInterval(this.batchTimer),this.batchTimer=null)}startHeartbeat(){this.heartbeatTimer=setInterval(()=>{this.canTrack()&&this.sendHeartbeat()},30*1e3),typeof document<"u"&&(this.visibilityHandler=()=>{document.visibilityState==="hidden"?(this.sendHeartbeatBeacon(),this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)):document.visibilityState==="visible"&&(this.heartbeatTimer||(this.sendHeartbeat(),this.heartbeatTimer=setInterval(()=>{this.canTrack()&&this.sendHeartbeat()},30*1e3)))},document.addEventListener("visibilitychange",this.visibilityHandler)),typeof window<"u"&&(this.unloadHeartbeatHandler=()=>this.sendHeartbeatBeacon(),window.addEventListener("beforeunload",this.unloadHeartbeatHandler))}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),typeof document<"u"&&this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),typeof window<"u"&&this.unloadHeartbeatHandler&&(window.removeEventListener("beforeunload",this.unloadHeartbeatHandler),this.unloadHeartbeatHandler=null)}sendHeartbeat(){let e=this.createBaseEvent("heartbeat");if(e.page_path=window.location.pathname,this.enqueue(e),this.storageWebId){let t=this.http.hasPublicKey()?"/v1/public":"/v1";this.http.post(`${t}/storages/web/${this.storageWebId}/sessions/heartbeat`,{visitor_uid:this.session.visitorUid,session_id:this.session.sessionId}).catch(()=>{})}}sendHeartbeatBeacon(){if(!this.storageWebId||typeof navigator>"u"||!navigator.sendBeacon)return;let e=this.http.hasPublicKey()?"/v1/public":"/v1",s=`${this.http.getBaseUrl()}${e}/storages/web/${this.storageWebId}/sessions/heartbeat`,i=JSON.stringify({visitor_uid:this.session.visitorUid,session_id:this.session.sessionId});try{navigator.sendBeacon(s,new Blob([i],{type:"application/json"}))}catch{}}setupAutoPageView(){this.popstateHandler=()=>this.trackPageView(),window.addEventListener("popstate",this.popstateHandler),this.origPushState=history.pushState.bind(history),this.origReplaceState=history.replaceState.bind(history);let e=this;history.pushState=function(...t){e.origPushState(...t),e.trackPageView()},history.replaceState=function(...t){e.origReplaceState(...t),e.trackPageView()}}removeAutoPageView(){this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.origPushState&&(history.pushState=this.origPushState,this.origPushState=null),this.origReplaceState&&(history.replaceState=this.origReplaceState,this.origReplaceState=null)}removeHeatmapListeners(){this.heatmapClickHandler&&(document.removeEventListener("click",this.heatmapClickHandler),this.heatmapClickHandler=null),this.heatmapScrollHandler&&(window.removeEventListener("scroll",this.heatmapScrollHandler),this.heatmapScrollHandler=null)}recordHeatmapEvent(e,t,s,i){if(!this.canTrack()||!this.storageWebId)return;let r=this.http.hasPublicKey()?"/v1/public":"/v1";if(this.heatmapQueue.push({page_path:window.location.pathname,event_type:e,x_percent:Math.round(t*100)/100,y_percent:Math.round(s*100)/100,viewport_width:window.innerWidth,viewport_height:window.innerHeight,scroll_depth_percent:i,session_id:this.session.sessionId}),this.heatmapQueue.length>=50){let n=this.heatmapQueue.splice(0);this.http.post(`${r}/storages/web/${this.storageWebId}/heatmap/batch`,{visitor_uid:this.session.visitorUid,events:n}).catch(()=>{})}}log(...e){this.config.debug&&console.log("[Analytics]",...e)}};var re=class{constructor(e,t,s,i){this.type="webtransport";this.transport=null;this.writer=null;this.config=e,this.onMessage=t,this.onClose=s,this.onError=i}async connect(){let e=this.buildUrl();this.transport=new WebTransport(e),await this.transport.ready,this.config.useUnreliableDatagrams!==!1&&this.readDatagrams();let t=await this.transport.createBidirectionalStream();this.writer=t.writable.getWriter(),this.readStream(t.readable),this.transport.closed.then(()=>{this.onClose()}).catch(s=>{this.onError(s)})}buildUrl(){let t=(this.config.gameServerUrl||"https://game.connectbase.world").replace(/^ws/,"http").replace(/^http:/,"https:"),s=new URLSearchParams;return s.set("client_id",this.config.clientId),this.config.publicKey&&s.set("public_key",this.config.publicKey),this.config.accessToken&&s.set("token",this.config.accessToken),`${t}/v1/game/webtransport?${s.toString()}`}async readDatagrams(){if(!this.transport)return;let e=this.transport.datagrams.readable.getReader();try{for(;;){let{value:t,done:s}=await e.read();if(s)break;this.onMessage(t)}}catch{}}async readStream(e){let t=e.getReader(),s=new Uint8Array(0);try{for(;;){let{value:i,done:r}=await t.read();if(r)break;let n=new Uint8Array(s.length+i.length);for(n.set(s),n.set(i,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 i=new Uint8Array(4);new DataView(i.buffer).setUint32(0,s.length,!0);let r=new Uint8Array(4+s.length);r.set(i),r.set(s,4),this.writer.write(r)}}else{let i=this.config.maxDatagramSize||1200;s.length<=i?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}},X=class{constructor(e,t,s,i){this.type="websocket";this.ws=null;this.config=e,this.onMessage=t,this.onClose=s,this.onError=i}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 i=()=>{e()},r=()=>{this.onClose()},n=c=>{let l=new Error("WebSocket error");this.onError(l),t(l)},o=c=>{c.data instanceof ArrayBuffer?this.onMessage(new Uint8Array(c.data)):typeof c.data=="string"&&this.onMessage(new TextEncoder().encode(c.data))};this.ws.addEventListener("open",i,{once:!0}),this.ws.addEventListener("close",r),this.ws.addEventListener("error",n,{once:!0}),this.ws.addEventListener("message",o)})}buildUrl(){let t=(this.config.gameServerUrl||"wss://game.connectbase.world").replace(/^http/,"ws"),s=new URLSearchParams;s.set("client_id",this.config.clientId),this.config.publicKey&&s.set("public_key",this.config.publicKey),this.config.accessToken&&s.set("token",this.config.accessToken);let i=this.config.appId||"";return`${t}/v1/game/${i}/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 ne(){return typeof WebTransport<"u"}var Y=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")&&ne(),i=o=>{this.handleMessage(this.decoder.decode(o))},r=()=>{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?.({code:"CONNECTION_ERROR",message:o.message})};if(s)try{this.transport=new re(this.config,i,r,n),await this.transport.connect(),this._transportType="webtransport"}catch{console.log("WebTransport failed, falling back to WebSocket"),this.transport=new X(this.config,i,r,n),await this.transport.connect(),this._transportType="websocket"}else this.transport=new X(this.config,i,r,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 i=r=>{if(r.type==="room_created"){let n=r.data;this._roomId=n.room_id,this._state=n.initial_state,t(n.initial_state)}else r.type==="error"&&s(new Error(r.data.message))};this.sendWithHandler("create_room",e,i)})}async joinRoom(e,t){return new Promise((s,i)=>{let r=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"&&i(new Error(n.data.message))};this.sendWithHandler("join_room",{room_id:e,metadata:t},r)})}async leaveRoom(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("Not in a room"));return}let s=i=>{i.type==="room_left"?(this._roomId=null,this._state=null,e()):i.type==="error"&&t(new Error(i.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++}}),i=t||this._transportType!=="webtransport";this.transport?.send(s,i)}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=i=>{if(i.type==="state"){let r=i.data;this._state=r,e(r)}else i.type==="error"&&t(new Error(i.data.message))};this.sendWithHandler("get_state",{},s)})}async ping(){return new Promise((e,t)=>{let s=Date.now(),i=r=>{if(r.type==="pong"){let n=r.data,o=Date.now()-n.clientTimestamp;this._latency=o,this.handlers.onPong?.(n),e(o)}else r.type==="error"&&t(new Error(r.data.message))};this.sendWithHandler("ping",{timestamp:s},i)})}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 i=`msg_${this.messageId++}`;this.pendingHandlers.set(i,s),setTimeout(()=>{this.pendingHandlers.delete(i)},1e4),this.send(e,{...t,_msg_id:i})}handleMessage(e){try{let t=JSON.parse(e);if(t._msg_id&&this.pendingHandlers.has(t._msg_id)){let s=this.pendingHandlers.get(t._msg_id);this.pendingHandlers.delete(t._msg_id),s(t);return}switch(t.type){case"delta":this.handleDelta(t);break;case"state":this._state=t.data,this.handlers.onStateUpdate?.(this._state);break;case"player_event":this.handlePlayerEvent(t);break;case"chat":this.handlers.onChat?.({roomId:t.room_id||"",clientId:t.client_id||"",userId:t.user_id,message:t.message||"",serverTime:t.server_time||0});break;case"error":this.handlers.onError?.({code:t.code||"UNKNOWN",message:t.message||"Unknown error"});break}}catch{console.error("Failed to parse game message:",e)}}handleDelta(e){let t=e.delta;if(!t)return;let s={fromVersion:t.from_version,toVersion:t.to_version,changes:t.changes.map(i=>({path:i.path,operation:i.operation,value:i.value,oldValue:i.old_value})),tick:t.tick};if(this._state){for(let i of s.changes)this.applyChange(i);this._state.version=s.toVersion}if(this.handlers.onAction){for(let i of s.changes)if(i.path.startsWith("actions.")&&i.operation==="set"&&i.value){let r=i.value;this.handlers.onAction({type:r.type||"",clientId:r.client_id||"",data:r.data,timestamp:r.timestamp||0})}}this.handlers.onDelta?.(s)}applyChange(e){if(!this._state)return;let t=e.path.split("."),s=this._state.state;for(let r=0;r<t.length-1;r++){let n=t[r];n in s||(s[n]={}),s=s[n]}let i=t[t.length-1];e.operation==="delete"?delete s[i]:s[i]=e.value}handlePlayerEvent(e){let t={clientId:e.player?.client_id||"",userId:e.player?.user_id,joinedAt:e.player?.joined_at||0,metadata:e.player?.metadata};e.event==="joined"?this.handlers.onPlayerJoined?.(t):e.event==="left"&&this.handlers.onPlayerLeft?.(t)}scheduleReconnect(e){if(this.reconnectAttempts>=(this.config.maxReconnectAttempts??5)){console.error("Max reconnect attempts reached");return}let t=Math.min((this.config.reconnectInterval??1e3)*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",ke="https://socket.connectbase.world",Ce="https://webrtc.connectbase.world",$e="https://video.connectbase.world",xe="https://game.connectbase.world",Z=class{constructor(e={}){let t={baseUrl:e.baseUrl||Ie,publicKey:e.publicKey,secretKey:e.secretKey,persistence:e.persistence,requestTimeoutMs:e.requestTimeoutMs,onError:e.onError,onTokenRefresh:e.onTokenRefresh,onAuthError:e.onAuthError,onTokenExpired:e.onTokenExpired};this.http=new A(t),this.auth=new U(this.http),this.database=new q(this.http),this.storage=new H(this.http),this.publicKey=new O(this.http),this.functions=new D(this.http),this.realtime=new L(this.http,e.socketUrl||ke),this.webrtc=new F(this.http,e.webrtcUrl||Ce,e.appId),this.errorTracker=new B(this.http,e.errorTracker),this.oauth=new N(this.http),this.payment=new G(this.http),this.subscription=new W(this.http),this.push=new K(this.http),this.video=new V(this.http,e.videoUrl||$e),this.game=new I(this.http,e.gameUrl||xe,e.appId),this.ads=new k(this.http),this.native=new C,this.knowledge=new j(this.http),this.ai=new $(this.http),this.queue=new z(this.http),this.analytics=new Q(this.http),this.auth._attachAnalytics(this.analytics)}setTokens(e,t){this.http.setTokens(e,t)}clearTokens(){this.http.clearTokens()}updateConfig(e){this.http.updateConfig(e)}},Ee=Z;return Pe(Me);})();
|
|
3
|
+
`);n=l.pop()||"";for(let d of l){if(!d.startsWith("data: "))continue;let p=d.slice(6).trim();if(p==="[DONE]"){t.onDone?.();return}try{let h=JSON.parse(p);if(h.type==="sources"&&h.sources){t.onSources?.(h.sources);continue}if(h.type==="tool_start"||h.type==="tool_end"){t.onToolEvent?.({type:h.type,name:h.name,toolCallId:h.toolCallId,arguments:h.arguments,result:h.result,success:h.success,durationMs:h.durationMs});continue}if(h.type==="heartbeat"||h.type==="searching")continue;if(h.content&&t.onToken?.(h.content),h.done){t.onDone?.();return}}catch{}}}}};var z=class{constructor(e){this.http=e}async publish(e,t){return this.http.post(`/v1/public/queues/${e}/messages`,t)}async publishBatch(e,t){return this.http.post(`/v1/public/queues/${e}/messages/batch`,t)}async consume(e,t){let s=new URLSearchParams;t?.max_messages&&s.set("max_messages",String(t.max_messages)),t?.visibility_timeout&&s.set("visibility_timeout",String(t.visibility_timeout)),t?.auto_ack!==void 0&&s.set("auto_ack",String(t.auto_ack));let i=s.toString(),r=await this.http.get(`/v1/public/queues/${e}/messages${i?`?${i}`:""}`);return f(r,{messages:{type:"array"}},"queue.consume"),r}async ack(e,t,s){let i={message_ids:t,ack_token:s};return this.http.post(`/v1/public/queues/${e}/messages/ack`,i)}async nack(e,t,s){return this.http.post(`/v1/public/queues/${e}/messages/${t}/nack`,s||{})}async getInfo(e){return this.http.get(`/v1/public/queues/${e}`)}};var Re=1800*1e3,se="__cb_session",ue="__cb_visitor_uid",J="__cb_last_activity";function ie(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,a=>{let e=Math.random()*16|0;return(a==="x"?e:e&3|8).toString(16)})}var E=class{constructor(){this._sessionId=null;this._visitorUid=null;this._lastActivity=0;this._isNewSession=!1}get sessionId(){return this.ensureSession(),this._sessionId}get visitorUid(){return this._visitorUid||(this._visitorUid=this.loadOrCreateVisitorUid()),this._visitorUid}get isNewSession(){return this._isNewSession}touch(){this._lastActivity=Date.now(),this._isNewSession=!1;try{typeof sessionStorage<"u"&&sessionStorage.setItem(J,String(this._lastActivity))}catch{}}reset(){this._sessionId=null,this._isNewSession=!1;try{typeof sessionStorage<"u"&&(sessionStorage.removeItem(se),sessionStorage.removeItem(J))}catch{}}ensureSession(){let e=Date.now();if(!this._sessionId)try{if(typeof sessionStorage<"u"){this._sessionId=sessionStorage.getItem(se);let t=sessionStorage.getItem(J);this._lastActivity=t?parseInt(t,10):0}}catch{}if(!this._sessionId||this._lastActivity>0&&e-this._lastActivity>Re){this._sessionId=ie(),this._isNewSession=!0,this._lastActivity=e;try{typeof sessionStorage<"u"&&(sessionStorage.setItem(se,this._sessionId),sessionStorage.setItem(J,String(e)))}catch{}}}loadOrCreateVisitorUid(){try{if(typeof localStorage<"u"){let e=localStorage.getItem(ue);if(e)return e;let t=ie();return localStorage.setItem(ue,t),t}}catch{}return ie()}};function ge(a){if(!a)return"";let e=new URLSearchParams;a.start_date!==void 0&&e.set("start_date",String(a.start_date)),a.end_date!==void 0&&e.set("end_date",String(a.end_date)),a.limit!==void 0&&e.set("limit",String(a.limit));let t=e.toString();return t?`?${t}`:""}function Te(){if(typeof window>"u")return{};try{let a=new URLSearchParams(window.location.search),e={};for(let t of["utm_source","utm_medium","utm_campaign","utm_content","utm_term"]){let s=a.get(t);s&&(e[t]=s)}return e}catch{return{}}}var Q=class{constructor(e){this.storageWebId=null;this.memberId=null;this.eventQueue=[];this.batchTimer=null;this.isInitialized=!1;this.heartbeatTimer=null;this.visibilityHandler=null;this.unloadHeartbeatHandler=null;this.popstateHandler=null;this.beforeUnloadHandler=null;this.origPushState=null;this.origReplaceState=null;this.heatmapClickHandler=null;this.heatmapScrollHandler=null;this.utm={};this.handleHeatmapClick=e=>{let t=e.clientX/window.innerWidth*100,s=e.clientY/window.innerHeight*100;this.recordHeatmapEvent("click",t,s)};this.heatmapQueue=[];this.http=e,this.config={trackPageViews:!0,trackEvents:!0,trackSessions:!0,heatmap:!1,recording:!1,batchSize:10,flushInterval:5e3,respectDoNotTrack:!0,debug:!1},this.consent={analytics:!0,heatmap:!1,recording:!1},this.session=new E}init(e,t){if(this.isInitialized){this.log("Analytics already initialized");return}if(typeof window>"u"){this.log("Analytics only works in browser environment");return}if(t?.respectDoNotTrack!==!1&&this.isDNT()){this.log("Do Not Track enabled, analytics disabled");return}this.storageWebId=e,Object.assign(this.config,t),this.isInitialized=!0,this.utm=Te(),this.config.trackSessions&&(this.trackSessionStart(),this.startHeartbeat()),this.config.trackPageViews&&(this.trackPageView(),this.setupAutoPageView()),this.startBatchTimer(),this.beforeUnloadHandler=()=>this.flushSync(),window.addEventListener("beforeunload",this.beforeUnloadHandler),this.log("Analytics initialized",{storageWebId:e})}destroy(){this.isInitialized&&(this.stopBatchTimer(),this.stopHeartbeat(),this.removeAutoPageView(),this.removeHeatmapListeners(),this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.flush(),this.isInitialized=!1,this.log("Analytics destroyed"))}setConsent(e){Object.assign(this.consent,e),this.log("Consent updated",e),e.analytics===!1&&this.isInitialized&&this.destroy()}getConsent(){return{...this.consent}}trackPageView(e){if(!this.canTrack())return;this.session.touch();let t=this.createBaseEvent("page_view");t.page_path=e||window.location.pathname,t.page_url=window.location.href,t.page_title=document.title,t.referrer=document.referrer||void 0,t.screen_width=window.screen.width,t.screen_height=window.screen.height,Object.assign(t,this.utm),this.enqueue(t)}trackEvent(e,t){if(!this.canTrack()||!this.config.trackEvents)return;this.session.touch();let s=this.createBaseEvent("event");s.event_name=e,s.event_properties=t,s.page_path=window.location.pathname,s.page_url=window.location.href,this.enqueue(s)}identify(e){this.setMemberId(e),this.linkMemberSilent(e)}setMemberId(e){this.memberId=e||null,typeof window<"u"&&(e?typeof window.__cbSetMember=="function"&&window.__cbSetMember(e):typeof window.__cbClearMember=="function"&&window.__cbClearMember()),this.log("Member id set",{memberId:e})}linkMemberSilent(e){if(!this.storageWebId)return;let t=this.http.hasPublicKey()?"/v1/public":"/v1";this.http.post(`${t}/storages/web/${this.storageWebId}/visitors/link-member`,{visitor_uid:this.session.visitorUid,app_member_id:e}).catch(()=>{})}getMemberId(){return this.memberId}enableHeatmap(e){if(!this.canTrack()||!this.consent.heatmap)return;let t=e?.click??!0,s=e?.scroll??!0;if(t&&(this.heatmapClickHandler=this.handleHeatmapClick,document.addEventListener("click",this.heatmapClickHandler)),s){let i=null,r=0;this.heatmapScrollHandler=()=>{let n=Math.round((window.scrollY+window.innerHeight)/document.documentElement.scrollHeight*100);r=Math.max(r,n),i&&clearTimeout(i),i=setTimeout(()=>{this.recordHeatmapEvent("scroll",50,r*(window.innerHeight/100),r)},500)},window.addEventListener("scroll",this.heatmapScrollHandler,{passive:!0})}this.log("Heatmap enabled",e)}enableHeartbeat(){if(!this.canTrack()||this.heartbeatTimer)return;let e=()=>{if(!this.canTrack())return;let t=this.http.hasPublicKey()?"/v1/public":"/v1";this.http.post(`${t}/storages/web/${this.storageWebId}/sessions/heartbeat`,{visitor_uid:this.session.visitorUid,session_id:this.session.sessionId}).catch(()=>{})};this.heartbeatTimer=setInterval(e,3e4),document.addEventListener("visibilitychange",()=>{document.hidden?this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null):this.heartbeatTimer||(this.heartbeatTimer=setInterval(e,3e4),e())}),window.addEventListener("beforeunload",()=>{if(typeof navigator<"u"&&navigator.sendBeacon&&this.storageWebId){let t=this.http.hasPublicKey()?"/v1/public":"/v1",s=`${this.http.getBaseUrl()}${t}/storages/web/${this.storageWebId}/sessions/heartbeat`;navigator.sendBeacon(s,JSON.stringify({visitor_uid:this.session.visitorUid,session_id:this.session.sessionId}))}}),e(),this.log("Heartbeat enabled (30s interval)")}async flush(){await this.flushQueue()}async getPopularPages(e,t){let s=this.requireServerSideStorageId(e,"getPopularPages"),i=ge(t);return this.http.get(`/v1/storages/web/${s}/popular-pages${i}`)}async getNavigationFlow(e,t){let s=this.requireServerSideStorageId(e,"getNavigationFlow"),i=ge(t);return this.http.get(`/v1/storages/web/${s}/navigation/flow${i}`)}async getVisitors(e,t){let s=this.requireServerSideStorageId(e,"getVisitors"),i=new URLSearchParams;t?.limit!==void 0&&i.set("limit",String(t.limit)),t?.offset!==void 0&&i.set("offset",String(t.offset)),t?.sort_by&&i.set("sort_by",t.sort_by);let r=i.toString()?`?${i.toString()}`:"";return this.http.get(`/v1/storages/web/${s}/visitors${r}`)}async getVisitorGroups(e,t){let s=this.requireServerSideStorageId(e,"getVisitorGroups"),i=new URLSearchParams;t?.limit!==void 0&&i.set("limit",String(t.limit)),t?.offset!==void 0&&i.set("offset",String(t.offset)),t?.sort_by&&i.set("sort_by",t.sort_by);let r=i.toString()?`?${i.toString()}`:"";return this.http.get(`/v1/storages/web/${s}/visitor-groups${r}`)}async getVisitorByMember(e,t){let s=this.requireServerSideStorageId(e,"getVisitorByMember");return this.http.get(`/v1/storages/web/${s}/members/${t}/visitor`)}async mergeVisitors(e,t){let s=this.requireServerSideStorageId(e,"mergeVisitors");return this.http.post(`/v1/storages/web/${s}/visitors/merge`,t)}requireServerSideStorageId(e,t){if(this.http.hasPublicKey())throw new Error(`cb.analytics.${t}() \uB294 \uCF58\uC194 JWT \uB610\uB294 User Secret Key(cb_sk_) \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uBE0C\uB77C\uC6B0\uC800 SDK \uC758 Public Key(cb_pk_) \uB85C\uB294 \uD638\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 Functions \uD658\uACBD\uC5D0\uC11C \uC0AC\uC6A9\uD558\uC138\uC694.`);let s=e??this.storageWebId;if(!s)throw new Error(`cb.analytics.${t}() \uD638\uCD9C \uC2DC storageWebId \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (init \uB610\uB294 \uC778\uC790\uB85C \uC804\uB2EC).`);return s}getSession(){return this.session}canTrack(){return!(!this.isInitialized||!this.storageWebId||!this.consent.analytics)}isDNT(){return typeof navigator>"u"?!1:navigator.doNotTrack==="1"||navigator.globalPrivacyControl===!0}createBaseEvent(e){return{type:e,timestamp:new Date().toISOString(),session_id:this.session.sessionId,visitor_uid:this.session.visitorUid}}enqueue(e){this.eventQueue.push(e),this.eventQueue.length>=this.config.batchSize&&this.flushQueue()}async flushQueue(){if(this.eventQueue.length===0||!this.storageWebId)return;let e=this.eventQueue.splice(0),t=this.http.hasPublicKey()?"/v1/public":"/v1";try{await this.http.post(`${t}/storages/web/${this.storageWebId}/visitors/batch`,{visitor_uid:this.session.visitorUid,...this.memberId?{app_member_id:this.memberId}:{},events:e.map(s=>({timestamp:s.timestamp,page_path:s.page_path||"",page_url:s.page_url||"",page_title:s.page_title||"",referrer:s.referrer||"",user_agent:typeof navigator<"u"?navigator.userAgent:"",screen_width:s.screen_width||0,screen_height:s.screen_height||0,session_id:s.session_id,session_start:s.type==="session_start",is_page_view:s.type==="page_view",event_name:s.event_name,event_properties:s.event_properties,utm_source:s.utm_source,utm_medium:s.utm_medium,utm_campaign:s.utm_campaign,utm_content:s.utm_content,utm_term:s.utm_term}))}),this.log(`Flushed ${e.length} events`)}catch(s){this.eventQueue.length<this.config.batchSize*3&&this.eventQueue.unshift(...e),this.log("Flush failed",s)}}flushSync(){if(this.eventQueue.length===0||!this.storageWebId||typeof navigator>"u"||!navigator.sendBeacon)return;let e=this.eventQueue.splice(0),t=this.http.hasPublicKey()?"/v1/public":"/v1",i=`${this.http.getBaseUrl()}${t}/storages/web/${this.storageWebId}/visitors/batch`,r=JSON.stringify({visitor_uid:this.session.visitorUid,...this.memberId?{app_member_id:this.memberId}:{},events:e.map(n=>({timestamp:n.timestamp,page_path:n.page_path||"",page_url:n.page_url||"",page_title:n.page_title||"",referrer:n.referrer||"",session_id:n.session_id,session_start:n.type==="session_start",is_page_view:n.type==="page_view",event_name:n.event_name,event_properties:n.event_properties}))});try{navigator.sendBeacon(i,new Blob([r],{type:"application/json"}))}catch{}}trackSessionStart(){let e=this.createBaseEvent("session_start");e.page_path=window.location.pathname,e.page_url=window.location.href,e.referrer=document.referrer||void 0,e.screen_width=window.screen.width,e.screen_height=window.screen.height,Object.assign(e,this.utm),this.enqueue(e)}startBatchTimer(){this.batchTimer=setInterval(()=>this.flushQueue(),this.config.flushInterval)}stopBatchTimer(){this.batchTimer&&(clearInterval(this.batchTimer),this.batchTimer=null)}startHeartbeat(){this.heartbeatTimer=setInterval(()=>{this.canTrack()&&this.sendHeartbeat()},30*1e3),typeof document<"u"&&(this.visibilityHandler=()=>{document.visibilityState==="hidden"?(this.sendHeartbeatBeacon(),this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)):document.visibilityState==="visible"&&(this.heartbeatTimer||(this.sendHeartbeat(),this.heartbeatTimer=setInterval(()=>{this.canTrack()&&this.sendHeartbeat()},30*1e3)))},document.addEventListener("visibilitychange",this.visibilityHandler)),typeof window<"u"&&(this.unloadHeartbeatHandler=()=>this.sendHeartbeatBeacon(),window.addEventListener("beforeunload",this.unloadHeartbeatHandler))}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),typeof document<"u"&&this.visibilityHandler&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),typeof window<"u"&&this.unloadHeartbeatHandler&&(window.removeEventListener("beforeunload",this.unloadHeartbeatHandler),this.unloadHeartbeatHandler=null)}sendHeartbeat(){let e=this.createBaseEvent("heartbeat");if(e.page_path=window.location.pathname,this.enqueue(e),this.storageWebId){let t=this.http.hasPublicKey()?"/v1/public":"/v1";this.http.post(`${t}/storages/web/${this.storageWebId}/sessions/heartbeat`,{visitor_uid:this.session.visitorUid,session_id:this.session.sessionId}).catch(()=>{})}}sendHeartbeatBeacon(){if(!this.storageWebId||typeof navigator>"u"||!navigator.sendBeacon)return;let e=this.http.hasPublicKey()?"/v1/public":"/v1",s=`${this.http.getBaseUrl()}${e}/storages/web/${this.storageWebId}/sessions/heartbeat`,i=JSON.stringify({visitor_uid:this.session.visitorUid,session_id:this.session.sessionId});try{navigator.sendBeacon(s,new Blob([i],{type:"application/json"}))}catch{}}setupAutoPageView(){this.popstateHandler=()=>this.trackPageView(),window.addEventListener("popstate",this.popstateHandler),this.origPushState=history.pushState.bind(history),this.origReplaceState=history.replaceState.bind(history);let e=this;history.pushState=function(...t){e.origPushState(...t),e.trackPageView()},history.replaceState=function(...t){e.origReplaceState(...t),e.trackPageView()}}removeAutoPageView(){this.popstateHandler&&(window.removeEventListener("popstate",this.popstateHandler),this.popstateHandler=null),this.origPushState&&(history.pushState=this.origPushState,this.origPushState=null),this.origReplaceState&&(history.replaceState=this.origReplaceState,this.origReplaceState=null)}removeHeatmapListeners(){this.heatmapClickHandler&&(document.removeEventListener("click",this.heatmapClickHandler),this.heatmapClickHandler=null),this.heatmapScrollHandler&&(window.removeEventListener("scroll",this.heatmapScrollHandler),this.heatmapScrollHandler=null)}recordHeatmapEvent(e,t,s,i){if(!this.canTrack()||!this.storageWebId)return;let r=this.http.hasPublicKey()?"/v1/public":"/v1";if(this.heatmapQueue.push({page_path:window.location.pathname,event_type:e,x_percent:Math.round(t*100)/100,y_percent:Math.round(s*100)/100,viewport_width:window.innerWidth,viewport_height:window.innerHeight,scroll_depth_percent:i,session_id:this.session.sessionId}),this.heatmapQueue.length>=50){let n=this.heatmapQueue.splice(0);this.http.post(`${r}/storages/web/${this.storageWebId}/heatmap/batch`,{visitor_uid:this.session.visitorUid,events:n}).catch(()=>{})}}log(...e){this.config.debug&&console.log("[Analytics]",...e)}};var re=class{constructor(e,t,s,i){this.type="webtransport";this.transport=null;this.writer=null;this.config=e,this.onMessage=t,this.onClose=s,this.onError=i}async connect(){let e=this.buildUrl();this.transport=new WebTransport(e),await this.transport.ready,this.config.useUnreliableDatagrams!==!1&&this.readDatagrams();let t=await this.transport.createBidirectionalStream();this.writer=t.writable.getWriter(),this.readStream(t.readable),this.transport.closed.then(()=>{this.onClose()}).catch(s=>{this.onError(s)})}buildUrl(){let t=(this.config.gameServerUrl||"https://game.connectbase.world").replace(/^ws/,"http").replace(/^http:/,"https:"),s=new URLSearchParams;return s.set("client_id",this.config.clientId),this.config.publicKey&&s.set("public_key",this.config.publicKey),this.config.accessToken&&s.set("token",this.config.accessToken),`${t}/v1/game/webtransport?${s.toString()}`}async readDatagrams(){if(!this.transport)return;let e=this.transport.datagrams.readable.getReader();try{for(;;){let{value:t,done:s}=await e.read();if(s)break;this.onMessage(t)}}catch{}}async readStream(e){let t=e.getReader(),s=new Uint8Array(0);try{for(;;){let{value:i,done:r}=await t.read();if(r)break;let n=new Uint8Array(s.length+i.length);for(n.set(s),n.set(i,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 i=new Uint8Array(4);new DataView(i.buffer).setUint32(0,s.length,!0);let r=new Uint8Array(4+s.length);r.set(i),r.set(s,4),this.writer.write(r)}}else{let i=this.config.maxDatagramSize||1200;s.length<=i?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}},X=class{constructor(e,t,s,i){this.type="websocket";this.ws=null;this.config=e,this.onMessage=t,this.onClose=s,this.onError=i}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 i=()=>{e()},r=()=>{this.onClose()},n=c=>{let l=new Error("WebSocket error");this.onError(l),t(l)},o=c=>{c.data instanceof ArrayBuffer?this.onMessage(new Uint8Array(c.data)):typeof c.data=="string"&&this.onMessage(new TextEncoder().encode(c.data))};this.ws.addEventListener("open",i,{once:!0}),this.ws.addEventListener("close",r),this.ws.addEventListener("error",n,{once:!0}),this.ws.addEventListener("message",o)})}buildUrl(){let t=(this.config.gameServerUrl||"wss://game.connectbase.world").replace(/^http/,"ws"),s=new URLSearchParams;s.set("client_id",this.config.clientId),this.config.publicKey&&s.set("public_key",this.config.publicKey),this.config.accessToken&&s.set("token",this.config.accessToken);let i=this.config.appId||"";return`${t}/v1/game/${i}/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 ne(){return typeof WebTransport<"u"}var Y=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")&&ne(),i=o=>{this.handleMessage(this.decoder.decode(o))},r=()=>{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?.({code:"CONNECTION_ERROR",message:o.message})};if(s)try{this.transport=new re(this.config,i,r,n),await this.transport.connect(),this._transportType="webtransport"}catch{console.log("WebTransport failed, falling back to WebSocket"),this.transport=new X(this.config,i,r,n),await this.transport.connect(),this._transportType="websocket"}else this.transport=new X(this.config,i,r,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 i=r=>{if(r.type==="room_created"){let n=r.data;this._roomId=n.room_id,this._state=n.initial_state,t(n.initial_state)}else r.type==="error"&&s(new Error(r.data.message))};this.sendWithHandler("create_room",e,i)})}async joinRoom(e,t){return new Promise((s,i)=>{let r=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"&&i(new Error(n.data.message))};this.sendWithHandler("join_room",{room_id:e,metadata:t},r)})}async leaveRoom(){return new Promise((e,t)=>{if(!this._roomId){t(new Error("Not in a room"));return}let s=i=>{i.type==="room_left"?(this._roomId=null,this._state=null,e()):i.type==="error"&&t(new Error(i.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++}}),i=t||this._transportType!=="webtransport";this.transport?.send(s,i)}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=i=>{if(i.type==="state"){let r=i.data;this._state=r,e(r)}else i.type==="error"&&t(new Error(i.data.message))};this.sendWithHandler("get_state",{},s)})}async ping(){return new Promise((e,t)=>{let s=Date.now(),i=r=>{if(r.type==="pong"){let n=r.data,o=Date.now()-n.clientTimestamp;this._latency=o,this.handlers.onPong?.(n),e(o)}else r.type==="error"&&t(new Error(r.data.message))};this.sendWithHandler("ping",{timestamp:s},i)})}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 i=`msg_${this.messageId++}`;this.pendingHandlers.set(i,s),setTimeout(()=>{this.pendingHandlers.delete(i)},1e4),this.send(e,{...t,_msg_id:i})}handleMessage(e){try{let t=JSON.parse(e);if(t._msg_id&&this.pendingHandlers.has(t._msg_id)){let s=this.pendingHandlers.get(t._msg_id);this.pendingHandlers.delete(t._msg_id),s(t);return}switch(t.type){case"delta":this.handleDelta(t);break;case"state":this._state=t.data,this.handlers.onStateUpdate?.(this._state);break;case"player_event":this.handlePlayerEvent(t);break;case"chat":this.handlers.onChat?.({roomId:t.room_id||"",clientId:t.client_id||"",userId:t.user_id,message:t.message||"",serverTime:t.server_time||0});break;case"error":this.handlers.onError?.({code:t.code||"UNKNOWN",message:t.message||"Unknown error"});break}}catch{console.error("Failed to parse game message:",e)}}handleDelta(e){let t=e.delta;if(!t)return;let s={fromVersion:t.from_version,toVersion:t.to_version,changes:t.changes.map(i=>({path:i.path,operation:i.operation,value:i.value,oldValue:i.old_value})),tick:t.tick};if(this._state){for(let i of s.changes)this.applyChange(i);this._state.version=s.toVersion}if(this.handlers.onAction){for(let i of s.changes)if(i.path.startsWith("actions.")&&i.operation==="set"&&i.value){let r=i.value;this.handlers.onAction({type:r.type||"",clientId:r.client_id||"",data:r.data,timestamp:r.timestamp||0})}}this.handlers.onDelta?.(s)}applyChange(e){if(!this._state)return;let t=e.path.split("."),s=this._state.state;for(let r=0;r<t.length-1;r++){let n=t[r];n in s||(s[n]={}),s=s[n]}let i=t[t.length-1];e.operation==="delete"?delete s[i]:s[i]=e.value}handlePlayerEvent(e){let t={clientId:e.player?.client_id||"",userId:e.player?.user_id,joinedAt:e.player?.joined_at||0,metadata:e.player?.metadata};e.event==="joined"?this.handlers.onPlayerJoined?.(t):e.event==="left"&&this.handlers.onPlayerLeft?.(t)}scheduleReconnect(e){if(this.reconnectAttempts>=(this.config.maxReconnectAttempts??5)){console.error("Max reconnect attempts reached");return}let t=Math.min((this.config.reconnectInterval??1e3)*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",ke="https://socket.connectbase.world",Ce="https://webrtc.connectbase.world",$e="https://video.connectbase.world",xe="https://game.connectbase.world",Z=class{constructor(e={}){let t={baseUrl:e.baseUrl||Ie,publicKey:e.publicKey,secretKey:e.secretKey,persistence:e.persistence,requestTimeoutMs:e.requestTimeoutMs,onError:e.onError,onTokenRefresh:e.onTokenRefresh,onAuthError:e.onAuthError,onTokenExpired:e.onTokenExpired};this.http=new A(t),this.auth=new U(this.http),this.database=new q(this.http),this.storage=new H(this.http),this.publicKey=new O(this.http),this.functions=new L(this.http),this.realtime=new D(this.http,e.socketUrl||ke),this.webrtc=new F(this.http,e.webrtcUrl||Ce,e.appId),this.errorTracker=new B(this.http,e.errorTracker),this.oauth=new N(this.http),this.payment=new G(this.http),this.subscription=new W(this.http),this.push=new K(this.http),this.video=new V(this.http,e.videoUrl||$e),this.game=new I(this.http,e.gameUrl||xe,e.appId),this.ads=new k(this.http),this.native=new C,this.knowledge=new j(this.http),this.ai=new $(this.http),this.queue=new z(this.http),this.analytics=new Q(this.http),this.auth._attachAnalytics(this.analytics)}setTokens(e,t){this.http.setTokens(e,t)}clearTokens(){this.http.clearTokens()}updateConfig(e){this.http.updateConfig(e)}},Ee=Z;return Pe(Me);})();
|
|
4
4
|
var ConnectBase = ConnectBaseModule.default || ConnectBaseModule.ConnectBase;
|
package/dist/index.d.mts
CHANGED
|
@@ -1360,14 +1360,6 @@ interface DatabaseRealtimeConnectOptions {
|
|
|
1360
1360
|
/** 디버그 로깅 (기본: false) */
|
|
1361
1361
|
debug?: boolean;
|
|
1362
1362
|
}
|
|
1363
|
-
/** 데이터베이스 실시간 프레즌스 상태 */
|
|
1364
|
-
interface DatabasePresenceState {
|
|
1365
|
-
user_id: string;
|
|
1366
|
-
status: 'online' | 'away' | 'offline';
|
|
1367
|
-
last_seen: string;
|
|
1368
|
-
device?: string;
|
|
1369
|
-
metadata?: Record<string, unknown>;
|
|
1370
|
-
}
|
|
1371
1363
|
interface CreateBackupRequest {
|
|
1372
1364
|
/** 백업 이름 */
|
|
1373
1365
|
name: string;
|
|
@@ -1898,14 +1890,6 @@ declare class DatabaseAPI {
|
|
|
1898
1890
|
* @returns 구독 해제 가능한 객체
|
|
1899
1891
|
*/
|
|
1900
1892
|
subscribe(tableId: string, handlers: DatabaseRealtimeHandlers, options?: DatabaseSubscribeOptions): DatabaseRealtimeSubscription;
|
|
1901
|
-
/**
|
|
1902
|
-
* 프레즌스 상태 설정
|
|
1903
|
-
*/
|
|
1904
|
-
setPresence(status: string, device?: string, metadata?: Record<string, unknown>): void;
|
|
1905
|
-
/**
|
|
1906
|
-
* 프레즌스 구독 (다른 사용자의 온라인 상태 감시)
|
|
1907
|
-
*/
|
|
1908
|
-
subscribePresence(userIds: string[], onPresence: (states: Record<string, DatabasePresenceState>) => void): void;
|
|
1909
1893
|
/**
|
|
1910
1894
|
* 실시간 연결 상태 확인
|
|
1911
1895
|
*/
|
|
@@ -7165,4 +7149,4 @@ declare class ConnectBase {
|
|
|
7165
7149
|
updateConfig(config: Partial<ConnectBaseConfig>): void;
|
|
7166
7150
|
}
|
|
7167
7151
|
|
|
7168
|
-
export { AIAPI, type AIChatRequest, type AIChatResponse, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchSetPageMetaRequest, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type
|
|
7152
|
+
export { AIAPI, type AIChatRequest, type AIChatResponse, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchSetPageMetaRequest, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type DatabaseRealtimeConnectOptions, type DatabaseRealtimeFilter, type DatabaseRealtimeHandlers, type DatabaseRealtimeSubscription, type DatabaseSnapshot, type DatabaseSnapshotMessage, type DatabaseSubscribeOptions, type DeleteWhereResponse, type DeviceInfo, type DocumentResponse, type EnabledProviderInfo, type EnabledProvidersResponse, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type ExportDataRequest, type ExportDataResponse, type FetchDataResponse, type FetchFilesResponse, type FetchPublicKeysResponse, type FileItem, type FileStats, GameAPI, type GameAction, type GameClientConfig, type GameConnectionState, type GameConnectionStatus, type GameDelta, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GenerateUploadURLByPathRequest, type GenerateUploadURLRequest, type GenerateUploadURLResponse, type GeoIndex, type GeoNear, type GeoPoint, type GeoPolygon, type GeoQuery, type GeoResponse, type GeoResult, type GeoWithin, type GetAuthorizationURLResponse, type GetFileByPathResponse, type GoogleConnectionStatus, type GuestMemberSignInResponse, type HistoryResponse, type ICEServer, type ICEServersResponse, type ImageResult, type ImportDataRequest, type ImportDataResponse, type IndexAnalysis, type IndexRecommendation, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinQueueRequest, type JoinRoomRequest, type JoinRoomResponse, type KnowledgeSearchRequest, type KnowledgeSearchResponse, type KnowledgeSearchResult, type LeaderboardEntry, type LifecyclePolicy, type ListBillingKeysResponse, type ListDocumentsResponse, type ListPageMetasOptions, type ListPageMetasResponse, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type LobbyInfo, type LobbyInvite, type LobbyMember, type LobbyVisibility, type MatchResult, type MatchmakingTicket, type MemberInfoResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MemoryInfo, type MessageHandler, type MigrateDataRequest, type MigrateDataResponse, type MoveFileRequest, type NackMessageRequest, NativeAPI, type OAuthCallbackResponse, type OAuthProvider, type OpenDialogOptions, type OpenDialogResult, type PageMetaResponse, type PartyInfo, type PartyInvite, type PartyMember, type PauseSubscriptionRequest, type PaymentDetail, type PaymentProvider, type PaymentStatus, type PeerInfo, type Platform, type PlayerEvent, type PlayerStats, type Playlist, type PlaylistItem, type PongMessage, type PopulateOption, type Position, type PreparePaymentRequest, type PreparePaymentResponse, type PresenceChangeHandler, type PresenceInfo, type PresenceSetOptions, type PresenceStatus, type PresenceStatusResult, type PublicKeyItem, type PublishBatchRequest, type PublishBatchResponse, type PublishMessageRequest, type PublishMessageResponse, type PushPlatform, type QualityProgress, type QueryOptions, type QueueInfoResponse, type QueueMessage, type ReadReceiptHandler, type ReadReceiptInfo, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RelationType, type RenameFileRequest, type RenameFileResponse, type ReplayHighlight, type ReplayInfo, type ReplayPlayerInfo, type RestoreBackupRequest, type RestoreBackupResponse, type RetentionPolicy, type RoomInfo, type RoomStats, type RoomsResponse, type SaveDialogOptions, type SaveDialogResult, type SearchIndex, type SearchOptions, type SearchResponse, type SearchResult, type SecurityRule, type SendOptions, type ServerMessage, SessionManager, type SetPageMetaRequest, type Shorts, type ShortsListResponse, type SignalingMessage, type SignalingMessageType, type SlowQueryInfo, type SpectatorInfo, type SpectatorPlayerState, type SpectatorState, type StateChange, type StateChangeHandler, type StorageUploadOptions, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamMessage, type StreamOptions, type StreamSession, type StreamTokenCallback, type StreamToolCallCallback, type StreamToolResultCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type SystemInfo, type TTLConfig, type TableAccessLevel, type TableColumnDef, type TableIndex, type TableRelation, type TableSchema, type TableSchemaDefinition, type TokenPersistence, type TransactionRead, type TransactionWrite, type TranscodeStatus, type TransportType, type Trigger, type TriggerEvent, type TriggerHandlerType, type TypingChangeHandler, type TypingInfo, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateCustomDataRequest, type UpdateCustomDataResponse, type UpdateDataRequest, type UpdateLobbyRequest, type UpdatePublicKeyRequest, type UpdatePublicKeyResponse, type UpdateSecurityRuleRequest, type UpdateSubscriptionRequest, type UpdateTriggerRequest, type UpdateVideoRequest, type UpdateVideoStorageRequest, type UploadByPathOptions, type UploadFileResponse, type UploadOptions, type UploadProgress, type VAPIDPublicKeyResponse, type ValidateResponse, type ValidationSchema, type ValidationSchemaField, type ValidationStateTransitions, type Video, type VideoComment, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoStorage, type VideoStorageListResponse, type VideoVisibility, type VoiceChannel, type VoiceMember, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, isWebTransportSupported };
|
package/dist/index.d.ts
CHANGED
|
@@ -1360,14 +1360,6 @@ interface DatabaseRealtimeConnectOptions {
|
|
|
1360
1360
|
/** 디버그 로깅 (기본: false) */
|
|
1361
1361
|
debug?: boolean;
|
|
1362
1362
|
}
|
|
1363
|
-
/** 데이터베이스 실시간 프레즌스 상태 */
|
|
1364
|
-
interface DatabasePresenceState {
|
|
1365
|
-
user_id: string;
|
|
1366
|
-
status: 'online' | 'away' | 'offline';
|
|
1367
|
-
last_seen: string;
|
|
1368
|
-
device?: string;
|
|
1369
|
-
metadata?: Record<string, unknown>;
|
|
1370
|
-
}
|
|
1371
1363
|
interface CreateBackupRequest {
|
|
1372
1364
|
/** 백업 이름 */
|
|
1373
1365
|
name: string;
|
|
@@ -1898,14 +1890,6 @@ declare class DatabaseAPI {
|
|
|
1898
1890
|
* @returns 구독 해제 가능한 객체
|
|
1899
1891
|
*/
|
|
1900
1892
|
subscribe(tableId: string, handlers: DatabaseRealtimeHandlers, options?: DatabaseSubscribeOptions): DatabaseRealtimeSubscription;
|
|
1901
|
-
/**
|
|
1902
|
-
* 프레즌스 상태 설정
|
|
1903
|
-
*/
|
|
1904
|
-
setPresence(status: string, device?: string, metadata?: Record<string, unknown>): void;
|
|
1905
|
-
/**
|
|
1906
|
-
* 프레즌스 구독 (다른 사용자의 온라인 상태 감시)
|
|
1907
|
-
*/
|
|
1908
|
-
subscribePresence(userIds: string[], onPresence: (states: Record<string, DatabasePresenceState>) => void): void;
|
|
1909
1893
|
/**
|
|
1910
1894
|
* 실시간 연결 상태 확인
|
|
1911
1895
|
*/
|
|
@@ -7165,4 +7149,4 @@ declare class ConnectBase {
|
|
|
7165
7149
|
updateConfig(config: Partial<ConnectBaseConfig>): void;
|
|
7166
7150
|
}
|
|
7167
7151
|
|
|
7168
|
-
export { AIAPI, type AIChatRequest, type AIChatResponse, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchSetPageMetaRequest, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type
|
|
7152
|
+
export { AIAPI, type AIChatRequest, type AIChatResponse, type AIMessage, type AISource, type AIStreamChunk, type AITool, type AIToolCall, type AIToolEvent, type AckMessagesRequest, type AdMobDailyReport, type AdMobReportResponse, type AdMobReportSummary, type AdReportResponse, type AdReportSummary, type AdmobConnectionInfo, AdsAPI, type AdsenseConnectionInfo, type AggregateResult, type AggregateStage, type AnalyticsConfig, type AnalyticsEvent, ApiError, type ApiErrorDetail, type AppStatsResponse, type ArchivePolicy, type AtomicOperator, type AtomicOperatorType, AuthError, type AuthSettingsResponse, type BackupInfo, type BatchOperation, type BatchSetPageMetaRequest, type BillingCycle, type BillingKeyResponse, type BiometricInfo, type BiometricResult, type BulkCreateResponse, type BulkError, type CPUInfo, type CancelPaymentRequest, type CancelPaymentResponse, type CancelSubscriptionRequest, type CategoryInfo, type Channel, type ChannelMembership, type ChargeWithBillingKeyRequest, type ChargeWithBillingKeyResponse, type ChatMessage, type ClientMessage, type ColumnSchema, type CommentListResponse, type CompleteUploadRequest, type CompleteUploadResponse, type ConfirmBillingKeyRequest, type ConfirmPaymentRequest, type ConfirmPaymentResponse, ConnectBase, type ConnectBaseConfig, type ConnectedData, type ConnectionState, type ConsentOptions, type ConsumeMessagesResponse, type ConsumeOptions, type CopyTableRequest, type CopyTableResponse, type CreateBackupRequest, type CreateChannelRequest, type CreateCheckoutSessionRequest, type CreateCheckoutSessionResponse, type CreateColumnRequest, type CreateDataRequest, type CreateDocumentRequest, type CreateFolderRequest, type CreateFolderResponse, type CreateGeoIndexRequest, type CreateIndexRequest, type CreateLobbyRequest, type CreatePlaylistRequest, type CreatePublicKeyRequest, type CreatePublicKeyResponse, type CreateRelationRequest, type CreateSearchIndexRequest, type CreateSecurityRuleRequest, type CreateSubscriptionRequest, type CreateTableRequest, type CreateTriggerRequest, type CreateVideoStorageRequest, type DailyReport, type DataItem, type DataType, type DatabaseChange, type DatabaseChangeMessage, type DatabaseChangeType, type DatabaseRealtimeConnectOptions, type DatabaseRealtimeFilter, type DatabaseRealtimeHandlers, type DatabaseRealtimeSubscription, type DatabaseSnapshot, type DatabaseSnapshotMessage, type DatabaseSubscribeOptions, type DeleteWhereResponse, type DeviceInfo, type DocumentResponse, type EnabledProviderInfo, type EnabledProvidersResponse, type ErrorHandler, type ErrorMessage, type ErrorReport, type ErrorTrackerConfig, type ErrorType, type ExportDataRequest, type ExportDataResponse, type FetchDataResponse, type FetchFilesResponse, type FetchPublicKeysResponse, type FileItem, type FileStats, GameAPI, type GameAction, type GameClientConfig, type GameConnectionState, type GameConnectionStatus, type GameDelta, type GameEventHandlers, type GamePlayer, GameRoom, type GameRoomConfig, type GameRoomInfo, GameRoomTransport, type GameServerMessage, type GameServerMessageType, type GameState, type GameTransportConfig, type GenerateUploadURLByPathRequest, type GenerateUploadURLRequest, type GenerateUploadURLResponse, type GeoIndex, type GeoNear, type GeoPoint, type GeoPolygon, type GeoQuery, type GeoResponse, type GeoResult, type GeoWithin, type GetAuthorizationURLResponse, type GetFileByPathResponse, type GoogleConnectionStatus, type GuestMemberSignInResponse, type HistoryResponse, type ICEServer, type ICEServersResponse, type ImageResult, type ImportDataRequest, type ImportDataResponse, type IndexAnalysis, type IndexRecommendation, type InitUploadResponse, type InvokeFunctionRequest, type InvokeFunctionResponse, type IssueBillingKeyRequest, type IssueBillingKeyResponse, type JoinQueueRequest, type JoinRoomRequest, type JoinRoomResponse, type KnowledgeSearchRequest, type KnowledgeSearchResponse, type KnowledgeSearchResult, type LeaderboardEntry, type LifecyclePolicy, type ListBillingKeysResponse, type ListDocumentsResponse, type ListPageMetasOptions, type ListPageMetasResponse, type ListSubscriptionPaymentsRequest, type ListSubscriptionPaymentsResponse, type ListSubscriptionsRequest, type ListSubscriptionsResponse, type LobbyInfo, type LobbyInvite, type LobbyMember, type LobbyVisibility, type MatchResult, type MatchmakingTicket, type MemberInfoResponse, type MemberSignInRequest, type MemberSignInResponse, type MemberSignUpRequest, type MemberSignUpResponse, type MembershipTier, type MemoryInfo, type MessageHandler, type MigrateDataRequest, type MigrateDataResponse, type MoveFileRequest, type NackMessageRequest, NativeAPI, type OAuthCallbackResponse, type OAuthProvider, type OpenDialogOptions, type OpenDialogResult, type PageMetaResponse, type PartyInfo, type PartyInvite, type PartyMember, type PauseSubscriptionRequest, type PaymentDetail, type PaymentProvider, type PaymentStatus, type PeerInfo, type Platform, type PlayerEvent, type PlayerStats, type Playlist, type PlaylistItem, type PongMessage, type PopulateOption, type Position, type PreparePaymentRequest, type PreparePaymentResponse, type PresenceChangeHandler, type PresenceInfo, type PresenceSetOptions, type PresenceStatus, type PresenceStatusResult, type PublicKeyItem, type PublishBatchRequest, type PublishBatchResponse, type PublishMessageRequest, type PublishMessageResponse, type PushPlatform, type QualityProgress, type QueryOptions, type QueueInfoResponse, type QueueMessage, type ReadReceiptHandler, type ReadReceiptInfo, type RealtimeConnectOptions, type RealtimeMessage, type RegisterDeviceRequest, type RelationType, type RenameFileRequest, type RenameFileResponse, type ReplayHighlight, type ReplayInfo, type ReplayPlayerInfo, type RestoreBackupRequest, type RestoreBackupResponse, type RetentionPolicy, type RoomInfo, type RoomStats, type RoomsResponse, type SaveDialogOptions, type SaveDialogResult, type SearchIndex, type SearchOptions, type SearchResponse, type SearchResult, type SecurityRule, type SendOptions, type ServerMessage, SessionManager, type SetPageMetaRequest, type Shorts, type ShortsListResponse, type SignalingMessage, type SignalingMessageType, type SlowQueryInfo, type SpectatorInfo, type SpectatorPlayerState, type SpectatorState, type StateChange, type StateChangeHandler, type StorageUploadOptions, type StreamDoneCallback, type StreamDoneData, type StreamErrorCallback, type StreamHandlers, type StreamMessage, type StreamOptions, type StreamSession, type StreamTokenCallback, type StreamToolCallCallback, type StreamToolResultCallback, type StreamURLResponse, type SubscribeOptions, type SubscribeTopicRequest, type SubscribedData, type Subscription, type SubscriptionPaymentResponse, type SubscriptionPaymentStatus, type SubscriptionResponse, type SubscriptionStatus, type SuperChat, type SystemInfo, type TTLConfig, type TableAccessLevel, type TableColumnDef, type TableIndex, type TableRelation, type TableSchema, type TableSchemaDefinition, type TokenPersistence, type TransactionRead, type TransactionWrite, type TranscodeStatus, type TransportType, type Trigger, type TriggerEvent, type TriggerHandlerType, type TypingChangeHandler, type TypingInfo, type UpdateBillingKeyRequest, type UpdateChannelRequest, type UpdateColumnRequest, type UpdateCustomDataRequest, type UpdateCustomDataResponse, type UpdateDataRequest, type UpdateLobbyRequest, type UpdatePublicKeyRequest, type UpdatePublicKeyResponse, type UpdateSecurityRuleRequest, type UpdateSubscriptionRequest, type UpdateTriggerRequest, type UpdateVideoRequest, type UpdateVideoStorageRequest, type UploadByPathOptions, type UploadFileResponse, type UploadOptions, type UploadProgress, type VAPIDPublicKeyResponse, type ValidateResponse, type ValidationSchema, type ValidationSchemaField, type ValidationStateTransitions, type Video, type VideoComment, type VideoListOptions, type VideoListResponse, VideoProcessingError, type VideoQuality, type VideoStatus, type VideoStorage, type VideoStorageListResponse, type VideoVisibility, type VoiceChannel, type VoiceMember, type WaitOptions, type WatchHistoryItem, type WebPushSubscription, type WebRTCConnectOptions, type WebRTCConnectionState, type WebRTCMode, type WhereCondition, type WhereOperator, ConnectBase as default, isWebTransportSupported };
|
package/dist/index.js
CHANGED
|
@@ -1651,41 +1651,6 @@ var DatabaseAPI = class {
|
|
|
1651
1651
|
}
|
|
1652
1652
|
};
|
|
1653
1653
|
}
|
|
1654
|
-
/**
|
|
1655
|
-
* 프레즌스 상태 설정
|
|
1656
|
-
*/
|
|
1657
|
-
setPresence(status, device, metadata) {
|
|
1658
|
-
if (this.realtimeState !== "connected") return;
|
|
1659
|
-
this.sendRealtimeMessage({
|
|
1660
|
-
type: "presence_set",
|
|
1661
|
-
request_id: this.generateRequestId(),
|
|
1662
|
-
status,
|
|
1663
|
-
device,
|
|
1664
|
-
metadata
|
|
1665
|
-
});
|
|
1666
|
-
}
|
|
1667
|
-
/**
|
|
1668
|
-
* 프레즌스 구독 (다른 사용자의 온라인 상태 감시)
|
|
1669
|
-
*/
|
|
1670
|
-
subscribePresence(userIds, onPresence) {
|
|
1671
|
-
if (this.realtimeState !== "connected") return;
|
|
1672
|
-
this.realtimeHandlers.set("__presence__", {
|
|
1673
|
-
onSnapshot: (docs) => {
|
|
1674
|
-
const states = {};
|
|
1675
|
-
for (const doc of docs) {
|
|
1676
|
-
if (doc.data) {
|
|
1677
|
-
states[doc.id] = doc.data;
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
onPresence(states);
|
|
1681
|
-
}
|
|
1682
|
-
});
|
|
1683
|
-
this.sendRealtimeMessage({
|
|
1684
|
-
type: "presence_subscribe",
|
|
1685
|
-
request_id: this.generateRequestId(),
|
|
1686
|
-
user_ids: userIds
|
|
1687
|
-
});
|
|
1688
|
-
}
|
|
1689
1654
|
/**
|
|
1690
1655
|
* 실시간 연결 상태 확인
|
|
1691
1656
|
*/
|
|
@@ -1727,7 +1692,7 @@ var DatabaseAPI = class {
|
|
|
1727
1692
|
this.setRealtimeState("connecting");
|
|
1728
1693
|
const baseUrl = this.realtimeOptions.dataServerUrl || this.http.getBaseUrl();
|
|
1729
1694
|
const wsUrl = baseUrl.replace(/^http/, "ws");
|
|
1730
|
-
const url = `${wsUrl}/v1/realtime/ws?access_token=${encodeURIComponent(this.realtimeOptions.accessToken)}`;
|
|
1695
|
+
const url = `${wsUrl}/v1/database/realtime/ws?access_token=${encodeURIComponent(this.realtimeOptions.accessToken)}`;
|
|
1731
1696
|
return new Promise((resolve, reject) => {
|
|
1732
1697
|
try {
|
|
1733
1698
|
this.realtimeWs = new WebSocket(url);
|
package/dist/index.mjs
CHANGED
|
@@ -1613,41 +1613,6 @@ var DatabaseAPI = class {
|
|
|
1613
1613
|
}
|
|
1614
1614
|
};
|
|
1615
1615
|
}
|
|
1616
|
-
/**
|
|
1617
|
-
* 프레즌스 상태 설정
|
|
1618
|
-
*/
|
|
1619
|
-
setPresence(status, device, metadata) {
|
|
1620
|
-
if (this.realtimeState !== "connected") return;
|
|
1621
|
-
this.sendRealtimeMessage({
|
|
1622
|
-
type: "presence_set",
|
|
1623
|
-
request_id: this.generateRequestId(),
|
|
1624
|
-
status,
|
|
1625
|
-
device,
|
|
1626
|
-
metadata
|
|
1627
|
-
});
|
|
1628
|
-
}
|
|
1629
|
-
/**
|
|
1630
|
-
* 프레즌스 구독 (다른 사용자의 온라인 상태 감시)
|
|
1631
|
-
*/
|
|
1632
|
-
subscribePresence(userIds, onPresence) {
|
|
1633
|
-
if (this.realtimeState !== "connected") return;
|
|
1634
|
-
this.realtimeHandlers.set("__presence__", {
|
|
1635
|
-
onSnapshot: (docs) => {
|
|
1636
|
-
const states = {};
|
|
1637
|
-
for (const doc of docs) {
|
|
1638
|
-
if (doc.data) {
|
|
1639
|
-
states[doc.id] = doc.data;
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
onPresence(states);
|
|
1643
|
-
}
|
|
1644
|
-
});
|
|
1645
|
-
this.sendRealtimeMessage({
|
|
1646
|
-
type: "presence_subscribe",
|
|
1647
|
-
request_id: this.generateRequestId(),
|
|
1648
|
-
user_ids: userIds
|
|
1649
|
-
});
|
|
1650
|
-
}
|
|
1651
1616
|
/**
|
|
1652
1617
|
* 실시간 연결 상태 확인
|
|
1653
1618
|
*/
|
|
@@ -1689,7 +1654,7 @@ var DatabaseAPI = class {
|
|
|
1689
1654
|
this.setRealtimeState("connecting");
|
|
1690
1655
|
const baseUrl = this.realtimeOptions.dataServerUrl || this.http.getBaseUrl();
|
|
1691
1656
|
const wsUrl = baseUrl.replace(/^http/, "ws");
|
|
1692
|
-
const url = `${wsUrl}/v1/realtime/ws?access_token=${encodeURIComponent(this.realtimeOptions.accessToken)}`;
|
|
1657
|
+
const url = `${wsUrl}/v1/database/realtime/ws?access_token=${encodeURIComponent(this.realtimeOptions.accessToken)}`;
|
|
1693
1658
|
return new Promise((resolve, reject) => {
|
|
1694
1659
|
try {
|
|
1695
1660
|
this.realtimeWs = new WebSocket(url);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "connectbase-client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"dist",
|
|
36
36
|
"LICENSE",
|
|
37
37
|
"CHANGELOG.md",
|
|
38
|
-
"README.md"
|
|
38
|
+
"README.md",
|
|
39
|
+
"MIGRATION-v2.md"
|
|
39
40
|
],
|
|
40
41
|
"scripts": {
|
|
41
42
|
"build": "tsup",
|