connectbase-client 5.11.0 → 5.12.1

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.d.mts CHANGED
@@ -442,6 +442,17 @@ declare class HttpClient {
442
442
  * 일은 없어야 한다.
443
443
  */
444
444
  private learnDirectOrigin;
445
+ /**
446
+ * SDK 인증 헤더를 **주입하지 않는** raw fetch. 저지연 원본 선택과 폴백만 얹는다.
447
+ *
448
+ * `cb.endpoint.call()`(`/v1/proxy/*`) 전용이다. 그 경로는 dumb pipe 라 요청 헤더가
449
+ * 사용자 PC 의 모델 서버로 그대로 전달된다 — `prepareHeaders()` 를 태우면 SDK 의
450
+ * `Authorization` 이 남의 서버로 나가고, 호출자가 자기 모델 서버용으로 설정한
451
+ * `Authorization` 을 덮어쓴다. 그래서 fetchRaw 를 재사용하지 않는다.
452
+ *
453
+ * @param path `/v1/proxy/...` 형태의 절대 경로 (origin 없이)
454
+ */
455
+ fetchPassthrough(path: string, init: RequestInit): Promise<Response>;
445
456
  private tryFetchOnce;
446
457
  get<T>(url: string, config?: RequestConfig): Promise<T>;
447
458
  post<T>(url: string, data?: unknown, config?: RequestConfig): Promise<T>;
@@ -3146,6 +3157,8 @@ declare class EndpointAPI {
3146
3157
  * @param init - fetch() 의 RequestInit + path. path 는 사용자 모델 서버의 엔드포인트 경로 (예: "/prompt", "/v1/chat/completions").
3147
3158
  */
3148
3159
  call(label: string, init: EndpointCallInit): Promise<Response>;
3160
+ /** `/v1/proxy/<label><path>` 절대 경로(origin 없이). URL 조립 규칙의 단일 출처. */
3161
+ private proxyPath;
3149
3162
  /**
3150
3163
  * 라벨 + path 의 최종 호출 URL `${baseUrl}/v1/proxy/${label}${path}` 을 조립해서
3151
3164
  * 반환. URL 을 다른 시스템 (Service Worker, 백엔드 워커, 로깅) 에 넘기거나
package/dist/index.d.ts CHANGED
@@ -442,6 +442,17 @@ declare class HttpClient {
442
442
  * 일은 없어야 한다.
443
443
  */
444
444
  private learnDirectOrigin;
445
+ /**
446
+ * SDK 인증 헤더를 **주입하지 않는** raw fetch. 저지연 원본 선택과 폴백만 얹는다.
447
+ *
448
+ * `cb.endpoint.call()`(`/v1/proxy/*`) 전용이다. 그 경로는 dumb pipe 라 요청 헤더가
449
+ * 사용자 PC 의 모델 서버로 그대로 전달된다 — `prepareHeaders()` 를 태우면 SDK 의
450
+ * `Authorization` 이 남의 서버로 나가고, 호출자가 자기 모델 서버용으로 설정한
451
+ * `Authorization` 을 덮어쓴다. 그래서 fetchRaw 를 재사용하지 않는다.
452
+ *
453
+ * @param path `/v1/proxy/...` 형태의 절대 경로 (origin 없이)
454
+ */
455
+ fetchPassthrough(path: string, init: RequestInit): Promise<Response>;
445
456
  private tryFetchOnce;
446
457
  get<T>(url: string, config?: RequestConfig): Promise<T>;
447
458
  post<T>(url: string, data?: unknown, config?: RequestConfig): Promise<T>;
@@ -3146,6 +3157,8 @@ declare class EndpointAPI {
3146
3157
  * @param init - fetch() 의 RequestInit + path. path 는 사용자 모델 서버의 엔드포인트 경로 (예: "/prompt", "/v1/chat/completions").
3147
3158
  */
3148
3159
  call(label: string, init: EndpointCallInit): Promise<Response>;
3160
+ /** `/v1/proxy/<label><path>` 절대 경로(origin 없이). URL 조립 규칙의 단일 출처. */
3161
+ private proxyPath;
3149
3162
  /**
3150
3163
  * 라벨 + path 의 최종 호출 URL `${baseUrl}/v1/proxy/${label}${path}` 을 조립해서
3151
3164
  * 반환. URL 을 다른 시스템 (Service Worker, 백엔드 워커, 로깅) 에 넘기거나
package/dist/index.js CHANGED
@@ -3121,7 +3121,7 @@ var EndpointAPI = class {
3121
3121
  `EndpointAPI.call: path must start with '/', got ${JSON.stringify(init.path)}`
3122
3122
  );
3123
3123
  }
3124
- const url = this.url(label, init.path);
3124
+ const path = this.proxyPath(label, init.path);
3125
3125
  const headers = new Headers(init.headers ?? {});
3126
3126
  if (!headers.has("X-Public-Key")) {
3127
3127
  const pk = this.http.getPublicKey();
@@ -3132,7 +3132,7 @@ var EndpointAPI = class {
3132
3132
  }
3133
3133
  headers.set("X-Public-Key", pk);
3134
3134
  }
3135
- return fetch(url, {
3135
+ return this.http.fetchPassthrough(path, {
3136
3136
  method: init.method ?? "GET",
3137
3137
  headers,
3138
3138
  body: init.body,
@@ -3141,6 +3141,16 @@ var EndpointAPI = class {
3141
3141
  redirect: "follow"
3142
3142
  });
3143
3143
  }
3144
+ /** `/v1/proxy/<label><path>` 절대 경로(origin 없이). URL 조립 규칙의 단일 출처. */
3145
+ proxyPath(label, path) {
3146
+ if (!label) throw new Error("EndpointAPI.call: label required");
3147
+ if (!path || !path.startsWith("/")) {
3148
+ throw new Error(
3149
+ `EndpointAPI.call: path must start with '/', got ${JSON.stringify(path)}`
3150
+ );
3151
+ }
3152
+ return `/v1/proxy/${encodeURIComponent(label)}${path}`;
3153
+ }
3144
3154
  /**
3145
3155
  * 라벨 + path 의 최종 호출 URL `${baseUrl}/v1/proxy/${label}${path}` 을 조립해서
3146
3156
  * 반환. URL 을 다른 시스템 (Service Worker, 백엔드 워커, 로깅) 에 넘기거나
@@ -7295,6 +7305,7 @@ var RealtimeAPI = class {
7295
7305
  * (degraded) AI stream() 이 SSE 로 동작하게 한다. 백그라운드 WS 재연결 머신은 정지한다.
7296
7306
  */
7297
7307
  activateSseFallback(cause) {
7308
+ if (this.sseFallbackActive) return;
7298
7309
  if (this.ws) {
7299
7310
  try {
7300
7311
  this.ws.close();
@@ -7302,14 +7313,20 @@ var RealtimeAPI = class {
7302
7313
  }
7303
7314
  this.ws = null;
7304
7315
  }
7316
+ const causeMsg = cause instanceof Error ? cause.message : String(cause);
7305
7317
  this.sseFallbackActive = true;
7306
7318
  this.activeTransport = "sse";
7307
7319
  this.state = "connected";
7308
7320
  this.retryCount = 0;
7309
7321
  this.log(
7310
- `WS unavailable (${cause instanceof Error ? cause.message : String(cause)}); using SSE fallback transport for AI streaming`
7322
+ `WS unavailable (${causeMsg}); using SSE fallback transport for AI streaming`
7311
7323
  );
7312
7324
  this.notifyStateChange();
7325
+ this.notifyError(
7326
+ new Error(
7327
+ `Realtime WebSocket unavailable, downgraded to SSE fallback transport (cb.realtime.transport === 'sse'). Only realtime.stream() works in this mode \u2014 subscribe/send/setPresence/typing calls will throw. Cause: ${causeMsg}`
7328
+ )
7329
+ );
7313
7330
  }
7314
7331
  /**
7315
7332
  * 연결 해제
@@ -11449,7 +11466,7 @@ function fetchCredentialsForPath(url) {
11449
11466
  if (COOKIE_BEARING_PUBLIC_PATHS.has(path)) {
11450
11467
  return "include";
11451
11468
  }
11452
- return path.startsWith("/v1/public/") ? "omit" : "include";
11469
+ return path.startsWith("/v1/public/") || path.startsWith("/v1/proxy/") ? "omit" : "include";
11453
11470
  }
11454
11471
  var TOKEN_STORAGE_KEY = "cb_auth_tokens";
11455
11472
  function gatewayCodeFromStatus(status) {
@@ -11648,7 +11665,7 @@ var HttpClient = class {
11648
11665
  if (this.persistence !== "none") return;
11649
11666
  if (!this.config.refreshToken) return;
11650
11667
  try {
11651
- await this.refreshAccessToken();
11668
+ await this.refreshAccessToken({ silent: true });
11652
11669
  } catch {
11653
11670
  }
11654
11671
  }
@@ -11781,7 +11798,8 @@ var HttpClient = class {
11781
11798
  getAppId() {
11782
11799
  return this.config.appId;
11783
11800
  }
11784
- async refreshAccessToken() {
11801
+ async refreshAccessToken(options) {
11802
+ const silent = options?.silent ?? false;
11785
11803
  if (this.isRefreshing) {
11786
11804
  return this.refreshPromise;
11787
11805
  }
@@ -11895,27 +11913,35 @@ var HttpClient = class {
11895
11913
  );
11896
11914
  this.refreshLockedUntil = Date.now() + backoffMs;
11897
11915
  if (failureKind === "permanent") {
11898
- this.clearTokens();
11899
- this.config.onTokenExpired?.();
11916
+ if (!silent) {
11917
+ this.clearTokens();
11918
+ this.config.onTokenExpired?.();
11919
+ }
11900
11920
  const error2 = new AuthError(`${baseMsg}. Please login again.`);
11901
- this.emitError(error2);
11902
- this.config.onAuthError?.(error2);
11921
+ if (!silent) {
11922
+ this.emitError(error2);
11923
+ this.config.onAuthError?.(error2);
11924
+ }
11903
11925
  throw error2;
11904
11926
  }
11905
11927
  if (failureKind === "client_bug") {
11906
11928
  const error2 = new AuthError(
11907
11929
  `${baseMsg}. Client request invalid; tokens preserved.`
11908
11930
  );
11909
- this.emitError(error2);
11910
- this.config.onAuthError?.(error2);
11931
+ if (!silent) {
11932
+ this.emitError(error2);
11933
+ this.config.onAuthError?.(error2);
11934
+ }
11911
11935
  throw error2;
11912
11936
  }
11913
11937
  const error = new AuthError(
11914
11938
  `${baseMsg}. Transient failure; tokens preserved, will retry after backoff.`
11915
11939
  );
11916
- this.emitError(error);
11917
- this.config.onTransientRefreshFailure?.(error);
11918
- this.config.onAuthError?.(error);
11940
+ if (!silent) {
11941
+ this.emitError(error);
11942
+ this.config.onTransientRefreshFailure?.(error);
11943
+ this.config.onAuthError?.(error);
11944
+ }
11919
11945
  throw error;
11920
11946
  } finally {
11921
11947
  cleanup();
@@ -12159,6 +12185,30 @@ var HttpClient = class {
12159
12185
  } catch {
12160
12186
  }
12161
12187
  }
12188
+ /**
12189
+ * SDK 인증 헤더를 **주입하지 않는** raw fetch. 저지연 원본 선택과 폴백만 얹는다.
12190
+ *
12191
+ * `cb.endpoint.call()`(`/v1/proxy/*`) 전용이다. 그 경로는 dumb pipe 라 요청 헤더가
12192
+ * 사용자 PC 의 모델 서버로 그대로 전달된다 — `prepareHeaders()` 를 태우면 SDK 의
12193
+ * `Authorization` 이 남의 서버로 나가고, 호출자가 자기 모델 서버용으로 설정한
12194
+ * `Authorization` 을 덮어쓴다. 그래서 fetchRaw 를 재사용하지 않는다.
12195
+ *
12196
+ * @param path `/v1/proxy/...` 형태의 절대 경로 (origin 없이)
12197
+ */
12198
+ async fetchPassthrough(path, init) {
12199
+ const origin = this.resolveOrigin(path);
12200
+ try {
12201
+ const response = await fetch(`${origin}${path}`, init);
12202
+ this.learnDirectOrigin(response);
12203
+ return response;
12204
+ } catch (err) {
12205
+ if (origin !== this.config.baseUrl && !isAbortError(err)) {
12206
+ this.directDisabled = true;
12207
+ return fetch(`${this.config.baseUrl}${path}`, init);
12208
+ }
12209
+ throw err;
12210
+ }
12211
+ }
12162
12212
  async tryFetchOnce(url, init, config) {
12163
12213
  const { signal, cleanup } = createTimeoutController({
12164
12214
  timeout: config?.timeout ?? this.config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
package/dist/index.mjs CHANGED
@@ -3072,7 +3072,7 @@ var EndpointAPI = class {
3072
3072
  `EndpointAPI.call: path must start with '/', got ${JSON.stringify(init.path)}`
3073
3073
  );
3074
3074
  }
3075
- const url = this.url(label, init.path);
3075
+ const path = this.proxyPath(label, init.path);
3076
3076
  const headers = new Headers(init.headers ?? {});
3077
3077
  if (!headers.has("X-Public-Key")) {
3078
3078
  const pk = this.http.getPublicKey();
@@ -3083,7 +3083,7 @@ var EndpointAPI = class {
3083
3083
  }
3084
3084
  headers.set("X-Public-Key", pk);
3085
3085
  }
3086
- return fetch(url, {
3086
+ return this.http.fetchPassthrough(path, {
3087
3087
  method: init.method ?? "GET",
3088
3088
  headers,
3089
3089
  body: init.body,
@@ -3092,6 +3092,16 @@ var EndpointAPI = class {
3092
3092
  redirect: "follow"
3093
3093
  });
3094
3094
  }
3095
+ /** `/v1/proxy/<label><path>` 절대 경로(origin 없이). URL 조립 규칙의 단일 출처. */
3096
+ proxyPath(label, path) {
3097
+ if (!label) throw new Error("EndpointAPI.call: label required");
3098
+ if (!path || !path.startsWith("/")) {
3099
+ throw new Error(
3100
+ `EndpointAPI.call: path must start with '/', got ${JSON.stringify(path)}`
3101
+ );
3102
+ }
3103
+ return `/v1/proxy/${encodeURIComponent(label)}${path}`;
3104
+ }
3095
3105
  /**
3096
3106
  * 라벨 + path 의 최종 호출 URL `${baseUrl}/v1/proxy/${label}${path}` 을 조립해서
3097
3107
  * 반환. URL 을 다른 시스템 (Service Worker, 백엔드 워커, 로깅) 에 넘기거나
@@ -7246,6 +7256,7 @@ var RealtimeAPI = class {
7246
7256
  * (degraded) AI stream() 이 SSE 로 동작하게 한다. 백그라운드 WS 재연결 머신은 정지한다.
7247
7257
  */
7248
7258
  activateSseFallback(cause) {
7259
+ if (this.sseFallbackActive) return;
7249
7260
  if (this.ws) {
7250
7261
  try {
7251
7262
  this.ws.close();
@@ -7253,14 +7264,20 @@ var RealtimeAPI = class {
7253
7264
  }
7254
7265
  this.ws = null;
7255
7266
  }
7267
+ const causeMsg = cause instanceof Error ? cause.message : String(cause);
7256
7268
  this.sseFallbackActive = true;
7257
7269
  this.activeTransport = "sse";
7258
7270
  this.state = "connected";
7259
7271
  this.retryCount = 0;
7260
7272
  this.log(
7261
- `WS unavailable (${cause instanceof Error ? cause.message : String(cause)}); using SSE fallback transport for AI streaming`
7273
+ `WS unavailable (${causeMsg}); using SSE fallback transport for AI streaming`
7262
7274
  );
7263
7275
  this.notifyStateChange();
7276
+ this.notifyError(
7277
+ new Error(
7278
+ `Realtime WebSocket unavailable, downgraded to SSE fallback transport (cb.realtime.transport === 'sse'). Only realtime.stream() works in this mode \u2014 subscribe/send/setPresence/typing calls will throw. Cause: ${causeMsg}`
7279
+ )
7280
+ );
7264
7281
  }
7265
7282
  /**
7266
7283
  * 연결 해제
@@ -11400,7 +11417,7 @@ function fetchCredentialsForPath(url) {
11400
11417
  if (COOKIE_BEARING_PUBLIC_PATHS.has(path)) {
11401
11418
  return "include";
11402
11419
  }
11403
- return path.startsWith("/v1/public/") ? "omit" : "include";
11420
+ return path.startsWith("/v1/public/") || path.startsWith("/v1/proxy/") ? "omit" : "include";
11404
11421
  }
11405
11422
  var TOKEN_STORAGE_KEY = "cb_auth_tokens";
11406
11423
  function gatewayCodeFromStatus(status) {
@@ -11599,7 +11616,7 @@ var HttpClient = class {
11599
11616
  if (this.persistence !== "none") return;
11600
11617
  if (!this.config.refreshToken) return;
11601
11618
  try {
11602
- await this.refreshAccessToken();
11619
+ await this.refreshAccessToken({ silent: true });
11603
11620
  } catch {
11604
11621
  }
11605
11622
  }
@@ -11732,7 +11749,8 @@ var HttpClient = class {
11732
11749
  getAppId() {
11733
11750
  return this.config.appId;
11734
11751
  }
11735
- async refreshAccessToken() {
11752
+ async refreshAccessToken(options) {
11753
+ const silent = options?.silent ?? false;
11736
11754
  if (this.isRefreshing) {
11737
11755
  return this.refreshPromise;
11738
11756
  }
@@ -11846,27 +11864,35 @@ var HttpClient = class {
11846
11864
  );
11847
11865
  this.refreshLockedUntil = Date.now() + backoffMs;
11848
11866
  if (failureKind === "permanent") {
11849
- this.clearTokens();
11850
- this.config.onTokenExpired?.();
11867
+ if (!silent) {
11868
+ this.clearTokens();
11869
+ this.config.onTokenExpired?.();
11870
+ }
11851
11871
  const error2 = new AuthError(`${baseMsg}. Please login again.`);
11852
- this.emitError(error2);
11853
- this.config.onAuthError?.(error2);
11872
+ if (!silent) {
11873
+ this.emitError(error2);
11874
+ this.config.onAuthError?.(error2);
11875
+ }
11854
11876
  throw error2;
11855
11877
  }
11856
11878
  if (failureKind === "client_bug") {
11857
11879
  const error2 = new AuthError(
11858
11880
  `${baseMsg}. Client request invalid; tokens preserved.`
11859
11881
  );
11860
- this.emitError(error2);
11861
- this.config.onAuthError?.(error2);
11882
+ if (!silent) {
11883
+ this.emitError(error2);
11884
+ this.config.onAuthError?.(error2);
11885
+ }
11862
11886
  throw error2;
11863
11887
  }
11864
11888
  const error = new AuthError(
11865
11889
  `${baseMsg}. Transient failure; tokens preserved, will retry after backoff.`
11866
11890
  );
11867
- this.emitError(error);
11868
- this.config.onTransientRefreshFailure?.(error);
11869
- this.config.onAuthError?.(error);
11891
+ if (!silent) {
11892
+ this.emitError(error);
11893
+ this.config.onTransientRefreshFailure?.(error);
11894
+ this.config.onAuthError?.(error);
11895
+ }
11870
11896
  throw error;
11871
11897
  } finally {
11872
11898
  cleanup();
@@ -12110,6 +12136,30 @@ var HttpClient = class {
12110
12136
  } catch {
12111
12137
  }
12112
12138
  }
12139
+ /**
12140
+ * SDK 인증 헤더를 **주입하지 않는** raw fetch. 저지연 원본 선택과 폴백만 얹는다.
12141
+ *
12142
+ * `cb.endpoint.call()`(`/v1/proxy/*`) 전용이다. 그 경로는 dumb pipe 라 요청 헤더가
12143
+ * 사용자 PC 의 모델 서버로 그대로 전달된다 — `prepareHeaders()` 를 태우면 SDK 의
12144
+ * `Authorization` 이 남의 서버로 나가고, 호출자가 자기 모델 서버용으로 설정한
12145
+ * `Authorization` 을 덮어쓴다. 그래서 fetchRaw 를 재사용하지 않는다.
12146
+ *
12147
+ * @param path `/v1/proxy/...` 형태의 절대 경로 (origin 없이)
12148
+ */
12149
+ async fetchPassthrough(path, init) {
12150
+ const origin = this.resolveOrigin(path);
12151
+ try {
12152
+ const response = await fetch(`${origin}${path}`, init);
12153
+ this.learnDirectOrigin(response);
12154
+ return response;
12155
+ } catch (err) {
12156
+ if (origin !== this.config.baseUrl && !isAbortError(err)) {
12157
+ this.directDisabled = true;
12158
+ return fetch(`${this.config.baseUrl}${path}`, init);
12159
+ }
12160
+ throw err;
12161
+ }
12162
+ }
12113
12163
  async tryFetchOnce(url, init, config) {
12114
12164
  const { signal, cleanup } = createTimeoutController({
12115
12165
  timeout: config?.timeout ?? this.config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "5.11.0",
3
+ "version": "5.12.1",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",