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/CHANGELOG.md +68 -0
- package/dist/connect-base.umd.js +5 -5
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +65 -15
- package/dist/index.mjs +65 -15
- package/package.json +1 -1
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
|
|
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
|
|
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 (${
|
|
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
|
-
|
|
11899
|
-
|
|
11916
|
+
if (!silent) {
|
|
11917
|
+
this.clearTokens();
|
|
11918
|
+
this.config.onTokenExpired?.();
|
|
11919
|
+
}
|
|
11900
11920
|
const error2 = new AuthError(`${baseMsg}. Please login again.`);
|
|
11901
|
-
|
|
11902
|
-
|
|
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
|
-
|
|
11910
|
-
|
|
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
|
-
|
|
11917
|
-
|
|
11918
|
-
|
|
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
|
|
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
|
|
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 (${
|
|
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
|
-
|
|
11850
|
-
|
|
11867
|
+
if (!silent) {
|
|
11868
|
+
this.clearTokens();
|
|
11869
|
+
this.config.onTokenExpired?.();
|
|
11870
|
+
}
|
|
11851
11871
|
const error2 = new AuthError(`${baseMsg}. Please login again.`);
|
|
11852
|
-
|
|
11853
|
-
|
|
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
|
-
|
|
11861
|
-
|
|
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
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
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,
|