connectbase-client 3.11.0 → 3.13.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.js CHANGED
@@ -1344,23 +1344,38 @@ var DatabaseAPI = class {
1344
1344
  // ============ Batch & Transactions ============
1345
1345
  /**
1346
1346
  * 배치 쓰기 (여러 테이블 다중 문서 원자적 처리)
1347
+ *
1348
+ * 서버는 HTTP 200 + `success:false` + 개별 op `error` 로 부분 실패를 표현한다.
1349
+ * SDK 는 호출자가 silent success 로 오해하지 않도록 첫 실패 op 의 메시지로 throw 한다.
1347
1350
  */
1348
1351
  async batch(operations) {
1349
1352
  const prefix = this.getPublicPrefix();
1350
- return this.http.post(
1353
+ const response = await this.http.post(
1351
1354
  `${prefix}/batch`,
1352
1355
  { operations }
1353
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;
1354
1362
  }
1355
1363
  /**
1356
1364
  * 트랜잭션 실행 (읽기 → 쓰기 ACID)
1365
+ *
1366
+ * 서버는 부분 실패가 없는 ACID 트랜잭션을 보장하지만, 검증/RLS/충돌은
1367
+ * `success:false` + `error` 로 알린다. SDK 는 silent success 회귀 방지 차원에서 throw.
1357
1368
  */
1358
1369
  async transaction(reads, writes) {
1359
1370
  const prefix = this.getPublicPrefix();
1360
- return this.http.post(
1371
+ const response = await this.http.post(
1361
1372
  `${prefix}/transactions`,
1362
1373
  { reads, writes }
1363
1374
  );
1375
+ if (response && response.success === false) {
1376
+ throw new Error(response.error || "transaction failed");
1377
+ }
1378
+ return response;
1364
1379
  }
1365
1380
  // ============ Populate (Relation Query) ============
1366
1381
  /**
@@ -9115,6 +9130,67 @@ var SupportAPI = class {
9115
9130
  if (req.recaptchaToken) body.recaptcha_token = req.recaptchaToken;
9116
9131
  return this.http.post("/v1/public/reports", body);
9117
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
+ }
9118
9194
  /**
9119
9195
  * ConnectBase 플랫폼 자체 버그/요청/문의를 발행한다.
9120
9196
  *
@@ -9208,7 +9284,7 @@ var SupportAPI = class {
9208
9284
  * 본인이 발행한 platform issue 의 처리 진행 상황을 단건 조회.
9209
9285
  *
9210
9286
  * AI 가 "내가 발행한 이슈 처리됐어?" 를 폴링하는 표준 경로. status / resolution_note /
9211
- * triage_summary / external_links 로 ConnectBase 운영팀의 처리 상태를 확인.
9287
+ * external_links 로 ConnectBase 운영팀의 처리 상태를 확인.
9212
9288
  *
9213
9289
  * @throws ApiError 404 — 본인 issue 가 아니거나 존재하지 않음
9214
9290
  */
@@ -9800,9 +9876,29 @@ var GameRoomTransport = class {
9800
9876
  case "error":
9801
9877
  this.handlers.onError?.({
9802
9878
  code: msg.code || "UNKNOWN",
9803
- 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
9804
9884
  });
9805
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;
9806
9902
  }
9807
9903
  } catch {
9808
9904
  console.error("Failed to parse game message:", data);
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
- return this.http.post(
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
- return this.http.post(
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
  /**
@@ -9073,6 +9088,67 @@ var SupportAPI = class {
9073
9088
  if (req.recaptchaToken) body.recaptcha_token = req.recaptchaToken;
9074
9089
  return this.http.post("/v1/public/reports", body);
9075
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
+ }
9076
9152
  /**
9077
9153
  * ConnectBase 플랫폼 자체 버그/요청/문의를 발행한다.
9078
9154
  *
@@ -9166,7 +9242,7 @@ var SupportAPI = class {
9166
9242
  * 본인이 발행한 platform issue 의 처리 진행 상황을 단건 조회.
9167
9243
  *
9168
9244
  * AI 가 "내가 발행한 이슈 처리됐어?" 를 폴링하는 표준 경로. status / resolution_note /
9169
- * triage_summary / external_links 로 ConnectBase 운영팀의 처리 상태를 확인.
9245
+ * external_links 로 ConnectBase 운영팀의 처리 상태를 확인.
9170
9246
  *
9171
9247
  * @throws ApiError 404 — 본인 issue 가 아니거나 존재하지 않음
9172
9248
  */
@@ -9758,9 +9834,29 @@ var GameRoomTransport = class {
9758
9834
  case "error":
9759
9835
  this.handlers.onError?.({
9760
9836
  code: msg.code || "UNKNOWN",
9761
- 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
9762
9842
  });
9763
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;
9764
9860
  }
9765
9861
  } catch {
9766
9862
  console.error("Failed to parse game message:", data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "3.11.0",
3
+ "version": "3.13.0",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",