connectbase-client 3.13.1 → 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/dist/index.mjs CHANGED
@@ -14,6 +14,20 @@ var AuthError = class extends Error {
14
14
  this.name = "AuthError";
15
15
  }
16
16
  };
17
+ var GameError = class extends Error {
18
+ constructor(init) {
19
+ super(init.message || init.code || "GameError");
20
+ this.name = "GameError";
21
+ this.code = init.code || "UNKNOWN";
22
+ this.phase = init.phase;
23
+ this.feature = init.feature;
24
+ this.roomId = init.roomId;
25
+ this.scriptId = init.scriptId;
26
+ this.originClientId = init.originClientId;
27
+ this.requested = init.requested;
28
+ this.available = init.available;
29
+ }
30
+ };
17
31
 
18
32
  // src/core/abort.ts
19
33
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
@@ -6235,6 +6249,21 @@ var GameConfigAPI = class {
6235
6249
  };
6236
6250
 
6237
6251
  // src/api/game.ts
6252
+ function parseGameError(msg) {
6253
+ const payload = msg.data ?? msg;
6254
+ const get = (k) => payload[k];
6255
+ return new GameError({
6256
+ code: get("code") || msg.code,
6257
+ message: get("message") || "Unknown error",
6258
+ phase: get("phase") || msg.phase,
6259
+ feature: get("feature") || msg.feature,
6260
+ roomId: get("room_id") || msg.room_id,
6261
+ scriptId: get("script_id") || msg.script_id,
6262
+ originClientId: get("origin_client_id") || msg.origin_client_id,
6263
+ requested: get("requested") || msg.requested,
6264
+ available: get("available") || msg.available
6265
+ });
6266
+ }
6238
6267
  var getDefaultGameServerUrl = () => {
6239
6268
  if (typeof window !== "undefined") {
6240
6269
  const hostname = window.location.hostname;
@@ -6264,6 +6293,8 @@ var GameRoom = class {
6264
6293
  this.actionSequence = 0;
6265
6294
  this._roomId = null;
6266
6295
  this._state = null;
6296
+ this._scriptName = null;
6297
+ this._scriptVersion = null;
6267
6298
  this._isConnected = false;
6268
6299
  this.msgIdCounter = 0;
6269
6300
  this.config = {
@@ -6353,21 +6384,48 @@ var GameRoom = class {
6353
6384
  }
6354
6385
  this._isConnected = false;
6355
6386
  this._roomId = null;
6387
+ this._scriptName = null;
6388
+ this._scriptVersion = null;
6356
6389
  }
6357
6390
  /**
6358
- * 룸 생성
6391
+ * 룸 생성 (호환 시그니처) — 초기 상태만 반환.
6392
+ *
6393
+ * Attached script 의 검증(scriptName/scriptVersion echo 검사) 이 필요하면
6394
+ * 인스턴스 getter (`room.scriptName`, `room.scriptVersion`) 를 함께 사용하거나,
6395
+ * 메타까지 한 번에 받고 싶다면 {@link createRoomDetailed} 를 사용한다.
6396
+ *
6397
+ * 미존재/비활성 scriptName 은 서버가 `SCRIPT_NOT_FOUND` 로 즉시 reject 한다.
6398
+ * reject 값은 `GameError` 인스턴스이며 `.code`/`.requested`/`.available` 로 분기 가능.
6359
6399
  */
6360
6400
  createRoom(config = {}) {
6401
+ return this.createRoomDetailed(config).then((r) => r.state);
6402
+ }
6403
+ /**
6404
+ * 룸 생성 + 메타 — server 가 attach 한 lua script 이름/버전을 함께 반환.
6405
+ *
6406
+ * @example
6407
+ * const { state, scriptName, scriptVersion } = await room.createRoomDetailed({ scriptName: 'njb-main' })
6408
+ * if (scriptName !== 'njb-main') throw new Error('script mismatch')
6409
+ * console.log('attached', scriptName, 'v', scriptVersion)
6410
+ */
6411
+ createRoomDetailed(config = {}) {
6361
6412
  return new Promise((resolve, reject) => {
6362
6413
  const handler = (msg) => {
6363
6414
  if (msg.type === "room_created") {
6364
6415
  const data = msg.data;
6365
6416
  this._roomId = data.room_id;
6366
6417
  this._state = data.initial_state;
6367
- resolve(data.initial_state);
6418
+ this._scriptName = data.script_name ?? null;
6419
+ this._scriptVersion = data.script_version ?? null;
6420
+ resolve({
6421
+ roomId: data.room_id,
6422
+ state: data.initial_state,
6423
+ scriptName: data.script_name,
6424
+ scriptVersion: data.script_version
6425
+ });
6368
6426
  return true;
6369
6427
  } else if (msg.type === "error") {
6370
- reject(new Error(msg.data.message));
6428
+ reject(parseGameError(msg));
6371
6429
  return true;
6372
6430
  }
6373
6431
  return false;
@@ -6375,6 +6433,14 @@ var GameRoom = class {
6375
6433
  this.sendWithHandler("create_room", toCreateRoomWire(config), handler, 15e3, reject);
6376
6434
  });
6377
6435
  }
6436
+ /** Attached lua script 이름 — createRoom 이후 server 가 echo 한 값. */
6437
+ get scriptName() {
6438
+ return this._scriptName;
6439
+ }
6440
+ /** Attached lua script 의 active version. */
6441
+ get scriptVersion() {
6442
+ return this._scriptVersion;
6443
+ }
6378
6444
  /**
6379
6445
  * 룸 참가
6380
6446
  */
@@ -6385,10 +6451,12 @@ var GameRoom = class {
6385
6451
  const data = msg.data;
6386
6452
  this._roomId = data.room_id;
6387
6453
  this._state = data.initial_state;
6454
+ this._scriptName = null;
6455
+ this._scriptVersion = null;
6388
6456
  resolve(data.initial_state);
6389
6457
  return true;
6390
6458
  } else if (msg.type === "error") {
6391
- reject(new Error(msg.data.message));
6459
+ reject(parseGameError(msg));
6392
6460
  return true;
6393
6461
  }
6394
6462
  return false;
@@ -6402,17 +6470,19 @@ var GameRoom = class {
6402
6470
  leaveRoom() {
6403
6471
  return new Promise((resolve, reject) => {
6404
6472
  if (!this._roomId) {
6405
- reject(new Error("Not in a room"));
6473
+ reject(new GameError({ code: "NOT_IN_ROOM", message: "Not in a room" }));
6406
6474
  return;
6407
6475
  }
6408
6476
  const handler = (msg) => {
6409
6477
  if (msg.type === "room_left") {
6410
6478
  this._roomId = null;
6411
6479
  this._state = null;
6480
+ this._scriptName = null;
6481
+ this._scriptVersion = null;
6412
6482
  resolve();
6413
6483
  return true;
6414
6484
  } else if (msg.type === "error") {
6415
- reject(new Error(msg.data.message));
6485
+ reject(parseGameError(msg));
6416
6486
  return true;
6417
6487
  }
6418
6488
  return false;
@@ -6425,7 +6495,7 @@ var GameRoom = class {
6425
6495
  */
6426
6496
  sendAction(action) {
6427
6497
  if (!this._roomId) {
6428
- throw new Error("Not in a room");
6498
+ throw new GameError({ code: "NOT_IN_ROOM", message: "Not in a room" });
6429
6499
  }
6430
6500
  this.send("action", {
6431
6501
  type: action.type,
@@ -6439,7 +6509,7 @@ var GameRoom = class {
6439
6509
  */
6440
6510
  sendChat(message) {
6441
6511
  if (!this._roomId) {
6442
- throw new Error("Not in a room");
6512
+ throw new GameError({ code: "NOT_IN_ROOM", message: "Not in a room" });
6443
6513
  }
6444
6514
  this.send("chat", { message });
6445
6515
  }
@@ -6449,7 +6519,7 @@ var GameRoom = class {
6449
6519
  requestState() {
6450
6520
  return new Promise((resolve, reject) => {
6451
6521
  if (!this._roomId) {
6452
- reject(new Error("Not in a room"));
6522
+ reject(new GameError({ code: "NOT_IN_ROOM", message: "Not in a room" }));
6453
6523
  return;
6454
6524
  }
6455
6525
  const handler = (msg) => {
@@ -6459,7 +6529,7 @@ var GameRoom = class {
6459
6529
  resolve(state);
6460
6530
  return true;
6461
6531
  } else if (msg.type === "error") {
6462
- reject(new Error(msg.data.message));
6532
+ reject(parseGameError(msg));
6463
6533
  return true;
6464
6534
  }
6465
6535
  return false;
@@ -6478,7 +6548,7 @@ var GameRoom = class {
6478
6548
  resolve(data.rooms);
6479
6549
  return true;
6480
6550
  } else if (msg.type === "error") {
6481
- reject(new Error(msg.data.message));
6551
+ reject(parseGameError(msg));
6482
6552
  return true;
6483
6553
  }
6484
6554
  return false;
@@ -6500,7 +6570,7 @@ var GameRoom = class {
6500
6570
  resolve(rtt);
6501
6571
  return true;
6502
6572
  } else if (msg.type === "error") {
6503
- reject(new Error(msg.data.message));
6573
+ reject(parseGameError(msg));
6504
6574
  return true;
6505
6575
  }
6506
6576
  return false;
@@ -6558,12 +6628,12 @@ var GameRoom = class {
6558
6628
  this.ws?.addEventListener("message", messageHandler);
6559
6629
  timeoutId = setTimeout(() => {
6560
6630
  cleanup();
6561
- const err = new Error(`Request '${type}' timed out after ${timeoutMs}ms`);
6562
- onError?.(err);
6563
- this.handlers.onError?.({
6631
+ const err = new GameError({
6564
6632
  code: "TIMEOUT",
6565
- message: err.message
6633
+ message: `Request '${type}' timed out after ${timeoutMs}ms`
6566
6634
  });
6635
+ onError?.(err);
6636
+ this.handlers.onError?.(err);
6567
6637
  }, timeoutMs);
6568
6638
  try {
6569
6639
  this.send(type, data, msgId);
@@ -6597,10 +6667,7 @@ var GameRoom = class {
6597
6667
  });
6598
6668
  break;
6599
6669
  case "error":
6600
- this.handlers.onError?.({
6601
- code: msg.code || "UNKNOWN",
6602
- message: msg.message || "Unknown error"
6603
- });
6670
+ this.handlers.onError?.(parseGameError(msg));
6604
6671
  break;
6605
6672
  default:
6606
6673
  break;
@@ -6678,10 +6745,10 @@ var GameRoom = class {
6678
6745
  scheduleReconnect(roomId) {
6679
6746
  if (this.reconnectAttempts >= (this.config.maxReconnectAttempts ?? 5)) {
6680
6747
  console.error("Max reconnect attempts reached");
6681
- this.handlers.onError?.({
6748
+ this.handlers.onError?.(new GameError({
6682
6749
  code: "MAX_RECONNECT_ATTEMPTS",
6683
6750
  message: "Maximum reconnection attempts reached"
6684
- });
6751
+ }));
6685
6752
  return;
6686
6753
  }
6687
6754
  const delay = Math.min(
@@ -6702,10 +6769,11 @@ var GameRoom = class {
6702
6769
  console.log(`Successfully rejoined room ${previousRoomId}`);
6703
6770
  } catch (rejoinError) {
6704
6771
  console.error(`Failed to rejoin room ${previousRoomId}:`, rejoinError);
6705
- this.handlers.onError?.({
6772
+ this.handlers.onError?.(new GameError({
6706
6773
  code: "REJOIN_FAILED",
6707
- message: `Failed to rejoin room: ${rejoinError}`
6708
- });
6774
+ message: `Failed to rejoin room: ${rejoinError}`,
6775
+ roomId: previousRoomId
6776
+ }));
6709
6777
  }
6710
6778
  }
6711
6779
  } catch {
@@ -7069,9 +7137,26 @@ var GameAPI = class {
7069
7137
  );
7070
7138
  }
7071
7139
  /**
7072
- * 스크립트 비활성화 (status=disabled). 코드/버전은 보존.
7140
+ * 스크립트 비활성화 (status=disabled, ActiveVersion=0). 코드/모든 버전은 보존되며
7141
+ * 이후 {@link activateScript} 로 재활성화할 수 있다.
7142
+ *
7143
+ * v3.14.0 BREAKING — 이전엔 `disableScript` 가 `DELETE /scripts/:name` 을 호출해
7144
+ * Disable 매핑이었으나, server 측 `DELETE` 가 hard-delete 로 분리됨에 따라
7145
+ * `POST /scripts/:name/deactivate` 로 endpoint 이동. 메서드명도 의도 명확화 위해 rename.
7146
+ */
7147
+ async deactivateScript(appId, name) {
7148
+ await this.http.post(`/v1/game/${appId}/scripts/${name}/deactivate`, {});
7149
+ }
7150
+ /**
7151
+ * 스크립트 영구 삭제 (hard-delete) — 메타 + 모든 버전 영구 제거. 복구 불가.
7152
+ *
7153
+ * 잘못 업로드된 스크립트나 더 이상 사용하지 않는 슬롯을 정리할 때 사용. 단순 비활성화는
7154
+ * {@link deactivateScript} 를 사용한다 (코드 보존, 재활성화 가능).
7155
+ *
7156
+ * v3.14.0 신규 — server 측 `DELETE /scripts/:name` 의 의미가 Disable → hard-delete 로
7157
+ * 변경된 것에 대응.
7073
7158
  */
7074
- async disableScript(appId, name) {
7159
+ async deleteScript(appId, name) {
7075
7160
  await this.http.delete(`/v1/game/${appId}/scripts/${name}`);
7076
7161
  }
7077
7162
  };
@@ -9378,7 +9463,7 @@ var WebTransportTransport = class {
9378
9463
  }
9379
9464
  send(data, reliable = true) {
9380
9465
  if (!this.transport) {
9381
- throw new Error("Not connected");
9466
+ throw new GameError({ code: "NOT_CONNECTED", message: "Not connected" });
9382
9467
  }
9383
9468
  const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
9384
9469
  if (reliable) {
@@ -9469,7 +9554,7 @@ var WebSocketTransport = class {
9469
9554
  }
9470
9555
  send(data, _reliable) {
9471
9556
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
9472
- throw new Error("Not connected");
9557
+ throw new GameError({ code: "NOT_CONNECTED", message: "Not connected" });
9473
9558
  }
9474
9559
  if (typeof data === "string") {
9475
9560
  this.ws.send(data);
@@ -9494,6 +9579,8 @@ var GameRoomTransport = class {
9494
9579
  this.actionSequence = 0;
9495
9580
  this._roomId = null;
9496
9581
  this._state = null;
9582
+ this._scriptName = null;
9583
+ this._scriptVersion = null;
9497
9584
  this._isConnected = false;
9498
9585
  this._connectionStatus = "disconnected";
9499
9586
  this._lastError = null;
@@ -9546,6 +9633,14 @@ var GameRoomTransport = class {
9546
9633
  get isConnected() {
9547
9634
  return this._isConnected;
9548
9635
  }
9636
+ /** Attached lua script 이름 — createRoom 이후 server 가 echo 한 값. */
9637
+ get scriptName() {
9638
+ return this._scriptName;
9639
+ }
9640
+ /** Attached lua script 의 active version. */
9641
+ get scriptVersion() {
9642
+ return this._scriptVersion;
9643
+ }
9549
9644
  /**
9550
9645
  * Connection state information
9551
9646
  */
@@ -9598,7 +9693,7 @@ var GameRoomTransport = class {
9598
9693
  const onError = (error) => {
9599
9694
  this._connectionStatus = "error";
9600
9695
  this._lastError = error;
9601
- this.handlers.onError?.({ code: "CONNECTION_ERROR", message: error.message });
9696
+ this.handlers.onError?.(new GameError({ code: "CONNECTION_ERROR", message: error.message }));
9602
9697
  };
9603
9698
  if (useWebTransport) {
9604
9699
  try {
@@ -9660,18 +9755,35 @@ var GameRoomTransport = class {
9660
9755
  this._state = null;
9661
9756
  }
9662
9757
  /**
9663
- * Create a new room
9758
+ * Create a new room (호환 시그니처) — 초기 상태만 반환.
9759
+ *
9760
+ * Attached script 메타가 필요하면 {@link createRoomDetailed} 또는 인스턴스 getter
9761
+ * (`transport.scriptName`, `transport.scriptVersion`) 를 사용.
9664
9762
  */
9665
9763
  async createRoom(config = {}) {
9764
+ const r = await this.createRoomDetailed(config);
9765
+ return r.state;
9766
+ }
9767
+ /**
9768
+ * Create a new room — server 가 attach 한 lua script 메타 함께 반환.
9769
+ */
9770
+ async createRoomDetailed(config = {}) {
9666
9771
  return new Promise((resolve, reject) => {
9667
9772
  const handler = (msg) => {
9668
9773
  if (msg.type === "room_created") {
9669
9774
  const data = msg.data;
9670
9775
  this._roomId = data.room_id;
9671
9776
  this._state = data.initial_state;
9672
- resolve(data.initial_state);
9777
+ this._scriptName = data.script_name ?? null;
9778
+ this._scriptVersion = data.script_version ?? null;
9779
+ resolve({
9780
+ roomId: data.room_id,
9781
+ state: data.initial_state,
9782
+ scriptName: data.script_name,
9783
+ scriptVersion: data.script_version
9784
+ });
9673
9785
  } else if (msg.type === "error") {
9674
- reject(new Error(msg.data.message));
9786
+ reject(parseGameError(msg));
9675
9787
  }
9676
9788
  };
9677
9789
  this.sendWithHandler("create_room", toCreateRoomWire(config), handler);
@@ -9687,9 +9799,11 @@ var GameRoomTransport = class {
9687
9799
  const data = msg.data;
9688
9800
  this._roomId = data.room_id;
9689
9801
  this._state = data.initial_state;
9802
+ this._scriptName = null;
9803
+ this._scriptVersion = null;
9690
9804
  resolve(data.initial_state);
9691
9805
  } else if (msg.type === "error") {
9692
- reject(new Error(msg.data.message));
9806
+ reject(parseGameError(msg));
9693
9807
  }
9694
9808
  };
9695
9809
  this.sendWithHandler("join_room", { room_id: roomId, metadata }, handler);
@@ -9701,16 +9815,18 @@ var GameRoomTransport = class {
9701
9815
  async leaveRoom() {
9702
9816
  return new Promise((resolve, reject) => {
9703
9817
  if (!this._roomId) {
9704
- reject(new Error("Not in a room"));
9818
+ reject(new GameError({ code: "NOT_IN_ROOM", message: "Not in a room" }));
9705
9819
  return;
9706
9820
  }
9707
9821
  const handler = (msg) => {
9708
9822
  if (msg.type === "room_left") {
9709
9823
  this._roomId = null;
9710
9824
  this._state = null;
9825
+ this._scriptName = null;
9826
+ this._scriptVersion = null;
9711
9827
  resolve();
9712
9828
  } else if (msg.type === "error") {
9713
- reject(new Error(msg.data.message));
9829
+ reject(parseGameError(msg));
9714
9830
  }
9715
9831
  };
9716
9832
  this.sendWithHandler("leave_room", {}, handler);
@@ -9722,7 +9838,7 @@ var GameRoomTransport = class {
9722
9838
  */
9723
9839
  sendAction(action, reliable = false) {
9724
9840
  if (!this._roomId) {
9725
- throw new Error("Not in a room");
9841
+ throw new GameError({ code: "NOT_IN_ROOM", message: "Not in a room" });
9726
9842
  }
9727
9843
  const message = JSON.stringify({
9728
9844
  type: "action",
@@ -9741,7 +9857,7 @@ var GameRoomTransport = class {
9741
9857
  */
9742
9858
  sendChat(message) {
9743
9859
  if (!this._roomId) {
9744
- throw new Error("Not in a room");
9860
+ throw new GameError({ code: "NOT_IN_ROOM", message: "Not in a room" });
9745
9861
  }
9746
9862
  this.send("chat", { message });
9747
9863
  }
@@ -9751,7 +9867,7 @@ var GameRoomTransport = class {
9751
9867
  async requestState() {
9752
9868
  return new Promise((resolve, reject) => {
9753
9869
  if (!this._roomId) {
9754
- reject(new Error("Not in a room"));
9870
+ reject(new GameError({ code: "NOT_IN_ROOM", message: "Not in a room" }));
9755
9871
  return;
9756
9872
  }
9757
9873
  const handler = (msg) => {
@@ -9760,7 +9876,7 @@ var GameRoomTransport = class {
9760
9876
  this._state = state;
9761
9877
  resolve(state);
9762
9878
  } else if (msg.type === "error") {
9763
- reject(new Error(msg.data.message));
9879
+ reject(parseGameError(msg));
9764
9880
  }
9765
9881
  };
9766
9882
  this.sendWithHandler("get_state", {}, handler);
@@ -9780,7 +9896,7 @@ var GameRoomTransport = class {
9780
9896
  this.handlers.onPong?.(pong);
9781
9897
  resolve(rtt);
9782
9898
  } else if (msg.type === "error") {
9783
- reject(new Error(msg.data.message));
9899
+ reject(parseGameError(msg));
9784
9900
  }
9785
9901
  };
9786
9902
  this.sendWithHandler("ping", { timestamp }, handler);
@@ -9789,7 +9905,7 @@ var GameRoomTransport = class {
9789
9905
  // Private methods
9790
9906
  send(type, data) {
9791
9907
  if (!this.transport?.isConnected()) {
9792
- throw new Error("Not connected");
9908
+ throw new GameError({ code: "NOT_CONNECTED", message: "Not connected" });
9793
9909
  }
9794
9910
  const message = JSON.stringify({ type, data });
9795
9911
  this.transport.send(message, true);
@@ -9832,14 +9948,7 @@ var GameRoomTransport = class {
9832
9948
  });
9833
9949
  break;
9834
9950
  case "error":
9835
- this.handlers.onError?.({
9836
- code: msg.code || "UNKNOWN",
9837
- message: msg.message || "Unknown error",
9838
- phase: msg.phase,
9839
- feature: msg.feature,
9840
- roomId: msg.room_id,
9841
- scriptId: msg.script_id
9842
- });
9951
+ this.handlers.onError?.(parseGameError(msg));
9843
9952
  break;
9844
9953
  case "room_stale":
9845
9954
  if (this.handlers.onRoomStale) {
@@ -10049,6 +10158,7 @@ export {
10049
10158
  EndpointAPI,
10050
10159
  GameAPI,
10051
10160
  GameConfigAPI,
10161
+ GameError,
10052
10162
  GameRoom,
10053
10163
  GameRoomTransport,
10054
10164
  NativeAPI,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "3.13.1",
3
+ "version": "3.14.0",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",