connectbase-client 1.12.0 → 3.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 CHANGED
@@ -3,6 +3,138 @@
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
+ ## [3.0.0] - 2026-04-28 — BREAKING
7
+
8
+ 게임 서버 mechanism-only 재설계. ConnectBase 가 박아두던 게임 룰 (파티/로비/랭킹/매치메이킹/
9
+ killcam/highlight) 을 모두 제거하고, primitive (`cb.game.matchqueue`, `cb.game.leaderboard`,
10
+ `cb.game.scripts`) + 사용자 Lua 로 대체. 1:1 마이그레이션은 [MIGRATION_v3.md](../../../docs/sdk/MIGRATION_v3.md).
11
+
12
+ ### Removed (BREAKING)
13
+
14
+ - **Lobby**: `listLobbies`, `createLobby`, `getLobby`, `joinLobby`, `leaveLobby`, `toggleReady`,
15
+ `startGame`, `kickPlayer`, `updateLobby`, `sendLobbyChat`, `invitePlayer`, `acceptInvite`,
16
+ `declineInvite`, `getPlayerInvites` 메서드 + 관련 타입 (`LobbyInfo`, `CreateLobbyRequest`,
17
+ `UpdateLobbyRequest`, `LobbyInvite`)
18
+ - **Party**: `createParty`, `joinParty`, `acceptPartyInvite`, `declinePartyInvite`,
19
+ `leaveParty`, `kickFromParty`, `inviteToParty`, `getParty`, `getMyParties`, `setReady`,
20
+ `setPartyMetadata`, `getPartyInvites`, `sendPartyChat` + `PartyInfo`, `PartyInvite`
21
+ - **Matchmaking**: `joinQueue`, `leaveQueue`, `getMatchStatus` + `JoinQueueRequest`,
22
+ `MatchmakingTicket`
23
+ - **Ranking**: `getLeaderboard`, `getPlayerStats`, `getPlayerRank` + `LeaderboardEntry` (구
24
+ player_id/rating/tier/wins/losses 시그니처. 신규 `LeaderboardScoreEntry` 가 대체).
25
+ - **Killcam/Highlight**: `cb.game.replay.killcam(...)`, `cb.game.replay.highlights(...)`
26
+ 관련 endpoint 가 백엔드에서 제거되어 SDK 호출 시 404. 사용자가 `cb.game.replay.download`
27
+ 로 raw frame 받아 클라/Lua 에서 직접 처리.
28
+
29
+ ### Added — v3 primitive
30
+
31
+ - **Matchqueue** (`cb.game.matchqueue.*` → `enqueueMatch / listMatchqueue / cancelMatch`):
32
+ rating/region 등 attributes 는 free-form. 매칭 알고리즘은 사용자 Lua 가 list → notify.
33
+ - **Leaderboard** (`submitScore / getTopScores / getMemberRank / getRankAround / resetLeaderboard / removeFromLeaderboard`):
34
+ ELO/티어/시즌 박지 않음. 시즌은 key suffix (`ranks:2026q2`) 로 분리, 점수 공식은 Lua.
35
+ - **Scripts** (`uploadScript / listScripts / getScript / listScriptVersions /
36
+ activateScript / rollbackScript / disableScript`): Lua 스크립트 영속 메타 + 버전 + hot reload.
37
+ - 신규 타입: `MatchqueueTicket`, `MatchqueueListResponse`, `LeaderboardScoreEntry`,
38
+ `LeaderboardListResponse`, `ScriptMeta`, `ScriptVersion`, `ScriptListResponse`,
39
+ `ScriptVersionListResponse`, `ScriptDetailResponse`.
40
+
41
+ ### Backend 영향
42
+
43
+ - game-server v3 부터 `/v1/game/:appID/matchqueue/:key/*`, `/leaderboards/:key/*`,
44
+ `/scripts/*` 라우트가 유일한 게임 메커니즘 endpoint. lobby/party/ranking/matchmaking
45
+ 엔드포인트는 모두 404.
46
+ - 자세한 변경: [`docs/sdk/MIGRATION_v3.md`](../../../docs/sdk/MIGRATION_v3.md),
47
+ [`docs/game-server/RECIPES.md`](../../../docs/game-server/RECIPES.md).
48
+
49
+ ### Migration 요약
50
+
51
+ ```ts
52
+ // Before (v2.x)
53
+ await cb.game.matchmaking.join({ mode: "ranked", rating: 1500 })
54
+ await cb.game.ranking.submit({ leaderboard: "elo", score: 2150 })
55
+
56
+ // After (v3.0)
57
+ await cb.game.enqueueMatch(appId, "ranked", userId, { rating: 1500 })
58
+ await cb.game.submitScore(appId, "elo", userId, 32, "incr")
59
+ ```
60
+
61
+ ## [2.0.0] - 2026-04-27 — BREAKING
62
+
63
+ Realtime presence/typing 단일화의 최종 단계. 1.13 deprecation 단계를 건너뛰고 즉시
64
+ deprecated 코드를 제거. presence/typing 의 단일 SoT 는 `cb.realtime.*`.
65
+
66
+ ### Removed (BREAKING)
67
+
68
+ - **`cb.database.setPresence(status, device, metadata)`** — 코드 자체 제거. 호출 시 `TypeError`.
69
+ - **`cb.database.subscribePresence(userIds, callback)`** — 코드 자체 제거.
70
+ - **`DatabasePresenceState`** type — `PresenceInfo` (from `'./realtime'`) 사용.
71
+ - 1.13 에서 추가됐던 deprecation `console.warn` 헬퍼도 함께 제거 (필요 없음).
72
+
73
+ ### Migration
74
+
75
+ ```ts
76
+ // Before
77
+ cb.database.setPresence('online', 'web', { nickname: '홍길동' })
78
+ cb.database.subscribePresence(['user1', 'user2'], (states) => {
79
+ console.log(states['user1']?.last_seen)
80
+ })
81
+
82
+ // After
83
+ await cb.realtime.setPresence('online', { device: 'web', metadata: { nickname: '홍길동' } })
84
+ const unsub = await cb.realtime.subscribePresence('user1', (info) => {
85
+ console.log(info.lastSeen)
86
+ })
87
+ ```
88
+
89
+ 자세한 가이드: [MIGRATION-v2.md](./MIGRATION-v2.md). 필드 차이는 `user_id`→`userId`,
90
+ `last_seen` (ISO) → `lastSeen` (epoch ms), `'busy'` 상태 추가.
91
+
92
+ ### Server-side
93
+
94
+ 데이터 서버 측 presence/typing 코드도 함께 제거되었습니다. `presence_set` / `presence_subscribe`
95
+ / `typing_*` 메시지를 데이터 서버 WebSocket 으로 보내면 `USE_SOCKET_SERVER` 에러가 반환됩니다.
96
+
97
+ ## [1.13.0] - 2026-04-27
98
+
99
+ Realtime presence/typing 단일화 — `cb.database.setPresence` / `cb.database.subscribePresence`
100
+ 는 v2.0.0 에서 제거됩니다. 신규 코드는 `cb.realtime.*` 를 사용하세요. 자세한 마이그레이션은
101
+ [MIGRATION-v2.md](./MIGRATION-v2.md) 참고.
102
+
103
+ ### Deprecated
104
+
105
+ - **`cb.database.setPresence(status, device, metadata)`** → `cb.realtime.setPresence(status, { device, metadata })`
106
+ - **`cb.database.subscribePresence(userIds, onPresence)`** → `cb.realtime.subscribePresence(userId, handler)` 또는
107
+ `cb.realtime.onPresenceChange(handler)` (다중 사용자 일괄 수신).
108
+ - **`DatabasePresenceState`** type → `PresenceInfo` (from `'./realtime'`).
109
+ - 필드 네이밍: `user_id` → `userId`, `last_seen` (ISO) → `lastSeen` (epoch ms 숫자).
110
+ - 상태값: `'busy'` 추가 (PresenceInfo 만 지원).
111
+
112
+ 호출 시 개발 환경(`process.env.NODE_ENV !== 'production'`)에서 `console.warn` 이 1회 출력됩니다.
113
+ 런타임 동작은 1.12.x 와 동일합니다 (이번 버전은 deprecate 만, 코드 제거는 v2.0.0).
114
+
115
+ ### Changed
116
+
117
+ - **WebSocket URL 기본값** `cb.database.connectRealtime` 의 기본 endpoint 가
118
+ `/v1/realtime/ws` → `/v1/database/realtime/ws` 로 변경. 기존 경로는 30일간 alias 로
119
+ 유지되며 응답에 `Sunset` (RFC 8594) + `X-Deprecated-Endpoint` 헤더가 부착됩니다.
120
+ 명시적으로 `dataServerUrl` 을 지정한 경우 자동 변환은 하지 않습니다 (Hyrum's Law 준수).
121
+
122
+ ### Migration
123
+
124
+ ```ts
125
+ // Before (1.12.x)
126
+ cb.database.setPresence('online', 'web', { nickname: '홍길동' })
127
+ cb.database.subscribePresence(['user1', 'user2'], (states) => {
128
+ console.log(states['user1']?.last_seen) // ISO string
129
+ })
130
+
131
+ // After (1.13+, 권장 — v2 호환)
132
+ cb.realtime.setPresence('online', { device: 'web', metadata: { nickname: '홍길동' } })
133
+ const unsubscribe = await cb.realtime.subscribePresence('user1', (info) => {
134
+ console.log(info.lastSeen) // epoch ms number
135
+ })
136
+ ```
137
+
6
138
  ## [1.12.0] - 2026-04-26
7
139
 
8
140
  Analytics 조회 정렬 옵션 정합성 수정 — `VisitorListOptions.sort_by` 의 TypeScript
@@ -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.