connectbase-client 1.10.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 +162 -0
- package/MIGRATION-v2.md +149 -0
- package/README.md +23 -0
- package/dist/connect-base.umd.js +3 -3
- package/dist/index.d.mts +134 -22
- package/dist/index.d.ts +134 -22
- package/dist/index.js +100 -40
- package/dist/index.mjs +100 -40
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,168 @@
|
|
|
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
|
+
|
|
83
|
+
## [1.12.0] - 2026-04-26
|
|
84
|
+
|
|
85
|
+
Analytics 조회 정렬 옵션 정합성 수정 — `VisitorListOptions.sort_by` 의 TypeScript
|
|
86
|
+
union 을 백엔드가 실제로 인식하는 값으로 정정합니다. 1.10/1.11 의 SDK 타입은
|
|
87
|
+
`'last_visit' | 'total_visits' | 'total_page_views'` 였으나 백엔드 정렬 분기
|
|
88
|
+
(`web_visitor_repository.GetStorageVisitors`,
|
|
89
|
+
`web_visitor_service.GetVisitorGroupsByMember`) 는 `last_visit` 외 값을
|
|
90
|
+
인식하지 못해 항상 default(`last_visit`) 분기로 떨어졌습니다 — silent 잘못된 정렬.
|
|
91
|
+
|
|
92
|
+
### Changed — `VisitorListOptions.sort_by` 타입
|
|
93
|
+
|
|
94
|
+
- 변경 전 (1.10/1.11): `'last_visit' | 'total_visits' | 'total_page_views'`
|
|
95
|
+
- 변경 후 (1.12+): `'last_visit' | 'visits' | 'page_views' | 'first_visit'`
|
|
96
|
+
- 영향 메서드: `cb.analytics.getVisitors()`, `cb.analytics.getVisitorGroups()`.
|
|
97
|
+
- 런타임은 SDK 가 sort_by 문자열을 그대로 백엔드에 전달하므로, 새 값을 쓰면
|
|
98
|
+
비로소 의도한 정렬이 동작합니다. 1.10/1.11 코드에서 `total_visits` /
|
|
99
|
+
`total_page_views` 를 넘긴 호출은 실제로는 default `last_visit` 정렬 결과를
|
|
100
|
+
받고 있었던 점에 주의.
|
|
101
|
+
|
|
102
|
+
### Migration
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// Before (1.10/1.11): 컴파일은 통과하지만 런타임은 last_visit 정렬
|
|
106
|
+
cb.analytics.getVisitors('019d8...', { sort_by: 'total_visits' })
|
|
107
|
+
|
|
108
|
+
// After (1.12+): 의도한대로 visits 기준 정렬
|
|
109
|
+
cb.analytics.getVisitors('019d8...', { sort_by: 'visits' })
|
|
110
|
+
cb.analytics.getVisitors('019d8...', { sort_by: 'page_views' }) // 페이지뷰 누적 기준
|
|
111
|
+
cb.analytics.getVisitors('019d8...', { sort_by: 'first_visit' }) // 최초 방문일 기준
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
기존 코드가 `'last_visit'` 만 사용했다면 변경 불필요 (대부분의 호출).
|
|
115
|
+
타입 변경은 breaking 이지만 런타임 호환은 유지됩니다 — 이전 union 의 두 값은
|
|
116
|
+
원래 동작하지 않았기 때문.
|
|
117
|
+
|
|
118
|
+
## [1.11.0] - 2026-04-26
|
|
119
|
+
|
|
120
|
+
Analytics 사용자 통합 강화 — 멤버별 합산 방문자 조회, 단건 멤버 조회, 즉시 backfill,
|
|
121
|
+
Visitor merge admin API. sisun 팀의 1.10 이후 후속 피드백이 시작점이며, 어드민에서
|
|
122
|
+
"회원 단위 합산 방문자" 위젯을 위한 페이지네이션 풀 다운 우회 코드를 SDK 한 줄 호출로
|
|
123
|
+
대체할 수 있도록 했습니다. 모든 추가 메서드는 `cb.analytics` 네임스페이스에 들어가며
|
|
124
|
+
JWT/cb_sk_ dual-auth 로 호출할 수 있습니다.
|
|
125
|
+
|
|
126
|
+
### Added — `cb.analytics.getVisitorGroups(storageWebId?, options?)`
|
|
127
|
+
|
|
128
|
+
- 같은 `app_member_id` 의 visitor row 들을 서버 단에서 합산해 단일 row 로 반환.
|
|
129
|
+
익명 visitor 는 단일 row 로 그대로 노출되어 페이지네이션 의미가 일관됨.
|
|
130
|
+
- 응답 필드: `app_member_id`, `visitor_uids[]`, `visitor_count`, `total_visits`,
|
|
131
|
+
`total_page_views`, `first_visit_at`(MIN), `last_visit_at`(MAX), `country`, `is_bot`.
|
|
132
|
+
- `visitor_count` 는 **"디바이스 수" 가 아닌 "추적 브라우저 인스턴스 수"** 입니다 (시크릿/일반
|
|
133
|
+
모드는 별도 카운트).
|
|
134
|
+
- 백엔드 `GET /v1/storages/web/:id/visitor-groups` 신설 (dual-auth).
|
|
135
|
+
|
|
136
|
+
### Added — `cb.analytics.getVisitorByMember(storageWebId?, memberId)`
|
|
137
|
+
|
|
138
|
+
- 어드민 회원 상세 페이지처럼 한 명만 필요할 때. 페이지네이션 풀 다운 없이 한 번 호출.
|
|
139
|
+
- 응답은 위 group item 과 동일 형태 (단건).
|
|
140
|
+
- 백엔드 `GET /v1/storages/web/:id/members/:member_id/visitor` 신설.
|
|
141
|
+
|
|
142
|
+
### Added — `cb.analytics.mergeVisitors(storageWebId?, request)`
|
|
143
|
+
|
|
144
|
+
- 두 visitor 가 동일인임을 외부에서 알게 됐을 때 admin 작업으로 통합. source 의 자식
|
|
145
|
+
레코드 (page_views, daily, custom_events, experiment_assignments, heatmap_events,
|
|
146
|
+
session_recordings) 를 target 으로 옮긴 뒤 source 삭제. 단일 트랜잭션, 부분 실패 시
|
|
147
|
+
전체 롤백.
|
|
148
|
+
- 입력: `source_visitor_uid` 필수 + (`target_visitor_uid` 또는 `target_member_id`) 중
|
|
149
|
+
하나 필수. `target_member_id` 만 주면 그 멤버의 가장 최근 활동 visitor 를 target 으로 잡음.
|
|
150
|
+
- 응답: `target_visitor_id`, `moved_records`(이전된 자식 레코드 수).
|
|
151
|
+
- 제약: `first_visit_at` 은 ent Immutable 필드라 target 값을 바꾸지 않습니다 (daily 가
|
|
152
|
+
실제 이력 보존). source/target 은 같은 storage_web 에 속해야 합니다.
|
|
153
|
+
- 백엔드 `POST /v1/storages/web/:id/visitors/merge` 신설.
|
|
154
|
+
|
|
155
|
+
### Changed — `cb.analytics.identify(memberId)` 동작 보강
|
|
156
|
+
|
|
157
|
+
- 1.10 까지: `identify()` 호출 후 다음 batch 가 백엔드에 닿을 때 게스트 visitor 가 회원으로
|
|
158
|
+
자동 연결됨 (백엔드 BatchRecordVisit 의 자동 LinkMember 로직).
|
|
159
|
+
- 1.11+: `identify()` 가 즉시 `POST /visitors/link-member` 를 한 번 호출하여 backfill 을
|
|
160
|
+
당겨옵니다. 첫 페이지뷰 전 호출 시 404 발생 가능 — silent fail (다음 batch 가 자가 복구).
|
|
161
|
+
- `setMemberId(memberId)` 는 즉시 backfill 을 호출하지 않습니다 (이전과 동일 동작).
|
|
162
|
+
|
|
163
|
+
### Migration
|
|
164
|
+
|
|
165
|
+
기존 호출은 그대로 동작합니다. `identify()` 의 즉시 backfill 동작이 추가 HTTP 요청 1번을
|
|
166
|
+
유발하므로, 같은 효과를 다음 batch 까지 미루고 싶다면 `setMemberId()` 를 사용하세요.
|
|
167
|
+
|
|
6
168
|
## [1.10.0] - 2026-04-26
|
|
7
169
|
|
|
8
170
|
OAuth 콜백 응답 email 노출 + Analytics 조회 메서드 신규 + Functions 자동화용 Push 발송 메서드 신규.
|
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.
|