connectbase-client 3.13.0 → 3.14.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,86 @@
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.14.0] - 2026-05-14
7
+
8
+ ### Added — `GameError` 클래스 + `createRoom` 응답 메타
9
+
10
+ - 신규 export: `GameError` (extends `Error`) — game-server 의 `error` 메시지를 일관된
11
+ 인스턴스로 surface. `.code`, `.phase`, `.feature`, `.originClientId`, `.requested`,
12
+ `.available` 모두 노출. `instanceof GameError` 로 UI 분기 가능.
13
+ - 신규 export: `GameErrorCode` literal union — `'SCRIPT_NOT_FOUND' | 'NO_ACTION_HANDLER'
14
+ | 'FEATURE_DISABLED' | 'SCRIPT_ERROR' | 'SCRIPT_TIMEOUT' | 'RATE_LIMITED' |
15
+ 'QUOTA_EXCEEDED' | 'TIMEOUT' | 'UNKNOWN' | (string & {})`.
16
+ - 신규 export: `CreateRoomResult` 타입 — `{ roomId, state, scriptName?, scriptVersion? }`.
17
+ - 신규 메서드: `GameRoom.createRoomDetailed(config)` + `GameRoomTransport.createRoomDetailed(config)`
18
+ — server 가 attach 한 lua script 의 이름/버전을 client 가 즉시 검증할 수 있게 응답에 포함.
19
+ - 신규 getter: `room.scriptName` / `room.scriptVersion` (`GameRoom` + `GameRoomTransport`)
20
+ — 마지막 `createRoom` 응답의 메타. `joinRoom`/`leaveRoom`/`disconnect` 시 reset.
21
+ - 기존 `createRoom(config)` 의 시그니처는 그대로 (호환). 내부적으로 `createRoomDetailed`
22
+ 를 호출하고 `.state` 만 반환.
23
+ - `onError` 콜백 시그니처가 `Event | ErrorMessage` → `Event | GameError` 로 변경 (호환:
24
+ `ErrorMessage` 의 모든 필드가 `GameError` 인스턴스에 동일하게 surface 됨).
25
+
26
+ ### Changed (BREAKING) — `disableScript` rename + `deleteScript` 신규
27
+
28
+ server 측 `DELETE /v1/game/:appID/scripts/:name` 의 의미가 **Disable → hard-delete** 로
29
+ 변경된 것에 대응:
30
+
31
+ - `GameAPI.disableScript(appId, name)` **제거**. 대체: `GameAPI.deactivateScript(appId, name)`
32
+ — `POST /scripts/:name/deactivate` 호출 (코드/버전 보존, 재활성화 가능).
33
+ - `GameAPI.deleteScript(appId, name)` **신규** — `DELETE /scripts/:name` 호출 (메타 + 모든
34
+ 버전 영구 제거, 복구 불가).
35
+
36
+ ### Fixed — `error` 메시지 분류 메타 손실 회귀
37
+
38
+ 이전엔 모든 `reject(new Error(msg.data.message))` 로 server 의 `code`/`phase`/`feature`/
39
+ `origin_client_id`/`requested`/`available` 가 손실되어 SDK 사용자가 UI 분기를 만들 수
40
+ 없었다 (platform-issue 019e21dd, NJB 2026-05-13). 이제 `GameError` 인스턴스로 reject
41
+ 되어 모든 메타가 보존된다.
42
+
43
+ ### Migration
44
+
45
+ ```ts
46
+ // Before
47
+ import { ErrorMessage } from 'connectbase-client'
48
+ room.on('error', (err) => {
49
+ if ((err as ErrorMessage).code === 'FEATURE_DISABLED') { /* ... */ }
50
+ })
51
+ await cb.game.disableScript(appId, 'old-script')
52
+
53
+ // After
54
+ import { GameError } from 'connectbase-client'
55
+ room.on('error', (err) => {
56
+ if (err instanceof GameError) {
57
+ if (err.code === 'SCRIPT_NOT_FOUND') console.error('candidates:', err.available)
58
+ if (err.code === 'NO_ACTION_HANDLER') console.error('handler for', err.phase, 'undefined')
59
+ if (err.code === 'FEATURE_DISABLED') console.error('feature off:', err.feature)
60
+ if (err.originClientId) console.warn('error from other player:', err.originClientId)
61
+ }
62
+ })
63
+ await cb.game.deactivateScript(appId, 'old-script') // 비활성화 (코드 보존)
64
+ await cb.game.deleteScript(appId, 'really-old') // 영구 삭제 (신규)
65
+
66
+ // createRoom 응답 검증 패턴 (NJB regression 가드)
67
+ const { state, scriptName, scriptVersion } = await room.createRoomDetailed({ scriptName: 'njb-main' })
68
+ if (scriptName !== 'njb-main') {
69
+ throw new Error(`script attach mismatch: expected njb-main got ${scriptName}`)
70
+ }
71
+ console.log('attached', scriptName, 'v', scriptVersion)
72
+ ```
73
+
74
+ ## [3.13.1] - 2026-05-13
75
+
76
+ ### Documentation — README batch/transaction 예제 타입 회귀 수정
77
+
78
+ README 의 `cb.database.batch()` / `cb.database.transaction()` 예제가 구 시그니처
79
+ (`table: 'string'`) 를 사용해 `tsc --noEmit` 에서 `TS2353` 발생하던 문제 수정.
80
+
81
+ - `table:` → `table_id:` (UUID, 실제 `BatchOperation` 타입과 일치)
82
+ - 3.12+ 의 `success:false` throw 동작 반영 — try/catch 패턴 명시
83
+
84
+ 코드 변경은 없음. README 만 갱신.
85
+
6
86
  ## [3.13.0] - 2026-05-13
