connectbase-client 4.4.1 → 5.0.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
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  AIAPI: () => AIAPI,
24
+ AIError: () => AIError,
24
25
  AUTH_MEMBER_ID_TOKEN: () => AUTH_MEMBER_ID_TOKEN,
25
26
  AdsAPI: () => AdsAPI,
26
27
  ApiError: () => ApiError,
@@ -40,6 +41,7 @@ __export(index_exports, {
40
41
  detectInAppBrowser: () => detectInAppBrowser,
41
42
  escapeToExternalBrowser: () => escapeToExternalBrowser,
42
43
  isWebTransportSupported: () => isWebTransportSupported,
44
+ toAIError: () => toAIError,
43
45
  toCreateRoomWire: () => toCreateRoomWire
44
46
  });
45
47
  module.exports = __toCommonJS(index_exports);
@@ -162,6 +164,123 @@ var AdsAPI = class {
162
164
  }
163
165
  };
164
166
 
167
+ // src/types/error.ts
168
+ var ApiError = class extends Error {
169
+ constructor(statusCode, message, code, details) {
170
+ super(message);
171
+ this.statusCode = statusCode;
172
+ this.name = "ApiError";
173
+ this.code = code;
174
+ this.details = details;
175
+ }
176
+ /**
177
+ * `JSON.stringify(err)` 가 `{}` 로 찍히지 않게 한다.
178
+ *
179
+ * 표준 `Error` 는 message/stack 이 non-enumerable 이라 JSON 직렬화 시 빈 객체가
180
+ * 된다. 로그를 `JSON.stringify` 로 남기는 소비자에게는 "에러 정보가 아무것도 안
181
+ * 온다"로 보여, 실제로는 있는 정보까지 없는 것으로 오진하게 만든다
182
+ * (platform-issue 019fa21c).
183
+ */
184
+ toJSON() {
185
+ return {
186
+ name: this.name,
187
+ message: this.message,
188
+ code: this.code,
189
+ statusCode: this.statusCode,
190
+ details: this.details
191
+ };
192
+ }
193
+ };
194
+ var AIError = class extends Error {
195
+ constructor(init) {
196
+ super(init.message || init.code || "AI request failed");
197
+ this.name = "AIError";
198
+ this.code = init.code || "stream_failed";
199
+ this.retryable = init.retryable ?? isRetryableAICode(this.code);
200
+ this.status = init.status;
201
+ this.provider = init.provider;
202
+ this.model = init.model;
203
+ this.retryAfter = init.retryAfter;
204
+ this.detailCode = init.detailCode;
205
+ this.sessionId = init.sessionId;
206
+ }
207
+ /** `JSON.stringify(err)` 가 `{}` 가 되지 않도록 한다 (ApiError.toJSON 참조). */
208
+ toJSON() {
209
+ return {
210
+ name: this.name,
211
+ code: this.code,
212
+ message: this.message,
213
+ retryable: this.retryable,
214
+ status: this.status,
215
+ provider: this.provider,
216
+ model: this.model,
217
+ retryAfter: this.retryAfter,
218
+ detailCode: this.detailCode,
219
+ sessionId: this.sessionId
220
+ };
221
+ }
222
+ };
223
+ function isRetryableAICode(code) {
224
+ return code === "provider_timeout" || code === "service_unavailable" || code === "rate_limit_exceeded" || code === "stream_failed";
225
+ }
226
+ function toAIError(payload, fallback) {
227
+ if (payload instanceof AIError) return payload;
228
+ const p = typeof payload === "object" && payload !== null ? payload : {};
229
+ const str = (v) => typeof v === "string" && v !== "" ? v : void 0;
230
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
231
+ const documented = str(p.error) ?? str(p.code) ?? fallback?.code;
232
+ const sub = str(p.detail_code) ?? str(p.detailCode) ?? str(p.code);
233
+ const code = documented ?? "stream_failed";
234
+ const detailCode = sub && sub !== code ? sub : void 0;
235
+ const message = str(p.message) ?? (payload instanceof Error ? payload.message : void 0) ?? fallback?.message ?? code;
236
+ return new AIError({
237
+ code,
238
+ message,
239
+ retryable: typeof p.retryable === "boolean" ? p.retryable : void 0,
240
+ status: num(p.status) ?? num(p.statusCode) ?? fallback?.status,
241
+ provider: str(p.provider),
242
+ model: str(p.model),
243
+ retryAfter: num(p.retry_after_seconds) ?? num(p.retryAfter),
244
+ detailCode,
245
+ sessionId: str(p.session_id) ?? str(p.sessionId) ?? fallback?.sessionId
246
+ });
247
+ }
248
+ var AuthError = class extends Error {
249
+ constructor(message) {
250
+ super(message);
251
+ this.name = "AuthError";
252
+ }
253
+ };
254
+ var GameError = class extends Error {
255
+ constructor(init) {
256
+ super(init.message || init.code || "GameError");
257
+ this.name = "GameError";
258
+ this.code = init.code || "UNKNOWN";
259
+ this.phase = init.phase;
260
+ this.feature = init.feature;
261
+ this.roomId = init.roomId;
262
+ this.scriptId = init.scriptId;
263
+ this.originClientId = init.originClientId;
264
+ this.requested = init.requested;
265
+ this.available = init.available;
266
+ }
267
+ /** `JSON.stringify(err)` 가 `{}` 가 되지 않도록 한다 (ApiError.toJSON 참조). */
268
+ toJSON() {
269
+ return {
270
+ name: this.name,
271
+ code: this.code,
272
+ message: this.message,
273
+ phase: this.phase,
274
+ feature: this.feature,
275
+ roomId: this.roomId,
276
+ scriptId: this.scriptId,
277
+ originClientId: this.originClientId,
278
+ requested: this.requested,
279
+ available: this.available
280
+ };
281
+ }
282
+ };
283
+
165
284
  // src/api/ai.ts
