connectbase-client 4.4.0 → 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();
@@ -10773,8 +10897,16 @@ function sanitizePathForBreadcrumb(rawUrl) {
10773
10897
  }
10774
10898
 
10775
10899
  // src/core/http.ts
10900
+ var COOKIE_BEARING_PUBLIC_PATHS = /* @__PURE__ */ new Set([
10901
+ "/v1/public/app-members/signup",
10902
+ "/v1/public/app-members/signin",
10903
+ "/v1/public/app-members/signout"
10904
+ ]);
10776
10905
  function fetchCredentialsForPath(url) {
10777
10906
  const path = url.split("?")[0];
10907
+ if (COOKIE_BEARING_PUBLIC_PATHS.has(path)) {
10908
+ return "include";
10909
+ }
10778
10910
  return path.startsWith("/v1/public/") ? "omit" : "include";
10779
10911
  }
10780
10912
  var TOKEN_STORAGE_KEY = "cb_auth_tokens";
@@ -11311,13 +11443,26 @@ var HttpClient = class {
11311
11443
  this.emitError(err2);
11312
11444
  throw err2;
11313
11445
  }
11314
- const message = typeof rawError === "string" ? rawError : errorData.message || "Unknown error";
11315
- 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
+ }
11316
11461
  const err = new ApiError(
11317
11462
  response.status,
11318
11463
  message,
11319
- void 0,
11320
- legacyDetails
11464
+ code,
11465
+ Object.keys(legacyDetails).length > 0 ? legacyDetails : void 0
11321
11466
  );
11322
11467
  this.emitError(err);
11323
11468
  throw err;
@@ -12296,6 +12441,7 @@ var index_default = ConnectBase;
12296
12441
  // Annotate the CommonJS export names for ESM import in node:
12297
12442
  0 && (module.exports = {
12298
12443
  AIAPI,
12444
+ AIError,
12299
12445
  AUTH_MEMBER_ID_TOKEN,
12300
12446
  AdsAPI,
12301
12447
  ApiError,
@@ -12314,5 +12460,6 @@ var index_default = ConnectBase;
12314
12460
  detectInAppBrowser,
12315
12461
  escapeToExternalBrowser,
12316
12462
  isWebTransportSupported,
12463
+ toAIError,
12317
12464
  toCreateRoomWire
12318
12465
  });