@djvlc/openapi-client-core 1.2.0 → 1.2.2
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/README.md +996 -146
- package/dist/index.d.mts +2635 -191
- package/dist/index.d.ts +2635 -191
- package/dist/index.js +2764 -338
- package/dist/index.mjs +2681 -333
- package/package.json +2 -2
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -11695,57 +11695,2194 @@ var require_follow_redirects = __commonJS({
|
|
|
11695
11695
|
// src/index.ts
|
|
11696
11696
|
var index_exports = {};
|
|
11697
11697
|
__export(index_exports, {
|
|
11698
|
+
AbortError: () => AbortError,
|
|
11698
11699
|
ApiError: () => ApiError,
|
|
11699
|
-
|
|
11700
|
+
ApiKeyAuthenticator: () => ApiKeyAuthenticator,
|
|
11701
|
+
AuthInterceptor: () => AuthInterceptor,
|
|
11702
|
+
BaseClient: () => BaseClient,
|
|
11703
|
+
BaseClientError: () => BaseClientError,
|
|
11704
|
+
BasicAuthenticator: () => BasicAuthenticator,
|
|
11705
|
+
BearerAuthenticator: () => BearerAuthenticator,
|
|
11706
|
+
BufferLogger: () => BufferLogger,
|
|
11707
|
+
ConsoleLogger: () => ConsoleLogger,
|
|
11708
|
+
CustomAuthenticator: () => CustomAuthenticator,
|
|
11709
|
+
DEFAULT_HEADERS: () => DEFAULT_HEADERS,
|
|
11710
|
+
DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
|
|
11711
|
+
DefaultMetricsCollector: () => DefaultMetricsCollector,
|
|
11712
|
+
ErrorTransformInterceptor: () => ErrorTransformInterceptor,
|
|
11713
|
+
FetchClient: () => FetchClient,
|
|
11714
|
+
InterceptorManager: () => InterceptorManager,
|
|
11715
|
+
LOG_LEVEL_PRIORITY: () => LOG_LEVEL_PRIORITY,
|
|
11716
|
+
LoggingInterceptor: () => LoggingInterceptor,
|
|
11717
|
+
MetricsErrorInterceptor: () => MetricsErrorInterceptor,
|
|
11718
|
+
MetricsRequestInterceptor: () => MetricsRequestInterceptor,
|
|
11719
|
+
MetricsResponseInterceptor: () => MetricsResponseInterceptor,
|
|
11700
11720
|
NetworkError: () => NetworkError,
|
|
11721
|
+
NoAuthenticator: () => NoAuthenticator,
|
|
11722
|
+
ReportingInterceptor: () => ReportingInterceptor,
|
|
11723
|
+
RequestDeduper: () => RequestDeduper,
|
|
11724
|
+
RequestIdInterceptor: () => RequestIdInterceptor,
|
|
11725
|
+
RetryInterceptor: () => RetryInterceptor,
|
|
11726
|
+
SDK_NAME: () => SDK_NAME,
|
|
11727
|
+
SilentLogger: () => SilentLogger,
|
|
11701
11728
|
TimeoutError: () => TimeoutError,
|
|
11729
|
+
TimeoutInterceptor: () => TimeoutInterceptor,
|
|
11730
|
+
TokenRefreshInterceptor: () => TokenRefreshInterceptor,
|
|
11731
|
+
TraceInterceptor: () => TraceInterceptor,
|
|
11702
11732
|
VERSION: () => VERSION3,
|
|
11733
|
+
buildUrl: () => buildUrl,
|
|
11703
11734
|
calculateRetryDelay: () => calculateRetryDelay,
|
|
11735
|
+
createApiKeyAuthenticator: () => createApiKeyAuthenticator,
|
|
11736
|
+
createAuthInterceptor: () => createAuthInterceptor,
|
|
11737
|
+
createAuthMiddleware: () => createAuthMiddleware,
|
|
11738
|
+
createAuthenticatorFromConfig: () => createAuthenticatorFromConfig,
|
|
11704
11739
|
createAxiosInstance: () => createAxiosInstance,
|
|
11705
|
-
|
|
11740
|
+
createBasicAuthenticator: () => createBasicAuthenticator,
|
|
11741
|
+
createBearerAuthenticator: () => createBearerAuthenticator,
|
|
11742
|
+
createBufferLogger: () => createBufferLogger,
|
|
11743
|
+
createConsoleLogger: () => createConsoleLogger,
|
|
11744
|
+
createCustomAuthenticator: () => createCustomAuthenticator,
|
|
11745
|
+
createErrorTransformInterceptor: () => createErrorTransformInterceptor,
|
|
11746
|
+
createFetchClient: () => createFetchClient,
|
|
11747
|
+
createInterceptorManager: () => createInterceptorManager,
|
|
11748
|
+
createLoggingInterceptor: () => createLoggingInterceptor,
|
|
11749
|
+
createMetricsCollector: () => createMetricsCollector,
|
|
11750
|
+
createMetricsInterceptors: () => createMetricsInterceptors,
|
|
11751
|
+
createMiddlewares: () => createMiddlewares,
|
|
11752
|
+
createNoAuthenticator: () => createNoAuthenticator,
|
|
11753
|
+
createReportingInterceptor: () => createReportingInterceptor,
|
|
11754
|
+
createRequestDeduper: () => createRequestDeduper,
|
|
11755
|
+
createRequestIdInterceptor: () => createRequestIdInterceptor,
|
|
11756
|
+
createRetryInterceptor: () => createRetryInterceptor,
|
|
11757
|
+
createSilentLogger: () => createSilentLogger,
|
|
11758
|
+
createTimeoutInterceptor: () => createTimeoutInterceptor,
|
|
11759
|
+
createTokenRefreshInterceptor: () => createTokenRefreshInterceptor,
|
|
11760
|
+
createTraceInterceptor: () => createTraceInterceptor,
|
|
11761
|
+
createTrackingMiddleware: () => createTrackingMiddleware,
|
|
11762
|
+
extractPath: () => extractPath,
|
|
11763
|
+
extractResponseHeaders: () => extractResponseHeaders,
|
|
11706
11764
|
generateRequestId: () => generateRequestId,
|
|
11707
|
-
|
|
11765
|
+
generateTraceId: () => generateTraceId,
|
|
11766
|
+
getErrorType: () => getErrorType,
|
|
11767
|
+
getHeader: () => getHeader,
|
|
11768
|
+
getRecommendedRetryDelay: () => getRecommendedRetryDelay,
|
|
11769
|
+
getRetryDelay: () => getRetryDelay,
|
|
11770
|
+
getSdkInfo: () => getSdkInfo,
|
|
11771
|
+
hasContentType: () => hasContentType,
|
|
11772
|
+
isClientError: () => isClientError,
|
|
11773
|
+
isRetryableError: () => isRetryableError,
|
|
11774
|
+
isValidRequestId: () => isValidRequestId,
|
|
11775
|
+
isValidTraceId: () => isValidTraceId,
|
|
11776
|
+
joinPaths: () => joinPaths,
|
|
11777
|
+
mergeHeaders: () => mergeHeaders,
|
|
11778
|
+
noAuthenticator: () => noAuthenticator,
|
|
11779
|
+
parseQueryString: () => parseQueryString,
|
|
11780
|
+
parseRetryAfter: () => parseRetryAfter,
|
|
11781
|
+
removeHeader: () => removeHeader,
|
|
11782
|
+
setHeader: () => setHeader,
|
|
11783
|
+
silentLogger: () => silentLogger,
|
|
11784
|
+
sleep: () => sleep,
|
|
11785
|
+
sleepWithAbort: () => sleepWithAbort
|
|
11708
11786
|
});
|
|
11709
11787
|
module.exports = __toCommonJS(index_exports);
|
|
11710
11788
|
|
|
11711
|
-
// src/
|
|
11712
|
-
var
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
|
|
11789
|
+
// src/types/retry.ts
|
|
11790
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
11791
|
+
maxRetries: 3,
|
|
11792
|
+
initialDelayMs: 1e3,
|
|
11793
|
+
maxDelayMs: 3e4,
|
|
11794
|
+
backoffStrategy: "exponential",
|
|
11795
|
+
retryableStatusCodes: [429, 500, 502, 503, 504],
|
|
11796
|
+
retryOnNetworkError: true,
|
|
11797
|
+
retryOnTimeout: true,
|
|
11798
|
+
respectRetryAfter: true,
|
|
11799
|
+
jitterFactor: 0.1
|
|
11800
|
+
};
|
|
11801
|
+
|
|
11802
|
+
// src/types/logger.ts
|
|
11803
|
+
var LOG_LEVEL_PRIORITY = {
|
|
11804
|
+
debug: 0,
|
|
11805
|
+
info: 1,
|
|
11806
|
+
warn: 2,
|
|
11807
|
+
error: 3
|
|
11808
|
+
};
|
|
11809
|
+
|
|
11810
|
+
// src/errors/base-error.ts
|
|
11811
|
+
var BaseClientError = class extends Error {
|
|
11812
|
+
constructor(message) {
|
|
11813
|
+
super(message);
|
|
11814
|
+
this.name = this.constructor.name;
|
|
11815
|
+
this.timestamp = /* @__PURE__ */ new Date();
|
|
11816
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
11817
|
+
if (Error.captureStackTrace) {
|
|
11818
|
+
Error.captureStackTrace(this, this.constructor);
|
|
11819
|
+
}
|
|
11820
|
+
}
|
|
11821
|
+
/**
|
|
11822
|
+
* 转换为 JSON 对象(用于日志和序列化)
|
|
11823
|
+
*/
|
|
11824
|
+
toJSON() {
|
|
11825
|
+
return {
|
|
11826
|
+
name: this.name,
|
|
11827
|
+
type: this.type,
|
|
11828
|
+
message: this.message,
|
|
11829
|
+
retryable: this.retryable,
|
|
11830
|
+
timestamp: this.timestamp.toISOString(),
|
|
11831
|
+
stack: this.stack
|
|
11832
|
+
};
|
|
11833
|
+
}
|
|
11834
|
+
/**
|
|
11835
|
+
* 转换为字符串
|
|
11836
|
+
*/
|
|
11837
|
+
toString() {
|
|
11838
|
+
return `${this.name}: ${this.message}`;
|
|
11839
|
+
}
|
|
11840
|
+
};
|
|
11841
|
+
|
|
11842
|
+
// src/errors/api-error.ts
|
|
11843
|
+
var ApiError = class _ApiError extends BaseClientError {
|
|
11844
|
+
constructor(config) {
|
|
11845
|
+
super(config.message);
|
|
11846
|
+
this.type = "API_ERROR";
|
|
11847
|
+
this.code = config.code;
|
|
11848
|
+
this.statusCode = config.statusCode;
|
|
11849
|
+
this.details = config.details;
|
|
11850
|
+
this.requestId = config.requestId;
|
|
11851
|
+
this.traceId = config.traceId;
|
|
11852
|
+
this.retryAfterSeconds = config.retryAfter;
|
|
11853
|
+
this.rawResponse = config.rawResponse;
|
|
11854
|
+
}
|
|
11855
|
+
/**
|
|
11856
|
+
* 是否可重试
|
|
11857
|
+
*
|
|
11858
|
+
* 5xx 和 429 错误通常可重试
|
|
11859
|
+
*/
|
|
11860
|
+
get retryable() {
|
|
11861
|
+
if (this.statusCode === 429) {
|
|
11862
|
+
return true;
|
|
11863
|
+
}
|
|
11864
|
+
if (this.statusCode >= 500 && this.statusCode < 600 && this.statusCode !== 501) {
|
|
11865
|
+
return true;
|
|
11866
|
+
}
|
|
11867
|
+
return false;
|
|
11868
|
+
}
|
|
11869
|
+
// ============================================================
|
|
11870
|
+
// 状态码判断方法
|
|
11871
|
+
// ============================================================
|
|
11872
|
+
/**
|
|
11873
|
+
* 是否为未授权错误 (401)
|
|
11874
|
+
*/
|
|
11875
|
+
isUnauthorized() {
|
|
11876
|
+
return this.statusCode === 401;
|
|
11877
|
+
}
|
|
11878
|
+
/**
|
|
11879
|
+
* 是否为禁止访问错误 (403)
|
|
11880
|
+
*/
|
|
11881
|
+
isForbidden() {
|
|
11882
|
+
return this.statusCode === 403;
|
|
11883
|
+
}
|
|
11884
|
+
/**
|
|
11885
|
+
* 是否为认证相关错误 (401/403)
|
|
11886
|
+
*/
|
|
11887
|
+
isAuthError() {
|
|
11888
|
+
return this.isUnauthorized() || this.isForbidden();
|
|
11889
|
+
}
|
|
11890
|
+
/**
|
|
11891
|
+
* 是否为未找到错误 (404)
|
|
11892
|
+
*/
|
|
11893
|
+
isNotFound() {
|
|
11894
|
+
return this.statusCode === 404;
|
|
11895
|
+
}
|
|
11896
|
+
/**
|
|
11897
|
+
* 是否为冲突错误 (409)
|
|
11898
|
+
*/
|
|
11899
|
+
isConflict() {
|
|
11900
|
+
return this.statusCode === 409;
|
|
11901
|
+
}
|
|
11902
|
+
/**
|
|
11903
|
+
* 是否为验证错误 (400/422)
|
|
11904
|
+
*/
|
|
11905
|
+
isValidationError() {
|
|
11906
|
+
return this.statusCode === 400 || this.statusCode === 422;
|
|
11907
|
+
}
|
|
11908
|
+
/**
|
|
11909
|
+
* 是否为限流错误 (429)
|
|
11910
|
+
*/
|
|
11911
|
+
isRateLimited() {
|
|
11912
|
+
return this.statusCode === 429;
|
|
11913
|
+
}
|
|
11914
|
+
/**
|
|
11915
|
+
* 是否为客户端错误 (4xx)
|
|
11916
|
+
*/
|
|
11917
|
+
isClientError() {
|
|
11918
|
+
return this.statusCode >= 400 && this.statusCode < 500;
|
|
11919
|
+
}
|
|
11920
|
+
/**
|
|
11921
|
+
* 是否为服务端错误 (5xx)
|
|
11922
|
+
*/
|
|
11923
|
+
isServerError() {
|
|
11924
|
+
return this.statusCode >= 500 && this.statusCode < 600;
|
|
11925
|
+
}
|
|
11926
|
+
// ============================================================
|
|
11927
|
+
// 重试相关方法
|
|
11928
|
+
// ============================================================
|
|
11929
|
+
/**
|
|
11930
|
+
* 获取 Retry-After 值(秒)
|
|
11931
|
+
*/
|
|
11932
|
+
getRetryAfter() {
|
|
11933
|
+
return this.retryAfterSeconds;
|
|
11934
|
+
}
|
|
11935
|
+
/**
|
|
11936
|
+
* 获取推荐的重试延迟(毫秒)
|
|
11937
|
+
*
|
|
11938
|
+
* @param defaultMs - 默认延迟(毫秒)
|
|
11939
|
+
*/
|
|
11940
|
+
getRetryDelayMs(defaultMs = 1e3) {
|
|
11941
|
+
if (this.retryAfterSeconds !== void 0) {
|
|
11942
|
+
return this.retryAfterSeconds * 1e3;
|
|
11943
|
+
}
|
|
11944
|
+
return defaultMs;
|
|
11945
|
+
}
|
|
11946
|
+
// ============================================================
|
|
11947
|
+
// 序列化
|
|
11948
|
+
// ============================================================
|
|
11949
|
+
toJSON() {
|
|
11950
|
+
return {
|
|
11951
|
+
...super.toJSON(),
|
|
11952
|
+
code: this.code,
|
|
11953
|
+
statusCode: this.statusCode,
|
|
11954
|
+
details: this.details,
|
|
11955
|
+
requestId: this.requestId,
|
|
11956
|
+
traceId: this.traceId,
|
|
11957
|
+
retryAfterSeconds: this.retryAfterSeconds
|
|
11958
|
+
};
|
|
11959
|
+
}
|
|
11960
|
+
// ============================================================
|
|
11961
|
+
// 静态方法
|
|
11962
|
+
// ============================================================
|
|
11963
|
+
/**
|
|
11964
|
+
* 类型守卫:判断是否为 ApiError
|
|
11965
|
+
*/
|
|
11966
|
+
static is(error) {
|
|
11967
|
+
return error instanceof _ApiError;
|
|
11968
|
+
}
|
|
11969
|
+
/**
|
|
11970
|
+
* 从响应数据创建 ApiError
|
|
11971
|
+
*/
|
|
11972
|
+
static fromResponse(data, statusCode, retryAfter) {
|
|
11973
|
+
return new _ApiError({
|
|
11974
|
+
message: data.message ?? `HTTP Error ${statusCode}`,
|
|
11975
|
+
code: data.code ?? `HTTP_${statusCode}`,
|
|
11976
|
+
statusCode,
|
|
11977
|
+
details: data.details,
|
|
11978
|
+
requestId: data.requestId ?? "unknown",
|
|
11979
|
+
traceId: data.traceId,
|
|
11980
|
+
retryAfter,
|
|
11981
|
+
rawResponse: data
|
|
11982
|
+
});
|
|
11983
|
+
}
|
|
11984
|
+
};
|
|
11985
|
+
|
|
11986
|
+
// src/errors/network-error.ts
|
|
11987
|
+
var NetworkError = class _NetworkError extends BaseClientError {
|
|
11988
|
+
constructor(message, cause) {
|
|
11989
|
+
super(message);
|
|
11990
|
+
this.type = "NETWORK_ERROR";
|
|
11991
|
+
this.retryable = true;
|
|
11992
|
+
this.cause = cause;
|
|
11993
|
+
}
|
|
11994
|
+
toJSON() {
|
|
11995
|
+
return {
|
|
11996
|
+
...super.toJSON(),
|
|
11997
|
+
cause: this.cause ? {
|
|
11998
|
+
name: this.cause.name,
|
|
11999
|
+
message: this.cause.message
|
|
12000
|
+
} : void 0
|
|
12001
|
+
};
|
|
12002
|
+
}
|
|
12003
|
+
/**
|
|
12004
|
+
* 类型守卫:判断是否为 NetworkError
|
|
12005
|
+
*/
|
|
12006
|
+
static is(error) {
|
|
12007
|
+
return error instanceof _NetworkError;
|
|
12008
|
+
}
|
|
12009
|
+
/**
|
|
12010
|
+
* 从原生 fetch 错误创建 NetworkError
|
|
12011
|
+
*/
|
|
12012
|
+
static fromFetchError(error) {
|
|
12013
|
+
const message = error.message.toLowerCase();
|
|
12014
|
+
if (message.includes("network") || message.includes("failed to fetch")) {
|
|
12015
|
+
return new _NetworkError("Network request failed", error);
|
|
12016
|
+
}
|
|
12017
|
+
if (message.includes("dns") || message.includes("getaddrinfo")) {
|
|
12018
|
+
return new _NetworkError("DNS resolution failed", error);
|
|
12019
|
+
}
|
|
12020
|
+
if (message.includes("connection refused") || message.includes("econnrefused")) {
|
|
12021
|
+
return new _NetworkError("Connection refused", error);
|
|
12022
|
+
}
|
|
12023
|
+
if (message.includes("connection reset") || message.includes("econnreset")) {
|
|
12024
|
+
return new _NetworkError("Connection reset", error);
|
|
12025
|
+
}
|
|
12026
|
+
if (message.includes("ssl") || message.includes("certificate")) {
|
|
12027
|
+
return new _NetworkError("SSL/TLS error", error);
|
|
12028
|
+
}
|
|
12029
|
+
return new _NetworkError(error.message || "Unknown network error", error);
|
|
12030
|
+
}
|
|
12031
|
+
};
|
|
12032
|
+
|
|
12033
|
+
// src/errors/timeout-error.ts
|
|
12034
|
+
var TimeoutError = class _TimeoutError extends BaseClientError {
|
|
12035
|
+
constructor(timeoutMs) {
|
|
12036
|
+
super(`Request timeout after ${timeoutMs}ms`);
|
|
12037
|
+
this.type = "TIMEOUT_ERROR";
|
|
12038
|
+
this.retryable = true;
|
|
12039
|
+
this.timeoutMs = timeoutMs;
|
|
12040
|
+
}
|
|
12041
|
+
toJSON() {
|
|
12042
|
+
return {
|
|
12043
|
+
...super.toJSON(),
|
|
12044
|
+
timeoutMs: this.timeoutMs
|
|
12045
|
+
};
|
|
12046
|
+
}
|
|
12047
|
+
/**
|
|
12048
|
+
* 类型守卫:判断是否为 TimeoutError
|
|
12049
|
+
*/
|
|
12050
|
+
static is(error) {
|
|
12051
|
+
return error instanceof _TimeoutError;
|
|
12052
|
+
}
|
|
12053
|
+
};
|
|
12054
|
+
|
|
12055
|
+
// src/errors/abort-error.ts
|
|
12056
|
+
var AbortError = class _AbortError extends BaseClientError {
|
|
12057
|
+
constructor(reason) {
|
|
12058
|
+
super(reason ?? "Request was aborted");
|
|
12059
|
+
this.type = "ABORT_ERROR";
|
|
12060
|
+
this.retryable = false;
|
|
12061
|
+
this.reason = reason;
|
|
12062
|
+
}
|
|
12063
|
+
toJSON() {
|
|
12064
|
+
return {
|
|
12065
|
+
...super.toJSON(),
|
|
12066
|
+
reason: this.reason
|
|
12067
|
+
};
|
|
12068
|
+
}
|
|
12069
|
+
/**
|
|
12070
|
+
* 类型守卫:判断是否为 AbortError
|
|
12071
|
+
*/
|
|
12072
|
+
static is(error) {
|
|
12073
|
+
return error instanceof _AbortError;
|
|
12074
|
+
}
|
|
12075
|
+
/**
|
|
12076
|
+
* 从原生 AbortError 创建
|
|
12077
|
+
*/
|
|
12078
|
+
static fromNative(error) {
|
|
12079
|
+
return new _AbortError(error.message);
|
|
12080
|
+
}
|
|
12081
|
+
};
|
|
12082
|
+
|
|
12083
|
+
// src/errors/index.ts
|
|
12084
|
+
function isRetryableError(error) {
|
|
12085
|
+
if (error instanceof ApiError || error instanceof NetworkError || error instanceof TimeoutError) {
|
|
12086
|
+
return error.retryable;
|
|
12087
|
+
}
|
|
12088
|
+
if (error instanceof AbortError) {
|
|
12089
|
+
return false;
|
|
12090
|
+
}
|
|
12091
|
+
return false;
|
|
12092
|
+
}
|
|
12093
|
+
function getRetryDelay(error, defaultMs = 1e3) {
|
|
12094
|
+
if (error instanceof ApiError) {
|
|
12095
|
+
return error.getRetryDelayMs(defaultMs);
|
|
12096
|
+
}
|
|
12097
|
+
return defaultMs;
|
|
12098
|
+
}
|
|
12099
|
+
function isClientError(error) {
|
|
12100
|
+
return error instanceof ApiError || error instanceof NetworkError || error instanceof TimeoutError || error instanceof AbortError;
|
|
12101
|
+
}
|
|
12102
|
+
function getErrorType(error) {
|
|
12103
|
+
if (error instanceof ApiError) {
|
|
12104
|
+
return error.type;
|
|
12105
|
+
}
|
|
12106
|
+
if (error instanceof NetworkError) {
|
|
12107
|
+
return error.type;
|
|
12108
|
+
}
|
|
12109
|
+
if (error instanceof TimeoutError) {
|
|
12110
|
+
return error.type;
|
|
12111
|
+
}
|
|
12112
|
+
if (error instanceof AbortError) {
|
|
12113
|
+
return error.type;
|
|
12114
|
+
}
|
|
12115
|
+
if (error instanceof Error) {
|
|
12116
|
+
return error.name;
|
|
12117
|
+
}
|
|
12118
|
+
return "UNKNOWN_ERROR";
|
|
12119
|
+
}
|
|
12120
|
+
|
|
12121
|
+
// src/auth/bearer-authenticator.ts
|
|
12122
|
+
var BearerAuthenticator = class {
|
|
12123
|
+
constructor(config) {
|
|
12124
|
+
this.type = "bearer";
|
|
12125
|
+
this.getToken = config.getToken;
|
|
12126
|
+
this.headerName = config.headerName ?? "Authorization";
|
|
12127
|
+
this.prefix = config.prefix ?? "Bearer";
|
|
12128
|
+
}
|
|
12129
|
+
async authenticate(headers) {
|
|
12130
|
+
const token = await this.getToken();
|
|
12131
|
+
if (token !== null && token !== "") {
|
|
12132
|
+
headers[this.headerName] = `${this.prefix} ${token}`;
|
|
12133
|
+
}
|
|
12134
|
+
}
|
|
12135
|
+
};
|
|
12136
|
+
function createBearerAuthenticator(config) {
|
|
12137
|
+
return new BearerAuthenticator(config);
|
|
12138
|
+
}
|
|
12139
|
+
|
|
12140
|
+
// src/auth/api-key-authenticator.ts
|
|
12141
|
+
var ApiKeyAuthenticator = class {
|
|
12142
|
+
constructor(config) {
|
|
12143
|
+
this.type = "api-key";
|
|
12144
|
+
this.apiKey = config.apiKey;
|
|
12145
|
+
this.headerName = config.headerName ?? "X-API-Key";
|
|
12146
|
+
}
|
|
12147
|
+
authenticate(headers) {
|
|
12148
|
+
headers[this.headerName] = this.apiKey;
|
|
12149
|
+
}
|
|
12150
|
+
};
|
|
12151
|
+
function createApiKeyAuthenticator(config) {
|
|
12152
|
+
return new ApiKeyAuthenticator(config);
|
|
12153
|
+
}
|
|
12154
|
+
|
|
12155
|
+
// src/auth/basic-authenticator.ts
|
|
12156
|
+
function encodeBase64(str) {
|
|
12157
|
+
if (typeof btoa !== "undefined") {
|
|
12158
|
+
const utf8Bytes = new TextEncoder().encode(str);
|
|
12159
|
+
const binaryString = Array.from(utf8Bytes, (byte) => String.fromCharCode(byte)).join("");
|
|
12160
|
+
return btoa(binaryString);
|
|
12161
|
+
}
|
|
12162
|
+
if (typeof Buffer !== "undefined") {
|
|
12163
|
+
return Buffer.from(str, "utf-8").toString("base64");
|
|
12164
|
+
}
|
|
12165
|
+
throw new Error("Base64 encoding is not supported in this environment");
|
|
12166
|
+
}
|
|
12167
|
+
var BasicAuthenticator = class {
|
|
12168
|
+
constructor(config) {
|
|
12169
|
+
this.type = "basic";
|
|
12170
|
+
this.encodedCredentials = encodeBase64(`${config.username}:${config.password}`);
|
|
12171
|
+
}
|
|
12172
|
+
authenticate(headers) {
|
|
12173
|
+
headers["Authorization"] = `Basic ${this.encodedCredentials}`;
|
|
12174
|
+
}
|
|
12175
|
+
};
|
|
12176
|
+
function createBasicAuthenticator(config) {
|
|
12177
|
+
return new BasicAuthenticator(config);
|
|
12178
|
+
}
|
|
12179
|
+
|
|
12180
|
+
// src/auth/custom-authenticator.ts
|
|
12181
|
+
var CustomAuthenticator = class {
|
|
12182
|
+
constructor(config) {
|
|
12183
|
+
this.type = "custom";
|
|
12184
|
+
this.authenticateFn = config.authenticate;
|
|
12185
|
+
}
|
|
12186
|
+
async authenticate(headers) {
|
|
12187
|
+
await this.authenticateFn(headers);
|
|
12188
|
+
}
|
|
12189
|
+
};
|
|
12190
|
+
function createCustomAuthenticator(config) {
|
|
12191
|
+
return new CustomAuthenticator(config);
|
|
12192
|
+
}
|
|
12193
|
+
|
|
12194
|
+
// src/auth/no-authenticator.ts
|
|
12195
|
+
var NoAuthenticator = class {
|
|
12196
|
+
constructor() {
|
|
12197
|
+
this.type = "none";
|
|
12198
|
+
}
|
|
12199
|
+
authenticate(_headers) {
|
|
12200
|
+
}
|
|
12201
|
+
};
|
|
12202
|
+
function createNoAuthenticator() {
|
|
12203
|
+
return new NoAuthenticator();
|
|
12204
|
+
}
|
|
12205
|
+
var noAuthenticator = new NoAuthenticator();
|
|
12206
|
+
|
|
12207
|
+
// src/auth/index.ts
|
|
12208
|
+
function createAuthenticatorFromConfig(config) {
|
|
12209
|
+
switch (config.type) {
|
|
12210
|
+
case "bearer":
|
|
12211
|
+
return new BearerAuthenticator({ getToken: config.getToken, headerName: config.headerName, prefix: config.prefix });
|
|
12212
|
+
case "api-key":
|
|
12213
|
+
return new ApiKeyAuthenticator({ apiKey: config.apiKey, headerName: config.headerName });
|
|
12214
|
+
case "basic":
|
|
12215
|
+
return new BasicAuthenticator({ username: config.username, password: config.password });
|
|
12216
|
+
case "custom":
|
|
12217
|
+
return new CustomAuthenticator({ authenticate: config.authenticate });
|
|
12218
|
+
case "none":
|
|
12219
|
+
return noAuthenticator;
|
|
12220
|
+
default: {
|
|
12221
|
+
const _exhaustiveCheck = config;
|
|
12222
|
+
throw new Error(`Unknown auth type: ${_exhaustiveCheck.type}`);
|
|
12223
|
+
}
|
|
12224
|
+
}
|
|
12225
|
+
}
|
|
12226
|
+
|
|
12227
|
+
// src/interceptors/manager.ts
|
|
12228
|
+
var InterceptorManager = class {
|
|
12229
|
+
constructor() {
|
|
12230
|
+
this.requestInterceptors = [];
|
|
12231
|
+
this.responseInterceptors = [];
|
|
12232
|
+
this.errorInterceptors = [];
|
|
12233
|
+
}
|
|
12234
|
+
/**
|
|
12235
|
+
* 获取所有拦截器
|
|
12236
|
+
*/
|
|
12237
|
+
get interceptors() {
|
|
12238
|
+
return {
|
|
12239
|
+
request: [...this.requestInterceptors],
|
|
12240
|
+
response: [...this.responseInterceptors],
|
|
12241
|
+
error: [...this.errorInterceptors]
|
|
12242
|
+
};
|
|
12243
|
+
}
|
|
12244
|
+
// ============================================================
|
|
12245
|
+
// 注册方法
|
|
12246
|
+
// ============================================================
|
|
12247
|
+
/**
|
|
12248
|
+
* 添加请求拦截器
|
|
12249
|
+
*
|
|
12250
|
+
* @param interceptor - 请求拦截器
|
|
12251
|
+
* @returns 返回自身,支持链式调用
|
|
12252
|
+
*/
|
|
12253
|
+
addRequestInterceptor(interceptor) {
|
|
12254
|
+
this.requestInterceptors.push(interceptor);
|
|
12255
|
+
this.sortInterceptors(this.requestInterceptors);
|
|
12256
|
+
return this;
|
|
12257
|
+
}
|
|
12258
|
+
/**
|
|
12259
|
+
* 添加响应拦截器
|
|
12260
|
+
*
|
|
12261
|
+
* @param interceptor - 响应拦截器
|
|
12262
|
+
* @returns 返回自身,支持链式调用
|
|
12263
|
+
*/
|
|
12264
|
+
addResponseInterceptor(interceptor) {
|
|
12265
|
+
this.responseInterceptors.push(interceptor);
|
|
12266
|
+
this.sortInterceptors(this.responseInterceptors);
|
|
12267
|
+
return this;
|
|
12268
|
+
}
|
|
12269
|
+
/**
|
|
12270
|
+
* 添加错误拦截器
|
|
12271
|
+
*
|
|
12272
|
+
* @param interceptor - 错误拦截器
|
|
12273
|
+
* @returns 返回自身,支持链式调用
|
|
12274
|
+
*/
|
|
12275
|
+
addErrorInterceptor(interceptor) {
|
|
12276
|
+
this.errorInterceptors.push(interceptor);
|
|
12277
|
+
this.sortInterceptors(this.errorInterceptors);
|
|
12278
|
+
return this;
|
|
12279
|
+
}
|
|
12280
|
+
/**
|
|
12281
|
+
* 批量添加拦截器
|
|
12282
|
+
*
|
|
12283
|
+
* @param interceptors - 拦截器配置
|
|
12284
|
+
* @returns 返回自身,支持链式调用
|
|
12285
|
+
*/
|
|
12286
|
+
addInterceptors(interceptors) {
|
|
12287
|
+
if (interceptors.request) {
|
|
12288
|
+
for (const i of interceptors.request) {
|
|
12289
|
+
this.addRequestInterceptor(i);
|
|
12290
|
+
}
|
|
12291
|
+
}
|
|
12292
|
+
if (interceptors.response) {
|
|
12293
|
+
for (const i of interceptors.response) {
|
|
12294
|
+
this.addResponseInterceptor(i);
|
|
12295
|
+
}
|
|
12296
|
+
}
|
|
12297
|
+
if (interceptors.error) {
|
|
12298
|
+
for (const i of interceptors.error) {
|
|
12299
|
+
this.addErrorInterceptor(i);
|
|
12300
|
+
}
|
|
12301
|
+
}
|
|
12302
|
+
return this;
|
|
12303
|
+
}
|
|
12304
|
+
// ============================================================
|
|
12305
|
+
// 移除方法
|
|
12306
|
+
// ============================================================
|
|
12307
|
+
/**
|
|
12308
|
+
* 移除请求拦截器
|
|
12309
|
+
*
|
|
12310
|
+
* @param name - 拦截器名称
|
|
12311
|
+
* @returns 是否移除成功
|
|
12312
|
+
*/
|
|
12313
|
+
removeRequestInterceptor(name) {
|
|
12314
|
+
return this.removeByName(this.requestInterceptors, name);
|
|
12315
|
+
}
|
|
12316
|
+
/**
|
|
12317
|
+
* 移除响应拦截器
|
|
12318
|
+
*
|
|
12319
|
+
* @param name - 拦截器名称
|
|
12320
|
+
* @returns 是否移除成功
|
|
12321
|
+
*/
|
|
12322
|
+
removeResponseInterceptor(name) {
|
|
12323
|
+
return this.removeByName(this.responseInterceptors, name);
|
|
12324
|
+
}
|
|
12325
|
+
/**
|
|
12326
|
+
* 移除错误拦截器
|
|
12327
|
+
*
|
|
12328
|
+
* @param name - 拦截器名称
|
|
12329
|
+
* @returns 是否移除成功
|
|
12330
|
+
*/
|
|
12331
|
+
removeErrorInterceptor(name) {
|
|
12332
|
+
return this.removeByName(this.errorInterceptors, name);
|
|
12333
|
+
}
|
|
12334
|
+
/**
|
|
12335
|
+
* 清除所有拦截器
|
|
12336
|
+
*/
|
|
12337
|
+
clear() {
|
|
12338
|
+
this.requestInterceptors.length = 0;
|
|
12339
|
+
this.responseInterceptors.length = 0;
|
|
12340
|
+
this.errorInterceptors.length = 0;
|
|
12341
|
+
}
|
|
12342
|
+
// ============================================================
|
|
12343
|
+
// 执行方法
|
|
12344
|
+
// ============================================================
|
|
12345
|
+
/**
|
|
12346
|
+
* 执行请求拦截器链
|
|
12347
|
+
*
|
|
12348
|
+
* @param context - 请求上下文
|
|
12349
|
+
* @returns 修改后的请求选项
|
|
12350
|
+
*/
|
|
12351
|
+
async executeRequestInterceptors(context) {
|
|
12352
|
+
let options = context.options;
|
|
12353
|
+
for (const interceptor of this.requestInterceptors) {
|
|
12354
|
+
options = await interceptor.intercept({
|
|
12355
|
+
...context,
|
|
12356
|
+
options
|
|
12357
|
+
});
|
|
12358
|
+
}
|
|
12359
|
+
return options;
|
|
12360
|
+
}
|
|
12361
|
+
/**
|
|
12362
|
+
* 执行响应拦截器链
|
|
12363
|
+
*
|
|
12364
|
+
* @param response - 响应数据
|
|
12365
|
+
* @param context - 请求上下文
|
|
12366
|
+
* @returns 修改后的响应数据
|
|
12367
|
+
*/
|
|
12368
|
+
async executeResponseInterceptors(response, context) {
|
|
12369
|
+
let result = response;
|
|
12370
|
+
for (const interceptor of this.responseInterceptors) {
|
|
12371
|
+
result = await interceptor.intercept(result, context);
|
|
12372
|
+
}
|
|
12373
|
+
return result;
|
|
12374
|
+
}
|
|
12375
|
+
/**
|
|
12376
|
+
* 执行错误拦截器链
|
|
12377
|
+
*
|
|
12378
|
+
* @param error - 错误对象
|
|
12379
|
+
* @param context - 请求上下文
|
|
12380
|
+
* @returns 如果返回 ResponseData,则表示错误被恢复;否则错误继续传播
|
|
12381
|
+
* @throws 如果拦截器抛出新错误,则使用新错误
|
|
12382
|
+
*/
|
|
12383
|
+
async executeErrorInterceptors(error, context) {
|
|
12384
|
+
for (const interceptor of this.errorInterceptors) {
|
|
12385
|
+
const result = await interceptor.intercept(error, context);
|
|
12386
|
+
if (result !== void 0) {
|
|
12387
|
+
return result;
|
|
12388
|
+
}
|
|
12389
|
+
}
|
|
12390
|
+
return void 0;
|
|
12391
|
+
}
|
|
12392
|
+
// ============================================================
|
|
12393
|
+
// 私有方法
|
|
12394
|
+
// ============================================================
|
|
12395
|
+
/**
|
|
12396
|
+
* 按 order 排序拦截器
|
|
12397
|
+
*/
|
|
12398
|
+
sortInterceptors(interceptors) {
|
|
12399
|
+
interceptors.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
12400
|
+
}
|
|
12401
|
+
/**
|
|
12402
|
+
* 按名称移除拦截器
|
|
12403
|
+
*/
|
|
12404
|
+
removeByName(interceptors, name) {
|
|
12405
|
+
const index = interceptors.findIndex((i) => i.name === name);
|
|
12406
|
+
if (index !== -1) {
|
|
12407
|
+
interceptors.splice(index, 1);
|
|
11725
12408
|
return true;
|
|
11726
12409
|
}
|
|
11727
|
-
|
|
11728
|
-
|
|
12410
|
+
return false;
|
|
12411
|
+
}
|
|
12412
|
+
};
|
|
12413
|
+
function createInterceptorManager() {
|
|
12414
|
+
return new InterceptorManager();
|
|
12415
|
+
}
|
|
12416
|
+
|
|
12417
|
+
// src/interceptors/request/auth.interceptor.ts
|
|
12418
|
+
var AuthInterceptor = class {
|
|
12419
|
+
constructor(config) {
|
|
12420
|
+
this.name = "auth";
|
|
12421
|
+
this.authenticator = config.authenticator;
|
|
12422
|
+
this.order = config.order ?? -100;
|
|
12423
|
+
}
|
|
12424
|
+
async intercept(context) {
|
|
12425
|
+
const { options } = context;
|
|
12426
|
+
if (options.skipAuth) {
|
|
12427
|
+
return options;
|
|
12428
|
+
}
|
|
12429
|
+
await this.authenticator.authenticate(options.headers);
|
|
12430
|
+
return options;
|
|
12431
|
+
}
|
|
12432
|
+
};
|
|
12433
|
+
function createAuthInterceptor(config) {
|
|
12434
|
+
return new AuthInterceptor(config);
|
|
12435
|
+
}
|
|
12436
|
+
|
|
12437
|
+
// src/utils/request-id.ts
|
|
12438
|
+
function generateRequestId() {
|
|
12439
|
+
const timestamp = Date.now().toString(36);
|
|
12440
|
+
const random = Math.random().toString(36).slice(2, 9);
|
|
12441
|
+
return `req_${timestamp}_${random}`;
|
|
12442
|
+
}
|
|
12443
|
+
function generateTraceId() {
|
|
12444
|
+
const timestamp = Date.now().toString(36);
|
|
12445
|
+
const random1 = Math.random().toString(36).slice(2, 9);
|
|
12446
|
+
const random2 = Math.random().toString(36).slice(2, 9);
|
|
12447
|
+
return `trace_${timestamp}_${random1}_${random2}`;
|
|
12448
|
+
}
|
|
12449
|
+
function isValidRequestId(id) {
|
|
12450
|
+
return /^req_[a-z0-9]+_[a-z0-9]+$/.test(id);
|
|
12451
|
+
}
|
|
12452
|
+
function isValidTraceId(id) {
|
|
12453
|
+
return /^trace_[a-z0-9]+_[a-z0-9]+_[a-z0-9]+$/.test(id);
|
|
12454
|
+
}
|
|
12455
|
+
|
|
12456
|
+
// src/interceptors/request/request-id.interceptor.ts
|
|
12457
|
+
var RequestIdInterceptor = class {
|
|
12458
|
+
constructor(config = {}) {
|
|
12459
|
+
this.name = "request-id";
|
|
12460
|
+
this.headerName = config.headerName ?? "X-Request-ID";
|
|
12461
|
+
this.generateId = config.generateId ?? generateRequestId;
|
|
12462
|
+
this.order = config.order ?? -90;
|
|
12463
|
+
}
|
|
12464
|
+
intercept(context) {
|
|
12465
|
+
const { options } = context;
|
|
12466
|
+
if (!options.headers[this.headerName]) {
|
|
12467
|
+
options.headers[this.headerName] = context.requestId;
|
|
12468
|
+
}
|
|
12469
|
+
return options;
|
|
12470
|
+
}
|
|
12471
|
+
};
|
|
12472
|
+
function createRequestIdInterceptor(config) {
|
|
12473
|
+
return new RequestIdInterceptor(config);
|
|
12474
|
+
}
|
|
12475
|
+
|
|
12476
|
+
// src/interceptors/request/trace.interceptor.ts
|
|
12477
|
+
var TraceInterceptor = class {
|
|
12478
|
+
constructor(config = {}) {
|
|
12479
|
+
this.name = "trace";
|
|
12480
|
+
this.traceIdHeader = config.traceIdHeader ?? "X-Trace-ID";
|
|
12481
|
+
this.addTraceparent = config.addTraceparent ?? false;
|
|
12482
|
+
this.getTraceId = config.getTraceId;
|
|
12483
|
+
this.getTraceparent = config.getTraceparent;
|
|
12484
|
+
this.order = config.order ?? -80;
|
|
12485
|
+
}
|
|
12486
|
+
intercept(context) {
|
|
12487
|
+
const { options } = context;
|
|
12488
|
+
if (!options.headers[this.traceIdHeader]) {
|
|
12489
|
+
const traceId = this.getTraceId?.() ?? context.traceId ?? generateTraceId();
|
|
12490
|
+
options.headers[this.traceIdHeader] = traceId;
|
|
12491
|
+
}
|
|
12492
|
+
if (this.addTraceparent && this.getTraceparent) {
|
|
12493
|
+
const traceparent = this.getTraceparent();
|
|
12494
|
+
if (traceparent && !options.headers["traceparent"]) {
|
|
12495
|
+
options.headers["traceparent"] = traceparent;
|
|
12496
|
+
}
|
|
12497
|
+
}
|
|
12498
|
+
return options;
|
|
12499
|
+
}
|
|
12500
|
+
};
|
|
12501
|
+
function createTraceInterceptor(config) {
|
|
12502
|
+
return new TraceInterceptor(config);
|
|
12503
|
+
}
|
|
12504
|
+
|
|
12505
|
+
// src/interceptors/request/timeout.interceptor.ts
|
|
12506
|
+
var TimeoutInterceptor = class {
|
|
12507
|
+
constructor(config = {}) {
|
|
12508
|
+
this.name = "timeout";
|
|
12509
|
+
this.defaultTimeout = config.defaultTimeout ?? 3e4;
|
|
12510
|
+
this.methodTimeouts = config.methodTimeouts ?? {};
|
|
12511
|
+
this.pathTimeouts = config.pathTimeouts ?? [];
|
|
12512
|
+
this.order = config.order ?? -70;
|
|
12513
|
+
}
|
|
12514
|
+
intercept(context) {
|
|
12515
|
+
const { options } = context;
|
|
12516
|
+
if (options.timeout !== void 0) {
|
|
12517
|
+
return options;
|
|
12518
|
+
}
|
|
12519
|
+
for (const { pattern, timeout } of this.pathTimeouts) {
|
|
12520
|
+
if (pattern.test(options.path)) {
|
|
12521
|
+
options.timeout = timeout;
|
|
12522
|
+
return options;
|
|
12523
|
+
}
|
|
12524
|
+
}
|
|
12525
|
+
const methodTimeout = this.methodTimeouts[options.method];
|
|
12526
|
+
if (methodTimeout !== void 0) {
|
|
12527
|
+
options.timeout = methodTimeout;
|
|
12528
|
+
return options;
|
|
12529
|
+
}
|
|
12530
|
+
options.timeout = this.defaultTimeout;
|
|
12531
|
+
return options;
|
|
12532
|
+
}
|
|
12533
|
+
};
|
|
12534
|
+
function createTimeoutInterceptor(config) {
|
|
12535
|
+
return new TimeoutInterceptor(config);
|
|
12536
|
+
}
|
|
12537
|
+
|
|
12538
|
+
// src/utils/retry-delay.ts
|
|
12539
|
+
function calculateRetryDelay(config, attempt) {
|
|
12540
|
+
const baseDelay = calculateBaseDelay(config.backoffStrategy, config.initialDelayMs, attempt);
|
|
12541
|
+
const jitterFactor = config.jitterFactor ?? 0.1;
|
|
12542
|
+
const delayWithJitter = addJitter(baseDelay, jitterFactor);
|
|
12543
|
+
return Math.min(delayWithJitter, config.maxDelayMs);
|
|
12544
|
+
}
|
|
12545
|
+
function calculateBaseDelay(strategy, initialDelayMs, attempt) {
|
|
12546
|
+
switch (strategy) {
|
|
12547
|
+
case "fixed":
|
|
12548
|
+
return initialDelayMs;
|
|
12549
|
+
case "linear":
|
|
12550
|
+
return initialDelayMs * (attempt + 1);
|
|
12551
|
+
case "exponential":
|
|
12552
|
+
return initialDelayMs * Math.pow(2, attempt);
|
|
12553
|
+
default: {
|
|
12554
|
+
const _exhaustiveCheck = strategy;
|
|
12555
|
+
throw new Error(`Unknown backoff strategy: ${_exhaustiveCheck}`);
|
|
12556
|
+
}
|
|
12557
|
+
}
|
|
12558
|
+
}
|
|
12559
|
+
function addJitter(delay, factor) {
|
|
12560
|
+
const validFactor = Math.max(0, Math.min(1, factor));
|
|
12561
|
+
const jitterRange = delay * validFactor;
|
|
12562
|
+
const jitter = (Math.random() - 0.5) * 2 * jitterRange;
|
|
12563
|
+
return Math.max(0, delay + jitter);
|
|
12564
|
+
}
|
|
12565
|
+
function parseRetryAfter(retryAfter) {
|
|
12566
|
+
if (!retryAfter) {
|
|
12567
|
+
return void 0;
|
|
12568
|
+
}
|
|
12569
|
+
const seconds = parseInt(retryAfter, 10);
|
|
12570
|
+
if (!isNaN(seconds) && seconds >= 0) {
|
|
12571
|
+
return seconds;
|
|
12572
|
+
}
|
|
12573
|
+
const date = new Date(retryAfter);
|
|
12574
|
+
if (!isNaN(date.getTime())) {
|
|
12575
|
+
const delayMs = date.getTime() - Date.now();
|
|
12576
|
+
return Math.max(0, Math.ceil(delayMs / 1e3));
|
|
12577
|
+
}
|
|
12578
|
+
return void 0;
|
|
12579
|
+
}
|
|
12580
|
+
function getRecommendedRetryDelay(config, attempt, retryAfterSeconds) {
|
|
12581
|
+
if (config.respectRetryAfter !== false && retryAfterSeconds !== void 0) {
|
|
12582
|
+
const retryAfterMs = retryAfterSeconds * 1e3;
|
|
12583
|
+
return Math.min(retryAfterMs, config.maxDelayMs);
|
|
12584
|
+
}
|
|
12585
|
+
return calculateRetryDelay(config, attempt);
|
|
12586
|
+
}
|
|
12587
|
+
|
|
12588
|
+
// src/utils/sleep.ts
|
|
12589
|
+
function sleep(ms) {
|
|
12590
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12591
|
+
}
|
|
12592
|
+
function sleepWithAbort(ms, signal) {
|
|
12593
|
+
return new Promise((resolve, reject) => {
|
|
12594
|
+
if (signal?.aborted) {
|
|
12595
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
12596
|
+
return;
|
|
12597
|
+
}
|
|
12598
|
+
const timeoutId = setTimeout(() => {
|
|
12599
|
+
resolve();
|
|
12600
|
+
}, ms);
|
|
12601
|
+
if (signal) {
|
|
12602
|
+
const abortHandler = () => {
|
|
12603
|
+
clearTimeout(timeoutId);
|
|
12604
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
12605
|
+
};
|
|
12606
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
12607
|
+
}
|
|
12608
|
+
});
|
|
12609
|
+
}
|
|
12610
|
+
|
|
12611
|
+
// src/interceptors/response/retry.interceptor.ts
|
|
12612
|
+
var RetryInterceptor = class {
|
|
12613
|
+
constructor(config) {
|
|
12614
|
+
this.name = "retry";
|
|
12615
|
+
this.config = {
|
|
12616
|
+
...DEFAULT_RETRY_CONFIG,
|
|
12617
|
+
maxRetries: config.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,
|
|
12618
|
+
initialDelayMs: config.initialDelayMs ?? DEFAULT_RETRY_CONFIG.initialDelayMs,
|
|
12619
|
+
maxDelayMs: config.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs,
|
|
12620
|
+
backoffStrategy: config.backoffStrategy ?? DEFAULT_RETRY_CONFIG.backoffStrategy,
|
|
12621
|
+
retryableStatusCodes: config.retryableStatusCodes ?? DEFAULT_RETRY_CONFIG.retryableStatusCodes,
|
|
12622
|
+
retryOnNetworkError: config.retryOnNetworkError ?? DEFAULT_RETRY_CONFIG.retryOnNetworkError,
|
|
12623
|
+
retryOnTimeout: config.retryOnTimeout ?? DEFAULT_RETRY_CONFIG.retryOnTimeout,
|
|
12624
|
+
respectRetryAfter: config.respectRetryAfter ?? DEFAULT_RETRY_CONFIG.respectRetryAfter,
|
|
12625
|
+
jitterFactor: config.jitterFactor ?? DEFAULT_RETRY_CONFIG.jitterFactor,
|
|
12626
|
+
onRetry: config.onRetry,
|
|
12627
|
+
shouldRetry: config.shouldRetry
|
|
12628
|
+
};
|
|
12629
|
+
this.executeRequest = config.executeRequest;
|
|
12630
|
+
this.order = config.order ?? 100;
|
|
12631
|
+
}
|
|
12632
|
+
async intercept(error, context) {
|
|
12633
|
+
if (!this.shouldRetry(error, context)) {
|
|
12634
|
+
return void 0;
|
|
12635
|
+
}
|
|
12636
|
+
const retryAfterSeconds = error instanceof ApiError ? error.getRetryAfter() : void 0;
|
|
12637
|
+
const delayMs = getRecommendedRetryDelay(this.config, context.retryCount, retryAfterSeconds);
|
|
12638
|
+
if (this.config.onRetry) {
|
|
12639
|
+
const retryInfo = {
|
|
12640
|
+
attempt: context.retryCount + 1,
|
|
12641
|
+
maxRetries: this.config.maxRetries,
|
|
12642
|
+
delayMs,
|
|
12643
|
+
error,
|
|
12644
|
+
url: context.url,
|
|
12645
|
+
method: context.options.method
|
|
12646
|
+
};
|
|
12647
|
+
this.config.onRetry(retryInfo);
|
|
12648
|
+
}
|
|
12649
|
+
await sleep(delayMs);
|
|
12650
|
+
context.retryCount++;
|
|
12651
|
+
return this.executeRequest(context);
|
|
12652
|
+
}
|
|
12653
|
+
/**
|
|
12654
|
+
* 判断是否应该重试
|
|
12655
|
+
*/
|
|
12656
|
+
shouldRetry(error, context) {
|
|
12657
|
+
if (context.retryCount >= this.config.maxRetries) {
|
|
12658
|
+
return false;
|
|
12659
|
+
}
|
|
12660
|
+
if (error instanceof AbortError) {
|
|
12661
|
+
return false;
|
|
12662
|
+
}
|
|
12663
|
+
if (this.config.shouldRetry) {
|
|
12664
|
+
const statusCode = error instanceof ApiError ? error.statusCode : void 0;
|
|
12665
|
+
const retryAfter = error instanceof ApiError ? error.getRetryAfter() : void 0;
|
|
12666
|
+
return this.config.shouldRetry(error, context.retryCount, {
|
|
12667
|
+
url: context.url,
|
|
12668
|
+
method: context.options.method,
|
|
12669
|
+
statusCode,
|
|
12670
|
+
retryAfter
|
|
12671
|
+
});
|
|
12672
|
+
}
|
|
12673
|
+
if (error instanceof NetworkError) {
|
|
12674
|
+
return this.config.retryOnNetworkError !== false;
|
|
12675
|
+
}
|
|
12676
|
+
if (error instanceof TimeoutError) {
|
|
12677
|
+
return this.config.retryOnTimeout !== false;
|
|
12678
|
+
}
|
|
12679
|
+
if (error instanceof ApiError) {
|
|
12680
|
+
const retryableCodes = this.config.retryableStatusCodes ?? [];
|
|
12681
|
+
return retryableCodes.includes(error.statusCode);
|
|
12682
|
+
}
|
|
12683
|
+
return false;
|
|
12684
|
+
}
|
|
12685
|
+
};
|
|
12686
|
+
function createRetryInterceptor(config) {
|
|
12687
|
+
return new RetryInterceptor(config);
|
|
12688
|
+
}
|
|
12689
|
+
|
|
12690
|
+
// src/interceptors/response/token-refresh.interceptor.ts
|
|
12691
|
+
var TokenRefreshInterceptor = class {
|
|
12692
|
+
constructor(config) {
|
|
12693
|
+
this.name = "token-refresh";
|
|
12694
|
+
this.state = {
|
|
12695
|
+
isRefreshing: false,
|
|
12696
|
+
refreshPromise: null,
|
|
12697
|
+
failCount: 0
|
|
12698
|
+
};
|
|
12699
|
+
this.refreshToken = config.refreshToken;
|
|
12700
|
+
this.executeRequest = config.executeRequest;
|
|
12701
|
+
this.triggerStatusCodes = config.triggerStatusCodes ?? [401];
|
|
12702
|
+
this.maxRetries = config.maxRetries ?? 1;
|
|
12703
|
+
this.onTokenRefreshed = config.onTokenRefreshed;
|
|
12704
|
+
this.onRefreshFailed = config.onRefreshFailed;
|
|
12705
|
+
this.order = config.order ?? 50;
|
|
12706
|
+
}
|
|
12707
|
+
async intercept(error, context) {
|
|
12708
|
+
if (!(error instanceof ApiError)) {
|
|
12709
|
+
return void 0;
|
|
12710
|
+
}
|
|
12711
|
+
if (!this.shouldRefresh(error, context)) {
|
|
12712
|
+
return void 0;
|
|
12713
|
+
}
|
|
12714
|
+
try {
|
|
12715
|
+
const newToken = await this.doRefresh();
|
|
12716
|
+
this.onTokenRefreshed?.(newToken);
|
|
12717
|
+
this.state.failCount = 0;
|
|
12718
|
+
return this.executeRequest(context);
|
|
12719
|
+
} catch (refreshError) {
|
|
12720
|
+
this.state.failCount++;
|
|
12721
|
+
this.onRefreshFailed?.(refreshError);
|
|
12722
|
+
return void 0;
|
|
12723
|
+
}
|
|
12724
|
+
}
|
|
12725
|
+
/**
|
|
12726
|
+
* 判断是否应该刷新 Token
|
|
12727
|
+
*/
|
|
12728
|
+
shouldRefresh(error, context) {
|
|
12729
|
+
if (!this.triggerStatusCodes.includes(error.statusCode)) {
|
|
12730
|
+
return false;
|
|
12731
|
+
}
|
|
12732
|
+
if (this.state.failCount >= this.maxRetries) {
|
|
12733
|
+
return false;
|
|
12734
|
+
}
|
|
12735
|
+
if (context.metadata["tokenRefreshAttempted"]) {
|
|
12736
|
+
return false;
|
|
12737
|
+
}
|
|
12738
|
+
return true;
|
|
12739
|
+
}
|
|
12740
|
+
/**
|
|
12741
|
+
* 执行 Token 刷新
|
|
12742
|
+
*
|
|
12743
|
+
* 合并并发请求,确保同一时间只有一个刷新请求
|
|
12744
|
+
*/
|
|
12745
|
+
async doRefresh() {
|
|
12746
|
+
if (this.state.isRefreshing && this.state.refreshPromise) {
|
|
12747
|
+
return this.state.refreshPromise;
|
|
12748
|
+
}
|
|
12749
|
+
this.state.isRefreshing = true;
|
|
12750
|
+
this.state.refreshPromise = this.refreshToken();
|
|
12751
|
+
try {
|
|
12752
|
+
const newToken = await this.state.refreshPromise;
|
|
12753
|
+
return newToken;
|
|
12754
|
+
} finally {
|
|
12755
|
+
this.state.isRefreshing = false;
|
|
12756
|
+
this.state.refreshPromise = null;
|
|
12757
|
+
}
|
|
12758
|
+
}
|
|
12759
|
+
/**
|
|
12760
|
+
* 重置状态(用于测试或手动重置)
|
|
12761
|
+
*/
|
|
12762
|
+
reset() {
|
|
12763
|
+
this.state.isRefreshing = false;
|
|
12764
|
+
this.state.refreshPromise = null;
|
|
12765
|
+
this.state.failCount = 0;
|
|
12766
|
+
}
|
|
12767
|
+
};
|
|
12768
|
+
function createTokenRefreshInterceptor(config) {
|
|
12769
|
+
return new TokenRefreshInterceptor(config);
|
|
12770
|
+
}
|
|
12771
|
+
|
|
12772
|
+
// src/interceptors/response/error-transform.interceptor.ts
|
|
12773
|
+
var ErrorTransformInterceptor = class {
|
|
12774
|
+
constructor(config = {}) {
|
|
12775
|
+
this.name = "error-transform";
|
|
12776
|
+
this.includeRawResponse = config.includeRawResponse ?? false;
|
|
12777
|
+
this.extractErrorMessage = config.extractErrorMessage;
|
|
12778
|
+
this.extractErrorCode = config.extractErrorCode;
|
|
12779
|
+
this.order = config.order ?? 0;
|
|
12780
|
+
}
|
|
12781
|
+
intercept(response, _context) {
|
|
12782
|
+
return response;
|
|
12783
|
+
}
|
|
12784
|
+
};
|
|
12785
|
+
function createErrorTransformInterceptor(config) {
|
|
12786
|
+
return new ErrorTransformInterceptor(config);
|
|
12787
|
+
}
|
|
12788
|
+
|
|
12789
|
+
// src/interceptors/error/logging.interceptor.ts
|
|
12790
|
+
var LoggingInterceptor = class {
|
|
12791
|
+
constructor(config) {
|
|
12792
|
+
this.name = "logging";
|
|
12793
|
+
this.logger = config.logger;
|
|
12794
|
+
this.verbose = config.verbose ?? false;
|
|
12795
|
+
this.logAborted = config.logAborted ?? false;
|
|
12796
|
+
this.filter = config.filter;
|
|
12797
|
+
this.order = config.order ?? -100;
|
|
12798
|
+
}
|
|
12799
|
+
intercept(error, context) {
|
|
12800
|
+
if (this.filter && !this.filter(error, context)) {
|
|
12801
|
+
return void 0;
|
|
12802
|
+
}
|
|
12803
|
+
if (error instanceof AbortError && !this.logAborted) {
|
|
12804
|
+
return void 0;
|
|
12805
|
+
}
|
|
12806
|
+
const logInfo = this.buildLogInfo(error, context);
|
|
12807
|
+
if (error instanceof ApiError && error.isClientError() && !error.isRateLimited()) {
|
|
12808
|
+
this.logger.warn(`[API Error] ${logInfo.summary}`, logInfo);
|
|
12809
|
+
} else if (error instanceof AbortError) {
|
|
12810
|
+
this.logger.debug(`[Aborted] ${logInfo.summary}`, logInfo);
|
|
12811
|
+
} else {
|
|
12812
|
+
this.logger.error(`[Error] ${logInfo.summary}`, logInfo);
|
|
12813
|
+
}
|
|
12814
|
+
return void 0;
|
|
12815
|
+
}
|
|
12816
|
+
/**
|
|
12817
|
+
* 构建日志信息
|
|
12818
|
+
*/
|
|
12819
|
+
buildLogInfo(error, context) {
|
|
12820
|
+
const duration = Date.now() - context.startTime;
|
|
12821
|
+
const base = {
|
|
12822
|
+
summary: `${context.options.method} ${context.options.path} - ${error.message}`,
|
|
12823
|
+
requestId: context.requestId,
|
|
12824
|
+
traceId: context.traceId,
|
|
12825
|
+
method: context.options.method,
|
|
12826
|
+
path: context.options.path,
|
|
12827
|
+
url: context.url,
|
|
12828
|
+
duration: `${duration}ms`,
|
|
12829
|
+
retryCount: context.retryCount
|
|
12830
|
+
};
|
|
12831
|
+
if (this.verbose) {
|
|
12832
|
+
const detailed = {
|
|
12833
|
+
...base,
|
|
12834
|
+
error: {
|
|
12835
|
+
name: error.name,
|
|
12836
|
+
message: error.message,
|
|
12837
|
+
stack: error.stack
|
|
12838
|
+
}
|
|
12839
|
+
};
|
|
12840
|
+
if (error instanceof ApiError) {
|
|
12841
|
+
detailed.apiError = {
|
|
12842
|
+
code: error.code,
|
|
12843
|
+
statusCode: error.statusCode,
|
|
12844
|
+
details: error.details,
|
|
12845
|
+
retryable: error.retryable
|
|
12846
|
+
};
|
|
12847
|
+
} else if (error instanceof TimeoutError) {
|
|
12848
|
+
detailed.timeout = error.timeoutMs;
|
|
12849
|
+
} else if (error instanceof NetworkError) {
|
|
12850
|
+
detailed.networkError = error.cause?.message;
|
|
12851
|
+
}
|
|
12852
|
+
return detailed;
|
|
12853
|
+
}
|
|
12854
|
+
if (error instanceof ApiError) {
|
|
12855
|
+
return {
|
|
12856
|
+
...base,
|
|
12857
|
+
code: error.code,
|
|
12858
|
+
statusCode: error.statusCode
|
|
12859
|
+
};
|
|
12860
|
+
}
|
|
12861
|
+
if (error instanceof TimeoutError) {
|
|
12862
|
+
return {
|
|
12863
|
+
...base,
|
|
12864
|
+
timeout: error.timeoutMs
|
|
12865
|
+
};
|
|
12866
|
+
}
|
|
12867
|
+
return base;
|
|
12868
|
+
}
|
|
12869
|
+
};
|
|
12870
|
+
function createLoggingInterceptor(config) {
|
|
12871
|
+
return new LoggingInterceptor(config);
|
|
12872
|
+
}
|
|
12873
|
+
|
|
12874
|
+
// src/interceptors/error/reporting.interceptor.ts
|
|
12875
|
+
var ReportingInterceptor = class {
|
|
12876
|
+
constructor(config) {
|
|
12877
|
+
this.name = "reporting";
|
|
12878
|
+
this.reporter = config.reporter;
|
|
12879
|
+
this.getSeverity = config.getSeverity ?? this.defaultGetSeverity;
|
|
12880
|
+
this.reportClientErrors = config.reportClientErrors ?? false;
|
|
12881
|
+
this.reportAborted = config.reportAborted ?? false;
|
|
12882
|
+
this.reportNetworkErrors = config.reportNetworkErrors ?? true;
|
|
12883
|
+
this.sampleRate = config.sampleRate ?? 1;
|
|
12884
|
+
this.extractContext = config.extractContext;
|
|
12885
|
+
this.order = config.order ?? -50;
|
|
12886
|
+
}
|
|
12887
|
+
intercept(error, context) {
|
|
12888
|
+
if (!this.shouldReport(error)) {
|
|
12889
|
+
return void 0;
|
|
12890
|
+
}
|
|
12891
|
+
if (this.sampleRate < 1 && Math.random() > this.sampleRate) {
|
|
12892
|
+
return void 0;
|
|
12893
|
+
}
|
|
12894
|
+
const reportContext = this.buildReportContext(error, context);
|
|
12895
|
+
try {
|
|
12896
|
+
this.reporter.report(error, reportContext);
|
|
12897
|
+
} catch {
|
|
12898
|
+
}
|
|
12899
|
+
return void 0;
|
|
12900
|
+
}
|
|
12901
|
+
/**
|
|
12902
|
+
* 判断是否应该上报
|
|
12903
|
+
*/
|
|
12904
|
+
shouldReport(error) {
|
|
12905
|
+
if (error instanceof AbortError) {
|
|
12906
|
+
return this.reportAborted;
|
|
12907
|
+
}
|
|
12908
|
+
if (error instanceof NetworkError) {
|
|
12909
|
+
return this.reportNetworkErrors;
|
|
12910
|
+
}
|
|
12911
|
+
if (error instanceof ApiError) {
|
|
12912
|
+
if (error.isClientError()) {
|
|
12913
|
+
return this.reportClientErrors;
|
|
12914
|
+
}
|
|
12915
|
+
return true;
|
|
12916
|
+
}
|
|
12917
|
+
if (error instanceof TimeoutError) {
|
|
12918
|
+
return true;
|
|
12919
|
+
}
|
|
12920
|
+
return true;
|
|
12921
|
+
}
|
|
12922
|
+
/**
|
|
12923
|
+
* 构建上报上下文
|
|
12924
|
+
*/
|
|
12925
|
+
buildReportContext(error, context) {
|
|
12926
|
+
const baseContext = {
|
|
12927
|
+
severity: this.getSeverity(error),
|
|
12928
|
+
requestId: context.requestId,
|
|
12929
|
+
traceId: context.traceId,
|
|
12930
|
+
url: context.url,
|
|
12931
|
+
method: context.options.method,
|
|
12932
|
+
path: context.options.path,
|
|
12933
|
+
retryCount: context.retryCount,
|
|
12934
|
+
duration: Date.now() - context.startTime
|
|
12935
|
+
};
|
|
12936
|
+
if (error instanceof ApiError) {
|
|
12937
|
+
baseContext["api"] = {
|
|
12938
|
+
code: error.code,
|
|
12939
|
+
statusCode: error.statusCode,
|
|
12940
|
+
retryable: error.retryable
|
|
12941
|
+
};
|
|
12942
|
+
} else if (error instanceof TimeoutError) {
|
|
12943
|
+
baseContext["timeout"] = error.timeoutMs;
|
|
12944
|
+
}
|
|
12945
|
+
if (this.extractContext) {
|
|
12946
|
+
const customContext = this.extractContext(error, context);
|
|
12947
|
+
return { ...baseContext, ...customContext };
|
|
12948
|
+
}
|
|
12949
|
+
return baseContext;
|
|
12950
|
+
}
|
|
12951
|
+
/**
|
|
12952
|
+
* 默认的严重程度判断
|
|
12953
|
+
*/
|
|
12954
|
+
defaultGetSeverity(error) {
|
|
12955
|
+
if (error instanceof AbortError) {
|
|
12956
|
+
return "debug";
|
|
12957
|
+
}
|
|
12958
|
+
if (error instanceof ApiError) {
|
|
12959
|
+
if (error.isServerError()) {
|
|
12960
|
+
return "error";
|
|
12961
|
+
}
|
|
12962
|
+
if (error.isRateLimited()) {
|
|
12963
|
+
return "warning";
|
|
12964
|
+
}
|
|
12965
|
+
return "info";
|
|
12966
|
+
}
|
|
12967
|
+
if (error instanceof NetworkError || error instanceof TimeoutError) {
|
|
12968
|
+
return "warning";
|
|
12969
|
+
}
|
|
12970
|
+
return "error";
|
|
12971
|
+
}
|
|
12972
|
+
};
|
|
12973
|
+
function createReportingInterceptor(config) {
|
|
12974
|
+
return new ReportingInterceptor(config);
|
|
12975
|
+
}
|
|
12976
|
+
|
|
12977
|
+
// src/plugins/deduplication.ts
|
|
12978
|
+
var RequestDeduper = class {
|
|
12979
|
+
constructor(config = {}) {
|
|
12980
|
+
this.inflightRequests = /* @__PURE__ */ new Map();
|
|
12981
|
+
this.getOnly = config.getOnly ?? true;
|
|
12982
|
+
this.generateKey = config.generateKey ?? this.defaultGenerateKey;
|
|
12983
|
+
this.maxSize = config.maxSize ?? 100;
|
|
12984
|
+
}
|
|
12985
|
+
/**
|
|
12986
|
+
* 执行请求(带去重)
|
|
12987
|
+
*
|
|
12988
|
+
* @param method - HTTP 方法
|
|
12989
|
+
* @param url - 请求 URL
|
|
12990
|
+
* @param execute - 实际执行请求的函数
|
|
12991
|
+
* @returns 请求结果
|
|
12992
|
+
*/
|
|
12993
|
+
async execute(method, url2, execute) {
|
|
12994
|
+
if (!this.shouldDeduplicate(method)) {
|
|
12995
|
+
return execute();
|
|
12996
|
+
}
|
|
12997
|
+
const key = this.generateKey(method, url2);
|
|
12998
|
+
const inflight = this.inflightRequests.get(key);
|
|
12999
|
+
if (inflight) {
|
|
13000
|
+
return inflight.promise;
|
|
13001
|
+
}
|
|
13002
|
+
if (this.inflightRequests.size >= this.maxSize) {
|
|
13003
|
+
this.cleanup();
|
|
13004
|
+
}
|
|
13005
|
+
const promise = execute().finally(() => {
|
|
13006
|
+
this.inflightRequests.delete(key);
|
|
13007
|
+
});
|
|
13008
|
+
this.inflightRequests.set(key, {
|
|
13009
|
+
promise,
|
|
13010
|
+
createdAt: Date.now()
|
|
13011
|
+
});
|
|
13012
|
+
return promise;
|
|
13013
|
+
}
|
|
13014
|
+
/**
|
|
13015
|
+
* 包装 fetch 函数
|
|
13016
|
+
*
|
|
13017
|
+
* @param fetch - 原始 fetch 函数
|
|
13018
|
+
* @returns 去重的 fetch 函数
|
|
13019
|
+
*/
|
|
13020
|
+
wrap(fetch2) {
|
|
13021
|
+
return (url2, init) => {
|
|
13022
|
+
const method = init?.method ?? "GET";
|
|
13023
|
+
return this.execute(method, url2, () => fetch2(url2, init));
|
|
13024
|
+
};
|
|
13025
|
+
}
|
|
13026
|
+
/**
|
|
13027
|
+
* 获取当前飞行中的请求数量
|
|
13028
|
+
*/
|
|
13029
|
+
get inflightCount() {
|
|
13030
|
+
return this.inflightRequests.size;
|
|
13031
|
+
}
|
|
13032
|
+
/**
|
|
13033
|
+
* 清空所有飞行中的请求
|
|
13034
|
+
*/
|
|
13035
|
+
clear() {
|
|
13036
|
+
this.inflightRequests.clear();
|
|
13037
|
+
}
|
|
13038
|
+
/**
|
|
13039
|
+
* 判断是否应该去重
|
|
13040
|
+
*/
|
|
13041
|
+
shouldDeduplicate(method) {
|
|
13042
|
+
if (this.getOnly) {
|
|
13043
|
+
return method.toUpperCase() === "GET";
|
|
13044
|
+
}
|
|
13045
|
+
return true;
|
|
13046
|
+
}
|
|
13047
|
+
/**
|
|
13048
|
+
* 默认的键生成函数
|
|
13049
|
+
*/
|
|
13050
|
+
defaultGenerateKey(method, url2) {
|
|
13051
|
+
return `${method.toUpperCase()}:${url2}`;
|
|
13052
|
+
}
|
|
13053
|
+
/**
|
|
13054
|
+
* 清理过期的请求
|
|
13055
|
+
*/
|
|
13056
|
+
cleanup() {
|
|
13057
|
+
const toDelete = Math.floor(this.maxSize * 0.2);
|
|
13058
|
+
const entries = Array.from(this.inflightRequests.entries()).sort((a, b) => a[1].createdAt - b[1].createdAt);
|
|
13059
|
+
for (let i = 0; i < toDelete && i < entries.length; i++) {
|
|
13060
|
+
this.inflightRequests.delete(entries[i][0]);
|
|
13061
|
+
}
|
|
13062
|
+
}
|
|
13063
|
+
};
|
|
13064
|
+
function createRequestDeduper(config) {
|
|
13065
|
+
return new RequestDeduper(config);
|
|
13066
|
+
}
|
|
13067
|
+
|
|
13068
|
+
// src/utils/url.ts
|
|
13069
|
+
function buildUrl(baseUrl, path, query) {
|
|
13070
|
+
const normalizedBase = baseUrl.replace(/\/+$/, "");
|
|
13071
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
13072
|
+
const url2 = new URL(`${normalizedBase}${normalizedPath}`);
|
|
13073
|
+
if (query) {
|
|
13074
|
+
for (const [key, value] of Object.entries(query)) {
|
|
13075
|
+
if (value !== void 0 && value !== null) {
|
|
13076
|
+
url2.searchParams.set(key, String(value));
|
|
13077
|
+
}
|
|
13078
|
+
}
|
|
13079
|
+
}
|
|
13080
|
+
return url2.toString();
|
|
13081
|
+
}
|
|
13082
|
+
function extractPath(url2) {
|
|
13083
|
+
try {
|
|
13084
|
+
const parsed = new URL(url2);
|
|
13085
|
+
return parsed.pathname;
|
|
13086
|
+
} catch {
|
|
13087
|
+
const queryIndex = url2.indexOf("?");
|
|
13088
|
+
const pathPart = queryIndex >= 0 ? url2.slice(0, queryIndex) : url2;
|
|
13089
|
+
return pathPart.startsWith("/") ? pathPart : `/${pathPart}`;
|
|
13090
|
+
}
|
|
13091
|
+
}
|
|
13092
|
+
function parseQueryString(queryString) {
|
|
13093
|
+
const result = {};
|
|
13094
|
+
const normalized = queryString.startsWith("?") ? queryString.slice(1) : queryString;
|
|
13095
|
+
if (!normalized) {
|
|
13096
|
+
return result;
|
|
13097
|
+
}
|
|
13098
|
+
const params = new URLSearchParams(normalized);
|
|
13099
|
+
for (const [key, value] of params.entries()) {
|
|
13100
|
+
result[key] = value;
|
|
13101
|
+
}
|
|
13102
|
+
return result;
|
|
13103
|
+
}
|
|
13104
|
+
function joinPaths(base, path) {
|
|
13105
|
+
const normalizedBase = base.replace(/\/+$/, "");
|
|
13106
|
+
const normalizedPath = path.replace(/^\/+/, "");
|
|
13107
|
+
if (!normalizedPath) {
|
|
13108
|
+
return normalizedBase || "/";
|
|
13109
|
+
}
|
|
13110
|
+
return `${normalizedBase}/${normalizedPath}`;
|
|
13111
|
+
}
|
|
13112
|
+
|
|
13113
|
+
// src/plugins/metrics.ts
|
|
13114
|
+
var DefaultMetricsCollector = class {
|
|
13115
|
+
constructor(config = {}) {
|
|
13116
|
+
this.metrics = [];
|
|
13117
|
+
this.maxMetrics = config.maxMetrics ?? 1e3;
|
|
13118
|
+
this.ttlMs = config.ttlMs ?? 36e5;
|
|
13119
|
+
this.onMetrics = config.onMetrics;
|
|
13120
|
+
}
|
|
13121
|
+
record(metrics) {
|
|
13122
|
+
this.onMetrics?.(metrics);
|
|
13123
|
+
this.metrics.push(metrics);
|
|
13124
|
+
this.cleanup();
|
|
13125
|
+
}
|
|
13126
|
+
summary() {
|
|
13127
|
+
if (this.metrics.length === 0) {
|
|
13128
|
+
return this.emptySummary();
|
|
13129
|
+
}
|
|
13130
|
+
const successful = this.metrics.filter((m) => m.success);
|
|
13131
|
+
const durations = this.metrics.map((m) => m.durationMs).sort((a, b) => a - b);
|
|
13132
|
+
const p = (percentile) => {
|
|
13133
|
+
const index = Math.ceil(durations.length * percentile / 100) - 1;
|
|
13134
|
+
return durations[Math.max(0, index)];
|
|
13135
|
+
};
|
|
13136
|
+
const statusCodeDistribution = {};
|
|
13137
|
+
for (const m of this.metrics) {
|
|
13138
|
+
if (m.status !== void 0) {
|
|
13139
|
+
statusCodeDistribution[m.status] = (statusCodeDistribution[m.status] ?? 0) + 1;
|
|
13140
|
+
}
|
|
13141
|
+
}
|
|
13142
|
+
const pathDistribution = {};
|
|
13143
|
+
const pathGroups = {};
|
|
13144
|
+
for (const m of this.metrics) {
|
|
13145
|
+
if (!pathGroups[m.path]) {
|
|
13146
|
+
pathGroups[m.path] = [];
|
|
13147
|
+
}
|
|
13148
|
+
pathGroups[m.path].push(m);
|
|
13149
|
+
}
|
|
13150
|
+
for (const [path, group] of Object.entries(pathGroups)) {
|
|
13151
|
+
const successCount = group.filter((m) => m.success).length;
|
|
13152
|
+
const avgDuration2 = group.reduce((sum, m) => sum + m.durationMs, 0) / group.length;
|
|
13153
|
+
pathDistribution[path] = {
|
|
13154
|
+
count: group.length,
|
|
13155
|
+
successCount,
|
|
13156
|
+
avgDurationMs: Math.round(avgDuration2)
|
|
13157
|
+
};
|
|
13158
|
+
}
|
|
13159
|
+
const totalRetries = this.metrics.reduce((sum, m) => sum + m.retryCount, 0);
|
|
13160
|
+
const avgDuration = this.metrics.reduce((sum, m) => sum + m.durationMs, 0) / this.metrics.length;
|
|
13161
|
+
return {
|
|
13162
|
+
totalRequests: this.metrics.length,
|
|
13163
|
+
successfulRequests: successful.length,
|
|
13164
|
+
failedRequests: this.metrics.length - successful.length,
|
|
13165
|
+
successRate: successful.length / this.metrics.length,
|
|
13166
|
+
avgDurationMs: Math.round(avgDuration),
|
|
13167
|
+
p50Ms: p(50),
|
|
13168
|
+
p90Ms: p(90),
|
|
13169
|
+
p95Ms: p(95),
|
|
13170
|
+
p99Ms: p(99),
|
|
13171
|
+
minDurationMs: durations[0],
|
|
13172
|
+
maxDurationMs: durations[durations.length - 1],
|
|
13173
|
+
totalRetries,
|
|
13174
|
+
statusCodeDistribution,
|
|
13175
|
+
pathDistribution
|
|
13176
|
+
};
|
|
13177
|
+
}
|
|
13178
|
+
getAll() {
|
|
13179
|
+
return [...this.metrics];
|
|
13180
|
+
}
|
|
13181
|
+
getByPath(path) {
|
|
13182
|
+
return this.metrics.filter((m) => m.path === path);
|
|
13183
|
+
}
|
|
13184
|
+
flush() {
|
|
13185
|
+
const result = [...this.metrics];
|
|
13186
|
+
this.metrics = [];
|
|
13187
|
+
return result;
|
|
13188
|
+
}
|
|
13189
|
+
clear() {
|
|
13190
|
+
this.metrics = [];
|
|
13191
|
+
}
|
|
13192
|
+
cleanup() {
|
|
13193
|
+
const now = Date.now();
|
|
13194
|
+
this.metrics = this.metrics.filter((m) => now - m.startTime < this.ttlMs);
|
|
13195
|
+
while (this.metrics.length > this.maxMetrics) {
|
|
13196
|
+
this.metrics.shift();
|
|
13197
|
+
}
|
|
13198
|
+
}
|
|
13199
|
+
emptySummary() {
|
|
13200
|
+
return {
|
|
13201
|
+
totalRequests: 0,
|
|
13202
|
+
successfulRequests: 0,
|
|
13203
|
+
failedRequests: 0,
|
|
13204
|
+
successRate: 0,
|
|
13205
|
+
avgDurationMs: 0,
|
|
13206
|
+
p50Ms: 0,
|
|
13207
|
+
p90Ms: 0,
|
|
13208
|
+
p95Ms: 0,
|
|
13209
|
+
p99Ms: 0,
|
|
13210
|
+
minDurationMs: 0,
|
|
13211
|
+
maxDurationMs: 0,
|
|
13212
|
+
totalRetries: 0,
|
|
13213
|
+
statusCodeDistribution: {},
|
|
13214
|
+
pathDistribution: {}
|
|
13215
|
+
};
|
|
13216
|
+
}
|
|
13217
|
+
};
|
|
13218
|
+
function createMetricsCollector(config) {
|
|
13219
|
+
return new DefaultMetricsCollector(config);
|
|
13220
|
+
}
|
|
13221
|
+
var MetricsRequestInterceptor = class {
|
|
13222
|
+
constructor() {
|
|
13223
|
+
this.name = "metrics-request";
|
|
13224
|
+
this.order = -1e3;
|
|
13225
|
+
}
|
|
13226
|
+
// 最先执行
|
|
13227
|
+
intercept(context) {
|
|
13228
|
+
return context.options;
|
|
13229
|
+
}
|
|
13230
|
+
};
|
|
13231
|
+
var MetricsResponseInterceptor = class {
|
|
13232
|
+
// 最后执行
|
|
13233
|
+
constructor(collector) {
|
|
13234
|
+
this.collector = collector;
|
|
13235
|
+
this.name = "metrics-response";
|
|
13236
|
+
this.order = 1e3;
|
|
13237
|
+
}
|
|
13238
|
+
intercept(response, context) {
|
|
13239
|
+
const endTime = Date.now();
|
|
13240
|
+
const metrics = {
|
|
13241
|
+
requestId: context.requestId,
|
|
13242
|
+
url: context.url,
|
|
13243
|
+
path: extractPath(context.url),
|
|
13244
|
+
method: context.options.method,
|
|
13245
|
+
startTime: context.startTime,
|
|
13246
|
+
endTime,
|
|
13247
|
+
durationMs: endTime - context.startTime,
|
|
13248
|
+
status: response.status,
|
|
13249
|
+
success: true,
|
|
13250
|
+
retryCount: context.retryCount,
|
|
13251
|
+
traceId: context.traceId
|
|
13252
|
+
};
|
|
13253
|
+
this.collector.record(metrics);
|
|
13254
|
+
return response;
|
|
13255
|
+
}
|
|
13256
|
+
};
|
|
13257
|
+
var MetricsErrorInterceptor = class {
|
|
13258
|
+
// 最后执行
|
|
13259
|
+
constructor(collector) {
|
|
13260
|
+
this.collector = collector;
|
|
13261
|
+
this.name = "metrics-error";
|
|
13262
|
+
this.order = 1e3;
|
|
13263
|
+
}
|
|
13264
|
+
intercept(error, context) {
|
|
13265
|
+
const endTime = Date.now();
|
|
13266
|
+
const metrics = {
|
|
13267
|
+
requestId: context.requestId,
|
|
13268
|
+
url: context.url,
|
|
13269
|
+
path: extractPath(context.url),
|
|
13270
|
+
method: context.options.method,
|
|
13271
|
+
startTime: context.startTime,
|
|
13272
|
+
endTime,
|
|
13273
|
+
durationMs: endTime - context.startTime,
|
|
13274
|
+
success: false,
|
|
13275
|
+
retryCount: context.retryCount,
|
|
13276
|
+
traceId: context.traceId,
|
|
13277
|
+
error: error.message
|
|
13278
|
+
};
|
|
13279
|
+
this.collector.record(metrics);
|
|
13280
|
+
return void 0;
|
|
13281
|
+
}
|
|
13282
|
+
};
|
|
13283
|
+
function createMetricsInterceptors(collector) {
|
|
13284
|
+
return {
|
|
13285
|
+
request: new MetricsRequestInterceptor(),
|
|
13286
|
+
response: new MetricsResponseInterceptor(collector),
|
|
13287
|
+
error: new MetricsErrorInterceptor(collector)
|
|
13288
|
+
};
|
|
13289
|
+
}
|
|
13290
|
+
|
|
13291
|
+
// src/plugins/logger.ts
|
|
13292
|
+
var ConsoleLogger = class {
|
|
13293
|
+
constructor(config = {}) {
|
|
13294
|
+
this.prefix = config.prefix ?? "[DJV-API]";
|
|
13295
|
+
this.level = config.level ?? "info";
|
|
13296
|
+
this.timestamp = config.timestamp ?? true;
|
|
13297
|
+
this.levelPriority = LOG_LEVEL_PRIORITY[this.level];
|
|
13298
|
+
}
|
|
13299
|
+
debug(message, ...args) {
|
|
13300
|
+
this.log("debug", message, args);
|
|
13301
|
+
}
|
|
13302
|
+
info(message, ...args) {
|
|
13303
|
+
this.log("info", message, args);
|
|
13304
|
+
}
|
|
13305
|
+
warn(message, ...args) {
|
|
13306
|
+
this.log("warn", message, args);
|
|
13307
|
+
}
|
|
13308
|
+
error(message, ...args) {
|
|
13309
|
+
this.log("error", message, args);
|
|
13310
|
+
}
|
|
13311
|
+
log(level, message, args) {
|
|
13312
|
+
if (LOG_LEVEL_PRIORITY[level] < this.levelPriority) {
|
|
13313
|
+
return;
|
|
13314
|
+
}
|
|
13315
|
+
const formattedMessage = this.formatMessage(level, message);
|
|
13316
|
+
switch (level) {
|
|
13317
|
+
case "debug":
|
|
13318
|
+
console.debug(formattedMessage, ...args);
|
|
13319
|
+
break;
|
|
13320
|
+
case "info":
|
|
13321
|
+
console.info(formattedMessage, ...args);
|
|
13322
|
+
break;
|
|
13323
|
+
case "warn":
|
|
13324
|
+
console.warn(formattedMessage, ...args);
|
|
13325
|
+
break;
|
|
13326
|
+
case "error":
|
|
13327
|
+
console.error(formattedMessage, ...args);
|
|
13328
|
+
break;
|
|
13329
|
+
}
|
|
13330
|
+
}
|
|
13331
|
+
formatMessage(level, message) {
|
|
13332
|
+
const parts = [];
|
|
13333
|
+
if (this.timestamp) {
|
|
13334
|
+
parts.push(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
|
|
13335
|
+
}
|
|
13336
|
+
parts.push(this.prefix);
|
|
13337
|
+
parts.push(`[${level.toUpperCase()}]`);
|
|
13338
|
+
parts.push(message);
|
|
13339
|
+
return parts.join(" ");
|
|
13340
|
+
}
|
|
13341
|
+
};
|
|
13342
|
+
function createConsoleLogger(config) {
|
|
13343
|
+
return new ConsoleLogger(config);
|
|
13344
|
+
}
|
|
13345
|
+
var SilentLogger = class {
|
|
13346
|
+
debug(_message, ..._args) {
|
|
13347
|
+
}
|
|
13348
|
+
info(_message, ..._args) {
|
|
13349
|
+
}
|
|
13350
|
+
warn(_message, ..._args) {
|
|
13351
|
+
}
|
|
13352
|
+
error(_message, ..._args) {
|
|
13353
|
+
}
|
|
13354
|
+
};
|
|
13355
|
+
function createSilentLogger() {
|
|
13356
|
+
return new SilentLogger();
|
|
13357
|
+
}
|
|
13358
|
+
var silentLogger = new SilentLogger();
|
|
13359
|
+
var BufferLogger = class {
|
|
13360
|
+
constructor(maxSize = 1e3) {
|
|
13361
|
+
this.buffer = [];
|
|
13362
|
+
this.maxSize = maxSize;
|
|
13363
|
+
}
|
|
13364
|
+
debug(message, ...args) {
|
|
13365
|
+
this.add("debug", message, args);
|
|
13366
|
+
}
|
|
13367
|
+
info(message, ...args) {
|
|
13368
|
+
this.add("info", message, args);
|
|
13369
|
+
}
|
|
13370
|
+
warn(message, ...args) {
|
|
13371
|
+
this.add("warn", message, args);
|
|
13372
|
+
}
|
|
13373
|
+
error(message, ...args) {
|
|
13374
|
+
this.add("error", message, args);
|
|
13375
|
+
}
|
|
13376
|
+
/**
|
|
13377
|
+
* 获取所有日志
|
|
13378
|
+
*/
|
|
13379
|
+
getLogs() {
|
|
13380
|
+
return [...this.buffer];
|
|
13381
|
+
}
|
|
13382
|
+
/**
|
|
13383
|
+
* 获取指定级别的日志
|
|
13384
|
+
*/
|
|
13385
|
+
getLogsByLevel(level) {
|
|
13386
|
+
return this.buffer.filter((log) => log.level === level);
|
|
13387
|
+
}
|
|
13388
|
+
/**
|
|
13389
|
+
* 清空日志
|
|
13390
|
+
*/
|
|
13391
|
+
clear() {
|
|
13392
|
+
this.buffer.length = 0;
|
|
13393
|
+
}
|
|
13394
|
+
/**
|
|
13395
|
+
* 获取日志数量
|
|
13396
|
+
*/
|
|
13397
|
+
get size() {
|
|
13398
|
+
return this.buffer.length;
|
|
13399
|
+
}
|
|
13400
|
+
add(level, message, args) {
|
|
13401
|
+
this.buffer.push({
|
|
13402
|
+
level,
|
|
13403
|
+
message,
|
|
13404
|
+
args,
|
|
13405
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
13406
|
+
});
|
|
13407
|
+
while (this.buffer.length > this.maxSize) {
|
|
13408
|
+
this.buffer.shift();
|
|
13409
|
+
}
|
|
13410
|
+
}
|
|
13411
|
+
};
|
|
13412
|
+
function createBufferLogger(maxSize) {
|
|
13413
|
+
return new BufferLogger(maxSize);
|
|
13414
|
+
}
|
|
13415
|
+
|
|
13416
|
+
// src/utils/headers.ts
|
|
13417
|
+
var DEFAULT_HEADERS = {
|
|
13418
|
+
"Content-Type": "application/json",
|
|
13419
|
+
Accept: "application/json"
|
|
13420
|
+
};
|
|
13421
|
+
function mergeHeaders(...headersList) {
|
|
13422
|
+
const result = {};
|
|
13423
|
+
const lowercaseKeyMap = {};
|
|
13424
|
+
for (const headers of headersList) {
|
|
13425
|
+
if (!headers) continue;
|
|
13426
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
13427
|
+
const lowercaseKey = key.toLowerCase();
|
|
13428
|
+
if (lowercaseKeyMap[lowercaseKey]) {
|
|
13429
|
+
delete result[lowercaseKeyMap[lowercaseKey]];
|
|
13430
|
+
}
|
|
13431
|
+
result[key] = value;
|
|
13432
|
+
lowercaseKeyMap[lowercaseKey] = key;
|
|
13433
|
+
}
|
|
13434
|
+
}
|
|
13435
|
+
return result;
|
|
13436
|
+
}
|
|
13437
|
+
function getHeader(headers, name) {
|
|
13438
|
+
const lowercaseName = name.toLowerCase();
|
|
13439
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
13440
|
+
if (key.toLowerCase() === lowercaseName) {
|
|
13441
|
+
return value;
|
|
13442
|
+
}
|
|
13443
|
+
}
|
|
13444
|
+
return void 0;
|
|
13445
|
+
}
|
|
13446
|
+
function setHeader(headers, name, value) {
|
|
13447
|
+
const lowercaseName = name.toLowerCase();
|
|
13448
|
+
for (const key of Object.keys(headers)) {
|
|
13449
|
+
if (key.toLowerCase() === lowercaseName) {
|
|
13450
|
+
delete headers[key];
|
|
13451
|
+
}
|
|
13452
|
+
}
|
|
13453
|
+
headers[name] = value;
|
|
13454
|
+
}
|
|
13455
|
+
function removeHeader(headers, name) {
|
|
13456
|
+
const lowercaseName = name.toLowerCase();
|
|
13457
|
+
let removed = false;
|
|
13458
|
+
for (const key of Object.keys(headers)) {
|
|
13459
|
+
if (key.toLowerCase() === lowercaseName) {
|
|
13460
|
+
delete headers[key];
|
|
13461
|
+
removed = true;
|
|
13462
|
+
}
|
|
13463
|
+
}
|
|
13464
|
+
return removed;
|
|
13465
|
+
}
|
|
13466
|
+
function extractResponseHeaders(response) {
|
|
13467
|
+
const headers = {};
|
|
13468
|
+
response.headers.forEach((value, key) => {
|
|
13469
|
+
headers[key] = value;
|
|
13470
|
+
});
|
|
13471
|
+
return headers;
|
|
13472
|
+
}
|
|
13473
|
+
function hasContentType(headers, contentType) {
|
|
13474
|
+
const value = getHeader(headers, "Content-Type");
|
|
13475
|
+
if (!value) return false;
|
|
13476
|
+
return value.toLowerCase().includes(contentType.toLowerCase());
|
|
13477
|
+
}
|
|
13478
|
+
|
|
13479
|
+
// src/clients/base-client.ts
|
|
13480
|
+
var BaseClient = class {
|
|
13481
|
+
constructor(config) {
|
|
13482
|
+
this.config = {
|
|
13483
|
+
timeout: 3e4,
|
|
13484
|
+
...config
|
|
13485
|
+
};
|
|
13486
|
+
this.interceptorManager = new InterceptorManager();
|
|
13487
|
+
this.authenticator = config.auth ? createAuthenticatorFromConfig(config.auth) : noAuthenticator;
|
|
13488
|
+
this.registerConfigInterceptors();
|
|
13489
|
+
}
|
|
13490
|
+
/**
|
|
13491
|
+
* GET 请求
|
|
13492
|
+
*/
|
|
13493
|
+
async get(path, query) {
|
|
13494
|
+
return this.request({ method: "GET", path, query });
|
|
13495
|
+
}
|
|
13496
|
+
/**
|
|
13497
|
+
* POST 请求
|
|
13498
|
+
*/
|
|
13499
|
+
async post(path, body, query) {
|
|
13500
|
+
return this.request({ method: "POST", path, body, query });
|
|
13501
|
+
}
|
|
13502
|
+
/**
|
|
13503
|
+
* PUT 请求
|
|
13504
|
+
*/
|
|
13505
|
+
async put(path, body) {
|
|
13506
|
+
return this.request({ method: "PUT", path, body });
|
|
13507
|
+
}
|
|
13508
|
+
/**
|
|
13509
|
+
* PATCH 请求
|
|
13510
|
+
*/
|
|
13511
|
+
async patch(path, body) {
|
|
13512
|
+
return this.request({ method: "PATCH", path, body });
|
|
13513
|
+
}
|
|
13514
|
+
/**
|
|
13515
|
+
* DELETE 请求
|
|
13516
|
+
*/
|
|
13517
|
+
async delete(path) {
|
|
13518
|
+
return this.request({ method: "DELETE", path });
|
|
13519
|
+
}
|
|
13520
|
+
/**
|
|
13521
|
+
* 获取拦截器管理器
|
|
13522
|
+
*/
|
|
13523
|
+
get interceptors() {
|
|
13524
|
+
return this.interceptorManager;
|
|
13525
|
+
}
|
|
13526
|
+
/**
|
|
13527
|
+
* 创建请求上下文
|
|
13528
|
+
*/
|
|
13529
|
+
createRequestContext(options) {
|
|
13530
|
+
const requestId = generateRequestId();
|
|
13531
|
+
const traceId = generateTraceId();
|
|
13532
|
+
const url2 = buildUrl(this.config.baseUrl, options.path, options.query);
|
|
13533
|
+
const headers = mergeHeaders(
|
|
13534
|
+
DEFAULT_HEADERS,
|
|
13535
|
+
this.config.headers,
|
|
13536
|
+
options.headers
|
|
13537
|
+
);
|
|
13538
|
+
const mutableOptions = {
|
|
13539
|
+
method: options.method,
|
|
13540
|
+
path: options.path,
|
|
13541
|
+
query: options.query ? { ...options.query } : void 0,
|
|
13542
|
+
body: options.body,
|
|
13543
|
+
headers,
|
|
13544
|
+
timeout: options.timeout ?? this.config.timeout,
|
|
13545
|
+
skipAuth: options.skipAuth ?? false,
|
|
13546
|
+
signal: options.signal,
|
|
13547
|
+
metadata: options.metadata ? { ...options.metadata } : {}
|
|
13548
|
+
};
|
|
13549
|
+
return {
|
|
13550
|
+
requestId,
|
|
13551
|
+
traceId,
|
|
13552
|
+
startTime: Date.now(),
|
|
13553
|
+
retryCount: 0,
|
|
13554
|
+
originalOptions: options,
|
|
13555
|
+
options: mutableOptions,
|
|
13556
|
+
url: url2,
|
|
13557
|
+
metadata: {}
|
|
13558
|
+
};
|
|
13559
|
+
}
|
|
13560
|
+
/**
|
|
13561
|
+
* 执行请求拦截器
|
|
13562
|
+
*/
|
|
13563
|
+
async runRequestInterceptors(context) {
|
|
13564
|
+
if (!context.options.skipAuth) {
|
|
13565
|
+
await this.authenticator.authenticate(context.options.headers);
|
|
13566
|
+
}
|
|
13567
|
+
return this.interceptorManager.executeRequestInterceptors(context);
|
|
13568
|
+
}
|
|
13569
|
+
/**
|
|
13570
|
+
* 执行响应拦截器
|
|
13571
|
+
*/
|
|
13572
|
+
async runResponseInterceptors(response, context) {
|
|
13573
|
+
return this.interceptorManager.executeResponseInterceptors(response, context);
|
|
13574
|
+
}
|
|
13575
|
+
/**
|
|
13576
|
+
* 执行错误拦截器
|
|
13577
|
+
*/
|
|
13578
|
+
async runErrorInterceptors(error, context) {
|
|
13579
|
+
return this.interceptorManager.executeErrorInterceptors(error, context);
|
|
13580
|
+
}
|
|
13581
|
+
/**
|
|
13582
|
+
* 注册配置中的拦截器
|
|
13583
|
+
*/
|
|
13584
|
+
registerConfigInterceptors() {
|
|
13585
|
+
if (this.config.requestInterceptors) {
|
|
13586
|
+
for (const interceptor of this.config.requestInterceptors) {
|
|
13587
|
+
this.interceptorManager.addRequestInterceptor(interceptor);
|
|
13588
|
+
}
|
|
13589
|
+
}
|
|
13590
|
+
if (this.config.responseInterceptors) {
|
|
13591
|
+
for (const interceptor of this.config.responseInterceptors) {
|
|
13592
|
+
this.interceptorManager.addResponseInterceptor(interceptor);
|
|
13593
|
+
}
|
|
13594
|
+
}
|
|
13595
|
+
if (this.config.errorInterceptors) {
|
|
13596
|
+
for (const interceptor of this.config.errorInterceptors) {
|
|
13597
|
+
this.interceptorManager.addErrorInterceptor(interceptor);
|
|
13598
|
+
}
|
|
13599
|
+
}
|
|
13600
|
+
}
|
|
13601
|
+
};
|
|
13602
|
+
|
|
13603
|
+
// src/clients/fetch-client.ts
|
|
13604
|
+
var FetchClient = class extends BaseClient {
|
|
13605
|
+
constructor(config) {
|
|
13606
|
+
super(config);
|
|
13607
|
+
this.enableRetry = config.enableRetry !== false;
|
|
13608
|
+
}
|
|
13609
|
+
/**
|
|
13610
|
+
* 发起请求
|
|
13611
|
+
*/
|
|
13612
|
+
async request(options) {
|
|
13613
|
+
const context = this.createRequestContext(options);
|
|
13614
|
+
try {
|
|
13615
|
+
const response = await this.executeWithRetry(context);
|
|
13616
|
+
return response.data;
|
|
13617
|
+
} catch (error) {
|
|
13618
|
+
const recovered = await this.runErrorInterceptors(error, context);
|
|
13619
|
+
if (recovered) {
|
|
13620
|
+
return recovered.data;
|
|
13621
|
+
}
|
|
13622
|
+
throw error;
|
|
13623
|
+
}
|
|
13624
|
+
}
|
|
13625
|
+
/**
|
|
13626
|
+
* 带重试的请求执行
|
|
13627
|
+
*/
|
|
13628
|
+
async executeWithRetry(context) {
|
|
13629
|
+
const retryConfig = this.config.retry;
|
|
13630
|
+
const maxRetries = this.enableRetry && retryConfig ? retryConfig.maxRetries : 0;
|
|
13631
|
+
for (; ; ) {
|
|
13632
|
+
try {
|
|
13633
|
+
return await this.executeRequest(context);
|
|
13634
|
+
} catch (error) {
|
|
13635
|
+
const err = error;
|
|
13636
|
+
if (!this.shouldRetry(err, context, retryConfig, maxRetries)) {
|
|
13637
|
+
throw err;
|
|
13638
|
+
}
|
|
13639
|
+
const retryAfterSeconds = err instanceof ApiError ? err.getRetryAfter() : void 0;
|
|
13640
|
+
let delayMs = calculateRetryDelay(retryConfig, context.retryCount);
|
|
13641
|
+
if (retryConfig?.respectRetryAfter !== false && retryAfterSeconds !== void 0) {
|
|
13642
|
+
delayMs = Math.min(retryAfterSeconds * 1e3, retryConfig.maxDelayMs);
|
|
13643
|
+
}
|
|
13644
|
+
if (retryConfig?.onRetry) {
|
|
13645
|
+
retryConfig.onRetry({
|
|
13646
|
+
attempt: context.retryCount + 1,
|
|
13647
|
+
maxRetries: retryConfig.maxRetries,
|
|
13648
|
+
delayMs,
|
|
13649
|
+
error: err,
|
|
13650
|
+
url: context.url,
|
|
13651
|
+
method: context.options.method
|
|
13652
|
+
});
|
|
13653
|
+
}
|
|
13654
|
+
this.logDebug(
|
|
13655
|
+
`[Retry] ${context.options.method} ${context.options.path} - attempt ${context.retryCount + 1}/${maxRetries}, delay ${delayMs}ms`
|
|
13656
|
+
);
|
|
13657
|
+
await sleep(delayMs);
|
|
13658
|
+
context.retryCount++;
|
|
13659
|
+
}
|
|
13660
|
+
}
|
|
13661
|
+
}
|
|
13662
|
+
/**
|
|
13663
|
+
* 判断是否应该重试
|
|
13664
|
+
*/
|
|
13665
|
+
shouldRetry(error, context, retryConfig, maxRetries) {
|
|
13666
|
+
if (!this.enableRetry || !retryConfig) {
|
|
13667
|
+
return false;
|
|
13668
|
+
}
|
|
13669
|
+
if (context.retryCount >= maxRetries) {
|
|
13670
|
+
return false;
|
|
13671
|
+
}
|
|
13672
|
+
if (error instanceof AbortError) {
|
|
13673
|
+
return false;
|
|
13674
|
+
}
|
|
13675
|
+
if (retryConfig.shouldRetry) {
|
|
13676
|
+
const statusCode = error instanceof ApiError ? error.statusCode : void 0;
|
|
13677
|
+
const retryAfter = error instanceof ApiError ? error.getRetryAfter() : void 0;
|
|
13678
|
+
return retryConfig.shouldRetry(error, context.retryCount, {
|
|
13679
|
+
url: context.url,
|
|
13680
|
+
method: context.options.method,
|
|
13681
|
+
statusCode,
|
|
13682
|
+
retryAfter
|
|
13683
|
+
});
|
|
13684
|
+
}
|
|
13685
|
+
if (error instanceof NetworkError) {
|
|
13686
|
+
return retryConfig.retryOnNetworkError !== false;
|
|
13687
|
+
}
|
|
13688
|
+
if (error instanceof TimeoutError) {
|
|
13689
|
+
return retryConfig.retryOnTimeout !== false;
|
|
13690
|
+
}
|
|
13691
|
+
if (error instanceof ApiError) {
|
|
13692
|
+
const retryableCodes = retryConfig.retryableStatusCodes ?? DEFAULT_RETRY_CONFIG.retryableStatusCodes;
|
|
13693
|
+
return retryableCodes?.includes(error.statusCode) ?? false;
|
|
13694
|
+
}
|
|
13695
|
+
return false;
|
|
13696
|
+
}
|
|
13697
|
+
/**
|
|
13698
|
+
* 执行单次请求
|
|
13699
|
+
*/
|
|
13700
|
+
async executeRequest(context) {
|
|
13701
|
+
const options = await this.runRequestInterceptors(context);
|
|
13702
|
+
context.options = options;
|
|
13703
|
+
const url2 = this.buildFullUrl(context);
|
|
13704
|
+
const init = this.buildFetchInit(context);
|
|
13705
|
+
const { signal, cleanup } = this.createTimeoutSignal(
|
|
13706
|
+
options.timeout ?? 3e4,
|
|
13707
|
+
options.signal
|
|
13708
|
+
);
|
|
13709
|
+
this.logDebug(`[Request] ${options.method} ${url2}`, {
|
|
13710
|
+
requestId: context.requestId,
|
|
13711
|
+
headers: options.headers
|
|
13712
|
+
});
|
|
13713
|
+
try {
|
|
13714
|
+
const fetchResponse = await fetch(url2, {
|
|
13715
|
+
...init,
|
|
13716
|
+
signal
|
|
13717
|
+
});
|
|
13718
|
+
const response = await this.parseResponse(fetchResponse, context);
|
|
13719
|
+
const duration = Date.now() - context.startTime;
|
|
13720
|
+
this.logDebug(
|
|
13721
|
+
`[Response] ${options.method} ${options.path} - ${fetchResponse.status} (${duration}ms)`,
|
|
13722
|
+
{ requestId: context.requestId, status: fetchResponse.status, duration }
|
|
13723
|
+
);
|
|
13724
|
+
return this.runResponseInterceptors(response, context);
|
|
13725
|
+
} catch (error) {
|
|
13726
|
+
const transformedError = this.transformError(error, context);
|
|
13727
|
+
const duration = Date.now() - context.startTime;
|
|
13728
|
+
this.logError(
|
|
13729
|
+
`[Error] ${options.method} ${options.path} - ${transformedError.message} (${duration}ms)`,
|
|
13730
|
+
{ requestId: context.requestId, error: transformedError.message, duration }
|
|
13731
|
+
);
|
|
13732
|
+
throw transformedError;
|
|
13733
|
+
} finally {
|
|
13734
|
+
cleanup();
|
|
13735
|
+
}
|
|
13736
|
+
}
|
|
13737
|
+
/**
|
|
13738
|
+
* 构建完整 URL
|
|
13739
|
+
*/
|
|
13740
|
+
buildFullUrl(context) {
|
|
13741
|
+
const { options } = context;
|
|
13742
|
+
const base = this.config.baseUrl.replace(/\/+$/, "");
|
|
13743
|
+
const path = options.path.startsWith("/") ? options.path : `/${options.path}`;
|
|
13744
|
+
const url2 = new URL(`${base}${path}`);
|
|
13745
|
+
if (options.query) {
|
|
13746
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
13747
|
+
if (value !== void 0 && value !== null) {
|
|
13748
|
+
url2.searchParams.set(key, String(value));
|
|
13749
|
+
}
|
|
13750
|
+
}
|
|
13751
|
+
}
|
|
13752
|
+
return url2.toString();
|
|
13753
|
+
}
|
|
13754
|
+
/**
|
|
13755
|
+
* 构建 fetch init 对象
|
|
13756
|
+
*/
|
|
13757
|
+
buildFetchInit(context) {
|
|
13758
|
+
const { options } = context;
|
|
13759
|
+
const init = {
|
|
13760
|
+
method: options.method,
|
|
13761
|
+
headers: options.headers
|
|
13762
|
+
};
|
|
13763
|
+
if (options.body !== void 0) {
|
|
13764
|
+
init.body = JSON.stringify(options.body);
|
|
13765
|
+
}
|
|
13766
|
+
return init;
|
|
13767
|
+
}
|
|
13768
|
+
/**
|
|
13769
|
+
* 创建超时信号
|
|
13770
|
+
*/
|
|
13771
|
+
createTimeoutSignal(timeoutMs, externalSignal) {
|
|
13772
|
+
const controller = new AbortController();
|
|
13773
|
+
const timeoutId = setTimeout(() => {
|
|
13774
|
+
controller.abort(new DOMException(`Timeout of ${timeoutMs}ms exceeded`, "TimeoutError"));
|
|
13775
|
+
}, timeoutMs);
|
|
13776
|
+
if (externalSignal) {
|
|
13777
|
+
if (externalSignal.aborted) {
|
|
13778
|
+
controller.abort(externalSignal.reason);
|
|
13779
|
+
} else {
|
|
13780
|
+
const onAbort = () => {
|
|
13781
|
+
controller.abort(externalSignal.reason);
|
|
13782
|
+
};
|
|
13783
|
+
externalSignal.addEventListener("abort", onAbort, { once: true });
|
|
13784
|
+
}
|
|
13785
|
+
}
|
|
13786
|
+
return {
|
|
13787
|
+
signal: controller.signal,
|
|
13788
|
+
cleanup: () => {
|
|
13789
|
+
if (timeoutId !== void 0) {
|
|
13790
|
+
clearTimeout(timeoutId);
|
|
13791
|
+
}
|
|
13792
|
+
}
|
|
13793
|
+
};
|
|
13794
|
+
}
|
|
13795
|
+
/**
|
|
13796
|
+
* 解析响应
|
|
13797
|
+
*/
|
|
13798
|
+
async parseResponse(fetchResponse, context) {
|
|
13799
|
+
const headers = extractResponseHeaders(fetchResponse);
|
|
13800
|
+
let data;
|
|
13801
|
+
try {
|
|
13802
|
+
data = await fetchResponse.json();
|
|
13803
|
+
} catch {
|
|
13804
|
+
if (!fetchResponse.ok) {
|
|
13805
|
+
throw ApiError.fromResponse(
|
|
13806
|
+
{
|
|
13807
|
+
code: `HTTP_${fetchResponse.status}`,
|
|
13808
|
+
message: fetchResponse.statusText || `HTTP Error ${fetchResponse.status}`,
|
|
13809
|
+
requestId: context.requestId
|
|
13810
|
+
},
|
|
13811
|
+
fetchResponse.status,
|
|
13812
|
+
parseRetryAfter(getHeader(headers, "Retry-After"))
|
|
13813
|
+
);
|
|
13814
|
+
}
|
|
13815
|
+
data = {
|
|
13816
|
+
success: true,
|
|
13817
|
+
data: void 0,
|
|
13818
|
+
requestId: context.requestId
|
|
13819
|
+
};
|
|
13820
|
+
}
|
|
13821
|
+
if (!fetchResponse.ok || data.success === false) {
|
|
13822
|
+
throw ApiError.fromResponse(
|
|
13823
|
+
{
|
|
13824
|
+
code: data.code,
|
|
13825
|
+
message: data.message,
|
|
13826
|
+
details: data.details,
|
|
13827
|
+
requestId: data.requestId ?? context.requestId,
|
|
13828
|
+
traceId: data.traceId ?? context.traceId
|
|
13829
|
+
},
|
|
13830
|
+
fetchResponse.status,
|
|
13831
|
+
parseRetryAfter(getHeader(headers, "Retry-After"))
|
|
13832
|
+
);
|
|
13833
|
+
}
|
|
13834
|
+
return {
|
|
13835
|
+
data: data.data,
|
|
13836
|
+
status: fetchResponse.status,
|
|
13837
|
+
statusText: fetchResponse.statusText,
|
|
13838
|
+
headers,
|
|
13839
|
+
requestId: data.requestId ?? context.requestId,
|
|
13840
|
+
traceId: data.traceId ?? context.traceId
|
|
13841
|
+
};
|
|
13842
|
+
}
|
|
13843
|
+
/**
|
|
13844
|
+
* 转换错误
|
|
13845
|
+
*/
|
|
13846
|
+
transformError(error, _context) {
|
|
13847
|
+
if (error instanceof ApiError || error instanceof NetworkError || error instanceof TimeoutError || error instanceof AbortError) {
|
|
13848
|
+
return error;
|
|
13849
|
+
}
|
|
13850
|
+
if (error.name === "AbortError") {
|
|
13851
|
+
if (error.message?.includes("Timeout")) {
|
|
13852
|
+
const match = error.message.match(/(\d+)ms/);
|
|
13853
|
+
const timeout = match ? parseInt(match[1], 10) : 3e4;
|
|
13854
|
+
return new TimeoutError(timeout);
|
|
13855
|
+
}
|
|
13856
|
+
return AbortError.fromNative(error);
|
|
13857
|
+
}
|
|
13858
|
+
if (error.name === "TypeError") {
|
|
13859
|
+
return NetworkError.fromFetchError(error);
|
|
11729
13860
|
}
|
|
11730
|
-
|
|
11731
|
-
return retryableCodes.includes(code);
|
|
13861
|
+
return new NetworkError(error.message, error);
|
|
11732
13862
|
}
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
11739
|
-
|
|
13863
|
+
// ============================================================
|
|
13864
|
+
// 日志辅助方法
|
|
13865
|
+
// ============================================================
|
|
13866
|
+
/**
|
|
13867
|
+
* 调试日志
|
|
13868
|
+
*/
|
|
13869
|
+
logDebug(message, data) {
|
|
13870
|
+
if (this.config.debug && this.config.logger) {
|
|
13871
|
+
this.config.logger.debug(message, data);
|
|
13872
|
+
}
|
|
11740
13873
|
}
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
this.
|
|
11746
|
-
|
|
13874
|
+
/**
|
|
13875
|
+
* 错误日志
|
|
13876
|
+
*/
|
|
13877
|
+
logError(message, data) {
|
|
13878
|
+
if (this.config.logger) {
|
|
13879
|
+
this.config.logger.error(message, data);
|
|
13880
|
+
}
|
|
11747
13881
|
}
|
|
11748
13882
|
};
|
|
13883
|
+
function createFetchClient(config) {
|
|
13884
|
+
return new FetchClient(config);
|
|
13885
|
+
}
|
|
11749
13886
|
|
|
11750
13887
|
// ../../node_modules/.pnpm/axios@1.13.2/node_modules/axios/lib/helpers/bind.js
|
|
11751
13888
|
function bind(fn, thisArg) {
|
|
@@ -12402,7 +14539,7 @@ function buildURL(url2, params, options) {
|
|
|
12402
14539
|
}
|
|
12403
14540
|
|
|
12404
14541
|
// ../../node_modules/.pnpm/axios@1.13.2/node_modules/axios/lib/core/InterceptorManager.js
|
|
12405
|
-
var
|
|
14542
|
+
var InterceptorManager2 = class {
|
|
12406
14543
|
constructor() {
|
|
12407
14544
|
this.handlers = [];
|
|
12408
14545
|
}
|
|
@@ -12463,7 +14600,7 @@ var InterceptorManager = class {
|
|
|
12463
14600
|
});
|
|
12464
14601
|
}
|
|
12465
14602
|
};
|
|
12466
|
-
var InterceptorManager_default =
|
|
14603
|
+
var InterceptorManager_default = InterceptorManager2;
|
|
12467
14604
|
|
|
12468
14605
|
// ../../node_modules/.pnpm/axios@1.13.2/node_modules/axios/lib/defaults/transitional.js
|
|
12469
14606
|
var transitional_default = {
|
|
@@ -12476,8 +14613,8 @@ var transitional_default = {
|
|
|
12476
14613
|
var import_crypto = __toESM(require("crypto"), 1);
|
|
12477
14614
|
|
|
12478
14615
|
// ../../node_modules/.pnpm/axios@1.13.2/node_modules/axios/lib/platform/node/classes/URLSearchParams.js
|
|
12479
|
-
var
|
|
12480
|
-
var URLSearchParams_default =
|
|
14616
|
+
var import_url3 = __toESM(require("url"), 1);
|
|
14617
|
+
var URLSearchParams_default = import_url3.default.URLSearchParams;
|
|
12481
14618
|
|
|
12482
14619
|
// ../../node_modules/.pnpm/axios@1.13.2/node_modules/axios/lib/platform/node/index.js
|
|
12483
14620
|
var ALPHA = "abcdefghijklmnopqrstuvwxyz";
|
|
@@ -12812,7 +14949,7 @@ var AxiosHeaders = class {
|
|
|
12812
14949
|
}
|
|
12813
14950
|
set(header, valueOrRewrite, rewrite) {
|
|
12814
14951
|
const self2 = this;
|
|
12815
|
-
function
|
|
14952
|
+
function setHeader2(_value, _header, _rewrite) {
|
|
12816
14953
|
const lHeader = normalizeHeader(_header);
|
|
12817
14954
|
if (!lHeader) {
|
|
12818
14955
|
throw new Error("header name must be a non-empty string");
|
|
@@ -12822,7 +14959,7 @@ var AxiosHeaders = class {
|
|
|
12822
14959
|
self2[key || _header] = normalizeValue(_value);
|
|
12823
14960
|
}
|
|
12824
14961
|
}
|
|
12825
|
-
const setHeaders = (headers, _rewrite) => utils_default.forEach(headers, (_value, _header) =>
|
|
14962
|
+
const setHeaders = (headers, _rewrite) => utils_default.forEach(headers, (_value, _header) => setHeader2(_value, _header, _rewrite));
|
|
12826
14963
|
if (utils_default.isPlainObject(header) || header instanceof this.constructor) {
|
|
12827
14964
|
setHeaders(header, valueOrRewrite);
|
|
12828
14965
|
} else if (utils_default.isString(header) && (header = header.trim()) && !isValidHeaderName(header)) {
|
|
@@ -12837,7 +14974,7 @@ var AxiosHeaders = class {
|
|
|
12837
14974
|
}
|
|
12838
14975
|
setHeaders(obj, valueOrRewrite);
|
|
12839
14976
|
} else {
|
|
12840
|
-
header != null &&
|
|
14977
|
+
header != null && setHeader2(valueOrRewrite, header, rewrite);
|
|
12841
14978
|
}
|
|
12842
14979
|
return this;
|
|
12843
14980
|
}
|
|
@@ -14596,7 +16733,7 @@ var factory = (env2) => {
|
|
|
14596
16733
|
const encodeText = isFetchSupported && (typeof TextEncoder2 === "function" ? /* @__PURE__ */ ((encoder) => (str) => encoder.encode(str))(new TextEncoder2()) : async (str) => new Uint8Array(await new Request(str).arrayBuffer()));
|
|
14597
16734
|
const supportsRequestStream = isRequestSupported && isReadableStreamSupported && test(() => {
|
|
14598
16735
|
let duplexAccessed = false;
|
|
14599
|
-
const
|
|
16736
|
+
const hasContentType2 = new Request(platform_default.origin, {
|
|
14600
16737
|
body: new ReadableStream2(),
|
|
14601
16738
|
method: "POST",
|
|
14602
16739
|
get duplex() {
|
|
@@ -14604,7 +16741,7 @@ var factory = (env2) => {
|
|
|
14604
16741
|
return "half";
|
|
14605
16742
|
}
|
|
14606
16743
|
}).headers.has("Content-Type");
|
|
14607
|
-
return duplexAccessed && !
|
|
16744
|
+
return duplexAccessed && !hasContentType2;
|
|
14608
16745
|
});
|
|
14609
16746
|
const supportsResponseStream = isResponseSupported && isReadableStreamSupported && test(() => utils_default.isReadableStream(new Response("").body));
|
|
14610
16747
|
const resolvers = {
|
|
@@ -15358,113 +17495,7 @@ var {
|
|
|
15358
17495
|
mergeConfig: mergeConfig2
|
|
15359
17496
|
} = axios_default;
|
|
15360
17497
|
|
|
15361
|
-
// src/
|
|
15362
|
-
async function addAuthToAxiosConfig(config, auth) {
|
|
15363
|
-
switch (auth.type) {
|
|
15364
|
-
case "bearer": {
|
|
15365
|
-
if (auth.getToken) {
|
|
15366
|
-
const token = typeof auth.getToken === "function" ? await auth.getToken() : auth.getToken;
|
|
15367
|
-
if (token) {
|
|
15368
|
-
config.headers.set("Authorization", `Bearer ${token}`);
|
|
15369
|
-
}
|
|
15370
|
-
}
|
|
15371
|
-
break;
|
|
15372
|
-
}
|
|
15373
|
-
case "api-key": {
|
|
15374
|
-
if (auth.apiKey) {
|
|
15375
|
-
const headerName = auth.apiKeyHeader ?? "X-API-Key";
|
|
15376
|
-
config.headers.set(headerName, auth.apiKey);
|
|
15377
|
-
}
|
|
15378
|
-
break;
|
|
15379
|
-
}
|
|
15380
|
-
case "basic": {
|
|
15381
|
-
if (auth.username && auth.password) {
|
|
15382
|
-
const encoded = btoa(`${auth.username}:${auth.password}`);
|
|
15383
|
-
config.headers.set("Authorization", `Basic ${encoded}`);
|
|
15384
|
-
}
|
|
15385
|
-
break;
|
|
15386
|
-
}
|
|
15387
|
-
case "custom": {
|
|
15388
|
-
if (auth.customAuth) {
|
|
15389
|
-
const headers = {};
|
|
15390
|
-
await auth.customAuth(headers);
|
|
15391
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
15392
|
-
config.headers.set(key, value);
|
|
15393
|
-
});
|
|
15394
|
-
}
|
|
15395
|
-
break;
|
|
15396
|
-
}
|
|
15397
|
-
}
|
|
15398
|
-
}
|
|
15399
|
-
async function addAuthToHeaders(headers, auth) {
|
|
15400
|
-
switch (auth.type) {
|
|
15401
|
-
case "bearer": {
|
|
15402
|
-
if (auth.getToken) {
|
|
15403
|
-
const token = typeof auth.getToken === "function" ? await auth.getToken() : auth.getToken;
|
|
15404
|
-
if (token) {
|
|
15405
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
15406
|
-
}
|
|
15407
|
-
}
|
|
15408
|
-
break;
|
|
15409
|
-
}
|
|
15410
|
-
case "api-key": {
|
|
15411
|
-
if (auth.apiKey) {
|
|
15412
|
-
const headerName = auth.apiKeyHeader ?? "X-API-Key";
|
|
15413
|
-
headers[headerName] = auth.apiKey;
|
|
15414
|
-
}
|
|
15415
|
-
break;
|
|
15416
|
-
}
|
|
15417
|
-
case "basic": {
|
|
15418
|
-
if (auth.username && auth.password) {
|
|
15419
|
-
const encoded = btoa(`${auth.username}:${auth.password}`);
|
|
15420
|
-
headers["Authorization"] = `Basic ${encoded}`;
|
|
15421
|
-
}
|
|
15422
|
-
break;
|
|
15423
|
-
}
|
|
15424
|
-
case "custom": {
|
|
15425
|
-
if (auth.customAuth) {
|
|
15426
|
-
await auth.customAuth(headers);
|
|
15427
|
-
}
|
|
15428
|
-
break;
|
|
15429
|
-
}
|
|
15430
|
-
}
|
|
15431
|
-
}
|
|
15432
|
-
|
|
15433
|
-
// src/utils.ts
|
|
15434
|
-
function generateRequestId() {
|
|
15435
|
-
return `req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
|
|
15436
|
-
}
|
|
15437
|
-
function sleep(ms) {
|
|
15438
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
15439
|
-
}
|
|
15440
|
-
function calculateRetryDelay(retry, attempt) {
|
|
15441
|
-
let delay;
|
|
15442
|
-
switch (retry.backoff) {
|
|
15443
|
-
case "fixed":
|
|
15444
|
-
delay = retry.initialDelay;
|
|
15445
|
-
break;
|
|
15446
|
-
case "linear":
|
|
15447
|
-
delay = retry.initialDelay * (attempt + 1);
|
|
15448
|
-
break;
|
|
15449
|
-
case "exponential":
|
|
15450
|
-
default:
|
|
15451
|
-
delay = retry.initialDelay * Math.pow(2, attempt);
|
|
15452
|
-
break;
|
|
15453
|
-
}
|
|
15454
|
-
const jitter = delay * 0.1 * Math.random();
|
|
15455
|
-
delay += jitter;
|
|
15456
|
-
return Math.min(delay, retry.maxDelay);
|
|
15457
|
-
}
|
|
15458
|
-
|
|
15459
|
-
// src/axios-instance.ts
|
|
15460
|
-
function shouldRetryAxios(error, retry) {
|
|
15461
|
-
if (!error.response) {
|
|
15462
|
-
return retry.retryOnNetworkError !== false;
|
|
15463
|
-
}
|
|
15464
|
-
const status = error.response.status;
|
|
15465
|
-
const retryableCodes = retry.retryableStatusCodes ?? [429, 500, 502, 503, 504];
|
|
15466
|
-
return retryableCodes.includes(status);
|
|
15467
|
-
}
|
|
17498
|
+
// src/clients/axios-client.ts
|
|
15468
17499
|
function createAxiosInstance(config) {
|
|
15469
17500
|
const instance = axios_default.create({
|
|
15470
17501
|
baseURL: config.baseUrl,
|
|
@@ -15475,248 +17506,644 @@ function createAxiosInstance(config) {
|
|
|
15475
17506
|
...config.headers
|
|
15476
17507
|
}
|
|
15477
17508
|
});
|
|
17509
|
+
const authenticator = config.auth ? createAuthenticatorFromConfig(config.auth) : noAuthenticator;
|
|
17510
|
+
const logger = config.logger;
|
|
17511
|
+
const debug = config.debug ?? false;
|
|
15478
17512
|
instance.interceptors.request.use(
|
|
15479
17513
|
async (axiosConfig) => {
|
|
17514
|
+
axiosConfig.__startTime = Date.now();
|
|
15480
17515
|
const requestId = generateRequestId();
|
|
17516
|
+
const traceId = generateTraceId();
|
|
17517
|
+
axiosConfig.__requestId = requestId;
|
|
17518
|
+
axiosConfig.__traceId = traceId;
|
|
15481
17519
|
axiosConfig.headers.set("X-Request-ID", requestId);
|
|
15482
|
-
|
|
15483
|
-
|
|
17520
|
+
axiosConfig.headers.set("X-Trace-ID", traceId);
|
|
17521
|
+
const headers = {};
|
|
17522
|
+
await authenticator.authenticate(headers);
|
|
17523
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
17524
|
+
axiosConfig.headers.set(key, value);
|
|
17525
|
+
}
|
|
17526
|
+
if (config.requestInterceptors) {
|
|
17527
|
+
await executeRequestInterceptors(config.requestInterceptors, axiosConfig);
|
|
17528
|
+
}
|
|
17529
|
+
if (debug && logger) {
|
|
17530
|
+
logger.debug(`[Request] ${axiosConfig.method?.toUpperCase()} ${axiosConfig.url}`, {
|
|
17531
|
+
requestId,
|
|
17532
|
+
headers: Object.fromEntries(
|
|
17533
|
+
Object.entries(axiosConfig.headers).filter(([, v]) => typeof v === "string")
|
|
17534
|
+
)
|
|
17535
|
+
});
|
|
15484
17536
|
}
|
|
15485
17537
|
return axiosConfig;
|
|
15486
17538
|
},
|
|
15487
17539
|
(error) => Promise.reject(error)
|
|
15488
17540
|
);
|
|
15489
17541
|
instance.interceptors.response.use(
|
|
15490
|
-
(response) =>
|
|
17542
|
+
async (response) => {
|
|
17543
|
+
const axiosConfig = response.config;
|
|
17544
|
+
const duration = Date.now() - (axiosConfig.__startTime ?? Date.now());
|
|
17545
|
+
if (debug && logger) {
|
|
17546
|
+
logger.debug(
|
|
17547
|
+
`[Response] ${axiosConfig.method?.toUpperCase()} ${axiosConfig.url} - ${response.status} (${duration}ms)`,
|
|
17548
|
+
{
|
|
17549
|
+
requestId: axiosConfig.__requestId,
|
|
17550
|
+
status: response.status,
|
|
17551
|
+
duration
|
|
17552
|
+
}
|
|
17553
|
+
);
|
|
17554
|
+
}
|
|
17555
|
+
if (config.responseInterceptors) {
|
|
17556
|
+
await executeResponseInterceptors(config.responseInterceptors, response);
|
|
17557
|
+
}
|
|
17558
|
+
return response;
|
|
17559
|
+
},
|
|
15491
17560
|
async (error) => {
|
|
15492
|
-
|
|
15493
|
-
|
|
15494
|
-
|
|
17561
|
+
const axiosConfig = error.config;
|
|
17562
|
+
const duration = Date.now() - (axiosConfig?.__startTime ?? Date.now());
|
|
17563
|
+
const customError = transformAxiosError(error);
|
|
17564
|
+
if (logger) {
|
|
17565
|
+
logger.error(
|
|
17566
|
+
`[Error] ${axiosConfig?.method?.toUpperCase()} ${axiosConfig?.url} - ${customError.message} (${duration}ms)`,
|
|
17567
|
+
{
|
|
17568
|
+
requestId: axiosConfig?.__requestId,
|
|
17569
|
+
error: customError.message,
|
|
17570
|
+
duration
|
|
17571
|
+
}
|
|
17572
|
+
);
|
|
17573
|
+
}
|
|
17574
|
+
if (config.errorInterceptors) {
|
|
17575
|
+
const recovered = await executeErrorInterceptors(
|
|
17576
|
+
config.errorInterceptors,
|
|
17577
|
+
customError,
|
|
17578
|
+
axiosConfig
|
|
17579
|
+
);
|
|
17580
|
+
if (recovered) {
|
|
17581
|
+
return recovered;
|
|
17582
|
+
}
|
|
17583
|
+
}
|
|
17584
|
+
if (config.enableRetry !== false && config.retry && shouldRetry(error, config.retry)) {
|
|
17585
|
+
const retryCount = axiosConfig?.__retryCount ?? 0;
|
|
15495
17586
|
if (retryCount < config.retry.maxRetries) {
|
|
15496
|
-
|
|
15497
|
-
|
|
15498
|
-
|
|
15499
|
-
|
|
15500
|
-
|
|
17587
|
+
let delayMs = calculateRetryDelay(config.retry, retryCount);
|
|
17588
|
+
if (config.retry.respectRetryAfter !== false && error.response) {
|
|
17589
|
+
const retryAfter = parseRetryAfter(
|
|
17590
|
+
error.response.headers["retry-after"]
|
|
17591
|
+
);
|
|
17592
|
+
if (retryAfter !== void 0) {
|
|
17593
|
+
delayMs = Math.min(retryAfter * 1e3, config.retry.maxDelayMs);
|
|
17594
|
+
}
|
|
17595
|
+
}
|
|
17596
|
+
if (config.retry.onRetry) {
|
|
17597
|
+
config.retry.onRetry({
|
|
17598
|
+
attempt: retryCount + 1,
|
|
17599
|
+
maxRetries: config.retry.maxRetries,
|
|
17600
|
+
delayMs,
|
|
17601
|
+
error: customError,
|
|
17602
|
+
url: axiosConfig?.url ?? "",
|
|
17603
|
+
method: axiosConfig?.method?.toUpperCase() ?? "GET"
|
|
17604
|
+
});
|
|
17605
|
+
}
|
|
17606
|
+
if (debug && logger) {
|
|
17607
|
+
logger.debug(
|
|
17608
|
+
`[Retry] ${axiosConfig?.method?.toUpperCase()} ${axiosConfig?.url} - attempt ${retryCount + 1}/${config.retry.maxRetries}, delay ${delayMs}ms`
|
|
17609
|
+
);
|
|
17610
|
+
}
|
|
17611
|
+
await sleep(delayMs);
|
|
17612
|
+
if (axiosConfig) {
|
|
17613
|
+
axiosConfig.__retryCount = retryCount + 1;
|
|
17614
|
+
return instance.request(axiosConfig);
|
|
15501
17615
|
}
|
|
15502
17616
|
}
|
|
15503
17617
|
}
|
|
15504
|
-
return Promise.reject(
|
|
17618
|
+
return Promise.reject(customError);
|
|
15505
17619
|
}
|
|
15506
17620
|
);
|
|
15507
17621
|
return instance;
|
|
15508
17622
|
}
|
|
15509
|
-
|
|
15510
|
-
|
|
15511
|
-
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
|
|
17623
|
+
async function executeRequestInterceptors(interceptors, axiosConfig) {
|
|
17624
|
+
const sorted = [...interceptors].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
17625
|
+
for (const interceptor of sorted) {
|
|
17626
|
+
const context = {
|
|
17627
|
+
requestId: axiosConfig.__requestId ?? "",
|
|
17628
|
+
traceId: axiosConfig.__traceId,
|
|
17629
|
+
startTime: axiosConfig.__startTime ?? Date.now(),
|
|
17630
|
+
retryCount: axiosConfig.__retryCount ?? 0,
|
|
17631
|
+
originalOptions: {
|
|
17632
|
+
method: axiosConfig.method?.toUpperCase() ?? "GET",
|
|
17633
|
+
path: axiosConfig.url ?? ""
|
|
17634
|
+
},
|
|
17635
|
+
options: {
|
|
17636
|
+
method: axiosConfig.method?.toUpperCase() ?? "GET",
|
|
17637
|
+
path: axiosConfig.url ?? "",
|
|
17638
|
+
headers: Object.fromEntries(
|
|
17639
|
+
Object.entries(axiosConfig.headers).filter(([, v]) => typeof v === "string")
|
|
17640
|
+
),
|
|
17641
|
+
body: axiosConfig.data,
|
|
17642
|
+
timeout: axiosConfig.timeout,
|
|
17643
|
+
metadata: {}
|
|
17644
|
+
},
|
|
17645
|
+
url: `${axiosConfig.baseURL ?? ""}${axiosConfig.url ?? ""}`,
|
|
17646
|
+
metadata: {}
|
|
15517
17647
|
};
|
|
17648
|
+
const result = await interceptor.intercept(context);
|
|
17649
|
+
if (result.headers) {
|
|
17650
|
+
for (const [key, value] of Object.entries(result.headers)) {
|
|
17651
|
+
axiosConfig.headers.set(key, value);
|
|
17652
|
+
}
|
|
17653
|
+
}
|
|
17654
|
+
if (result.timeout !== void 0) {
|
|
17655
|
+
axiosConfig.timeout = result.timeout;
|
|
17656
|
+
}
|
|
15518
17657
|
}
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
17658
|
+
}
|
|
17659
|
+
async function executeResponseInterceptors(interceptors, response) {
|
|
17660
|
+
const sorted = [...interceptors].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
17661
|
+
const axiosConfig = response.config;
|
|
17662
|
+
for (const interceptor of sorted) {
|
|
17663
|
+
const context = {
|
|
17664
|
+
requestId: axiosConfig.__requestId ?? "",
|
|
17665
|
+
traceId: axiosConfig.__traceId,
|
|
17666
|
+
startTime: axiosConfig.__startTime ?? Date.now(),
|
|
17667
|
+
retryCount: axiosConfig.__retryCount ?? 0,
|
|
17668
|
+
originalOptions: {
|
|
17669
|
+
method: axiosConfig.method?.toUpperCase() ?? "GET",
|
|
17670
|
+
path: axiosConfig.url ?? ""
|
|
17671
|
+
},
|
|
17672
|
+
options: {
|
|
17673
|
+
method: axiosConfig.method?.toUpperCase() ?? "GET",
|
|
17674
|
+
path: axiosConfig.url ?? "",
|
|
17675
|
+
headers: Object.fromEntries(
|
|
17676
|
+
Object.entries(axiosConfig.headers).filter(([, v]) => typeof v === "string")
|
|
17677
|
+
),
|
|
17678
|
+
body: axiosConfig.data,
|
|
17679
|
+
timeout: axiosConfig.timeout,
|
|
17680
|
+
metadata: {}
|
|
17681
|
+
},
|
|
17682
|
+
url: `${axiosConfig.baseURL ?? ""}${axiosConfig.url ?? ""}`,
|
|
17683
|
+
metadata: {}
|
|
17684
|
+
};
|
|
17685
|
+
const responseData = {
|
|
17686
|
+
data: response.data,
|
|
17687
|
+
status: response.status,
|
|
17688
|
+
statusText: response.statusText,
|
|
17689
|
+
headers: response.headers,
|
|
17690
|
+
requestId: axiosConfig.__requestId ?? "",
|
|
17691
|
+
traceId: axiosConfig.__traceId
|
|
17692
|
+
};
|
|
17693
|
+
await interceptor.intercept(responseData, context);
|
|
15541
17694
|
}
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
17695
|
+
}
|
|
17696
|
+
async function executeErrorInterceptors(interceptors, error, axiosConfig) {
|
|
17697
|
+
const sorted = [...interceptors].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
17698
|
+
for (const interceptor of sorted) {
|
|
17699
|
+
const context = {
|
|
17700
|
+
requestId: axiosConfig?.__requestId ?? "",
|
|
17701
|
+
traceId: axiosConfig?.__traceId,
|
|
17702
|
+
startTime: axiosConfig?.__startTime ?? Date.now(),
|
|
17703
|
+
retryCount: axiosConfig?.__retryCount ?? 0,
|
|
17704
|
+
originalOptions: {
|
|
17705
|
+
method: axiosConfig?.method?.toUpperCase() ?? "GET",
|
|
17706
|
+
path: axiosConfig?.url ?? ""
|
|
17707
|
+
},
|
|
17708
|
+
options: {
|
|
17709
|
+
method: axiosConfig?.method?.toUpperCase() ?? "GET",
|
|
17710
|
+
path: axiosConfig?.url ?? "",
|
|
17711
|
+
headers: axiosConfig ? Object.fromEntries(
|
|
17712
|
+
Object.entries(axiosConfig.headers).filter(([, v]) => typeof v === "string")
|
|
17713
|
+
) : {},
|
|
17714
|
+
body: axiosConfig?.data,
|
|
17715
|
+
timeout: axiosConfig?.timeout,
|
|
17716
|
+
metadata: {}
|
|
17717
|
+
},
|
|
17718
|
+
url: `${axiosConfig?.baseURL ?? ""}${axiosConfig?.url ?? ""}`,
|
|
17719
|
+
metadata: {}
|
|
17720
|
+
};
|
|
17721
|
+
const result = await interceptor.intercept(error, context);
|
|
17722
|
+
if (result !== void 0) {
|
|
17723
|
+
return {
|
|
17724
|
+
data: result.data,
|
|
17725
|
+
status: result.status,
|
|
17726
|
+
statusText: result.statusText,
|
|
17727
|
+
headers: result.headers,
|
|
17728
|
+
config: axiosConfig
|
|
17729
|
+
};
|
|
17730
|
+
}
|
|
15547
17731
|
}
|
|
15548
|
-
|
|
15549
|
-
|
|
15550
|
-
|
|
15551
|
-
|
|
15552
|
-
|
|
17732
|
+
return void 0;
|
|
17733
|
+
}
|
|
17734
|
+
function shouldRetry(error, retry) {
|
|
17735
|
+
if (!retry) return false;
|
|
17736
|
+
if (!error.response) {
|
|
17737
|
+
return retry.retryOnNetworkError !== false;
|
|
15553
17738
|
}
|
|
15554
|
-
|
|
15555
|
-
|
|
15556
|
-
|
|
15557
|
-
|
|
15558
|
-
|
|
17739
|
+
const status = error.response.status;
|
|
17740
|
+
const retryableCodes = retry.retryableStatusCodes ?? DEFAULT_RETRY_CONFIG.retryableStatusCodes;
|
|
17741
|
+
return retryableCodes?.includes(status) ?? false;
|
|
17742
|
+
}
|
|
17743
|
+
function transformAxiosError(error) {
|
|
17744
|
+
if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") {
|
|
17745
|
+
const timeout = error.config?.timeout ?? 3e4;
|
|
17746
|
+
return new TimeoutError(timeout);
|
|
15559
17747
|
}
|
|
15560
|
-
|
|
15561
|
-
|
|
15562
|
-
*/
|
|
15563
|
-
async patch(path, body) {
|
|
15564
|
-
return this.request({ method: "PATCH", path, body });
|
|
17748
|
+
if (!error.response) {
|
|
17749
|
+
return new NetworkError(error.message, error);
|
|
15565
17750
|
}
|
|
15566
|
-
|
|
15567
|
-
|
|
15568
|
-
|
|
15569
|
-
|
|
15570
|
-
|
|
17751
|
+
const response = error.response;
|
|
17752
|
+
const retryAfter = parseRetryAfter(
|
|
17753
|
+
response.headers["retry-after"]
|
|
17754
|
+
);
|
|
17755
|
+
return ApiError.fromResponse(
|
|
17756
|
+
{
|
|
17757
|
+
code: response.data?.code,
|
|
17758
|
+
message: response.data?.message ?? error.message,
|
|
17759
|
+
details: response.data?.details,
|
|
17760
|
+
requestId: response.data?.requestId ?? error.config?.headers?.["X-Request-ID"],
|
|
17761
|
+
traceId: response.data?.traceId
|
|
17762
|
+
},
|
|
17763
|
+
response.status,
|
|
17764
|
+
retryAfter
|
|
17765
|
+
);
|
|
17766
|
+
}
|
|
17767
|
+
|
|
17768
|
+
// src/clients/middleware-factory.ts
|
|
17769
|
+
function generateUniqueRequestKey() {
|
|
17770
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
17771
|
+
}
|
|
17772
|
+
var REQUEST_KEY_HEADER = "X-Internal-Request-Key";
|
|
17773
|
+
var retryStateMap = /* @__PURE__ */ new Map();
|
|
17774
|
+
function getRequestKey(init) {
|
|
17775
|
+
const headers = init.headers;
|
|
17776
|
+
return headers?.[REQUEST_KEY_HEADER] ?? "";
|
|
17777
|
+
}
|
|
17778
|
+
function createMiddlewares(config = {}) {
|
|
17779
|
+
const middlewares = [];
|
|
17780
|
+
middlewares.push(createRequestMiddleware(config));
|
|
17781
|
+
if (config.logger) {
|
|
17782
|
+
middlewares.push(createLoggingMiddleware(config.logger, config.debug ?? false));
|
|
15571
17783
|
}
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
*/
|
|
15575
|
-
buildUrl(path, query) {
|
|
15576
|
-
const base = this.config.baseUrl.replace(/\/$/, "");
|
|
15577
|
-
const pathname = path.startsWith("/") ? path : `/${path}`;
|
|
15578
|
-
const url2 = new URL(`${base}${pathname}`);
|
|
15579
|
-
if (query) {
|
|
15580
|
-
for (const [key, value] of Object.entries(query)) {
|
|
15581
|
-
if (value !== void 0) {
|
|
15582
|
-
url2.searchParams.set(key, String(value));
|
|
15583
|
-
}
|
|
15584
|
-
}
|
|
15585
|
-
}
|
|
15586
|
-
return url2.toString();
|
|
17784
|
+
if (config.enableRetry !== false && config.retry) {
|
|
17785
|
+
middlewares.push(createRetryMiddleware(config));
|
|
15587
17786
|
}
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
async buildHeaders(options) {
|
|
15592
|
-
const headers = {
|
|
15593
|
-
"Content-Type": "application/json",
|
|
15594
|
-
Accept: "application/json",
|
|
15595
|
-
...this.config.headers,
|
|
15596
|
-
...options.headers
|
|
15597
|
-
};
|
|
15598
|
-
if (options.context?.requestId) {
|
|
15599
|
-
headers["X-Request-ID"] = options.context.requestId;
|
|
15600
|
-
} else {
|
|
15601
|
-
headers["X-Request-ID"] = generateRequestId();
|
|
17787
|
+
if (config.preMiddlewares?.length) {
|
|
17788
|
+
for (const preFn of config.preMiddlewares) {
|
|
17789
|
+
middlewares.push({ pre: preFn });
|
|
15602
17790
|
}
|
|
15603
|
-
|
|
15604
|
-
|
|
15605
|
-
|
|
15606
|
-
|
|
15607
|
-
await addAuthToHeaders(headers, this.config.auth);
|
|
17791
|
+
}
|
|
17792
|
+
if (config.postMiddlewares?.length) {
|
|
17793
|
+
for (const postFn of config.postMiddlewares) {
|
|
17794
|
+
middlewares.push({ post: postFn });
|
|
15608
17795
|
}
|
|
15609
|
-
return headers;
|
|
15610
17796
|
}
|
|
15611
|
-
|
|
15612
|
-
|
|
15613
|
-
|
|
15614
|
-
async executeWithRetry(url2, init, options) {
|
|
15615
|
-
const retry = this.config.retry || {
|
|
15616
|
-
maxRetries: 0,
|
|
15617
|
-
initialDelay: 1e3,
|
|
15618
|
-
maxDelay: 3e4,
|
|
15619
|
-
backoff: "exponential"
|
|
15620
|
-
};
|
|
15621
|
-
let lastError;
|
|
15622
|
-
let attempt = 0;
|
|
15623
|
-
while (attempt <= retry.maxRetries) {
|
|
15624
|
-
try {
|
|
15625
|
-
return await this.execute(url2, init, options);
|
|
15626
|
-
} catch (error) {
|
|
15627
|
-
lastError = error;
|
|
15628
|
-
const shouldRetry = this.shouldRetry(error, retry, attempt);
|
|
15629
|
-
if (!shouldRetry) {
|
|
15630
|
-
throw error;
|
|
15631
|
-
}
|
|
15632
|
-
const delay = calculateRetryDelay(retry, attempt);
|
|
15633
|
-
await sleep(delay);
|
|
15634
|
-
attempt++;
|
|
15635
|
-
}
|
|
17797
|
+
if (config.errorMiddlewares?.length) {
|
|
17798
|
+
for (const errorFn of config.errorMiddlewares) {
|
|
17799
|
+
middlewares.push({ onError: errorFn });
|
|
15636
17800
|
}
|
|
15637
|
-
throw lastError;
|
|
15638
17801
|
}
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
const
|
|
15648
|
-
|
|
15649
|
-
|
|
17802
|
+
return middlewares;
|
|
17803
|
+
}
|
|
17804
|
+
function createRequestMiddleware(config) {
|
|
17805
|
+
const authenticator = config.auth ? createAuthenticatorFromConfig(config.auth) : noAuthenticator;
|
|
17806
|
+
return {
|
|
17807
|
+
async pre(context) {
|
|
17808
|
+
const requestId = generateRequestId();
|
|
17809
|
+
const traceId = generateTraceId();
|
|
17810
|
+
const requestKey = generateUniqueRequestKey();
|
|
17811
|
+
retryStateMap.set(requestKey, {
|
|
17812
|
+
attempt: 0,
|
|
17813
|
+
startTime: Date.now(),
|
|
17814
|
+
requestId,
|
|
17815
|
+
traceId
|
|
15650
17816
|
});
|
|
15651
|
-
|
|
15652
|
-
const
|
|
15653
|
-
if (
|
|
15654
|
-
|
|
15655
|
-
|
|
15656
|
-
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
15660
|
-
details: data.details,
|
|
15661
|
-
traceId: data.traceId
|
|
15662
|
-
};
|
|
15663
|
-
const error = new ApiError(errorResponse, response.status);
|
|
15664
|
-
for (const interceptor of this.config.onError || []) {
|
|
15665
|
-
await interceptor(error, options);
|
|
17817
|
+
const existingHeaders = context.init.headers ?? {};
|
|
17818
|
+
const headers = {};
|
|
17819
|
+
if (existingHeaders instanceof Headers) {
|
|
17820
|
+
existingHeaders.forEach((value, key) => {
|
|
17821
|
+
headers[key] = value;
|
|
17822
|
+
});
|
|
17823
|
+
} else if (Array.isArray(existingHeaders)) {
|
|
17824
|
+
for (const [key, value] of existingHeaders) {
|
|
17825
|
+
headers[key] = value;
|
|
15666
17826
|
}
|
|
15667
|
-
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
17827
|
+
} else {
|
|
17828
|
+
Object.assign(headers, existingHeaders);
|
|
17829
|
+
}
|
|
17830
|
+
headers[REQUEST_KEY_HEADER] = requestKey;
|
|
17831
|
+
headers["X-Request-ID"] = requestId;
|
|
17832
|
+
headers["X-Trace-ID"] = traceId;
|
|
17833
|
+
if (config.headers) {
|
|
17834
|
+
Object.assign(headers, config.headers);
|
|
17835
|
+
}
|
|
17836
|
+
await authenticator.authenticate(headers);
|
|
17837
|
+
return {
|
|
17838
|
+
url: context.url,
|
|
17839
|
+
init: {
|
|
17840
|
+
...context.init,
|
|
17841
|
+
headers
|
|
17842
|
+
}
|
|
17843
|
+
};
|
|
17844
|
+
}
|
|
17845
|
+
};
|
|
17846
|
+
}
|
|
17847
|
+
function createLoggingMiddleware(logger, debugMode) {
|
|
17848
|
+
return {
|
|
17849
|
+
async pre(context) {
|
|
17850
|
+
if (!debugMode) return void 0;
|
|
17851
|
+
const method = context.init.method ?? "GET";
|
|
17852
|
+
const requestId = context.init.headers?.["X-Request-ID"] ?? "";
|
|
17853
|
+
logger.debug(`[Request] ${method} ${context.url}`, { requestId });
|
|
17854
|
+
return void 0;
|
|
17855
|
+
},
|
|
17856
|
+
async post(context) {
|
|
17857
|
+
const requestKey = getRequestKey(context.init);
|
|
17858
|
+
const state = retryStateMap.get(requestKey);
|
|
17859
|
+
const duration = state ? Date.now() - state.startTime : 0;
|
|
17860
|
+
if (requestKey) {
|
|
17861
|
+
retryStateMap.delete(requestKey);
|
|
17862
|
+
}
|
|
17863
|
+
if (!debugMode) return void 0;
|
|
17864
|
+
const method = context.init.method ?? "GET";
|
|
17865
|
+
const requestId = context.init.headers?.["X-Request-ID"] ?? "";
|
|
17866
|
+
logger.debug(`[Response] ${method} ${context.url} - ${context.response.status} (${duration}ms)`, {
|
|
17867
|
+
requestId,
|
|
17868
|
+
status: context.response.status,
|
|
17869
|
+
duration
|
|
17870
|
+
});
|
|
17871
|
+
return void 0;
|
|
17872
|
+
},
|
|
17873
|
+
async onError(context) {
|
|
17874
|
+
const method = context.init.method ?? "GET";
|
|
17875
|
+
const requestId = context.init.headers?.["X-Request-ID"] ?? "";
|
|
17876
|
+
const requestKey = getRequestKey(context.init);
|
|
17877
|
+
const state = retryStateMap.get(requestKey);
|
|
17878
|
+
const duration = state ? Date.now() - state.startTime : 0;
|
|
17879
|
+
const errorMessage = context.error instanceof Error ? context.error.message : String(context.error);
|
|
17880
|
+
logger.error(`[Error] ${method} ${context.url} - ${errorMessage} (${duration}ms)`, {
|
|
17881
|
+
requestId,
|
|
17882
|
+
error: errorMessage,
|
|
17883
|
+
duration
|
|
17884
|
+
});
|
|
17885
|
+
return void 0;
|
|
17886
|
+
}
|
|
17887
|
+
};
|
|
17888
|
+
}
|
|
17889
|
+
function createRetryMiddleware(config) {
|
|
17890
|
+
const retryConfig = config.retry;
|
|
17891
|
+
const maxRetries = retryConfig.maxRetries;
|
|
17892
|
+
const logger = config.logger;
|
|
17893
|
+
const debug = config.debug ?? false;
|
|
17894
|
+
return {
|
|
17895
|
+
async onError(context) {
|
|
17896
|
+
const requestKey = getRequestKey(context.init);
|
|
17897
|
+
const state = retryStateMap.get(requestKey);
|
|
17898
|
+
if (!state) {
|
|
17899
|
+
return void 0;
|
|
17900
|
+
}
|
|
17901
|
+
if (!shouldRetry2(context.error, state.attempt, maxRetries, retryConfig)) {
|
|
17902
|
+
retryStateMap.delete(requestKey);
|
|
17903
|
+
return void 0;
|
|
17904
|
+
}
|
|
17905
|
+
state.attempt++;
|
|
17906
|
+
const delay = calculateRetryDelay2(state.attempt, retryConfig);
|
|
17907
|
+
if (retryConfig.onRetry) {
|
|
17908
|
+
const method = context.init.method ?? "GET";
|
|
17909
|
+
retryConfig.onRetry({
|
|
17910
|
+
attempt: state.attempt,
|
|
17911
|
+
maxRetries,
|
|
17912
|
+
delayMs: delay,
|
|
17913
|
+
error: context.error instanceof Error ? context.error : new Error(String(context.error)),
|
|
17914
|
+
url: context.url,
|
|
17915
|
+
method
|
|
17916
|
+
});
|
|
15672
17917
|
}
|
|
15673
|
-
|
|
15674
|
-
|
|
15675
|
-
|
|
15676
|
-
if (error instanceof ApiError) {
|
|
15677
|
-
throw error;
|
|
17918
|
+
if (debug && logger) {
|
|
17919
|
+
const method = context.init.method ?? "GET";
|
|
17920
|
+
logger.debug(`[Retry] ${method} ${context.url} - attempt ${state.attempt}/${maxRetries}, delay ${delay}ms`);
|
|
15678
17921
|
}
|
|
15679
|
-
|
|
15680
|
-
|
|
17922
|
+
await sleep(delay);
|
|
17923
|
+
try {
|
|
17924
|
+
const response = await context.fetch(context.url, context.init);
|
|
17925
|
+
if (!response.ok && shouldRetryStatus(response.status, state.attempt, maxRetries, retryConfig)) {
|
|
17926
|
+
state.attempt++;
|
|
17927
|
+
const nextDelay = calculateRetryDelay2(state.attempt, retryConfig);
|
|
17928
|
+
if (debug && logger) {
|
|
17929
|
+
const method = context.init.method ?? "GET";
|
|
17930
|
+
logger.debug(`[Retry] ${method} ${context.url} - status ${response.status}, attempt ${state.attempt}/${maxRetries}, delay ${nextDelay}ms`);
|
|
17931
|
+
}
|
|
17932
|
+
await sleep(nextDelay);
|
|
17933
|
+
return context.fetch(context.url, context.init);
|
|
17934
|
+
}
|
|
17935
|
+
retryStateMap.delete(requestKey);
|
|
17936
|
+
return response;
|
|
17937
|
+
} catch (retryError) {
|
|
17938
|
+
if (state.attempt < maxRetries) {
|
|
17939
|
+
throw retryError;
|
|
17940
|
+
}
|
|
17941
|
+
retryStateMap.delete(requestKey);
|
|
17942
|
+
throw retryError;
|
|
15681
17943
|
}
|
|
15682
|
-
throw new NetworkError(error.message, error);
|
|
15683
17944
|
}
|
|
17945
|
+
};
|
|
17946
|
+
}
|
|
17947
|
+
function shouldRetry2(error, attempt, maxRetries, retryConfig) {
|
|
17948
|
+
if (attempt >= maxRetries) return false;
|
|
17949
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
17950
|
+
return false;
|
|
15684
17951
|
}
|
|
15685
|
-
|
|
15686
|
-
|
|
15687
|
-
|
|
15688
|
-
|
|
15689
|
-
|
|
15690
|
-
|
|
15691
|
-
|
|
15692
|
-
|
|
15693
|
-
|
|
17952
|
+
if (error instanceof TypeError || error instanceof NetworkError) {
|
|
17953
|
+
return retryConfig.retryOnNetworkError !== false;
|
|
17954
|
+
}
|
|
17955
|
+
if (error instanceof TimeoutError) {
|
|
17956
|
+
return retryConfig.retryOnTimeout !== false;
|
|
17957
|
+
}
|
|
17958
|
+
return true;
|
|
17959
|
+
}
|
|
17960
|
+
function shouldRetryStatus(status, attempt, maxRetries, retryConfig) {
|
|
17961
|
+
if (attempt >= maxRetries) return false;
|
|
17962
|
+
const retryableCodes = retryConfig.retryableStatusCodes ?? [429, 500, 502, 503, 504];
|
|
17963
|
+
return retryableCodes.includes(status);
|
|
17964
|
+
}
|
|
17965
|
+
function calculateRetryDelay2(attempt, retryConfig) {
|
|
17966
|
+
const {
|
|
17967
|
+
initialDelayMs = 1e3,
|
|
17968
|
+
maxDelayMs = 3e4,
|
|
17969
|
+
backoffStrategy = "exponential",
|
|
17970
|
+
jitterFactor = 0.1
|
|
17971
|
+
} = retryConfig;
|
|
17972
|
+
let delay;
|
|
17973
|
+
switch (backoffStrategy) {
|
|
17974
|
+
case "fixed":
|
|
17975
|
+
delay = initialDelayMs;
|
|
17976
|
+
break;
|
|
17977
|
+
case "linear":
|
|
17978
|
+
delay = initialDelayMs * attempt;
|
|
17979
|
+
break;
|
|
17980
|
+
case "exponential":
|
|
17981
|
+
default:
|
|
17982
|
+
delay = initialDelayMs * Math.pow(2, attempt - 1);
|
|
17983
|
+
break;
|
|
17984
|
+
}
|
|
17985
|
+
delay = Math.min(delay, maxDelayMs);
|
|
17986
|
+
if (jitterFactor > 0) {
|
|
17987
|
+
const jitter = delay * jitterFactor * (Math.random() * 2 - 1);
|
|
17988
|
+
delay = Math.max(0, delay + jitter);
|
|
17989
|
+
}
|
|
17990
|
+
return Math.round(delay);
|
|
17991
|
+
}
|
|
17992
|
+
function createAuthMiddleware(authConfig) {
|
|
17993
|
+
const authenticator = createAuthenticatorFromConfig(authConfig);
|
|
17994
|
+
return {
|
|
17995
|
+
async pre(context) {
|
|
17996
|
+
const headers = {};
|
|
17997
|
+
const existingHeaders = context.init.headers ?? {};
|
|
17998
|
+
if (existingHeaders instanceof Headers) {
|
|
17999
|
+
existingHeaders.forEach((value, key) => {
|
|
18000
|
+
headers[key] = value;
|
|
18001
|
+
});
|
|
18002
|
+
} else if (Array.isArray(existingHeaders)) {
|
|
18003
|
+
for (const [key, value] of existingHeaders) {
|
|
18004
|
+
headers[key] = value;
|
|
18005
|
+
}
|
|
18006
|
+
} else {
|
|
18007
|
+
Object.assign(headers, existingHeaders);
|
|
18008
|
+
}
|
|
18009
|
+
await authenticator.authenticate(headers);
|
|
18010
|
+
return {
|
|
18011
|
+
url: context.url,
|
|
18012
|
+
init: {
|
|
18013
|
+
...context.init,
|
|
18014
|
+
headers
|
|
18015
|
+
}
|
|
18016
|
+
};
|
|
15694
18017
|
}
|
|
15695
|
-
|
|
15696
|
-
|
|
15697
|
-
|
|
18018
|
+
};
|
|
18019
|
+
}
|
|
18020
|
+
function createTrackingMiddleware() {
|
|
18021
|
+
return {
|
|
18022
|
+
async pre(context) {
|
|
18023
|
+
const requestId = generateRequestId();
|
|
18024
|
+
const traceId = generateTraceId();
|
|
18025
|
+
const headers = {};
|
|
18026
|
+
const existingHeaders = context.init.headers ?? {};
|
|
18027
|
+
if (existingHeaders instanceof Headers) {
|
|
18028
|
+
existingHeaders.forEach((value, key) => {
|
|
18029
|
+
headers[key] = value;
|
|
18030
|
+
});
|
|
18031
|
+
} else if (Array.isArray(existingHeaders)) {
|
|
18032
|
+
for (const [key, value] of existingHeaders) {
|
|
18033
|
+
headers[key] = value;
|
|
18034
|
+
}
|
|
18035
|
+
} else {
|
|
18036
|
+
Object.assign(headers, existingHeaders);
|
|
18037
|
+
}
|
|
18038
|
+
headers["X-Request-ID"] = requestId;
|
|
18039
|
+
headers["X-Trace-ID"] = traceId;
|
|
18040
|
+
return {
|
|
18041
|
+
url: context.url,
|
|
18042
|
+
init: {
|
|
18043
|
+
...context.init,
|
|
18044
|
+
headers
|
|
18045
|
+
}
|
|
18046
|
+
};
|
|
15698
18047
|
}
|
|
15699
|
-
|
|
15700
|
-
}
|
|
15701
|
-
};
|
|
15702
|
-
function createClient(config) {
|
|
15703
|
-
return new HttpClient(config);
|
|
18048
|
+
};
|
|
15704
18049
|
}
|
|
15705
18050
|
|
|
15706
18051
|
// src/index.ts
|
|
15707
18052
|
var VERSION3 = "2.0.0";
|
|
18053
|
+
var SDK_NAME = "@djvlc/openapi-client-core";
|
|
18054
|
+
function getSdkInfo() {
|
|
18055
|
+
return { name: SDK_NAME, version: VERSION3 };
|
|
18056
|
+
}
|
|
15708
18057
|
// Annotate the CommonJS export names for ESM import in node:
|
|
15709
18058
|
0 && (module.exports = {
|
|
18059
|
+
AbortError,
|
|
15710
18060
|
ApiError,
|
|
15711
|
-
|
|
18061
|
+
ApiKeyAuthenticator,
|
|
18062
|
+
AuthInterceptor,
|
|
18063
|
+
BaseClient,
|
|
18064
|
+
BaseClientError,
|
|
18065
|
+
BasicAuthenticator,
|
|
18066
|
+
BearerAuthenticator,
|
|
18067
|
+
BufferLogger,
|
|
18068
|
+
ConsoleLogger,
|
|
18069
|
+
CustomAuthenticator,
|
|
18070
|
+
DEFAULT_HEADERS,
|
|
18071
|
+
DEFAULT_RETRY_CONFIG,
|
|
18072
|
+
DefaultMetricsCollector,
|
|
18073
|
+
ErrorTransformInterceptor,
|
|
18074
|
+
FetchClient,
|
|
18075
|
+
InterceptorManager,
|
|
18076
|
+
LOG_LEVEL_PRIORITY,
|
|
18077
|
+
LoggingInterceptor,
|
|
18078
|
+
MetricsErrorInterceptor,
|
|
18079
|
+
MetricsRequestInterceptor,
|
|
18080
|
+
MetricsResponseInterceptor,
|
|
15712
18081
|
NetworkError,
|
|
18082
|
+
NoAuthenticator,
|
|
18083
|
+
ReportingInterceptor,
|
|
18084
|
+
RequestDeduper,
|
|
18085
|
+
RequestIdInterceptor,
|
|
18086
|
+
RetryInterceptor,
|
|
18087
|
+
SDK_NAME,
|
|
18088
|
+
SilentLogger,
|
|
15713
18089
|
TimeoutError,
|
|
18090
|
+
TimeoutInterceptor,
|
|
18091
|
+
TokenRefreshInterceptor,
|
|
18092
|
+
TraceInterceptor,
|
|
15714
18093
|
VERSION,
|
|
18094
|
+
buildUrl,
|
|
15715
18095
|
calculateRetryDelay,
|
|
18096
|
+
createApiKeyAuthenticator,
|
|
18097
|
+
createAuthInterceptor,
|
|
18098
|
+
createAuthMiddleware,
|
|
18099
|
+
createAuthenticatorFromConfig,
|
|
15716
18100
|
createAxiosInstance,
|
|
15717
|
-
|
|
18101
|
+
createBasicAuthenticator,
|
|
18102
|
+
createBearerAuthenticator,
|
|
18103
|
+
createBufferLogger,
|
|
18104
|
+
createConsoleLogger,
|
|
18105
|
+
createCustomAuthenticator,
|
|
18106
|
+
createErrorTransformInterceptor,
|
|
18107
|
+
createFetchClient,
|
|
18108
|
+
createInterceptorManager,
|
|
18109
|
+
createLoggingInterceptor,
|
|
18110
|
+
createMetricsCollector,
|
|
18111
|
+
createMetricsInterceptors,
|
|
18112
|
+
createMiddlewares,
|
|
18113
|
+
createNoAuthenticator,
|
|
18114
|
+
createReportingInterceptor,
|
|
18115
|
+
createRequestDeduper,
|
|
18116
|
+
createRequestIdInterceptor,
|
|
18117
|
+
createRetryInterceptor,
|
|
18118
|
+
createSilentLogger,
|
|
18119
|
+
createTimeoutInterceptor,
|
|
18120
|
+
createTokenRefreshInterceptor,
|
|
18121
|
+
createTraceInterceptor,
|
|
18122
|
+
createTrackingMiddleware,
|
|
18123
|
+
extractPath,
|
|
18124
|
+
extractResponseHeaders,
|
|
15718
18125
|
generateRequestId,
|
|
15719
|
-
|
|
18126
|
+
generateTraceId,
|
|
18127
|
+
getErrorType,
|
|
18128
|
+
getHeader,
|
|
18129
|
+
getRecommendedRetryDelay,
|
|
18130
|
+
getRetryDelay,
|
|
18131
|
+
getSdkInfo,
|
|
18132
|
+
hasContentType,
|
|
18133
|
+
isClientError,
|
|
18134
|
+
isRetryableError,
|
|
18135
|
+
isValidRequestId,
|
|
18136
|
+
isValidTraceId,
|
|
18137
|
+
joinPaths,
|
|
18138
|
+
mergeHeaders,
|
|
18139
|
+
noAuthenticator,
|
|
18140
|
+
parseQueryString,
|
|
18141
|
+
parseRetryAfter,
|
|
18142
|
+
removeHeader,
|
|
18143
|
+
setHeader,
|
|
18144
|
+
silentLogger,
|
|
18145
|
+
sleep,
|
|
18146
|
+
sleepWithAbort
|
|
15720
18147
|
});
|
|
15721
18148
|
/*! Bundled license information:
|
|
15722
18149
|
|
|
@@ -15736,4 +18163,3 @@ mime-types/index.js:
|
|
|
15736
18163
|
* MIT Licensed
|
|
15737
18164
|
*)
|
|
15738
18165
|
*/
|
|
15739
|
-
//# sourceMappingURL=index.js.map
|