connectbase-client 5.6.4 → 5.8.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/dist/index.mjs CHANGED
@@ -3693,6 +3693,7 @@ function parseGameError(msg) {
3693
3693
  available: get("available") || msg.available
3694
3694
  });
3695
3695
  }
3696
+ var SYNC_LOST_RESYNC_MIN_INTERVAL_MS = 1e3;
3696
3697
  var getDefaultGameServerUrl = () => {
3697
3698
  if (typeof window !== "undefined") {
3698
3699
  const hostname = window.location.hostname;
@@ -3727,6 +3728,9 @@ var GameRoom = class {
3727
3728
  this._scriptVersion = null;
3728
3729
  this._isConnected = false;
3729
3730
  this.msgIdCounter = 0;
3731
+ /** sync_lost 자동 재동기화 억제 상태 — handleSyncLost 참고. */
3732
+ this.resyncInFlight = false;
3733
+ this.lastResyncAt = 0;
3730
3734
  this.config = {
3731
3735
  gameServerUrl: getDefaultGameServerUrl(),
3732
3736
  autoReconnect: true,
@@ -4037,6 +4041,11 @@ var GameRoom = class {
4037
4041
  const baseUrl = this.config.gameServerUrl;
4038
4042
  const wsUrl = baseUrl.replace(/^http/, "ws");
4039
4043
  const params = new URLSearchParams();
4044
+ if (!this.config.clientId) {
4045
+ throw new Error(
4046
+ "cb.game: clientId is required to build a game connection URL. Pass it via `cb.game.createClient({ appId, clientId })`."
4047
+ );
4048
+ }
4040
4049
  params.set("client_id", this.config.clientId);
4041
4050
  if (roomId) {
4042
4051
  params.set("room_id", roomId);
@@ -4127,6 +4136,25 @@ var GameRoom = class {
4127
4136
  case "error":
4128
4137
  this.handlers.onError?.(parseGameError(msg));
4129
4138
  break;
4139
+ case "sync_lost":
4140
+ this.handleSyncLost(msg);
4141
+ break;
4142
+ case "room_stale":
4143
+ if (this.handlers.onRoomStale) {
4144
+ this.handlers.onRoomStale({
4145
+ reason: msg.reason || "unknown",
4146
+ roomId: msg.room_id || "",
4147
+ scriptId: msg.script_id,
4148
+ scriptVersion: msg.script_version,
4149
+ serverTime: msg.server_time || 0
4150
+ });
4151
+ } else {
4152
+ console.warn(
4153
+ "[connect-base game] room_stale received but onRoomStale handler not set:",
4154
+ msg.reason
4155
+ );
4156
+ }
4157
+ break;
4130
4158
  default:
4131
4159
  this.handlers.onMessage?.(
4132
4160
  msg
@@ -4137,6 +4165,34 @@ var GameRoom = class {
4137
4165
  console.error("Failed to parse game message:", data);
4138
4166
  }
4139
4167
  }
4168
+ /**
4169
+ * `sync_lost` 처리 — broadcaster 가 이 클라이언트로 보내는 버퍼가 넘쳐서 delta 를
4170
+ * 몇 개 버렸다는 서버의 통지다. delta 는 이전 상태에 누적 적용되므로, 하나라도
4171
+ * 놓치면 이후 모든 상태가 어긋난 채로 계속 간다. 그래서 전체 상태를 다시 받아
4172
+ * 기준점을 맞춘다 (서버 규약: `app/game/room.go` 의 emitSyncLostHintIfNeeded).
4173
+ *
4174
+ * 서버는 버퍼가 계속 막혀 있으면 tick 마다 재시도하므로 두 겹으로 억제한다 —
4175
+ * 이미 재동기화가 진행 중이면 건너뛰고, 최소 간격 안의 중복 통지도 무시한다.
4176
+ * 안 그러면 혼잡한 상황에서 get_state 폭풍이 혼잡을 더 키운다.
4177
+ */
4178
+ handleSyncLost(msg) {
4179
+ this.handlers.onSyncLost?.({
4180
+ roomId: msg.room_id || "",
4181
+ tick: msg.tick || 0,
4182
+ hint: msg.hint || "",
4183
+ serverTime: msg.server_time || 0
4184
+ });
4185
+ if (this.resyncInFlight) return;
4186
+ const now = Date.now();
4187
+ if (now - this.lastResyncAt < SYNC_LOST_RESYNC_MIN_INTERVAL_MS) return;
4188
+ this.resyncInFlight = true;
4189
+ this.lastResyncAt = now;
4190
+ this.requestState().catch((err) => {
4191
+ console.warn("[connect-base game] sync_lost resync failed:", err);
4192
+ }).finally(() => {
4193
+ this.resyncInFlight = false;
4194
+ });
4195
+ }
4140
4196
  handleDelta(msg) {
4141
4197
  const raw = msg.delta;
4142
4198
  if (!raw) return;
@@ -4774,6 +4830,54 @@ var GameAPI = class {
4774
4830
  "GAME_DELETE_SCRIPT_FAILED"
4775
4831
  );
4776
4832
  }
4833
+ /**
4834
+ * 스크립트 로그 조회 — 훅 에러 + Lua `log()` 출력.
4835
+ *
4836
+ * **서버 Lua 를 디버깅하는 유일한 경로다.** 업로드 → 활성화 후 게임을 돌려보고
4837
+ * 이 메서드로 실패 원인을 확인한다. 에러 줄에는 훅 이름, setup/hook 실측 시간,
4838
+ * Lua traceback 의 파일:줄이 함께 실린다:
4839
+ *
4840
+ * ```
4841
+ * [onTick] onTick error: <string>:12: attempt to index a nil value
4842
+ * (setup 3ms / hook 5ms)
4843
+ * ```
4844
+ *
4845
+ * 예산 초과(`SCRIPT_TIMEOUT`)면 한도와 조치 방향까지 덧붙는다.
4846
+ *
4847
+ * @param since epoch ms. 직전 호출의 최신 timestamp 를 넣어 증분 폴링한다.
4848
+ * @param limit 1~1000 (기본 100)
4849
+ *
4850
+ * 버퍼는 스크립트당 최근 ~500 엔트리이며 파드 로컬이라 재시작 시 소실된다.
4851
+ */
4852
+ async getScriptLogs(appId, name, options = {}) {
4853
+ const params = new URLSearchParams();
4854
+ if (typeof options.since === "number")
4855
+ params.set("since", String(options.since));
4856
+ if (typeof options.limit === "number")
4857
+ params.set("limit", String(options.limit));
4858
+ const qs = params.toString();
4859
+ return this.gameFetch(
4860
+ "GET",
4861
+ `/v1/game/${appId}/scripts/${name}/logs${qs ? `?${qs}` : ""}`,
4862
+ void 0,
4863
+ "GAME_GET_SCRIPT_LOGS_FAILED"
4864
+ );
4865
+ }
4866
+ /**
4867
+ * 스크립트 메트릭 조회 — 호출 수, 에러율, setup/hook 레이턴시.
4868
+ *
4869
+ * `SCRIPT_TIMEOUT` 진단에 쓴다. `maxHookLatencyMs` 가 frame 훅 예산(기본 100ms)에
4870
+ * 근접하면 무거운 작업을 `onInit`(5s 예산)으로 옮겨야 한다는 신호다.
4871
+ * `entity.*` 같은 primitive 는 블로킹 I/O 라 호출한 훅의 예산을 소모한다.
4872
+ */
4873
+ async getScriptMetrics(appId, name) {
4874
+ return this.gameFetch(
4875
+ "GET",
4876
+ `/v1/game/${appId}/scripts/${name}/metrics`,
4877
+ void 0,
4878
+ "GAME_GET_SCRIPT_METRICS_FAILED"
4879
+ );
4880
+ }
4777
4881
  };
4778
4882
 
4779
4883
  // src/api/knowledge.ts
@@ -11938,6 +12042,14 @@ var HttpClient = class {
11938
12042
  };
11939
12043
 
11940
12044
  // src/api/game-transport.ts
12045
+ function requireClientId(clientId) {
12046
+ if (!clientId) {
12047
+ throw new Error(
12048
+ "cb.game: clientId is required to build a game connection URL. Pass it via `cb.game.createClient({ appId, clientId })`."
12049
+ );
12050
+ }
12051
+ return clientId;
12052
+ }
11941
12053
  var WebTransportTransport = class {
11942
12054
  constructor(config, onMessage, onClose, onError) {
11943
12055
  this.type = "webtransport";
@@ -11968,7 +12080,7 @@ var WebTransportTransport = class {
11968
12080
  const baseUrl = this.config.gameServerUrl || "https://game.connectbase.world";
11969
12081
  const httpsUrl = baseUrl.replace(/^ws/, "http").replace(/^http:/, "https:");
11970
12082
  const params = new URLSearchParams();
11971
- params.set("client_id", this.config.clientId);
12083
+ params.set("client_id", requireClientId(this.config.clientId));
11972
12084
  if (this.config.publicKey) {
11973
12085
  params.set("public_key", this.config.publicKey);
11974
12086
  }
@@ -12093,15 +12205,19 @@ var WebSocketTransport = class {
12093
12205
  const baseUrl = this.config.gameServerUrl || "wss://game.connectbase.world";
12094
12206
  const wsUrl = baseUrl.replace(/^http/, "ws");
12095
12207
  const params = new URLSearchParams();
12096
- params.set("client_id", this.config.clientId);
12208
+ params.set("client_id", requireClientId(this.config.clientId));
12097
12209
  if (this.config.publicKey) {
12098
12210
  params.set("public_key", this.config.publicKey);
12099
12211
  }
12100
12212
  if (this.config.accessToken) {
12101
12213
  params.set("token", this.config.accessToken);
12102
12214
  }
12103
- const appId = this.config.appId || "";
12104
- return `${wsUrl}/v1/game/${appId}/ws?${params.toString()}`;
12215
+ if (!this.config.appId) {
12216
+ throw new Error(
12217
+ "cb.game: appId is required to build a game connection URL. Pass it to `new ConnectBase({ appId })` or per-client via `cb.game.createClient({ appId, clientId })`."
12218
+ );
12219
+ }
12220
+ return `${wsUrl}/v1/game/${this.config.appId}/ws?${params.toString()}`;
12105
12221
  }
12106
12222
  disconnect() {
12107
12223
  if (this.ws) {
package/package.json CHANGED
@@ -1,77 +1,77 @@
1
1
  {
2
- "name": "connectbase-client",
3
- "version": "5.6.4",
4
- "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
- "repository": {
6
- "type": "git",
7
- "url": "git+https://github.com/connectbase-world/connectbase.git",
8
- "directory": "frontend/package/public/connect-base-client"
9
- },
10
- "homepage": "https://github.com/connectbase-world/connectbase/tree/release/frontend/package/public/connect-base-client#readme",
11
- "bugs": {
12
- "url": "https://github.com/connectbase-world/connectbase/issues"
13
- },
14
- "publishConfig": {
15
- "access": "public"
16
- },
17
- "sideEffects": false,
18
- "main": "dist/index.js",
19
- "module": "dist/index.mjs",
20
- "types": "dist/index.d.ts",
21
- "browser": "dist/connect-base.umd.js",
22
- "unpkg": "dist/connect-base.umd.js",
23
- "bin": {
24
- "connectbase": "dist/cli.js",
25
- "connectbase-client": "dist/cli.js"
26
- },
27
- "exports": {
28
- ".": {
29
- "types": "./dist/index.d.ts",
30
- "import": "./dist/index.mjs",
31
- "require": "./dist/index.js"
32
- }
33
- },
34
- "files": [
35
- "dist",
36
- "LICENSE",
37
- "CHANGELOG.md",
38
- "README.md",
39
- "MIGRATION-v2.md"
40
- ],
41
- "scripts": {
42
- "build": "tsup",
43
- "dev": "tsup --watch",
44
- "typecheck": "tsc --noEmit",
45
- "test": "vitest run",
46
- "test:types": "tsc --noEmit -p test/tsconfig.json",
47
- "release": "pnpm build && npm publish --access public",
48
- "lint": "biome lint .",
49
- "format": "biome format --write .",
50
- "check": "biome check ."
51
- },
52
- "keywords": [
53
- "connect-base",
54
- "baas",
55
- "backend-as-a-service",
56
- "database",
57
- "storage",
58
- "sdk",
59
- "cli",
60
- "deploy",
61
- "ai",
62
- "streaming",
63
- "realtime",
64
- "tunnel"
65
- ],
66
- "author": "Connect Base",
67
- "license": "MIT",
68
- "devDependencies": {
69
- "@types/node": "^22.15.18",
70
- "tsup": "^8.5.1",
71
- "typescript": "^5.9.3",
72
- "vitest": "^3.2.4"
73
- },
74
- "engines": {
75
- "node": ">=16"
76
- }
2
+ "name": "connectbase-client",
3
+ "version": "5.8.0",
4
+ "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/connectbase-world/connectbase.git",
8
+ "directory": "frontend/package/public/connect-base-client"
9
+ },
10
+ "homepage": "https://github.com/connectbase-world/connectbase/tree/release/frontend/package/public/connect-base-client#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/connectbase-world/connectbase/issues"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "sideEffects": false,
18
+ "main": "dist/index.js",
19
+ "module": "dist/index.mjs",
20
+ "types": "dist/index.d.ts",
21
+ "browser": "dist/connect-base.umd.js",
22
+ "unpkg": "dist/connect-base.umd.js",
23
+ "bin": {
24
+ "connectbase": "dist/cli.js",
25
+ "connectbase-client": "dist/cli.js"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.mjs",
31
+ "require": "./dist/index.js"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "LICENSE",
37
+ "CHANGELOG.md",
38
+ "README.md",
39
+ "MIGRATION-v2.md"
40
+ ],
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "dev": "tsup --watch",
44
+ "typecheck": "tsc --noEmit",
45
+ "test": "vitest run",
46
+ "test:types": "tsc --noEmit -p test/tsconfig.json",
47
+ "release": "pnpm build && npm publish --access public",
48
+ "lint": "biome lint .",
49
+ "format": "biome format --write .",
50
+ "check": "biome check ."
51
+ },
52
+ "keywords": [
53
+ "connect-base",
54
+ "baas",
55
+ "backend-as-a-service",
56
+ "database",
57
+ "storage",
58
+ "sdk",
59
+ "cli",
60
+ "deploy",
61
+ "ai",
62
+ "streaming",
63
+ "realtime",
64
+ "tunnel"
65
+ ],
66
+ "author": "Connect Base",
67
+ "license": "MIT",
68
+ "devDependencies": {
69
+ "@types/node": "^22.15.18",
70
+ "tsup": "^8.5.1",
71
+ "typescript": "^5.9.3",
72
+ "vitest": "^3.2.4"
73
+ },
74
+ "engines": {
75
+ "node": ">=16"
76
+ }
77
77
  }