@be-link/http 1.0.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,845 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var axios = require('axios');
6
+ var CryptoJS = require('crypto-js');
7
+
8
+ /**
9
+ * 时间同步服务
10
+ *
11
+ * 用于同步客户端和服务器时间,解决客户端时间不准确的问题
12
+ * 主要用于 Token 加密时使用服务器时间,防止重放攻击
13
+ *
14
+ * 使用 performance.now() 计算时间流逝,防止用户修改系统时间导致的问题
15
+ *
16
+ * @module TimeSyncService
17
+ */
18
+ /**
19
+ * localStorage 存储键名常量
20
+ */
21
+ const STORAGE_KEYS = {
22
+ /** 服务器时间存储键 */
23
+ SERVER_TIME: 'belink_server_time',
24
+ /** 客户端时间存储键(同步时的客户端时间,用于判断是否需要重新同步) */
25
+ CLIENT_TIME: 'belink_client_time',
26
+ };
27
+ /**
28
+ * 检测是否为浏览器环境
29
+ */
30
+ function isBrowser() {
31
+ return typeof window !== 'undefined' && typeof localStorage !== 'undefined';
32
+ }
33
+ /**
34
+ * 获取带前缀的存储 key
35
+ */
36
+ function getStorageKey(prefix, key) {
37
+ return prefix ? `${prefix}_${key}` : key;
38
+ }
39
+ /**
40
+ * 获取 performance.now(),兼容非浏览器环境
41
+ */
42
+ function getPerformanceNow() {
43
+ if (typeof performance !== 'undefined' && performance.now) {
44
+ return performance.now();
45
+ }
46
+ return Date.now();
47
+ }
48
+ /**
49
+ * 时间同步服务
50
+ *
51
+ * 负责同步客户端和服务器时间,提供以下功能:
52
+ * - 自动检测是否需要同步
53
+ * - 获取服务器时间
54
+ * - 使用 performance.now() 计算时间流逝,防止用户修改系统时间
55
+ * - 获取调整后的当前时间
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const timeSync = new TimeSyncService({
60
+ * syncUrl: 'https://api.example.com/api/time',
61
+ * syncGapTime: 50000,
62
+ * });
63
+ *
64
+ * // 确保时间同步
65
+ * await timeSync.ensureSync();
66
+ *
67
+ * // 获取调整后的时间
68
+ * const adjustedTime = timeSync.getAdjustedTime();
69
+ * ```
70
+ */
71
+ class TimeSyncService {
72
+ /**
73
+ * 创建时间同步服务实例
74
+ */
75
+ constructor(config) {
76
+ /**
77
+ * 同步时的服务器时间(内存存储)
78
+ * 用于计算调整后的时间
79
+ */
80
+ this.syncServerTime = null;
81
+ /**
82
+ * 同步时的 performance.now() 值(内存存储)
83
+ * 使用 performance.now() 而不是 Date.now(),防止用户修改系统时间
84
+ */
85
+ this.syncPerformanceTime = null;
86
+ /**
87
+ * 正在进行的同步 Promise(用于防止并发请求时重复同步)
88
+ * 当有同步请求正在进行时,其他请求会等待这个 Promise 完成
89
+ */
90
+ this.syncPromise = null;
91
+ this.config = {
92
+ enabled: true,
93
+ syncGapTime: 50 * 1000, // 默认 50 秒
94
+ ...config,
95
+ };
96
+ }
97
+ /**
98
+ * 确保时间已同步
99
+ *
100
+ * 检查是否需要同步:
101
+ * 1. 内存中没有同步数据(页面刷新后)
102
+ * 2. 距离上次同步的时间超过了 syncGapTime 阈值
103
+ *
104
+ * 并发控制:
105
+ * - 如果已有同步请求正在进行,直接等待该请求完成
106
+ * - 避免多个并发请求同时触发多次时间同步
107
+ */
108
+ async ensureSync() {
109
+ if (!this.config.enabled || !isBrowser()) {
110
+ return;
111
+ }
112
+ // 如果已有同步请求正在进行,等待它完成
113
+ if (this.syncPromise) {
114
+ await this.syncPromise;
115
+ return;
116
+ }
117
+ // 如果内存中没有同步数据,需要同步
118
+ if (this.syncServerTime === null || this.syncPerformanceTime === null) {
119
+ await this.sync();
120
+ return;
121
+ }
122
+ // 使用 performance.now() 计算时间流逝,检查是否需要重新同步
123
+ const elapsed = getPerformanceNow() - this.syncPerformanceTime;
124
+ if (elapsed > (this.config.syncGapTime || 50 * 1000)) {
125
+ await this.sync();
126
+ }
127
+ }
128
+ /**
129
+ * 执行时间同步
130
+ *
131
+ * 向服务器发送请求获取服务器时间
132
+ * 同时记录 performance.now() 用于后续计算时间流逝
133
+ *
134
+ * 并发控制:
135
+ * - 使用 syncPromise 作为锁,确保同一时间只有一个同步请求
136
+ * - 同步完成后清除锁,允许后续同步请求
137
+ */
138
+ async sync() {
139
+ if (!isBrowser() || !this.config.syncUrl) {
140
+ return;
141
+ }
142
+ // 如果已有同步请求正在进行,等待它完成
143
+ if (this.syncPromise) {
144
+ await this.syncPromise;
145
+ return;
146
+ }
147
+ // 创建同步 Promise 并保存引用(作为锁)
148
+ this.syncPromise = this.doSync();
149
+ try {
150
+ await this.syncPromise;
151
+ }
152
+ finally {
153
+ // 同步完成后清除锁
154
+ this.syncPromise = null;
155
+ }
156
+ }
157
+ /**
158
+ * 实际执行时间同步的内部方法
159
+ */
160
+ async doSync() {
161
+ try {
162
+ // 记录请求发起时的 performance.now()
163
+ const requestStartTime = getPerformanceNow();
164
+ const response = await fetch(this.config.syncUrl, { method: 'POST' });
165
+ if (!response.ok) {
166
+ console.error('[TimeSyncService] Sync failed with status:', response.status);
167
+ this.clear();
168
+ return;
169
+ }
170
+ const res = await response.json();
171
+ const serverTime = res.data.timestamp;
172
+ if (!serverTime) {
173
+ this.clear();
174
+ return;
175
+ }
176
+ // 计算网络延迟的一半,补偿到服务器时间
177
+ const requestEndTime = getPerformanceNow();
178
+ const networkDelay = (requestEndTime - requestStartTime) / 2;
179
+ // 存储到内存(使用 performance.now())
180
+ this.syncServerTime = serverTime + networkDelay;
181
+ this.syncPerformanceTime = requestEndTime;
182
+ // 同时存储到 localStorage(用于调试和日志)
183
+ const serverTimeKey = getStorageKey(this.config.storagePrefix, STORAGE_KEYS.SERVER_TIME);
184
+ const clientTimeKey = getStorageKey(this.config.storagePrefix, STORAGE_KEYS.CLIENT_TIME);
185
+ localStorage.setItem(serverTimeKey, String(this.syncServerTime));
186
+ localStorage.setItem(clientTimeKey, String(Date.now()));
187
+ }
188
+ catch (error) {
189
+ console.error('[TimeSyncService] Failed to sync time:', error);
190
+ this.clear();
191
+ }
192
+ }
193
+ /**
194
+ * 获取服务器时间
195
+ *
196
+ * 返回同步时获取的服务器时间戳
197
+ */
198
+ getServerTime() {
199
+ return this.syncServerTime;
200
+ }
201
+ /**
202
+ * 获取时间差
203
+ *
204
+ * 返回服务器时间与客户端时间的差值
205
+ * 注意:此方法仅用于向后兼容,实际计算使用 getAdjustedTime()
206
+ */
207
+ getTimeDiff() {
208
+ if (this.syncServerTime === null) {
209
+ return null;
210
+ }
211
+ return this.syncServerTime - Date.now();
212
+ }
213
+ /**
214
+ * 获取调整后的当前时间
215
+ *
216
+ * 使用 performance.now() 计算时间流逝,得到准确的服务器时间
217
+ * 计算公式:同步时的服务器时间 + (当前 performance.now() - 同步时的 performance.now())
218
+ *
219
+ * 优势:即使用户修改系统时间,performance.now() 的流逝是正确的
220
+ *
221
+ * @returns 调整后的时间戳(毫秒),未同步时返回当前客户端时间
222
+ */
223
+ getAdjustedTime() {
224
+ if (this.syncServerTime === null || this.syncPerformanceTime === null) {
225
+ return Date.now();
226
+ }
227
+ // 使用 performance.now() 计算时间流逝
228
+ const elapsed = getPerformanceNow() - this.syncPerformanceTime;
229
+ return Math.round(this.syncServerTime + elapsed);
230
+ }
231
+ /**
232
+ * 清除同步数据
233
+ */
234
+ clear() {
235
+ this.syncServerTime = null;
236
+ this.syncPerformanceTime = null;
237
+ if (isBrowser()) {
238
+ const serverTimeKey = getStorageKey(this.config.storagePrefix, STORAGE_KEYS.SERVER_TIME);
239
+ const clientTimeKey = getStorageKey(this.config.storagePrefix, STORAGE_KEYS.CLIENT_TIME);
240
+ localStorage.removeItem(serverTimeKey);
241
+ localStorage.removeItem(clientTimeKey);
242
+ }
243
+ }
244
+ /**
245
+ * 更新配置
246
+ */
247
+ updateConfig(config) {
248
+ this.config = { ...this.config, ...config };
249
+ }
250
+ }
251
+
252
+ /**
253
+ * 加密服务
254
+ *
255
+ * 提供 Token 加密/解密功能,使用 AES-CBC 模式
256
+ * 加密后的 Token 包含时间戳,用于防止重放攻击
257
+ *
258
+ * @module EncryptionService
259
+ */
260
+ /**
261
+ * 加密服务
262
+ *
263
+ * 负责 Token 的 AES 加密和解密,提供以下功能:
264
+ * - Token 加密(带时间戳)
265
+ * - Token 解密
266
+ * - 与时间同步服务集成
267
+ *
268
+ * 加密格式说明:
269
+ * - 原始数据格式: `${token}|+|${timestamp}`
270
+ * - 加密算法: AES-128-CBC
271
+ * - 输出格式: Base64 编码
272
+ *
273
+ * @example
274
+ * ```ts
275
+ * const encryption = new EncryptionService({
276
+ * key: '1234567890123456', // 16 位
277
+ * iv: '1234567890123456', // 16 位
278
+ * });
279
+ *
280
+ * // 加密 Token
281
+ * const encryptedToken = encryption.encryptToken('my-token');
282
+ *
283
+ * // 解密 Token
284
+ * const { token, timestamp } = encryption.decryptToken(encryptedToken);
285
+ * ```
286
+ */
287
+ class EncryptionService {
288
+ /**
289
+ * 创建加密服务实例
290
+ *
291
+ * @param config - 加密配置
292
+ * @param timeSyncService - 时间同步服务实例(可选),用于获取服务器校正后的时间
293
+ */
294
+ constructor(config, timeSyncService) {
295
+ this.config = {
296
+ enabled: true,
297
+ ...config,
298
+ };
299
+ this.timeSyncService = timeSyncService;
300
+ }
301
+ /**
302
+ * 加密 Token
303
+ *
304
+ * 将 Token 与时间戳组合后进行 AES 加密
305
+ * 加密后的数据用于请求头的 Authorization 字段
306
+ *
307
+ * 加密流程:
308
+ * 1. 组合原始数据: `${token}|+|${timestamp}`
309
+ * 2. 使用 AES-128-CBC 加密
310
+ * 3. 输出 Base64 编码的密文
311
+ *
312
+ * @param token - 原始 Token 字符串
313
+ * @param timestamp - 可选的时间戳(毫秒),默认使用调整后的当前时间
314
+ * @returns 加密后的 Token 字符串(Base64 编码)
315
+ *
316
+ * @example
317
+ * ```ts
318
+ * // 使用默认时间戳
319
+ * const encrypted = encryption.encryptToken('my-token');
320
+ *
321
+ * // 使用指定时间戳
322
+ * const encrypted = encryption.encryptToken('my-token', Date.now());
323
+ * ```
324
+ */
325
+ encryptToken(token, timestamp) {
326
+ // 如果 Token 为空或加密功能禁用,直接返回原始 Token
327
+ if (!token || !this.config.enabled) {
328
+ return token || '';
329
+ }
330
+ // 获取时间戳(优先使用传入的时间戳,否则使用调整后的时间)
331
+ const time = timestamp ?? this.getAdjustedTime();
332
+ // 组合原始数据:Token + 分隔符 + 时间戳
333
+ const data = `${token}|+|${time}`;
334
+ // 使用 AES-CBC 模式加密
335
+ return CryptoJS.AES.encrypt(data, CryptoJS.enc.Utf8.parse(this.config.key), {
336
+ iv: CryptoJS.enc.Utf8.parse(this.config.iv),
337
+ mode: CryptoJS.mode.CBC,
338
+ }).toString();
339
+ }
340
+ /**
341
+ * 解密 Token
342
+ *
343
+ * 将加密后的 Token 解密,提取原始 Token 和时间戳
344
+ *
345
+ * @param encryptedToken - 加密后的 Token 字符串
346
+ * @returns 包含原始 Token 和时间戳的对象
347
+ *
348
+ * @example
349
+ * ```ts
350
+ * const { token, timestamp } = encryption.decryptToken(encryptedToken);
351
+ * console.log(token); // 'my-token'
352
+ * console.log(timestamp); // 1703577600000
353
+ * ```
354
+ */
355
+ decryptToken(encryptedToken) {
356
+ // 使用 AES-CBC 模式解密
357
+ const decrypted = CryptoJS.AES.decrypt(encryptedToken, CryptoJS.enc.Utf8.parse(this.config.key), {
358
+ iv: CryptoJS.enc.Utf8.parse(this.config.iv),
359
+ mode: CryptoJS.mode.CBC,
360
+ }).toString(CryptoJS.enc.Utf8);
361
+ // 分割原始数据,提取 Token 和时间戳
362
+ const parts = decrypted.split('|+|');
363
+ return {
364
+ token: parts[0],
365
+ timestamp: parseInt(parts[1], 10),
366
+ };
367
+ }
368
+ /**
369
+ * 获取调整后的时间戳
370
+ *
371
+ * 优先使用时间同步服务获取校正后的时间
372
+ * 如果没有时间同步服务,则使用当前客户端时间
373
+ *
374
+ * @returns 时间戳(毫秒)
375
+ */
376
+ getAdjustedTime() {
377
+ if (this.timeSyncService) {
378
+ return this.timeSyncService.getAdjustedTime();
379
+ }
380
+ return Date.now();
381
+ }
382
+ /**
383
+ * 更新配置
384
+ *
385
+ * 动态更新加密服务的配置
386
+ *
387
+ * @param config - 要更新的配置项
388
+ */
389
+ updateConfig(config) {
390
+ this.config = { ...this.config, ...config };
391
+ }
392
+ /**
393
+ * 设置时间同步服务
394
+ *
395
+ * 用于后期绑定时间同步服务实例
396
+ *
397
+ * @param timeSyncService - 时间同步服务实例
398
+ */
399
+ setTimeSyncService(timeSyncService) {
400
+ this.timeSyncService = timeSyncService;
401
+ }
402
+ }
403
+
404
+ /**
405
+ * 请求拦截器
406
+ *
407
+ * 在请求发出前对请求进行处理,包括:
408
+ * - 时间同步
409
+ * - 设置默认请求头
410
+ * - 添加用户 ID
411
+ * - Token 加密和添加
412
+ * - 调用自定义拦截器
413
+ *
414
+ * @module interceptors/request
415
+ */
416
+ /**
417
+ * 设置请求拦截器
418
+ *
419
+ * 为 Axios 实例添加请求拦截器,处理请求前的通用逻辑
420
+ *
421
+ * 拦截器执行顺序:
422
+ * 1. 确保时间同步(如果配置了 TimeSyncService)
423
+ * 2. 设置默认 Content-Type
424
+ * 3. 添加用户 ID 到请求头
425
+ * 4. 加密并添加 Token 到请求头
426
+ * 5. 调用自定义请求拦截器
427
+ *
428
+ * @param instance - Axios 实例
429
+ * @param options - 请求配置选项
430
+ * @param timeSyncService - 时间同步服务实例(可选)
431
+ * @param encryptionService - 加密服务实例(可选)
432
+ *
433
+ * @example
434
+ * ```ts
435
+ * const instance = axios.create({ baseURL: 'https://api.example.com' });
436
+ *
437
+ * setRequestInterceptor(
438
+ * instance,
439
+ * {
440
+ * baseURL: 'https://api.example.com',
441
+ * getToken: () => localStorage.getItem('token'),
442
+ * getUserId: () => localStorage.getItem('userId'),
443
+ * },
444
+ * timeSyncService,
445
+ * encryptionService,
446
+ * );
447
+ * ```
448
+ */
449
+ function setRequestInterceptor(instance, options, timeSyncService, encryptionService) {
450
+ // 获取 header 名称,使用默认值
451
+ const tokenHeaderName = options.tokenHeaderName || 'X-BeLink-Token';
452
+ const userIdHeaderName = options.userIdHeaderName || 'X-BeLink-UserId';
453
+ instance.interceptors.request.use(async (config) => {
454
+ // 步骤 1: 确保时间同步
455
+ // 在请求发出前检查并同步服务器时间,确保后续 Token 加密使用正确的时间戳
456
+ if (timeSyncService) {
457
+ await timeSyncService.ensureSync();
458
+ }
459
+ // 步骤 2: 设置默认 Content-Type
460
+ // 如果请求未指定 Content-Type,默认使用 JSON 格式
461
+ if (!config.headers['Content-Type']) {
462
+ config.headers['Content-Type'] = 'application/json';
463
+ }
464
+ // 步骤 3: 添加用户 ID
465
+ // 调用用户配置的 getUserId 函数获取用户 ID,添加到请求头
466
+ if (options.getUserId) {
467
+ const userId = options.getUserId();
468
+ if (userId) {
469
+ config.headers[userIdHeaderName] = userId;
470
+ }
471
+ }
472
+ // 步骤 4: 添加 Token
473
+ // 根据是否配置了加密服务,决定是否对 Token 进行加密
474
+ if (options.getToken && encryptionService) {
475
+ // 有加密服务:获取 Token 并加密后添加到请求头
476
+ const token = options.getToken();
477
+ if (token) {
478
+ config.headers[tokenHeaderName] = encryptionService.encryptToken(token);
479
+ }
480
+ }
481
+ else if (options.getToken) {
482
+ // 无加密服务:直接使用原始 Token
483
+ const token = options.getToken();
484
+ if (token) {
485
+ config.headers[tokenHeaderName] = token;
486
+ }
487
+ }
488
+ // 步骤 5: 调用自定义请求拦截器
489
+ // 允许用户进一步自定义请求配置
490
+ if (options.onRequest) {
491
+ return options.onRequest(config);
492
+ }
493
+ return config;
494
+ });
495
+ }
496
+
497
+ /**
498
+ * 响应拦截器
499
+ *
500
+ * 在收到响应后对响应进行处理,包括:
501
+ * - 提取响应数据
502
+ * - 调用自定义响应拦截器
503
+ * - 统一错误处理
504
+ *
505
+ * @module interceptors/response
506
+ */
507
+ /**
508
+ * 设置响应拦截器
509
+ *
510
+ * 为 Axios 实例添加响应拦截器,处理响应后的通用逻辑
511
+ *
512
+ * 成功响应处理:
513
+ * 1. 提取响应数据(response.data)
514
+ * 2. 调用自定义响应拦截器(如果配置)
515
+ * 3. 返回处理后的数据
516
+ *
517
+ * 错误响应处理:
518
+ * 1. 调用自定义错误处理(如果配置)
519
+ * 2. 检查业务错误(success === false)
520
+ * 3. 抛出错误
521
+ *
522
+ * @param instance - Axios 实例
523
+ * @param options - 请求配置选项
524
+ *
525
+ * @example
526
+ * ```ts
527
+ * const instance = axios.create({ baseURL: 'https://api.example.com' });
528
+ *
529
+ * setResponseInterceptor(instance, {
530
+ * baseURL: 'https://api.example.com',
531
+ * onResponse: (data) => {
532
+ * console.log('响应数据:', data);
533
+ * return data;
534
+ * },
535
+ * onError: (error) => {
536
+ * if (error.response?.status === 401) {
537
+ * // 处理未授权错误
538
+ * window.location.href = '/login';
539
+ * }
540
+ * throw error;
541
+ * },
542
+ * });
543
+ * ```
544
+ */
545
+ function setResponseInterceptor(instance, options) {
546
+ instance.interceptors.response.use(
547
+ // 成功响应处理
548
+ (response) => {
549
+ // 提取响应数据(Axios 的响应数据在 response.data 中)
550
+ const data = response.data;
551
+ // 调用自定义响应拦截器
552
+ // 允许用户对响应数据进行进一步处理
553
+ if (options.onResponse) {
554
+ return options.onResponse(data);
555
+ }
556
+ return data;
557
+ },
558
+ // 错误响应处理
559
+ (error) => {
560
+ // 优先调用自定义错误处理
561
+ // 如果用户配置了 onError,由用户自行处理错误
562
+ if (options.onError) {
563
+ return options.onError(error);
564
+ }
565
+ // 获取响应数据(如果有)
566
+ const responseData = error?.response?.data;
567
+ // 检查业务错误
568
+ // 业务错误:HTTP 状态码可能是 200,但响应体中 success === false
569
+ if (responseData && typeof responseData === 'object' && !responseData.success) {
570
+ // 创建业务错误对象
571
+ const bizError = new Error(responseData.message || '接口请求失败');
572
+ // 附加原始响应信息,方便调试
573
+ bizError.response = error.response;
574
+ bizError.data = responseData;
575
+ throw bizError;
576
+ }
577
+ // 其他错误(网络错误、超时等)直接抛出
578
+ throw error;
579
+ });
580
+ }
581
+
582
+ /**
583
+ * BeLinkHttp 请求客户端
584
+ *
585
+ * 单例模式的 HTTP 请求客户端
586
+ * 支持时间同步和 Token 加密
587
+ *
588
+ * @module BeLinkHttp
589
+ *
590
+ * @example
591
+ * ```ts
592
+ * import { beLinkHttp } from '@be-link/http';
593
+ *
594
+ * // 初始化
595
+ * beLinkHttp.init({
596
+ * baseURL: 'https://api.example.com',
597
+ * timeSync: { syncUrl: 'https://api.example.com/api/time' },
598
+ * encryption: { key: '...', iv: '...' },
599
+ * getToken: () => localStorage.getItem('token'),
600
+ * });
601
+ *
602
+ * // 发起请求
603
+ * const data = await beLinkHttp.get('/api/users');
604
+ * ```
605
+ */
606
+ /**
607
+ * BeLinkHttp 单例类
608
+ *
609
+ * 提供统一的 HTTP 请求接口,支持:
610
+ * - 时间同步
611
+ * - Token 加密
612
+ * - 请求/响应拦截
613
+ */
614
+ class BeLinkHttp {
615
+ constructor() {
616
+ /** Axios 实例 */
617
+ this.instance = null;
618
+ /** 时间同步服务 */
619
+ this.timeSyncService = null;
620
+ /** 加密服务 */
621
+ this.encryptionService = null;
622
+ /** 是否已初始化 */
623
+ this.initialized = false;
624
+ }
625
+ /**
626
+ * 初始化请求客户端
627
+ *
628
+ * 必须在使用其他方法之前调用此方法进行初始化
629
+ *
630
+ * @param options - 请求配置选项
631
+ *
632
+ * @example
633
+ * ```ts
634
+ * beLinkHttp.init({
635
+ * baseURL: 'https://api.example.com',
636
+ * timeout: 30000,
637
+ * timeSync: {
638
+ * syncUrl: 'https://api.example.com/api/time',
639
+ * },
640
+ * encryption: {
641
+ * key: 'your-aes-key-16ch',
642
+ * iv: 'your-aes-iv-16ch',
643
+ * },
644
+ * getToken: () => localStorage.getItem('token'),
645
+ * getUserId: () => localStorage.getItem('userId'),
646
+ * });
647
+ * ```
648
+ */
649
+ init(options) {
650
+ const { baseURL, timeout = 30000, headers = {}, timeSync, encryption } = options;
651
+ // 创建 Axios 实例
652
+ this.instance = axios.create({
653
+ baseURL,
654
+ timeout,
655
+ headers: {
656
+ 'Content-Type': 'application/json',
657
+ ...headers,
658
+ },
659
+ });
660
+ // 创建时间同步服务
661
+ if (timeSync?.syncUrl) {
662
+ this.timeSyncService = new TimeSyncService(timeSync);
663
+ }
664
+ // 创建加密服务
665
+ if (encryption?.key && encryption?.iv) {
666
+ this.encryptionService = new EncryptionService(encryption, this.timeSyncService || undefined);
667
+ }
668
+ // 设置拦截器
669
+ setRequestInterceptor(this.instance, options, this.timeSyncService || undefined, this.encryptionService || undefined);
670
+ setResponseInterceptor(this.instance, options);
671
+ this.initialized = true;
672
+ }
673
+ /**
674
+ * 检查是否已初始化
675
+ */
676
+ checkInitialized() {
677
+ if (!this.initialized || !this.instance) {
678
+ throw new Error('[BeLinkHttp] 请先调用 init() 方法进行初始化');
679
+ }
680
+ }
681
+ /**
682
+ * GET 请求
683
+ *
684
+ * @param url - 请求 URL
685
+ * @param config - 可选的 Axios 请求配置
686
+ * @returns Promise 响应数据
687
+ *
688
+ * @example
689
+ * ```ts
690
+ * const users = await beLinkHttp.get('/api/users');
691
+ * const user = await beLinkHttp.get('/api/users/1', { params: { include: 'profile' } });
692
+ * ```
693
+ */
694
+ async get(url, config) {
695
+ this.checkInitialized();
696
+ return this.instance.get(url, config);
697
+ }
698
+ /**
699
+ * POST 请求
700
+ *
701
+ * @param url - 请求 URL
702
+ * @param data - 请求体数据
703
+ * @param config - 可选的 Axios 请求配置
704
+ * @returns Promise 响应数据
705
+ *
706
+ * @example
707
+ * ```ts
708
+ * const result = await beLinkHttp.post('/api/users', { name: '张三', age: 25 });
709
+ * ```
710
+ */
711
+ async post(url, data, config) {
712
+ this.checkInitialized();
713
+ return this.instance.post(url, data, config);
714
+ }
715
+ /**
716
+ * PUT 请求
717
+ *
718
+ * @param url - 请求 URL
719
+ * @param data - 请求体数据
720
+ * @param config - 可选的 Axios 请求配置
721
+ * @returns Promise 响应数据
722
+ *
723
+ * @example
724
+ * ```ts
725
+ * const result = await beLinkHttp.put('/api/users/1', { name: '李四' });
726
+ * ```
727
+ */
728
+ async put(url, data, config) {
729
+ this.checkInitialized();
730
+ return this.instance.put(url, data, config);
731
+ }
732
+ /**
733
+ * PATCH 请求
734
+ *
735
+ * @param url - 请求 URL
736
+ * @param data - 请求体数据
737
+ * @param config - 可选的 Axios 请求配置
738
+ * @returns Promise 响应数据
739
+ *
740
+ * @example
741
+ * ```ts
742
+ * const result = await beLinkHttp.patch('/api/users/1', { age: 26 });
743
+ * ```
744
+ */
745
+ async patch(url, data, config) {
746
+ this.checkInitialized();
747
+ return this.instance.patch(url, data, config);
748
+ }
749
+ /**
750
+ * DELETE 请求
751
+ *
752
+ * @param url - 请求 URL
753
+ * @param config - 可选的 Axios 请求配置
754
+ * @returns Promise 响应数据
755
+ *
756
+ * @example
757
+ * ```ts
758
+ * await beLinkHttp.delete('/api/users/1');
759
+ * ```
760
+ */
761
+ async delete(url, config) {
762
+ this.checkInitialized();
763
+ return this.instance.delete(url, config);
764
+ }
765
+ /**
766
+ * 通用请求方法
767
+ *
768
+ * @param config - Axios 请求配置
769
+ * @returns Promise 响应数据
770
+ *
771
+ * @example
772
+ * ```ts
773
+ * const result = await beLinkHttp.request({
774
+ * method: 'post',
775
+ * url: '/api/users',
776
+ * data: { name: '张三' },
777
+ * headers: { 'X-Custom': 'value' },
778
+ * });
779
+ * ```
780
+ */
781
+ async request(config) {
782
+ this.checkInitialized();
783
+ return this.instance.request(config);
784
+ }
785
+ /**
786
+ * 获取时间同步服务实例
787
+ *
788
+ * @returns 时间同步服务实例,未初始化时返回 null
789
+ */
790
+ getTimeSyncService() {
791
+ return this.timeSyncService;
792
+ }
793
+ /**
794
+ * 获取加密服务实例
795
+ *
796
+ * @returns 加密服务实例,未初始化时返回 null
797
+ */
798
+ getEncryptionService() {
799
+ return this.encryptionService;
800
+ }
801
+ /**
802
+ * 获取 Axios 实例
803
+ *
804
+ * @returns Axios 实例,未初始化时返回 null
805
+ */
806
+ getAxiosInstance() {
807
+ return this.instance;
808
+ }
809
+ /**
810
+ * 重置客户端
811
+ *
812
+ * 清除所有配置和实例,需要重新调用 init() 初始化
813
+ */
814
+ reset() {
815
+ this.instance = null;
816
+ this.timeSyncService = null;
817
+ this.encryptionService = null;
818
+ this.initialized = false;
819
+ }
820
+ }
821
+ /**
822
+ * BeLinkHttp 单例实例
823
+ *
824
+ * @example
825
+ * ```ts
826
+ * import { beLinkHttp } from '@be-link/http';
827
+ *
828
+ * // 初始化
829
+ * beLinkHttp.init({
830
+ * baseURL: 'https://api.example.com',
831
+ * });
832
+ *
833
+ * // 使用
834
+ * const data = await beLinkHttp.get('/api/users');
835
+ * ```
836
+ */
837
+ const beLinkHttp = new BeLinkHttp();
838
+
839
+ exports.BeLinkHttp = BeLinkHttp;
840
+ exports.EncryptionService = EncryptionService;
841
+ exports.TimeSyncService = TimeSyncService;
842
+ exports.beLinkHttp = beLinkHttp;
843
+ exports.default = beLinkHttp;
844
+ exports.setRequestInterceptor = setRequestInterceptor;
845
+ exports.setResponseInterceptor = setResponseInterceptor;