7
87
 
8
88
  ### Removed — `PlatformIssueDetail.triage_summary` / `triaged_at` 필드
package/README.md CHANGED
@@ -65,8 +65,20 @@ gameClient
65
65
  await gameClient.connect()
66
66
  const state = await gameClient.createRoom({
67
67
  maxPlayers: 4,
68
- tickRate: 64
68
+ tickRate: 64,
69
+ scriptName: 'my-script', // Optional: attach a lua script (must be pre-uploaded + active)
69
70
  })
71
+
72
+ // 3.14+ — Attached script 의 이름/버전을 검증하고 싶으면 createRoomDetailed 사용
73
+ import { GameError } from 'connectbase-client'
74
+ try {
75
+ const r = await gameClient.createRoomDetailed({ scriptName: 'my-script' })
76
+ console.log('attached', r.scriptName, 'v', r.scriptVersion)
77
+ } catch (e) {
78
+ if (e instanceof GameError && e.code === 'SCRIPT_NOT_FOUND') {
79
+ console.error('script missing — candidates:', e.available)
80
+ }
81
+ }
70
82
  ```
71
83
 
72
84
  ## Features
@@ -587,26 +599,41 @@ nearby.results.forEach(place => {
587
599
 
588
600
  #### Batch & Transactions
589
601
 
602
+ `table_id` 는 항상 UUID. 콘솔/REST 로 생성한 테이블의 UUID 를 그대로 사용한다.
603
+
604
+ v3.12+ 부터 server 가 부분 실패(`success: false`)를 응답하면 SDK 가 첫 실패 op 의
605
+ error 메시지로 throw 한다 — silent success 회귀 방지 차원. 호출자는 try/catch 로 감싼다.
606
+
590
607
  ```typescript
591
608
  // Batch: atomic multi-table operations
592
- await cb.database.batch([
593
- { type: 'create', table: 'orders', data: { product: 'A', qty: 1 } },
594
- { type: 'update', table: 'inventory', doc_id: 'item-a', operators: {
595
- qty: { type: 'increment', value: -1 }
596
- }},
597
- { type: 'update', table: 'stats', doc_id: 'daily', operators: {
598
- order_count: { type: 'increment', value: 1 },
599
- last_order: { type: 'serverTimestamp' }
600
- }}
601
- ])
609
+ try {
610
+ const result = await cb.database.batch([
611
+ { type: 'create', table_id: ORDERS_TABLE_ID, data: { product: 'A', qty: 1 } },
612
+ { type: 'update', table_id: INVENTORY_TABLE_ID, doc_id: 'item-a', operators: {
613
+ qty: { type: 'increment', value: -1 }
614
+ }},
615
+ { type: 'update', table_id: STATS_TABLE_ID, doc_id: 'daily', operators: {
616
+ order_count: { type: 'increment', value: 1 },
617
+ last_order: { type: 'serverTimestamp' }
618
+ }}
619
+ ])
620
+ // result.success, result.results[i].{success, doc_id, error}
621
+ } catch (e) {
622
+ // RLS 거부 / 검증 실패 / table_id 오타 등 — 전체 batch 가 atomic 하게 롤백
623
+ console.error('batch failed:', (e as Error).message)
624
+ }
602
625
 
603
626
  // Transaction: read-then-write with ACID guarantees
604
- await cb.database.transaction(
605
- [{ table: 'accounts', doc_id: 'user-1', alias: 'sender' }],
606
- [{ type: 'update', table: 'accounts', doc_id: 'user-1', operators: {
607
- balance: { type: 'increment', value: -100 }
608
- }}]
609
- )
627
+ try {
628
+ await cb.database.transaction(
629
+ [{ table_id: ACCOUNTS_TABLE_ID, doc_id: 'user-1', alias: 'sender' }],
630
+ [{ type: 'update', table_id: ACCOUNTS_TABLE_ID, doc_id: 'user-1', operators: {
631
+ balance: { type: 'increment', value: -100 }
632
+ }}]
633
+ )
634
+ } catch (e) {
635
+ console.error('transaction failed:', (e as Error).message)
636
+ }
610
637
  ```
611
638
 
612
639
  #### Populate (Relation Query / JOIN)