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.mjs CHANGED
@@ -116,6 +116,123 @@ var AdsAPI = class {
116
116
  }
117
117
  };
118
118
 
119
+ // src/types/error.ts
120
+ var ApiError = class extends Error {
121
+ constructor(statusCode, message, code, details) {
122
+ super(message);
123
+ this.statusCode = statusCode;
124
+ this.name = "ApiError";
125
+ this.code = code;
126
+ this.details = details;
127
+ }
128
+ /**
129
+ * `JSON.stringify(err)` 가 `{}` 로 찍히지 않게 한다.
130
+ *
131
+ * 표준 `Error` 는 message/stack 이 non-enumerable 이라 JSON 직렬화 시 빈 객체가
132
+ * 된다. 로그를 `JSON.stringify` 로 남기는 소비자에게는 "에러 정보가 아무것도 안
133
+ * 온다"로 보여, 실제로는 있는 정보까지 없는 것으로 오진하게 만든다
134
+ * (platform-issue 019fa21c).
135
+ */
136
+ toJSON() {
137
+ return {
138
+ name: this.name,
139
+ message: this.message,
140
+ code: this.code,
141
+ statusCode: this.statusCode,
142
+ details: this.details
143
+ };
144
+ }
145
+ };
146
+ var AIError = class extends Error {
147
+ constructor(init) {
148
+ super(init.message || init.code || "AI request failed");
149
+ this.name = "AIError";
150
+ this.code = init.code || "stream_failed";
151
+ this.retryable = init.retryable ?? isRetryableAICode(this.code);
152
+ this.status = init.status;
153
+ this.provider = init.provider;
154
+ this.model = init.model;
155
+ this.retryAfter = init.retryAfter;
156
+ this.detailCode = init.detailCode;
157
+ this.sessionId = init.sessionId;
158
+ }
159
+ /** `JSON.stringify(err)` 가 `{}` 가 되지 않도록 한다 (ApiError.toJSON 참조). */
160
+ toJSON() {
161
+ return {
162
+ name: this.name,
163
+ code: this.code,
164
+ message: this.message,
165
+ retryable: this.retryable,
166
+ status: this.status,
167
+ provider: this.provider,
168
+ model: this.model,
169
+ retryAfter: this.retryAfter,
170
+ detailCode: this.detailCode,
171
+ sessionId: this.sessionId
172
+ };
173
+ }
174
+ };
175
+ function isRetryableAICode(code) {
176
+ return code === "provider_timeout" || code === "service_unavailable" || code === "rate_limit_exceeded" || code === "stream_failed";
177
+ }
178
+ function toAIError(payload, fallback) {
179
+ if (payload instanceof AIError) return payload;
180
+ const p = typeof payload === "object" && payload !== null ? payload : {};
181
+ const str = (v) => typeof v === "string" && v !== "" ? v : void 0;
182
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
183
+ const documented = str(p.error) ?? str(p.code) ?? fallback?.code;
184
+ const sub = str(p.detail_code) ?? str(p.detailCode) ?? str(p.code);
185
+ const code = documented ?? "stream_failed";
186
+ const detailCode = sub && sub !== code ? sub : void 0;
187
+ const message = str(p.message) ?? (payload instanceof Error ? payload.message : void 0) ?? fallback?.message ?? code;
188
+ return new AIError({
189
+ code,
190
+ message,
191
+ retryable: typeof p.retryable === "boolean" ? p.retryable : void 0,
192
+ status: num(p.status) ?? num(p.statusCode) ?? fallback?.status,
193
+ provider: str(p.provider),
194
+ model: str(p.model),
195
+ retryAfter: num(p.retry_after_seconds) ?? num(p.retryAfter),
196
+ detailCode,
197
+ sessionId: str(p.session_id) ?? str(p.sessionId) ?? fallback?.sessionId
198
+ });
199
+ }
200
+ var AuthError = class extends Error {
201
+ constructor(message) {
202
+ super(message);
203
+ this.name = "AuthError";
204
+ }
205
+ };
206
+ var GameError = class extends Error {
207
+ constructor(init) {
208
+ super(init.message || init.code || "GameError");
209
+ this.name = "GameError";
210
+ this.code = init.code || "UNKNOWN";
211
+ this.phase = init.phase;
212
+ this.feature = init.feature;
213
+ this.roomId = init.roomId;
214
+ this.scriptId = init.scriptId;
215
+ this.originClientId = init.originClientId;
216
+ this.requested = init.requested;
217
+ this.available = init.available;
218
+ }
219
+ /** `JSON.stringify(err)` 가 `{}` 가 되지 않도록 한다 (ApiError.toJSON 참조). */
220
+ toJSON() {
221
+ return {
222
+ name: this.name,
223
+ code: this.code,
224
+ message: this.message,
225
+ phase: this.phase,
226
+ feature: this.feature,
227
+ roomId: this.roomId,
228
+ scriptId: this.scriptId,
229
+ originClientId: this.originClientId,
230
+ requested: this.requested,
231
+ available: this.available
232
+ };
233
+ }
234
+ };
235
+
119
236
  // src/api/ai.ts
