connectbase-client 3.10.0 → 3.12.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 +96 -0
- package/README.md +4 -1
- package/dist/connect-base.umd.js +4 -4
- package/dist/index.d.mts +154 -8
- package/dist/index.d.ts +154 -8
- package/dist/index.js +115 -7
- package/dist/index.mjs +113 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,7 +35,8 @@ __export(index_exports, {
|
|
|
35
35
|
SessionManager: () => SessionManager,
|
|
36
36
|
VideoProcessingError: () => VideoProcessingError,
|
|
37
37
|
default: () => index_default,
|
|
38
|
-
isWebTransportSupported: () => isWebTransportSupported
|
|
38
|
+
isWebTransportSupported: () => isWebTransportSupported,
|
|
39
|
+
toCreateRoomWire: () => toCreateRoomWire
|
|
39
40
|
});
|
|
40
41
|
module.exports = __toCommonJS(index_exports);
|
|
41
42
|
|
|
@@ -1343,23 +1344,38 @@ var DatabaseAPI = class {
|
|
|
1343
1344
|
// ============ Batch & Transactions ============
|
|
1344
1345
|
/**
|
|
1345
1346
|
* 배치 쓰기 (여러 테이블 다중 문서 원자적 처리)
|
|
1347
|
+
*
|
|
1348
|
+
* 서버는 HTTP 200 + `success:false` + 개별 op `error` 로 부분 실패를 표현한다.
|
|
1349
|
+
* SDK 는 호출자가 silent success 로 오해하지 않도록 첫 실패 op 의 메시지로 throw 한다.
|
|
1346
1350
|
*/
|
|
1347
1351
|
async batch(operations) {
|
|
1348
1352
|
const prefix = this.getPublicPrefix();
|
|
1349
|
-
|
|
1353
|
+
const response = await this.http.post(
|
|
1350
1354
|
`${prefix}/batch`,
|
|
1351
1355
|
{ operations }
|
|
1352
1356
|
);
|
|
1357
|
+
if (response && response.success === false) {
|
|
1358
|
+
const first = response.results?.find((r) => r && r.success === false);
|
|
1359
|
+
throw new Error(first?.error || "batch operation failed");
|
|
1360
|
+
}
|
|
1361
|
+
return response;
|
|
1353
1362
|
}
|
|
1354
1363
|
/**
|
|
1355
1364
|
* 트랜잭션 실행 (읽기 → 쓰기 ACID)
|
|
1365
|
+
*
|
|
1366
|
+
* 서버는 부분 실패가 없는 ACID 트랜잭션을 보장하지만, 검증/RLS/충돌은
|
|
1367
|
+
* `success:false` + `error` 로 알린다. SDK 는 silent success 회귀 방지 차원에서 throw.
|
|
1356
1368
|
*/
|
|
1357
1369
|
async transaction(reads, writes) {
|
|
1358
1370
|
const prefix = this.getPublicPrefix();
|
|
1359
|
-
|
|
1371
|
+
const response = await this.http.post(
|
|
1360
1372
|
`${prefix}/transactions`,
|
|
1361
1373
|
{ reads, writes }
|
|
1362
1374
|
);
|
|
1375
|
+
if (response && response.success === false) {
|
|
1376
|
+
throw new Error(response.error || "transaction failed");
|
|
1377
|
+
}
|
|
1378
|
+
return response;
|
|
1363
1379
|
}
|
|
1364
1380
|
// ============ Populate (Relation Query) ============
|
|
1365
1381
|
/**
|
|
@@ -6270,6 +6286,16 @@ var getDefaultGameServerUrl = () => {
|
|
|
6270
6286
|
}
|
|
6271
6287
|
return "wss://game.connectbase.world";
|
|
6272
6288
|
};
|
|
6289
|
+
function toCreateRoomWire(config) {
|
|
6290
|
+
const out = {};
|
|
6291
|
+
if (config.roomId) out.room_id = config.roomId;
|
|
6292
|
+
if (config.categoryId) out.category_id = config.categoryId;
|
|
6293
|
+
if (typeof config.tickRate === "number") out.tick_rate = config.tickRate;
|
|
6294
|
+
if (typeof config.maxPlayers === "number") out.max_players = config.maxPlayers;
|
|
6295
|
+
if (config.scriptName) out.script_name = config.scriptName;
|
|
6296
|
+
if (config.metadata) out.metadata = config.metadata;
|
|
6297
|
+
return out;
|
|
6298
|
+
}
|
|
6273
6299
|
var GameRoom = class {
|
|
6274
6300
|
constructor(config) {
|
|
6275
6301
|
this.ws = null;
|
|
@@ -6388,7 +6414,7 @@ var GameRoom = class {
|
|
|
6388
6414
|
}
|
|
6389
6415
|
return false;
|
|
6390
6416
|
};
|
|
6391
|
-
this.sendWithHandler("create_room", config, handler, 15e3, reject);
|
|
6417
|
+
this.sendWithHandler("create_room", toCreateRoomWire(config), handler, 15e3, reject);
|
|
6392
6418
|
});
|
|
6393
6419
|
}
|
|
6394
6420
|
/**
|
|
@@ -9104,6 +9130,67 @@ var SupportAPI = class {
|
|
|
9104
9130
|
if (req.recaptchaToken) body.recaptcha_token = req.recaptchaToken;
|
|
9105
9131
|
return this.http.post("/v1/public/reports", body);
|
|
9106
9132
|
}
|
|
9133
|
+
/**
|
|
9134
|
+
* 다른 앱(target) 에 자기 사용자(end-user) 명의로 cross-app 위임 이슈를 발행한다.
|
|
9135
|
+
*
|
|
9136
|
+
* **사용 시나리오**: source_app(예: Makers) 의 backend 가 OAuth `authorization_code` grant 로
|
|
9137
|
+
* 발급받은 사용자 access_token 을 사용해, target_app(예: ai-tool) 에게 "Makers user A 가 보낸"
|
|
9138
|
+
* 이슈로 제보. target 측 콘솔 inbox 에는 `reporter_kind=user` + `reporter_app_id=Makers` 로 표시.
|
|
9139
|
+
*
|
|
9140
|
+
* 본 메서드는 SDK 의 publicKey/secretKey 인증 컨텍스트를 사용하지 않고, 호출자가 명시적으로 전달한
|
|
9141
|
+
* cross-app OAuth Bearer access_token 만 사용 (server-to-server 위임 흐름).
|
|
9142
|
+
*
|
|
9143
|
+
* @param req - 발행 본문
|
|
9144
|
+
* @param req.targetAppId - 수신 앱 ID (= OAuth token 의 audience `app:<uuid>` 와 일치해야 함)
|
|
9145
|
+
* @param req.accessToken - source_app 이 발급받은 cross-app OAuth access_token (authorization_code grant)
|
|
9146
|
+
*
|
|
9147
|
+
* @throws ApiError 401 — `delegated_user_required`: access_token 이 client_credentials grant
|
|
9148
|
+
* @throws ApiError 403 — `trust_chain_violation`: source_app 이 target_app 의 cross-app provider 로 등록되지 않음
|
|
9149
|
+
* @throws ApiError 403 — `insufficient_scope`: token 에 `issue:report:on-behalf-of-user` 미포함
|
|
9150
|
+
*
|
|
9151
|
+
* @example Makers backend → ai-tool 에 사용자 명의 제보
|
|
9152
|
+
* ```typescript
|
|
9153
|
+
* await cb.support.reportIssueOnBehalfOfUser({
|
|
9154
|
+
* targetAppId: 'ai-tool-uuid',
|
|
9155
|
+
* accessToken: makersUserAccessToken, // authorization_code grant 토큰
|
|
9156
|
+
* title: '생성 실패',
|
|
9157
|
+
* body: 'prompt X 가 5분째 응답 없음',
|
|
9158
|
+
* category: 'bug',
|
|
9159
|
+
* metadata: { prompt_id: 'p_42' },
|
|
9160
|
+
* })
|
|
9161
|
+
* ```
|
|
9162
|
+
*/
|
|
9163
|
+
async reportIssueOnBehalfOfUser(req) {
|
|
9164
|
+
if (!req.targetAppId) throw new Error("targetAppId is required");
|
|
9165
|
+
if (!req.accessToken) throw new Error("accessToken is required");
|
|
9166
|
+
const body = {
|
|
9167
|
+
title: req.title,
|
|
9168
|
+
body: req.body
|
|
9169
|
+
};
|
|
9170
|
+
if (req.category) body.category = req.category;
|
|
9171
|
+
if (req.metadata) body.metadata = req.metadata;
|
|
9172
|
+
const url = `${this.http.getBaseUrl()}/v1/integrations/providers/${encodeURIComponent(req.targetAppId)}/issues/by-user`;
|
|
9173
|
+
const response = await fetch(url, {
|
|
9174
|
+
method: "POST",
|
|
9175
|
+
headers: {
|
|
9176
|
+
"Content-Type": "application/json",
|
|
9177
|
+
Authorization: `Bearer ${req.accessToken}`
|
|
9178
|
+
},
|
|
9179
|
+
body: JSON.stringify(body)
|
|
9180
|
+
});
|
|
9181
|
+
if (!response.ok) {
|
|
9182
|
+
let errorBody;
|
|
9183
|
+
try {
|
|
9184
|
+
errorBody = await response.json();
|
|
9185
|
+
} catch {
|
|
9186
|
+
errorBody = { error: response.statusText };
|
|
9187
|
+
}
|
|
9188
|
+
const errObj = errorBody ?? {};
|
|
9189
|
+
const message = errObj.error_description ?? errObj.error ?? `HTTP ${response.status}`;
|
|
9190
|
+
throw new Error(`reportIssueOnBehalfOfUser failed (${response.status}): ${message}`);
|
|
9191
|
+
}
|
|
9192
|
+
return await response.json();
|
|
9193
|
+
}
|
|
9107
9194
|
/**
|
|
9108
9195
|
* ConnectBase 플랫폼 자체 버그/요청/문의를 발행한다.
|
|
9109
9196
|
*
|
|
@@ -9629,7 +9716,7 @@ var GameRoomTransport = class {
|
|
|
9629
9716
|
reject(new Error(msg.data.message));
|
|
9630
9717
|
}
|
|
9631
9718
|
};
|
|
9632
|
-
this.sendWithHandler("create_room", config, handler);
|
|
9719
|
+
this.sendWithHandler("create_room", toCreateRoomWire(config), handler);
|
|
9633
9720
|
});
|
|
9634
9721
|
}
|
|
9635
9722
|
/**
|
|
@@ -9789,9 +9876,29 @@ var GameRoomTransport = class {
|
|
|
9789
9876
|
case "error":
|
|
9790
9877
|
this.handlers.onError?.({
|
|
9791
9878
|
code: msg.code || "UNKNOWN",
|
|
9792
|
-
message: msg.message || "Unknown error"
|
|
9879
|
+
message: msg.message || "Unknown error",
|
|
9880
|
+
phase: msg.phase,
|
|
9881
|
+
feature: msg.feature,
|
|
9882
|
+
roomId: msg.room_id,
|
|
9883
|
+
scriptId: msg.script_id
|
|
9793
9884
|
});
|
|
9794
9885
|
break;
|
|
9886
|
+
case "room_stale":
|
|
9887
|
+
if (this.handlers.onRoomStale) {
|
|
9888
|
+
this.handlers.onRoomStale({
|
|
9889
|
+
reason: msg.reason || "unknown",
|
|
9890
|
+
roomId: msg.room_id || "",
|
|
9891
|
+
scriptId: msg.script_id,
|
|
9892
|
+
scriptVersion: msg.script_version,
|
|
9893
|
+
serverTime: msg.server_time || 0
|
|
9894
|
+
});
|
|
9895
|
+
} else {
|
|
9896
|
+
console.warn(
|
|
9897
|
+
"[connect-base game] room_stale received but onRoomStale handler not set:",
|
|
9898
|
+
{ reason: msg.reason, roomId: msg.room_id, scriptVersion: msg.script_version }
|
|
9899
|
+
);
|
|
9900
|
+
}
|
|
9901
|
+
break;
|
|
9795
9902
|
}
|
|
9796
9903
|
} catch {
|
|
9797
9904
|
console.error("Failed to parse game message:", data);
|
|
@@ -9990,5 +10097,6 @@ var index_default = ConnectBase;
|
|
|
9990
10097
|
NativeAPI,
|
|
9991
10098
|
SessionManager,
|
|
9992
10099
|
VideoProcessingError,
|
|
9993
|
-
isWebTransportSupported
|
|
10100
|
+
isWebTransportSupported,
|
|
10101
|
+
toCreateRoomWire
|
|
9994
10102
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1302,23 +1302,38 @@ var DatabaseAPI = class {
|
|
|
1302
1302
|
// ============ Batch & Transactions ============
|
|
1303
1303
|
/**
|
|
1304
1304
|
* 배치 쓰기 (여러 테이블 다중 문서 원자적 처리)
|
|
1305
|
+
*
|
|
1306
|
+
* 서버는 HTTP 200 + `success:false` + 개별 op `error` 로 부분 실패를 표현한다.
|
|
1307
|
+
* SDK 는 호출자가 silent success 로 오해하지 않도록 첫 실패 op 의 메시지로 throw 한다.
|
|
1305
1308
|
*/
|
|
1306
1309
|
async batch(operations) {
|
|
1307
1310
|
const prefix = this.getPublicPrefix();
|
|
1308
|
-
|
|
1311
|
+
const response = await this.http.post(
|
|
1309
1312
|
`${prefix}/batch`,
|
|
1310
1313
|
{ operations }
|
|
1311
1314
|
);
|
|
1315
|
+
if (response && response.success === false) {
|
|
1316
|
+
const first = response.results?.find((r) => r && r.success === false);
|
|
1317
|
+
throw new Error(first?.error || "batch operation failed");
|
|
1318
|
+
}
|
|
1319
|
+
return response;
|
|
1312
1320
|
}
|
|
1313
1321
|
/**
|
|
1314
1322
|
* 트랜잭션 실행 (읽기 → 쓰기 ACID)
|
|
1323
|
+
*
|
|
1324
|
+
* 서버는 부분 실패가 없는 ACID 트랜잭션을 보장하지만, 검증/RLS/충돌은
|
|
1325
|
+
* `success:false` + `error` 로 알린다. SDK 는 silent success 회귀 방지 차원에서 throw.
|
|
1315
1326
|
*/
|
|
1316
1327
|
async transaction(reads, writes) {
|
|
1317
1328
|
const prefix = this.getPublicPrefix();
|
|
1318
|
-
|
|
1329
|
+
const response = await this.http.post(
|
|
1319
1330
|
`${prefix}/transactions`,
|
|
1320
1331
|
{ reads, writes }
|
|
1321
1332
|
);
|
|
1333
|
+
if (response && response.success === false) {
|
|
1334
|
+
throw new Error(response.error || "transaction failed");
|
|
1335
|
+
}
|
|
1336
|
+
return response;
|
|
1322
1337
|
}
|
|
1323
1338
|
// ============ Populate (Relation Query) ============
|
|
1324
1339
|
/**
|
|
@@ -6229,6 +6244,16 @@ var getDefaultGameServerUrl = () => {
|
|
|
6229
6244
|
}
|
|
6230
6245
|
return "wss://game.connectbase.world";
|
|
6231
6246
|
};
|
|
6247
|
+
function toCreateRoomWire(config) {
|
|
6248
|
+
const out = {};
|
|
6249
|
+
if (config.roomId) out.room_id = config.roomId;
|
|
6250
|
+
if (config.categoryId) out.category_id = config.categoryId;
|
|
6251
|
+
if (typeof config.tickRate === "number") out.tick_rate = config.tickRate;
|
|
6252
|
+
if (typeof config.maxPlayers === "number") out.max_players = config.maxPlayers;
|
|
6253
|
+
if (config.scriptName) out.script_name = config.scriptName;
|
|
6254
|
+
if (config.metadata) out.metadata = config.metadata;
|
|
6255
|
+
return out;
|
|
6256
|
+
}
|
|
6232
6257
|
var GameRoom = class {
|
|
6233
6258
|
constructor(config) {
|
|
6234
6259
|
this.ws = null;
|
|
@@ -6347,7 +6372,7 @@ var GameRoom = class {
|
|
|
6347
6372
|
}
|
|
6348
6373
|
return false;
|
|
6349
6374
|
};
|
|
6350
|
-
this.sendWithHandler("create_room", config, handler, 15e3, reject);
|
|
6375
|
+
this.sendWithHandler("create_room", toCreateRoomWire(config), handler, 15e3, reject);
|
|
6351
6376
|
});
|
|
6352
6377
|
}
|
|
6353
6378
|
/**
|
|
@@ -9063,6 +9088,67 @@ var SupportAPI = class {
|
|
|
9063
9088
|
if (req.recaptchaToken) body.recaptcha_token = req.recaptchaToken;
|
|
9064
9089
|
return this.http.post("/v1/public/reports", body);
|
|
9065
9090
|
}
|
|
9091
|
+
/**
|
|
9092
|
+
* 다른 앱(target) 에 자기 사용자(end-user) 명의로 cross-app 위임 이슈를 발행한다.
|
|
9093
|
+
*
|
|
9094
|
+
* **사용 시나리오**: source_app(예: Makers) 의 backend 가 OAuth `authorization_code` grant 로
|
|
9095
|
+
* 발급받은 사용자 access_token 을 사용해, target_app(예: ai-tool) 에게 "Makers user A 가 보낸"
|
|
9096
|
+
* 이슈로 제보. target 측 콘솔 inbox 에는 `reporter_kind=user` + `reporter_app_id=Makers` 로 표시.
|
|
9097
|
+
*
|
|
9098
|
+
* 본 메서드는 SDK 의 publicKey/secretKey 인증 컨텍스트를 사용하지 않고, 호출자가 명시적으로 전달한
|
|
9099
|
+
* cross-app OAuth Bearer access_token 만 사용 (server-to-server 위임 흐름).
|
|
9100
|
+
*
|
|
9101
|
+
* @param req - 발행 본문
|
|
9102
|
+
* @param req.targetAppId - 수신 앱 ID (= OAuth token 의 audience `app:<uuid>` 와 일치해야 함)
|
|
9103
|
+
* @param req.accessToken - source_app 이 발급받은 cross-app OAuth access_token (authorization_code grant)
|
|
9104
|
+
*
|
|
9105
|
+
* @throws ApiError 401 — `delegated_user_required`: access_token 이 client_credentials grant
|
|
9106
|
+
* @throws ApiError 403 — `trust_chain_violation`: source_app 이 target_app 의 cross-app provider 로 등록되지 않음
|
|
9107
|
+
* @throws ApiError 403 — `insufficient_scope`: token 에 `issue:report:on-behalf-of-user` 미포함
|
|
9108
|
+
*
|
|
9109
|
+
* @example Makers backend → ai-tool 에 사용자 명의 제보
|
|
9110
|
+
* ```typescript
|
|
9111
|
+
* await cb.support.reportIssueOnBehalfOfUser({
|
|
9112
|
+
* targetAppId: 'ai-tool-uuid',
|
|
9113
|
+
* accessToken: makersUserAccessToken, // authorization_code grant 토큰
|
|
9114
|
+
* title: '생성 실패',
|
|
9115
|
+
* body: 'prompt X 가 5분째 응답 없음',
|
|
9116
|
+
* category: 'bug',
|
|
9117
|
+
* metadata: { prompt_id: 'p_42' },
|
|
9118
|
+
* })
|
|
9119
|
+
* ```
|
|
9120
|
+
*/
|
|
9121
|
+
async reportIssueOnBehalfOfUser(req) {
|
|
9122
|
+
if (!req.targetAppId) throw new Error("targetAppId is required");
|
|
9123
|
+
if (!req.accessToken) throw new Error("accessToken is required");
|
|
9124
|
+
const body = {
|
|
9125
|
+
title: req.title,
|
|
9126
|
+
body: req.body
|
|
9127
|
+
};
|
|
9128
|
+
if (req.category) body.category = req.category;
|
|
9129
|
+
if (req.metadata) body.metadata = req.metadata;
|
|
9130
|
+
const url = `${this.http.getBaseUrl()}/v1/integrations/providers/${encodeURIComponent(req.targetAppId)}/issues/by-user`;
|
|
9131
|
+
const response = await fetch(url, {
|
|
9132
|
+
method: "POST",
|
|
9133
|
+
headers: {
|
|
9134
|
+
"Content-Type": "application/json",
|
|
9135
|
+
Authorization: `Bearer ${req.accessToken}`
|
|
9136
|
+
},
|
|
9137
|
+
body: JSON.stringify(body)
|
|
9138
|
+
});
|
|
9139
|
+
if (!response.ok) {
|
|
9140
|
+
let errorBody;
|
|
9141
|
+
try {
|
|
9142
|
+
errorBody = await response.json();
|
|
9143
|
+
} catch {
|
|
9144
|
+
errorBody = { error: response.statusText };
|
|
9145
|
+
}
|
|
9146
|
+
const errObj = errorBody ?? {};
|
|
9147
|
+
const message = errObj.error_description ?? errObj.error ?? `HTTP ${response.status}`;
|
|
9148
|
+
throw new Error(`reportIssueOnBehalfOfUser failed (${response.status}): ${message}`);
|
|
9149
|
+
}
|
|
9150
|
+
return await response.json();
|
|
9151
|
+
}
|
|
9066
9152
|
/**
|
|
9067
9153
|
* ConnectBase 플랫폼 자체 버그/요청/문의를 발행한다.
|
|
9068
9154
|
*
|
|
@@ -9588,7 +9674,7 @@ var GameRoomTransport = class {
|
|
|
9588
9674
|
reject(new Error(msg.data.message));
|
|
9589
9675
|
}
|
|
9590
9676
|
};
|
|
9591
|
-
this.sendWithHandler("create_room", config, handler);
|
|
9677
|
+
this.sendWithHandler("create_room", toCreateRoomWire(config), handler);
|
|
9592
9678
|
});
|
|
9593
9679
|
}
|
|
9594
9680
|
/**
|
|
@@ -9748,9 +9834,29 @@ var GameRoomTransport = class {
|
|
|
9748
9834
|
case "error":
|
|
9749
9835
|
this.handlers.onError?.({
|
|
9750
9836
|
code: msg.code || "UNKNOWN",
|
|
9751
|
-
message: msg.message || "Unknown error"
|
|
9837
|
+
message: msg.message || "Unknown error",
|
|
9838
|
+
phase: msg.phase,
|
|
9839
|
+
feature: msg.feature,
|
|
9840
|
+
roomId: msg.room_id,
|
|
9841
|
+
scriptId: msg.script_id
|
|
9752
9842
|
});
|
|
9753
9843
|
break;
|
|
9844
|
+
case "room_stale":
|
|
9845
|
+
if (this.handlers.onRoomStale) {
|
|
9846
|
+
this.handlers.onRoomStale({
|
|
9847
|
+
reason: msg.reason || "unknown",
|
|
9848
|
+
roomId: msg.room_id || "",
|
|
9849
|
+
scriptId: msg.script_id,
|
|
9850
|
+
scriptVersion: msg.script_version,
|
|
9851
|
+
serverTime: msg.server_time || 0
|
|
9852
|
+
});
|
|
9853
|
+
} else {
|
|
9854
|
+
console.warn(
|
|
9855
|
+
"[connect-base game] room_stale received but onRoomStale handler not set:",
|
|
9856
|
+
{ reason: msg.reason, roomId: msg.room_id, scriptVersion: msg.script_version }
|
|
9857
|
+
);
|
|
9858
|
+
}
|
|
9859
|
+
break;
|
|
9754
9860
|
}
|
|
9755
9861
|
} catch {
|
|
9756
9862
|
console.error("Failed to parse game message:", data);
|
|
@@ -9949,5 +10055,6 @@ export {
|
|
|
9949
10055
|
SessionManager,
|
|
9950
10056
|
VideoProcessingError,
|
|
9951
10057
|
index_default as default,
|
|
9952
|
-
isWebTransportSupported
|
|
10058
|
+
isWebTransportSupported,
|
|
10059
|
+
toCreateRoomWire
|
|
9953
10060
|
};
|