@djvlc/openapi-client-core 1.2.0 → 1.2.1

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