120
237
  var AIAPI = class {
121
238
  constructor(http) {
@@ -180,13 +297,24 @@ var AIAPI = class {
180
297
  signal
181
298
  });
182
299
  if (!response.ok) {
183
- const errorData = await response.json().catch(() => ({ error: "Stream request failed" }));
184
- callbacks.onError?.(errorData.error || "Stream request failed");
300
+ const errorData = await response.json().catch(() => ({ error: "stream_failed" }));
301
+ callbacks.onError?.(
302
+ toAIError(errorData, {
303
+ message: "Stream request failed",
304
+ status: response.status
305
+ })
306
+ );
185
307
  return;
186
308
  }
187
309
  reader = response.body?.getReader();
188
310
  if (!reader) {
189
- callbacks.onError?.("ReadableStream not supported");
311
+ callbacks.onError?.(
312
+ new AIError({
313
+ code: "stream_failed",
314
+ message: "ReadableStream not supported",
315
+ retryable: false
316
+ })
317
+ );
190
318
  return;
191
319
  }
192
320
  const decoder = new TextDecoder();
@@ -207,9 +335,7 @@ var AIAPI = class {
207
335
  try {
208
336
  const event = JSON.parse(data);
209
337
  if (event.error) {
210
- callbacks.onError?.(
211
- event.message || event.error || "stream error"
212
- );
338
+ callbacks.onError?.(toAIError(event));
213
339
  return;
214
340
  }
215
341
  if (event.type === "sources" && event.sources) {
@@ -1194,37 +1320,6 @@ var AnalyticsAPI = class {
1194
1320
  }
1195
1321
  };
1196
1322
 
1197
- // src/types/error.ts
1198
- var ApiError = class extends Error {
1199
- constructor(statusCode, message, code, details) {
1200
- super(message);
1201
- this.statusCode = statusCode;
1202
- this.name = "ApiError";
1203
- this.code = code;
1204
- this.details = details;
1205
- }
1206
- };
1207
- var AuthError = class extends Error {
1208
- constructor(message) {
1209
- super(message);
1210
- this.name = "AuthError";
1211
- }
1212
- };
1213
- var GameError = class extends Error {
1214
- constructor(init) {
1215
- super(init.message || init.code || "GameError");
1216
- this.name = "GameError";
1217
- this.code = init.code || "UNKNOWN";
1218
- this.phase = init.phase;
1219
- this.feature = init.feature;
1220
- this.roomId = init.roomId;
1221
- this.scriptId = init.scriptId;
1222
- this.originClientId = init.originClientId;
1223
- this.requested = init.requested;
1224
- this.available = init.available;
1225
- }
1226
- };
1227
-
1228
1323
  // src/core/validate.ts
1229
1324
  function checkType(value, type) {
1230
1325
  switch (type) {
@@ -6748,9 +6843,16 @@ var RealtimeAPI = class {
6748
6843
  });
6749
6844
  this.pendingRequests.clear();
6750
6845
  this.subscriptions.clear();
6751
- this.streamSessions.forEach((session) => {
6846
+ this.streamSessions.forEach((session, sessionId) => {
6752
6847
  if (session.handlers.onError) {
6753
- session.handlers.onError(new Error("Connection closed"));
6848
+ session.handlers.onError(
6849
+ new AIError({
6850
+ code: "service_unavailable",
6851
+ 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.",
6852
+ retryable: true,
6853
+ sessionId
6854
+ })
6855
+ );
6754
6856
  }
6755
6857
  });
6756
6858
  this.streamSessions.clear();
@@ -7015,17 +7117,26 @@ var RealtimeAPI = class {
7015
7117
  signal
7016
7118
  });
7017
7119
  if (!response.ok) {
7018
- const errData = await response.json().catch(() => ({ error: "Stream request failed" }));
7120
+ const errData = await response.json().catch(() => ({ error: "stream_failed" }));
7019
7121
  handlers.onError?.(
7020
- new Error(
7021
- errData.error || errData.message || "Stream request failed"
7022
- )
7122
+ toAIError(errData, {
7123
+ message: "Stream request failed",
7124
+ sessionId,
7125
+ status: response.status
7126
+ })
7023
7127
  );
7024
7128
  return;
7025
7129
  }
7026
7130
  reader = response.body?.getReader();
7027
7131
  if (!reader) {
7028
- handlers.onError?.(new Error("ReadableStream not supported"));
7132
+ handlers.onError?.(
7133
+ new AIError({
7134
+ code: "stream_failed",
7135
+ message: "ReadableStream not supported",
7136
+ retryable: false,
7137
+ sessionId
7138
+ })
7139
+ );
7029
7140
  return;
7030
7141
  }
7031
7142
  const decoder = new TextDecoder();
@@ -7052,9 +7163,7 @@ var RealtimeAPI = class {
7052
7163
  try {
7053
7164
  const ev = JSON.parse(data);
7054
7165
  if (ev.error) {
7055
- handlers.onError?.(
7056
- new Error(ev.message || ev.error || "stream error")
7057
- );
7166
+ handlers.onError?.(toAIError(ev, { sessionId }));
7058
7167
  return;
7059
7168
  }
7060
7169
  if (ev.type === "tool_start") {
@@ -7098,7 +7207,14 @@ var RealtimeAPI = class {
7098
7207
  } catch (err) {
7099
7208
  const aborted = signal.aborted || err instanceof DOMException && err.name === "AbortError" || typeof err === "object" && err !== null && err.name === "AbortError";
7100
7209
  if (!aborted) {
7101
- handlers.onError?.(err instanceof Error ? err : new Error(String(err)));
7210
+ handlers.onError?.(
7211
+ new AIError({
7212
+ code: "service_unavailable",
7213
+ message: err instanceof Error ? err.message : String(err) || "AI \uC2A4\uD2B8\uB9AC\uBC0D \uC694\uCCAD\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
7214
+ retryable: true,
7215
+ sessionId
7216
+ })
7217
+ );
7102
7218
  }
7103
7219
  } finally {
7104
7220
  this.sseSessions.delete(sessionId);
@@ -7736,12 +7852,11 @@ var RealtimeAPI = class {
7736
7852
  break;
7737
7853
  }
7738
7854
  case "stream_error": {
7739
- const data = msg.data;
7740
7855
  if (msg.request_id) {
7741
7856
  for (const [sessionId, session] of this.streamSessions) {
7742
7857
  if (session.requestId === msg.request_id) {
7743
7858
  if (session.handlers.onError) {
7744
- session.handlers.onError(new Error(data.message));
7859
+ session.handlers.onError(toAIError(msg.data, { sessionId }));
7745
7860
  }
7746
7861
  this.streamSessions.delete(sessionId);
7747
7862
  break;
@@ -7814,9 +7929,16 @@ var RealtimeAPI = class {
7814
7929
  }
7815
7930
  this.ws = null;
7816
7931
  this._connectionId = null;
7817
- this.streamSessions.forEach((session) => {
7932
+ this.streamSessions.forEach((session, sessionId) => {
7818
7933
  if (session.handlers.onError) {
7819
- session.handlers.onError(new Error("Connection lost"));
7934
+ session.handlers.onError(
7935
+ new AIError({
7936
+ code: "service_unavailable",
7937
+ 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.",
7938
+ retryable: true,
7939
+ sessionId
7940
+ })
7941
+ );
7820
7942
  }
7821
7943
  });
7822
7944
  this.streamSessions.clear();
@@ -10727,8 +10849,16 @@ function sanitizePathForBreadcrumb(rawUrl) {
10727
10849
  }
10728
10850
 
10729
10851
  // src/core/http.ts
10852
+ var COOKIE_BEARING_PUBLIC_PATHS = /* @__PURE__ */ new Set([
10853
+ "/v1/public/app-members/signup",
10854
+ "/v1/public/app-members/signin",
10855
+ "/v1/public/app-members/signout"
10856
+ ]);
10730
10857
  function fetchCredentialsForPath(url) {
10731
10858
  const path = url.split("?")[0];
10859
+ if (COOKIE_BEARING_PUBLIC_PATHS.has(path)) {
10860
+ return "include";
10861
+ }
10732
10862
  return path.startsWith("/v1/public/") ? "omit" : "include";
10733
10863
  }
10734
10864
  var TOKEN_STORAGE_KEY = "cb_auth_tokens";
@@ -11265,13 +11395,26 @@ var HttpClient = class {
11265
11395
  this.emitError(err2);
11266
11396
  throw err2;
11267
11397
  }
11268
- const message = typeof rawError === "string" ? rawError : errorData.message || "Unknown error";
11269
- const legacyDetails = retryAfterSeconds !== void 0 ? { retry_after_seconds: retryAfterSeconds } : void 0;
11398
+ const flatMessage = typeof errorData.message === "string" && errorData.message !== "" ? errorData.message : void 0;
11399
+ const explicitCode = typeof errorData.code === "string" && errorData.code !== "" ? errorData.code : void 0;
11400
+ const errorIsCode = typeof rawError === "string" && /^[a-z][a-z0-9_]*$/.test(rawError);
11401
+ const message = flatMessage ?? (typeof rawError === "string" && rawError !== "" ? rawError : "Unknown error");
11402
+ const code = explicitCode ?? (errorIsCode ? rawError : void 0);
11403
+ const legacyDetails = {};
11404
+ if (retryAfterSeconds !== void 0) {
11405
+ legacyDetails.retry_after_seconds = retryAfterSeconds;
11406
+ }
11407
+ if (typeof errorData.provider === "string") {
11408
+ legacyDetails.provider = errorData.provider;
11409
+ }
11410
+ if (typeof errorData.model === "string") {
11411
+ legacyDetails.model = errorData.model;
11412
+ }
11270
11413
  const err = new ApiError(
11271
11414
  response.status,
11272
11415
  message,
11273
- void 0,
11274
- legacyDetails
11416
+ code,
11417
+ Object.keys(legacyDetails).length > 0 ? legacyDetails : void 0
11275
11418
  );
11276
11419
  this.emitError(err);
11277
11420
  throw err;
@@ -12249,6 +12392,7 @@ var ConnectBase = class {
12249
12392
  var index_default = ConnectBase;
12250
12393
  export {
12251
12394
  AIAPI,
12395
+ AIError,
12252
12396
  AUTH_MEMBER_ID_TOKEN,
12253
12397
  AdsAPI,
12254
12398
  ApiError,
@@ -12268,5 +12412,6 @@ export {
12268
12412
  detectInAppBrowser,
12269
12413
  escapeToExternalBrowser,
12270
12414
  isWebTransportSupported,
12415
+ toAIError,
12271
12416
  toCreateRoomWire
12272
12417
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "4.4.0",
3
+ "version": "5.0.0",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",