166
285
  var AIAPI = class {
167
286
  constructor(http) {
@@ -226,13 +345,24 @@ var AIAPI = class {
226
345
  signal
227
346
  });
228
347
  if (!response.ok) {
229
- const errorData = await response.json().catch(() => ({ error: "Stream request failed" }));
230
- callbacks.onError?.(errorData.error || "Stream request failed");
348
+ const errorData = await response.json().catch(() => ({ error: "stream_failed" }));
349
+ callbacks.onError?.(
350
+ toAIError(errorData, {
351
+ message: "Stream request failed",
352
+ status: response.status
353
+ })
354
+ );
231
355
  return;
232
356
  }
233
357
  reader = response.body?.getReader();
234
358
  if (!reader) {
235
- callbacks.onError?.("ReadableStream not supported");
359
+ callbacks.onError?.(
360
+ new AIError({
361
+ code: "stream_failed",
362
+ message: "ReadableStream not supported",
363
+ retryable: false
364
+ })
365
+ );
236
366
  return;
237
367
  }
238
368
  const decoder = new TextDecoder();
@@ -253,9 +383,7 @@ var AIAPI = class {
253
383
  try {
254
384
  const event = JSON.parse(data);
255
385
  if (event.error) {
256
- callbacks.onError?.(
257
- event.message || event.error || "stream error"
258
- );
386
+ callbacks.onError?.(toAIError(event));
259
387
  return;
260
388
  }
261
389
  if (event.type === "sources" && event.sources) {
@@ -1240,37 +1368,6 @@ var AnalyticsAPI = class {
1240
1368
  }
1241
1369
  };
1242
1370
 
1243
- // src/types/error.ts
1244
- var ApiError = class extends Error {
1245
- constructor(statusCode, message, code, details) {
1246
- super(message);
1247
- this.statusCode = statusCode;
1248
- this.name = "ApiError";
1249
- this.code = code;
1250
- this.details = details;
1251
- }
1252
- };
1253
- var AuthError = class extends Error {
1254
- constructor(message) {
1255
- super(message);
1256
- this.name = "AuthError";
1257
- }
1258
- };
1259
- var GameError = class extends Error {
1260
- constructor(init) {
1261
- super(init.message || init.code || "GameError");
1262
- this.name = "GameError";
1263
- this.code = init.code || "UNKNOWN";
1264
- this.phase = init.phase;
1265
- this.feature = init.feature;
1266
- this.roomId = init.roomId;
1267
- this.scriptId = init.scriptId;
1268
- this.originClientId = init.originClientId;
1269
- this.requested = init.requested;
1270
- this.available = init.available;
1271
- }
1272
- };
1273
-
1274
1371
  // src/core/validate.ts
1275
1372
  function checkType(value, type) {
1276
1373
  switch (type) {
@@ -6794,9 +6891,16 @@ var RealtimeAPI = class {
6794
6891
  });
6795
6892
  this.pendingRequests.clear();
6796
6893
  this.subscriptions.clear();
6797
- this.streamSessions.forEach((session) => {
6894
+ this.streamSessions.forEach((session, sessionId) => {
6798
6895
  if (session.handlers.onError) {
6799
- session.handlers.onError(new Error("Connection closed"));
6896
+ session.handlers.onError(
6897
+ new AIError({
6898
+ code: "service_unavailable",
6899
+ message: "\uC2E4\uC2DC\uAC04 \uC5F0\uACB0\uC774 \uC885\uB8CC\uB418\uC5B4 AI \uC2A4\uD2B8\uB9AC\uBC0D\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC7AC\uC5F0\uACB0 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
6900
+ retryable: true,
6901
+ sessionId
6902
+ })
6903
+ );
6800
6904
  }
6801
6905
  });
6802
6906
  this.streamSessions.clear();
@@ -7061,17 +7165,26 @@ var RealtimeAPI = class {
7061
7165
  signal
7062
7166
  });
7063
7167
  if (!response.ok) {
7064
- const errData = await response.json().catch(() => ({ error: "Stream request failed" }));
7168
+ const errData = await response.json().catch(() => ({ error: "stream_failed" }));
7065
7169
  handlers.onError?.(
7066
- new Error(
7067
- errData.error || errData.message || "Stream request failed"
7068
- )
7170
+ toAIError(errData, {
7171
+ message: "Stream request failed",
7172
+ sessionId,
7173
+ status: response.status
7174
+ })
7069
7175
  );
7070
7176
  return;
7071
7177
  }
7072
7178
  reader = response.body?.getReader();
7073
7179
  if (!reader) {
7074
- handlers.onError?.(new Error("ReadableStream not supported"));
7180
+ handlers.onError?.(
7181
+ new AIError({
7182
+ code: "stream_failed",
7183
+ message: "ReadableStream not supported",
7184
+ retryable: false,
7185
+ sessionId
7186
+ })
7187
+ );
7075
7188
  return;
7076
7189
  }
7077
7190
  const decoder = new TextDecoder();
@@ -7098,9 +7211,7 @@ var RealtimeAPI = class {
7098
7211
  try {
7099
7212
  const ev = JSON.parse(data);
7100
7213
  if (ev.error) {
7101
- handlers.onError?.(
7102
- new Error(ev.message || ev.error || "stream error")
7103
- );
7214
+ handlers.onError?.(toAIError(ev, { sessionId }));
7104
7215
  return;
7105
7216
  }
7106
7217
  if (ev.type === "tool_start") {
@@ -7144,7 +7255,14 @@ var RealtimeAPI = class {
7144
7255
  } catch (err) {
7145
7256
  const aborted = signal.aborted || err instanceof DOMException && err.name === "AbortError" || typeof err === "object" && err !== null && err.name === "AbortError";
7146
7257
  if (!aborted) {
7147
- handlers.onError?.(err instanceof Error ? err : new Error(String(err)));
7258
+ handlers.onError?.(
7259
+ new AIError({
7260
+ code: "service_unavailable",
7261
+ message: err instanceof Error ? err.message : String(err) || "AI \uC2A4\uD2B8\uB9AC\uBC0D \uC694\uCCAD\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
7262
+ retryable: true,
7263
+ sessionId
7264
+ })
7265
+ );
7148
7266
  }
7149
7267
  } finally {
7150
7268
  this.sseSessions.delete(sessionId);
@@ -7782,12 +7900,11 @@ var RealtimeAPI = class {
7782
7900
  break;
7783
7901
  }
7784
7902
  case "stream_error": {
7785
- const data = msg.data;
7786
7903
  if (msg.request_id) {
7787
7904
  for (const [sessionId, session] of this.streamSessions) {
7788
7905
  if (session.requestId === msg.request_id) {
7789
7906
  if (session.handlers.onError) {
7790
- session.handlers.onError(new Error(data.message));
7907
+ session.handlers.onError(toAIError(msg.data, { sessionId }));
7791
7908
  }
7792
7909
  this.streamSessions.delete(sessionId);
7793
7910
  break;
@@ -7860,9 +7977,16 @@ var RealtimeAPI = class {
7860
7977
  }
7861
7978
  this.ws = null;
7862
7979
  this._connectionId = null;
7863
- this.streamSessions.forEach((session) => {
7980
+ this.streamSessions.forEach((session, sessionId) => {
7864
7981
  if (session.handlers.onError) {
7865
- session.handlers.onError(new Error("Connection lost"));
7982
+ session.handlers.onError(
7983
+ new AIError({
7984
+ code: "service_unavailable",
7985
+ message: "\uC2E4\uC2DC\uAC04 \uC5F0\uACB0\uC774 \uB04A\uACA8 AI \uC2A4\uD2B8\uB9AC\uBC0D\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC7AC\uC5F0\uACB0 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
7986
+ retryable: true,
7987
+ sessionId
7988
+ })
7989
+ );
7866
7990
  }
7867
7991
  });
7868
7992
  this.streamSessions.clear();
@@ -11319,13 +11443,26 @@ var HttpClient = class {
11319
11443
  this.emitError(err2);
11320
11444
  throw err2;
11321
11445
  }
11322
- const message = typeof rawError === "string" ? rawError : errorData.message || "Unknown error";
11323
- const legacyDetails = retryAfterSeconds !== void 0 ? { retry_after_seconds: retryAfterSeconds } : void 0;
11446
+ const flatMessage = typeof errorData.message === "string" && errorData.message !== "" ? errorData.message : void 0;
11447
+ const explicitCode = typeof errorData.code === "string" && errorData.code !== "" ? errorData.code : void 0;
11448
+ const errorIsCode = typeof rawError === "string" && /^[a-z][a-z0-9_]*$/.test(rawError);
11449
+ const message = flatMessage ?? (typeof rawError === "string" && rawError !== "" ? rawError : "Unknown error");
11450
+ const code = explicitCode ?? (errorIsCode ? rawError : void 0);
11451
+ const legacyDetails = {};
11452
+ if (retryAfterSeconds !== void 0) {
11453
+ legacyDetails.retry_after_seconds = retryAfterSeconds;
11454
+ }
11455
+ if (typeof errorData.provider === "string") {
11456
+ legacyDetails.provider = errorData.provider;
11457
+ }
11458
+ if (typeof errorData.model === "string") {
11459
+ legacyDetails.model = errorData.model;
11460
+ }
11324
11461
  const err = new ApiError(
11325
11462
  response.status,
11326
11463
  message,
11327
- void 0,
11328
- legacyDetails
11464
+ code,
11465
+ Object.keys(legacyDetails).length > 0 ? legacyDetails : void 0
11329
11466
  );
11330
11467
  this.emitError(err);
11331
11468
  throw err;
@@ -12304,6 +12441,7 @@ var index_default = ConnectBase;
12304
12441
  // Annotate the CommonJS export names for ESM import in node:
12305
12442
  0 && (module.exports = {
12306
12443
  AIAPI,
12444
+ AIError,
12307
12445
  AUTH_MEMBER_ID_TOKEN,
12308
12446
  AdsAPI,
12309
12447
  ApiError,
@@ -12322,5 +12460,6 @@ var index_default = ConnectBase;
12322
12460
  detectInAppBrowser,
12323
12461
  escapeToExternalBrowser,
12324
12462
  isWebTransportSupported,
12463
+ toAIError,
12325
12464
  toCreateRoomWire
12326
12465
  });