taro-uno-ui 0.9.0 → 1.0.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.
Files changed (312) hide show
  1. package/README.md +21 -0
  2. package/dist/js/{index-DffLRSro.js → index-CDFsvu80.js} +15369 -10741
  3. package/dist/js/index-CDFsvu80.js.map +1 -0
  4. package/dist/js/index-DFdcksbe.js.map +1 -1
  5. package/dist/js/index-DXRIkWX1.js.map +1 -1
  6. package/dist/js/{index-6NJ3A1Dn.js → index-JffnTUrv.js} +15430 -10801
  7. package/dist/js/index-JffnTUrv.js.map +1 -0
  8. package/dist/utils/http/request.d.ts +280 -0
  9. package/package.json +14 -10
  10. package/src/components/basic/Button/Button.tsx +53 -13
  11. package/src/components/basic/Button/Button.types.ts +45 -9
  12. package/src/components/basic/Divider/Divider.tsx +60 -29
  13. package/src/components/basic/Icon/Icon.data.ts +474 -0
  14. package/src/components/basic/Icon/Icon.test.tsx +2 -2
  15. package/src/components/basic/Icon/Icon.tsx +48 -35
  16. package/src/components/basic/Icon/IconManager.ts +229 -0
  17. package/src/components/basic/Text/Text.styles.ts +3 -3
  18. package/src/components/basic/Text/Text.types.ts +14 -4
  19. package/src/components/basic/Typography/Typography.styles.ts +10 -9
  20. package/src/components/basic/Typography/Typography.tsx +15 -13
  21. package/src/components/basic/Typography/Typography.types.ts +41 -41
  22. package/src/components/basic/Typography/index.tsx +1 -1
  23. package/src/components/basic/Video/Video.styles.ts +777 -0
  24. package/src/components/basic/Video/Video.test.tsx +490 -0
  25. package/src/components/basic/Video/Video.tsx +1468 -0
  26. package/src/components/basic/Video/Video.types.ts +500 -0
  27. package/src/components/basic/Video/index.tsx +26 -0
  28. package/src/components/basic/index.tsx +13 -15
  29. package/src/components/common/ErrorBoundary.tsx +1 -1
  30. package/src/components/common/LazyComponent.tsx +9 -8
  31. package/src/components/common/SecurityProvider.tsx +2 -14
  32. package/src/components/common/ThemeProvider.tsx +43 -56
  33. package/src/components/common/VirtualList.tsx +187 -205
  34. package/src/components/common/index.tsx +25 -0
  35. package/src/components/display/Avatar/Avatar.styles.ts +1 -1
  36. package/src/components/display/Avatar/Avatar.tsx +6 -19
  37. package/src/components/display/Avatar/Avatar.types.ts +1 -1
  38. package/src/components/display/Avatar/index.ts +1 -1
  39. package/src/components/display/Badge/Badge.tsx +3 -16
  40. package/src/components/display/Badge/Badge.types.ts +1 -1
  41. package/src/components/display/Badge/index.ts +1 -1
  42. package/src/components/display/Calendar/Calendar.styles.ts +36 -36
  43. package/src/components/display/Calendar/Calendar.test.tsx +27 -15
  44. package/src/components/display/Calendar/Calendar.tsx +56 -35
  45. package/src/components/display/Calendar/Calendar.types.ts +1 -1
  46. package/src/components/display/Calendar/index.ts +1 -1
  47. package/src/components/display/Card/Card.styles.ts +2 -2
  48. package/src/components/display/Card/Card.test.tsx +6 -4
  49. package/src/components/display/Card/Card.tsx +1 -1
  50. package/src/components/display/Card/Card.types.ts +4 -4
  51. package/src/components/display/Card/index.ts +1 -1
  52. package/src/components/display/Carousel/Carousel.styles.ts +31 -31
  53. package/src/components/display/Carousel/Carousel.tsx +34 -39
  54. package/src/components/display/Carousel/Carousel.types.ts +1 -1
  55. package/src/components/display/Carousel/index.ts +1 -1
  56. package/src/components/display/List/List.styles.ts +3 -3
  57. package/src/components/display/List/List.tsx +0 -1
  58. package/src/components/display/List/index.ts +1 -1
  59. package/src/components/display/Rate/Rate.styles.ts +5 -17
  60. package/src/components/display/Rate/Rate.tsx +6 -14
  61. package/src/components/display/Rate/Rate.types.ts +4 -3
  62. package/src/components/display/Rate/index.ts +3 -11
  63. package/src/components/display/Table/Table.test.tsx +2 -0
  64. package/src/components/display/Table/Table.tsx +3 -7
  65. package/src/components/display/Table/Table.types.ts +3 -2
  66. package/src/components/display/Tag/Tag.styles.ts +31 -31
  67. package/src/components/display/Tag/Tag.tsx +9 -26
  68. package/src/components/display/Tag/Tag.types.ts +1 -1
  69. package/src/components/display/Tag/index.ts +1 -1
  70. package/src/components/display/Timeline/Timeline.styles.ts +32 -32
  71. package/src/components/display/Timeline/Timeline.tsx +23 -42
  72. package/src/components/display/Timeline/Timeline.types.ts +1 -1
  73. package/src/components/display/Timeline/index.ts +1 -1
  74. package/src/components/display/index.tsx +33 -29
  75. package/src/components/feedback/Loading/Loading.tsx +6 -1
  76. package/src/components/feedback/Loading/index.ts +2 -5
  77. package/src/components/feedback/Message/Message.styles.ts +3 -3
  78. package/src/components/feedback/Message/index.ts +2 -5
  79. package/src/components/feedback/Modal/Modal.styles.ts +1 -1
  80. package/src/components/feedback/Modal/Modal.tsx +9 -31
  81. package/src/components/feedback/Modal/Modal.types.ts +12 -2
  82. package/src/components/feedback/Notification/Notification.styles.ts +49 -39
  83. package/src/components/feedback/Notification/Notification.test.tsx +1 -1
  84. package/src/components/feedback/Notification/Notification.tsx +97 -120
  85. package/src/components/feedback/Notification/Notification.types.ts +11 -8
  86. package/src/components/feedback/Notification/NotificationManager.tsx +135 -106
  87. package/src/components/feedback/Notification/index.ts +10 -3
  88. package/src/components/feedback/Notification/index.tsx +16 -26
  89. package/src/components/feedback/Progress/Progress.styles.ts +23 -14
  90. package/src/components/feedback/Progress/Progress.tsx +93 -113
  91. package/src/components/feedback/Progress/Progress.types.ts +1 -1
  92. package/src/components/feedback/Progress/index.ts +1 -1
  93. package/src/components/feedback/Progress/utils/animation.ts +12 -23
  94. package/src/components/feedback/Progress/utils/index.ts +2 -2
  95. package/src/components/feedback/Progress/utils/progress-calculator.ts +14 -32
  96. package/src/components/feedback/Result/Result.styles.ts +29 -29
  97. package/src/components/feedback/Result/Result.tsx +8 -20
  98. package/src/components/feedback/Result/Result.types.ts +7 -7
  99. package/src/components/feedback/Result/index.tsx +1 -1
  100. package/src/components/feedback/Toast/Toast.styles.ts +1 -1
  101. package/src/components/feedback/Toast/Toast.tsx +25 -13
  102. package/src/components/feedback/Tooltip/Tooltip.examples.tsx +21 -44
  103. package/src/components/feedback/Tooltip/Tooltip.styles.ts +16 -22
  104. package/src/components/feedback/Tooltip/Tooltip.test.tsx +1 -1
  105. package/src/components/feedback/Tooltip/Tooltip.tsx +65 -46
  106. package/src/components/feedback/Tooltip/Tooltip.types.ts +14 -20
  107. package/src/components/feedback/Tooltip/index.ts +1 -1
  108. package/src/components/feedback/Tooltip/index.tsx +12 -24
  109. package/src/components/feedback/index.tsx +54 -42
  110. package/src/components/form/Cascader/Cascader.styles.ts +2 -2
  111. package/src/components/form/Cascader/Cascader.tsx +84 -88
  112. package/src/components/form/Cascader/Cascader.types.ts +49 -50
  113. package/src/components/form/Cascader/hooks/useCascaderFieldNames.ts +11 -8
  114. package/src/components/form/Cascader/hooks/useCascaderOptions.ts +73 -55
  115. package/src/components/form/Cascader/hooks/useCascaderState.ts +31 -25
  116. package/src/components/form/Cascader/index.ts +1 -1
  117. package/src/components/form/Cascader/utils/formatDisplayValue.ts +4 -4
  118. package/src/components/form/Checkbox/Checkbox.styles.ts +83 -84
  119. package/src/components/form/Checkbox/Checkbox.tsx +2 -9
  120. package/src/components/form/Checkbox/CheckboxGroup.tsx +7 -7
  121. package/src/components/form/DatePicker/DatePicker.test.tsx +1 -1
  122. package/src/components/form/DatePicker/DatePicker.tsx +91 -75
  123. package/src/components/form/DatePicker/DatePicker.types.ts +4 -1
  124. package/src/components/form/Form/Form.tsx +66 -504
  125. package/src/components/form/Form/Form.types.ts +16 -1
  126. package/src/components/form/Form/useFormLogic.ts +497 -0
  127. package/src/components/form/Input/Input.styles.ts +8 -1
  128. package/src/components/form/Input/Input.tsx +55 -291
  129. package/src/components/form/Input/Input.types.ts +13 -1
  130. package/src/components/form/Input/useInputLogic.test.ts +82 -0
  131. package/src/components/form/Input/useInputLogic.ts +260 -0
  132. package/src/components/form/InputNumber/InputNumber.styles.ts +76 -25
  133. package/src/components/form/InputNumber/InputNumber.tsx +53 -21
  134. package/src/components/form/InputNumber/InputNumber.types.ts +21 -3
  135. package/src/components/form/InputNumber/components/InputNumberClearButton.tsx +3 -11
  136. package/src/components/form/InputNumber/components/InputNumberControls.tsx +3 -12
  137. package/src/components/form/InputNumber/hooks/index.ts +1 -1
  138. package/src/components/form/InputNumber/hooks/useInputNumberState.ts +7 -9
  139. package/src/components/form/InputNumber/hooks/useInputNumberValidation.ts +18 -17
  140. package/src/components/form/InputNumber/index.ts +7 -7
  141. package/src/components/form/Radio/Radio.styles.ts +1 -8
  142. package/src/components/form/Radio/Radio.tsx +3 -9
  143. package/src/components/form/Radio/Radio.types.ts +5 -1
  144. package/src/components/form/Select/Select.styles.ts +5 -1
  145. package/src/components/form/Select/Select.tsx +15 -15
  146. package/src/components/form/Select/Select.types.ts +2 -1
  147. package/src/components/form/Slider/Slider.styles.ts +13 -13
  148. package/src/components/form/Slider/Slider.tsx +19 -33
  149. package/src/components/form/Slider/Slider.types.ts +14 -12
  150. package/src/components/form/Slider/index.tsx +2 -9
  151. package/src/components/form/Switch/Switch.styles.ts +1 -7
  152. package/src/components/form/Switch/Switch.tsx +7 -13
  153. package/src/components/form/Textarea/Textarea.styles.ts +4 -4
  154. package/src/components/form/Textarea/Textarea.tsx +7 -1
  155. package/src/components/form/Textarea/Textarea.types.ts +4 -1
  156. package/src/components/form/TimePicker/TimePicker.styles.ts +8 -12
  157. package/src/components/form/TimePicker/TimePicker.tsx +122 -100
  158. package/src/components/form/TimePicker/TimePicker.types.ts +2 -2
  159. package/src/components/form/TimePicker/index.ts +1 -1
  160. package/src/components/form/Transfer/Transfer.styles.ts +3 -15
  161. package/src/components/form/Transfer/Transfer.tsx +146 -134
  162. package/src/components/form/Transfer/Transfer.types.ts +34 -26
  163. package/src/components/form/Transfer/components/TransferItem.tsx +55 -62
  164. package/src/components/form/Transfer/components/TransferList.tsx +212 -199
  165. package/src/components/form/Transfer/components/TransferOperations.tsx +52 -55
  166. package/src/components/form/Transfer/components/TransferPagination.tsx +115 -111
  167. package/src/components/form/Transfer/components/TransferSearch.tsx +52 -55
  168. package/src/components/form/Transfer/hooks/useTransferData.ts +91 -81
  169. package/src/components/form/Transfer/hooks/useTransferState.ts +22 -16
  170. package/src/components/form/Transfer/index.ts +2 -8
  171. package/src/components/form/Upload/Upload.styles.ts +21 -21
  172. package/src/components/form/Upload/Upload.tsx +189 -142
  173. package/src/components/form/Upload/Upload.types.ts +31 -31
  174. package/src/components/form/Upload/index.tsx +1 -1
  175. package/src/components/form/index.tsx +60 -29
  176. package/src/components/index.tsx +0 -1
  177. package/src/components/layout/Affix/Affix.styles.ts +16 -11
  178. package/src/components/layout/Affix/Affix.tsx +67 -75
  179. package/src/components/layout/Affix/Affix.types.ts +18 -18
  180. package/src/components/layout/Affix/index.tsx +1 -1
  181. package/src/components/layout/Col/Col.styles.ts +17 -17
  182. package/src/components/layout/Col/Col.test.tsx +7 -5
  183. package/src/components/layout/Col/Col.tsx +3 -21
  184. package/src/components/layout/Col/Col.types.ts +1 -1
  185. package/src/components/layout/Container/Container.styles.ts +3 -1
  186. package/src/components/layout/Container/Container.tsx +2 -11
  187. package/src/components/layout/Grid/Grid.tsx +3 -53
  188. package/src/components/layout/Layout/Content.tsx +24 -32
  189. package/src/components/layout/Layout/Footer.tsx +24 -32
  190. package/src/components/layout/Layout/Header.tsx +24 -32
  191. package/src/components/layout/Layout/Layout.styles.ts +17 -17
  192. package/src/components/layout/Layout/Layout.tsx +14 -25
  193. package/src/components/layout/Layout/Layout.types.ts +29 -29
  194. package/src/components/layout/Layout/Sider.tsx +44 -56
  195. package/src/components/layout/Layout/index.tsx +16 -2
  196. package/src/components/layout/Row/Row.tsx +15 -43
  197. package/src/components/layout/Space/Space.tsx +3 -11
  198. package/src/components/layout/Space/Space.types.ts +1 -1
  199. package/src/components/layout/index.tsx +29 -19
  200. package/src/components/navigation/Menu/Menu.constants.ts +69 -0
  201. package/src/components/navigation/Menu/Menu.stories.tsx +107 -0
  202. package/src/components/navigation/Menu/Menu.styles.ts +25 -37
  203. package/src/components/navigation/Menu/Menu.tsx +8 -11
  204. package/src/components/navigation/Menu/Menu.types.ts +2 -2
  205. package/src/components/navigation/Menu/Menu.utils.ts +17 -17
  206. package/src/components/navigation/Menu/MenuItem.tsx +9 -11
  207. package/src/components/navigation/Menu/SubMenu.tsx +8 -6
  208. package/src/components/navigation/Menu/index.tsx +4 -69
  209. package/src/components/navigation/NavBar/NavBar.styles.ts +1 -1
  210. package/src/components/navigation/NavBar/NavBar.tsx +7 -10
  211. package/src/components/navigation/NavBar/NavBar.types.ts +3 -3
  212. package/src/components/navigation/NavBar/index.tsx +1 -1
  213. package/src/components/navigation/Pagination/Pagination.test.tsx +2 -3
  214. package/src/components/navigation/Pagination/Pagination.tsx +3 -3
  215. package/src/components/navigation/Pagination/Pagination.types.ts +3 -2
  216. package/src/components/navigation/Pagination/index.ts +9 -3
  217. package/src/components/navigation/Steps/Step.tsx +24 -44
  218. package/src/components/navigation/Steps/Steps.styles.ts +28 -13
  219. package/src/components/navigation/Steps/Steps.test.tsx +2 -0
  220. package/src/components/navigation/Steps/Steps.tsx +88 -89
  221. package/src/components/navigation/Steps/Steps.types.ts +30 -30
  222. package/src/components/navigation/Steps/index.tsx +1 -1
  223. package/src/components/navigation/Tabs/Tabs.test.tsx +3 -2
  224. package/src/components/navigation/Tabs/Tabs.types.ts +4 -3
  225. package/src/components/navigation/index.tsx +21 -16
  226. package/src/constants/index.ts +1 -1
  227. package/src/hooks/index.ts +52 -102
  228. package/src/hooks/types.ts +4 -5
  229. package/src/hooks/useAsync.ts +46 -47
  230. package/src/hooks/useClickOutside.ts +52 -0
  231. package/src/hooks/useCounter.ts +87 -0
  232. package/src/hooks/useDebounce.ts +150 -0
  233. package/src/hooks/useDeepCompareEffect.ts +88 -0
  234. package/src/hooks/useEventListener.ts +77 -0
  235. package/src/hooks/useMediaQuery.ts +75 -0
  236. package/src/hooks/useMutation.ts +233 -0
  237. package/src/hooks/usePerformance.ts +1 -64
  238. package/src/hooks/usePlatform.ts +3 -1
  239. package/src/hooks/usePrevious.ts +25 -0
  240. package/src/hooks/useRequest.ts +12 -7
  241. package/src/hooks/useStateManagement.ts +1 -1
  242. package/src/hooks/useStorage.ts +169 -0
  243. package/src/hooks/useStyle.ts +8 -2
  244. package/src/hooks/useToggle.ts +54 -0
  245. package/src/index.ts +34 -9
  246. package/src/theme/ThemeProvider.tsx +3 -7
  247. package/src/theme/ThemeProvider.types.ts +1 -1
  248. package/src/theme/defaults.ts +1 -1
  249. package/src/theme/design-system.ts +2 -2
  250. package/src/theme/design-tokens.ts +85 -99
  251. package/src/theme/generated/dark-theme.scss +1 -1
  252. package/src/theme/generated/tokens.scss +82 -18
  253. package/src/theme/index.ts +8 -29
  254. package/src/theme/responsive.tsx +36 -34
  255. package/src/theme/styles.ts +1 -1
  256. package/src/theme/useThemeUtils.ts +43 -43
  257. package/src/theme/utils.ts +32 -32
  258. package/src/theme/variables.ts +70 -51
  259. package/src/types/accessibility.ts +36 -37
  260. package/src/types/button.ts +25 -27
  261. package/src/types/component-props.ts +6 -1
  262. package/src/types/glob.d.ts +4 -0
  263. package/src/types/index.ts +2 -2
  264. package/src/types/standardized-components.ts +9 -3
  265. package/src/types/utils.ts +13 -23
  266. package/src/utils/__tests__/responsiveUtils.test.ts +5 -4
  267. package/src/utils/abort-controller.ts +48 -0
  268. package/src/utils/cache.ts +2 -6
  269. package/src/utils/createNamespace.ts +4 -4
  270. package/src/utils/environment.ts +26 -6
  271. package/src/utils/error-handler.ts +2 -2
  272. package/src/utils/errorLogger.ts +16 -20
  273. package/src/utils/formatUtils.ts +38 -70
  274. package/src/utils/http/error-codes.ts +314 -0
  275. package/src/utils/http/http-client.test.ts +63 -0
  276. package/src/utils/{network → http}/http-client.ts +45 -35
  277. package/src/utils/http/request-cache.ts +127 -0
  278. package/src/utils/http/request.ts +954 -0
  279. package/src/utils/http/taro-adapter.test.ts +74 -0
  280. package/src/utils/http/taro-adapter.ts +24 -0
  281. package/src/utils/http/types.ts +414 -0
  282. package/src/utils/http/web-adapter.ts +33 -0
  283. package/src/utils/index.ts +5 -8
  284. package/src/utils/inputValidator.ts +17 -14
  285. package/src/utils/performance/performance.ts +60 -71
  286. package/src/utils/responsiveUtils.ts +7 -16
  287. package/src/utils/rtl-support.ts +29 -19
  288. package/src/utils/security/api-security.ts +47 -39
  289. package/src/utils/securityHeaders.ts +61 -67
  290. package/src/utils/typeHelpers.ts +10 -10
  291. package/src/utils/types/dataProcessing.ts +93 -92
  292. package/src/utils/types/typeHelpers.ts +31 -21
  293. package/src/utils/xssProtection.ts +96 -48
  294. package/dist/js/index-6NJ3A1Dn.js.map +0 -1
  295. package/dist/js/index-DffLRSro.js.map +0 -1
  296. package/src/components/form/Input/Input.enhanced.tsx +0 -732
  297. package/src/components/navigation/Menu/__tests__/Menu.test.tsx +0 -687
  298. package/src/components/navigation/Tree/Tree.styles.ts +0 -553
  299. package/src/components/navigation/Tree/Tree.test.basic.tsx +0 -7
  300. package/src/components/navigation/Tree/Tree.test.functional.tsx +0 -496
  301. package/src/components/navigation/Tree/Tree.test.import.check.tsx +0 -6
  302. package/src/components/navigation/Tree/Tree.test.import.tsx +0 -6
  303. package/src/components/navigation/Tree/Tree.test.minimal.tsx +0 -5
  304. package/src/components/navigation/Tree/Tree.test.simple.tsx +0 -30
  305. package/src/components/navigation/Tree/Tree.test.tsx +0 -908
  306. package/src/components/navigation/Tree/Tree.test.working.tsx +0 -673
  307. package/src/components/navigation/Tree/Tree.tsx +0 -600
  308. package/src/components/navigation/Tree/Tree.types.ts +0 -909
  309. package/src/components/navigation/Tree/Tree.utils.ts +0 -452
  310. package/src/components/navigation/Tree/index.ts +0 -33
  311. package/src/components/navigation/Tree/index.tsx +0 -23
  312. package/src/utils/network/http-client.test.ts +0 -18
@@ -0,0 +1,954 @@
1
+ /**
2
+ * Unified Request Client
3
+ * Production-ready HTTP client with multi-platform support, caching, retry logic, and interceptors
4
+ */
5
+
6
+ import { ErrorHandlingManager } from '@/utils/error-handler';
7
+ import { createApiInterceptor, buildSecureHeaders, isSecureUrl } from '@/utils/security/api-security';
8
+ import { TaroAdapter } from './taro-adapter';
9
+ import { WebAdapter } from './web-adapter';
10
+ import { RequestCache } from './request-cache';
11
+ import type {
12
+ RequestConfig,
13
+ RequestOptions,
14
+ ResponseData,
15
+ IRequestAdapter,
16
+ RequestInterceptor,
17
+ ResponseInterceptor,
18
+ RetryConfig,
19
+ HttpMethod,
20
+ Platform,
21
+ InterceptorPriority,
22
+ InterceptorRegistration,
23
+ } from './types';
24
+ import { HttpError, NetworkError, TimeoutError, CancelError } from './types';
25
+
26
+ export interface RequestInstanceConfig {
27
+ /** Base URL for all requests */
28
+ baseURL?: string;
29
+ /** Default headers */
30
+ headers?: Record<string, string>;
31
+ /** Default timeout in milliseconds */
32
+ timeout?: number;
33
+ /** Custom adapter */
34
+ adapter?: IRequestAdapter;
35
+ /** Enable request caching */
36
+ enableCache?: boolean;
37
+ /** Default cache TTL */
38
+ cacheTTL?: number;
39
+ /** Default retry configuration */
40
+ retry?: RetryConfig;
41
+ /** Enable security features */
42
+ enableSecurity?: boolean;
43
+ }
44
+
45
+ /**
46
+ * Unified Request Client
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const client = new Request({
51
+ * baseURL: 'https://api.example.com',
52
+ * timeout: 10000,
53
+ * enableCache: true,
54
+ * });
55
+ *
56
+ * // GET request
57
+ * const data = await client.get<User>('/users/1');
58
+ *
59
+ * // POST request
60
+ * const created = await client.post<User>('/users', { name: 'John' });
61
+ * ```
62
+ */
63
+ export class Request {
64
+ private baseURL: string;
65
+ private defaultHeaders: Record<string, string>;
66
+ private defaultTimeout: number;
67
+ private adapter: IRequestAdapter;
68
+ private cache: RequestCache;
69
+ private errorManager: ReturnType<typeof ErrorHandlingManager.getInstance>;
70
+ private security: ReturnType<typeof createApiInterceptor>;
71
+ private requestInterceptors: Array<{ id: string } & RequestInterceptor> = [];
72
+ private responseInterceptors: Array<{ id: string } & ResponseInterceptor> = [];
73
+ private static globalRequestInterceptors: Array<{ id: string } & RequestInterceptor> = [];
74
+ private static globalResponseInterceptors: Array<{ id: string } & ResponseInterceptor> = [];
75
+ private defaultRetryConfig: RetryConfig;
76
+ private enableSecurity: boolean;
77
+ private enableCache: boolean;
78
+ private cacheTTL: number;
79
+
80
+ constructor(
81
+ config: RequestInstanceConfig & {
82
+ _testing?: {
83
+ cache?: RequestCache;
84
+ errorManager?: any;
85
+ security?: any;
86
+ };
87
+ } = {},
88
+ ) {
89
+ this.baseURL = config.baseURL || '';
90
+ this.defaultHeaders = config.headers || {
91
+ 'Content-Type': 'application/json',
92
+ };
93
+ this.defaultTimeout = config.timeout || 10000;
94
+ this.adapter = config.adapter || this.detectAdapter();
95
+
96
+ // Allow external dependencies injection for testing via _testing property
97
+ const testingConfig = config._testing || {};
98
+ this.cache = testingConfig.cache || new RequestCache();
99
+ this.errorManager = testingConfig.errorManager || ErrorHandlingManager.getInstance();
100
+ this.security = testingConfig.security || createApiInterceptor();
101
+
102
+ this.enableSecurity = config.enableSecurity !== false;
103
+ this.enableCache = config.enableCache || false;
104
+ this.cacheTTL = config.cacheTTL || 5 * 60 * 1000; // 5 minutes default
105
+ this.defaultRetryConfig = {
106
+ retries: 3,
107
+ retryDelay: 1000,
108
+ retryDelayStrategy: 'exponential',
109
+ maxRetryDelay: 10000,
110
+ shouldRetry: (error, _attempt) => {
111
+ // Retry on network errors and 5xx errors
112
+ if (error.name === 'NetworkError' || error.name === 'TimeoutError') return true;
113
+ if ((error as any).statusCode >= 500 && (error as any).statusCode < 600) return true;
114
+ return false;
115
+ },
116
+ ...config.retry,
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Detect appropriate adapter based on environment
122
+ */
123
+ private detectAdapter(): IRequestAdapter {
124
+ // Check Taro environment
125
+ if (typeof process !== 'undefined' && process.env['TARO_ENV']) {
126
+ const taroEnv = process.env['TARO_ENV'];
127
+ if (taroEnv === 'h5' || taroEnv === 'web') {
128
+ return new WebAdapter();
129
+ }
130
+ return new TaroAdapter();
131
+ }
132
+
133
+ // Check for browser environment
134
+ if (typeof window !== 'undefined' && typeof window.fetch !== 'undefined') {
135
+ return new WebAdapter();
136
+ }
137
+
138
+ // Default to Taro adapter
139
+ return new TaroAdapter();
140
+ }
141
+
142
+ /**
143
+ * Convert priority string to number
144
+ */
145
+ private getPriorityValue(priority: InterceptorPriority): number {
146
+ if (typeof priority === 'number') {
147
+ return priority;
148
+ }
149
+ switch (priority) {
150
+ case 'high':
151
+ return 100;
152
+ case 'medium':
153
+ return 50;
154
+ case 'low':
155
+ default:
156
+ return 0;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Sort interceptors by priority
162
+ */
163
+ private sortInterceptors<T extends { priority?: InterceptorPriority }>(interceptors: T[]): T[] {
164
+ return [...interceptors].sort((a, b) => {
165
+ const priorityA = this.getPriorityValue(a.priority || 'medium');
166
+ const priorityB = this.getPriorityValue(b.priority || 'medium');
167
+ return priorityB - priorityA; // Higher priority comes first
168
+ });
169
+ }
170
+
171
+ /**
172
+ * Add request interceptor with enhanced features
173
+ */
174
+ useRequestInterceptor(interceptor: RequestInterceptor): InterceptorRegistration {
175
+ const interceptorWithId = {
176
+ id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
177
+ ...interceptor,
178
+ };
179
+
180
+ this.requestInterceptors.push(interceptorWithId);
181
+
182
+ // Return cleanup function with registration info
183
+ return {
184
+ id: interceptorWithId.id,
185
+ eject: () => {
186
+ const index = this.requestInterceptors.findIndex((i) => i.id === interceptorWithId.id);
187
+ if (index > -1) {
188
+ this.requestInterceptors.splice(index, 1);
189
+ }
190
+ },
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Add response interceptor with enhanced features
196
+ */
197
+ useResponseInterceptor(interceptor: ResponseInterceptor): InterceptorRegistration {
198
+ const interceptorWithId = {
199
+ id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
200
+ ...interceptor,
201
+ };
202
+
203
+ this.responseInterceptors.push(interceptorWithId);
204
+
205
+ // Return cleanup function with registration info
206
+ return {
207
+ id: interceptorWithId.id,
208
+ eject: () => {
209
+ const index = this.responseInterceptors.findIndex((i) => i.id === interceptorWithId.id);
210
+ if (index > -1) {
211
+ this.responseInterceptors.splice(index, 1);
212
+ }
213
+ },
214
+ };
215
+ }
216
+
217
+ /**
218
+ * Add global request interceptor (shared across all instances)
219
+ */
220
+ static useGlobalRequestInterceptor(interceptor: RequestInterceptor): InterceptorRegistration {
221
+ const interceptorWithId = {
222
+ id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
223
+ ...interceptor,
224
+ };
225
+
226
+ Request.globalRequestInterceptors.push(interceptorWithId);
227
+
228
+ return {
229
+ id: interceptorWithId.id,
230
+ eject: () => {
231
+ const index = Request.globalRequestInterceptors.findIndex((i: { id: string }) => i.id === interceptorWithId.id);
232
+ if (index > -1) {
233
+ Request.globalRequestInterceptors.splice(index, 1);
234
+ }
235
+ },
236
+ };
237
+ }
238
+
239
+ /**
240
+ * Add global response interceptor (shared across all instances)
241
+ */
242
+ static useGlobalResponseInterceptor(interceptor: ResponseInterceptor): InterceptorRegistration {
243
+ const interceptorWithId = {
244
+ id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
245
+ ...interceptor,
246
+ };
247
+
248
+ Request.globalResponseInterceptors.push(interceptorWithId);
249
+
250
+ return {
251
+ id: interceptorWithId.id,
252
+ eject: () => {
253
+ const index = Request.globalResponseInterceptors.findIndex((i: { id: string }) => i.id === interceptorWithId.id);
254
+ if (index > -1) {
255
+ Request.globalResponseInterceptors.splice(index, 1);
256
+ }
257
+ },
258
+ };
259
+ }
260
+
261
+ /**
262
+ * Clear all interceptors or by group
263
+ */
264
+ clearInterceptors(group?: string): void {
265
+ if (group) {
266
+ this.requestInterceptors = this.requestInterceptors.filter((i) => i.group !== group);
267
+ this.responseInterceptors = this.responseInterceptors.filter((i) => i.group !== group);
268
+ } else {
269
+ this.requestInterceptors = [];
270
+ this.responseInterceptors = [];
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Clear all global interceptors or by group
276
+ */
277
+ static clearGlobalInterceptors(group?: string): void {
278
+ if (group) {
279
+ Request.globalRequestInterceptors = Request.globalRequestInterceptors.filter((i: { group?: string }) => i.group !== group);
280
+ Request.globalResponseInterceptors = Request.globalResponseInterceptors.filter((i: { group?: string }) => i.group !== group);
281
+ } else {
282
+ Request.globalRequestInterceptors = [];
283
+ Request.globalResponseInterceptors = [];
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Get all interceptors
289
+ */
290
+ getInterceptors() {
291
+ return {
292
+ request: [...this.requestInterceptors],
293
+ response: [...this.responseInterceptors],
294
+ };
295
+ }
296
+
297
+ /**
298
+ * Get all global interceptors
299
+ */
300
+ static getGlobalInterceptors() {
301
+ return {
302
+ request: [...Request.globalRequestInterceptors],
303
+ response: [...Request.globalResponseInterceptors],
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Build full URL from base and relative URL
309
+ */
310
+ private buildURL(url: string, params?: Record<string, any>): string {
311
+ // If URL is absolute, use it directly
312
+ if (url.startsWith('http://') || url.startsWith('https://')) {
313
+ return this.appendParams(url, params);
314
+ }
315
+
316
+ // Build URL with baseURL
317
+ const base = this.baseURL.replace(/\/$/, '');
318
+ const path = url.startsWith('/') ? url : `/${url}`;
319
+ const fullURL = `${base}${path}`;
320
+
321
+ return this.appendParams(fullURL, params);
322
+ }
323
+
324
+ /**
325
+ * Append query parameters to URL
326
+ */
327
+ private appendParams(url: string, params?: Record<string, any>): string {
328
+ if (!params || Object.keys(params).length === 0) {
329
+ return url;
330
+ }
331
+
332
+ const searchParams = new URLSearchParams();
333
+ Object.entries(params).forEach(([key, value]) => {
334
+ if (value === undefined || value === null) return;
335
+
336
+ if (Array.isArray(value)) {
337
+ value.forEach((item) => searchParams.append(key, String(item)));
338
+ } else {
339
+ searchParams.append(key, String(value));
340
+ }
341
+ });
342
+
343
+ const separator = url.includes('?') ? '&' : '?';
344
+ return `${url}${separator}${searchParams.toString()}`;
345
+ }
346
+
347
+ /**
348
+ * Calculate retry delay based on strategy
349
+ */
350
+ private calculateRetryDelay(attempt: number, config: RetryConfig): number {
351
+ const { retryDelay = 1000, retryDelayStrategy = 'exponential', maxRetryDelay = 10000 } = config;
352
+
353
+ let delay: number;
354
+ switch (retryDelayStrategy) {
355
+ case 'exponential':
356
+ delay = retryDelay * Math.pow(2, attempt - 1);
357
+ break;
358
+ case 'linear':
359
+ delay = retryDelay * attempt;
360
+ break;
361
+ case 'fixed':
362
+ default:
363
+ delay = retryDelay;
364
+ }
365
+
366
+ return Math.min(delay, maxRetryDelay);
367
+ }
368
+
369
+ /**
370
+ * Execute request with retry logic
371
+ */
372
+ private async executeWithRetry<T>(requestFn: () => Promise<T>, retryConfig: RetryConfig): Promise<T> {
373
+ const { retries = 0, shouldRetry } = retryConfig;
374
+ let lastError: Error;
375
+
376
+ for (let attempt = 1; attempt <= retries + 1; attempt++) {
377
+ try {
378
+ return await requestFn();
379
+ } catch (error) {
380
+ lastError = error as Error;
381
+
382
+ // Don't retry if it's the last attempt
383
+ if (attempt > retries) break;
384
+
385
+ // Check if we should retry this error
386
+ if (shouldRetry && !shouldRetry(lastError, attempt)) break;
387
+
388
+ // Wait before retrying
389
+ const delay = this.calculateRetryDelay(attempt, retryConfig);
390
+ await new Promise((resolve) => setTimeout(resolve, delay));
391
+ }
392
+ }
393
+
394
+ throw lastError!;
395
+ }
396
+
397
+ /**
398
+ * Build request config from options
399
+ */
400
+ private buildRequestConfig(options: RequestOptions, method?: HttpMethod): RequestConfig {
401
+ return {
402
+ method: method || options.method || 'GET',
403
+ url: this.buildURL(options.url, options.params),
404
+ headers: {
405
+ ...this.defaultHeaders,
406
+ ...options.headers,
407
+ },
408
+ data: options.data,
409
+ timeout: options.timeout || this.defaultTimeout,
410
+ responseType: options.responseType,
411
+ withCredentials: options.withCredentials,
412
+ meta: options.meta,
413
+ };
414
+ }
415
+
416
+ /**
417
+ * Perform security checks and add security headers
418
+ */
419
+ private performSecurityCheck(config: RequestConfig, options: RequestOptions): void {
420
+ // Security check
421
+ if (this.enableSecurity && !isSecureUrl(config.url)) {
422
+ const error = this.errorManager.createAuthorizationError('insecure_url');
423
+ options.hooks?.onError?.(error);
424
+ throw error;
425
+ }
426
+
427
+ // Add security headers
428
+ if (this.enableSecurity && config.headers) {
429
+ Object.assign(config.headers, buildSecureHeaders(config.method!, config.url, config.data));
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Execute request interceptors with global + instance interceptors and priority support
435
+ */
436
+ private async executeRequestInterceptors(config: RequestConfig, options: RequestOptions): Promise<RequestConfig> {
437
+ let processedConfig = config;
438
+
439
+ // Merge global and instance interceptors, then sort by priority
440
+ const allRequestInterceptors = this.sortInterceptors([
441
+ ...Request.globalRequestInterceptors,
442
+ ...this.requestInterceptors,
443
+ ]);
444
+
445
+ try {
446
+ // Security interceptor first
447
+ if (this.enableSecurity) {
448
+ processedConfig = this.security.request.execute(processedConfig);
449
+ }
450
+
451
+ // Execute all request interceptors in order
452
+ for (const interceptor of allRequestInterceptors) {
453
+ if (interceptor.onRequest) {
454
+ processedConfig = await interceptor.onRequest(processedConfig);
455
+ }
456
+ }
457
+
458
+ // Call before request hook
459
+ options.hooks?.beforeRequest?.(processedConfig);
460
+ } catch (error) {
461
+ // Handle interceptor errors in reverse order
462
+ const reversedInterceptors = [...allRequestInterceptors].reverse();
463
+ for (const interceptor of reversedInterceptors) {
464
+ if (interceptor.onRequestError) {
465
+ try {
466
+ error = await interceptor.onRequestError(error as Error);
467
+ } catch (e) {
468
+ // If an error interceptor throws, continue with the original error
469
+ console.warn('Request interceptor error handler failed:', e);
470
+ }
471
+ }
472
+ }
473
+
474
+ options.hooks?.onError?.(error as Error);
475
+ throw error;
476
+ }
477
+ return processedConfig;
478
+ }
479
+
480
+ /**
481
+ * Execute response interceptors with global + instance interceptors and priority support
482
+ */
483
+ private async executeResponseInterceptors<T>(
484
+ response: ResponseData<T>,
485
+ options: RequestOptions,
486
+ ): Promise<ResponseData<T>> {
487
+ let processedResponse = response;
488
+
489
+ // Merge global and instance interceptors, then sort by priority
490
+ const allResponseInterceptors = this.sortInterceptors([
491
+ ...Request.globalResponseInterceptors,
492
+ ...this.responseInterceptors,
493
+ ]);
494
+
495
+ try {
496
+ // Security interceptor first
497
+ if (this.enableSecurity) {
498
+ processedResponse = this.security.response.execute(processedResponse);
499
+ }
500
+
501
+ // Execute all response interceptors in order
502
+ for (const interceptor of allResponseInterceptors) {
503
+ if (interceptor.onResponse) {
504
+ processedResponse = await interceptor.onResponse(processedResponse);
505
+ }
506
+ }
507
+ } catch (error) {
508
+ // Handle response interceptor errors in reverse order
509
+ const reversedInterceptors = [...allResponseInterceptors].reverse();
510
+ for (const interceptor of reversedInterceptors) {
511
+ if (interceptor.onResponseError) {
512
+ try {
513
+ error = await interceptor.onResponseError(error as Error);
514
+ } catch (e) {
515
+ // If an error interceptor throws, continue with the original error
516
+ console.warn('Response interceptor error handler failed:', e);
517
+ }
518
+ }
519
+ }
520
+
521
+ options.hooks?.onError?.(error as Error);
522
+ throw error;
523
+ }
524
+ return processedResponse;
525
+ }
526
+
527
+ /**
528
+ * Handle request error
529
+ */
530
+ private handleRequestError(error: any, config: RequestConfig, options: RequestOptions, operation: string): never {
531
+ // Generate unique request ID for tracing
532
+ const requestId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
533
+
534
+ // Get platform info
535
+ const platform = ((typeof process !== 'undefined' && process.env['TARO_ENV']) as Platform) || 'web';
536
+
537
+ // Transform to appropriate error type
538
+ if ((error as any).name === 'AbortError' || (error as any).name === 'CancelError') {
539
+ const cancelError = new CancelError(`${operation} cancelled`, config, { platform, requestId });
540
+ options.hooks?.onError?.(cancelError);
541
+ throw cancelError;
542
+ }
543
+ if ((error as any).name === 'TimeoutError' || (error as any).message?.includes('timeout')) {
544
+ const timeoutError = new TimeoutError(`${operation} timeout`, config.timeout, config, { platform, requestId });
545
+ options.hooks?.onError?.(timeoutError);
546
+ throw timeoutError;
547
+ }
548
+ const networkError = new NetworkError(`Network error during ${operation}`, config, { platform, requestId });
549
+ options.hooks?.onError?.(networkError);
550
+ throw networkError;
551
+ }
552
+
553
+ /**
554
+ * Handle response status
555
+ */
556
+ private handleResponseStatus<T>(response: ResponseData<T>, config: RequestConfig, options: RequestOptions): void {
557
+ if (response.statusCode < 200 || response.statusCode >= 300) {
558
+ // Generate unique request ID for tracing
559
+ const requestId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
560
+
561
+ // Get platform info
562
+ const platform = ((typeof process !== 'undefined' && process.env['TARO_ENV']) as Platform) || 'web';
563
+
564
+ const error = new HttpError(
565
+ response.errMsg || `HTTP ${response.statusCode}`,
566
+ response.statusCode,
567
+ response,
568
+ config,
569
+ { platform, requestId },
570
+ );
571
+ this.errorManager.handleError(error);
572
+ options.hooks?.onError?.(error);
573
+ throw error;
574
+ }
575
+ }
576
+
577
+ /**
578
+ * Execute HTTP request
579
+ */
580
+ async request<T = any>(options: RequestOptions): Promise<T> {
581
+ // Build request config
582
+ const config: RequestConfig = this.buildRequestConfig(options);
583
+
584
+ // Perform security checks
585
+ this.performSecurityCheck(config, options);
586
+
587
+ // Check cache for GET requests
588
+ const cacheEnabled = options.cache?.enabled !== false && this.enableCache;
589
+ const forceRefresh = options.cache?.forceRefresh || false;
590
+
591
+ if (cacheEnabled && config.method === 'GET' && !forceRefresh) {
592
+ const cacheKey = this.cache.generateKey(config.url, config.data);
593
+
594
+ // Check if there's a pending request for this key
595
+ if (this.cache.hasPendingRequest(cacheKey)) {
596
+ return this.cache.getPendingRequest<T>(cacheKey)!;
597
+ }
598
+
599
+ // Check cache
600
+ const cached = this.cache.get<T>(cacheKey);
601
+ if (cached !== null) {
602
+ options.hooks?.afterResponse?.({
603
+ data: cached,
604
+ statusCode: 200,
605
+ header: {},
606
+ errMsg: 'Success',
607
+ cookies: [],
608
+ });
609
+ return cached;
610
+ }
611
+
612
+ // Execute request with caching
613
+ const result = await this.executeRequestWithCommonLogic<T>(
614
+ options,
615
+ config.method as HttpMethod,
616
+ (processedConfig) => this.adapter.request<T>(processedConfig),
617
+ cacheKey,
618
+ );
619
+
620
+ // Cache successful GET responses
621
+ if (result !== null && result !== undefined) {
622
+ const cacheTTL = options.cache?.ttl || this.cacheTTL;
623
+ this.cache.set(cacheKey, result, cacheTTL);
624
+ }
625
+
626
+ return result;
627
+ }
628
+
629
+ // Execute request without caching
630
+ return this.executeRequestWithCommonLogic<T>(options, config.method as HttpMethod, (processedConfig) =>
631
+ this.adapter.request<T>(processedConfig),
632
+ );
633
+ }
634
+
635
+ /**
636
+ * GET request
637
+ */
638
+ get<T = any>(url: string, options: Omit<RequestOptions, 'url' | 'method' | 'data'> = {}): Promise<T> {
639
+ return this.request<T>({
640
+ ...options,
641
+ url,
642
+ method: 'GET',
643
+ });
644
+ }
645
+
646
+ /**
647
+ * POST request
648
+ */
649
+ post<T = any>(url: string, data?: any, options: Omit<RequestOptions, 'url' | 'method' | 'data'> = {}): Promise<T> {
650
+ return this.request<T>({
651
+ ...options,
652
+ url,
653
+ method: 'POST',
654
+ data,
655
+ });
656
+ }
657
+
658
+ /**
659
+ * PUT request
660
+ */
661
+ put<T = any>(url: string, data?: any, options: Omit<RequestOptions, 'url' | 'method' | 'data'> = {}): Promise<T> {
662
+ return this.request<T>({
663
+ ...options,
664
+ url,
665
+ method: 'PUT',
666
+ data,
667
+ });
668
+ }
669
+
670
+ /**
671
+ * PATCH request
672
+ */
673
+ patch<T = any>(url: string, data?: any, options: Omit<RequestOptions, 'url' | 'method' | 'data'> = {}): Promise<T> {
674
+ return this.request<T>({
675
+ ...options,
676
+ url,
677
+ method: 'PATCH',
678
+ data,
679
+ });
680
+ }
681
+
682
+ /**
683
+ * DELETE request
684
+ */
685
+ delete<T = any>(url: string, options: Omit<RequestOptions, 'url' | 'method' | 'data'> = {}): Promise<T> {
686
+ return this.request<T>({
687
+ ...options,
688
+ url,
689
+ method: 'DELETE',
690
+ });
691
+ }
692
+
693
+ /**
694
+ * Upload file
695
+ */
696
+ async upload<T = any>(
697
+ options: RequestOptions & {
698
+ filePath: string | File;
699
+ name?: string;
700
+ formData?: Record<string, any>;
701
+ onProgress?: (progress: { progress: number; totalBytesSent: number; totalBytesExpectedToSend: number }) => void;
702
+ },
703
+ ): Promise<T> {
704
+ return this.executeRequestWithCommonLogic<T>(options, 'POST', (processedConfig) => {
705
+ if (this.adapter.upload) {
706
+ return this.adapter.upload({
707
+ ...processedConfig,
708
+ filePath: options.filePath,
709
+ name: options.name,
710
+ formData: options.formData,
711
+ onProgress: options.onProgress,
712
+ });
713
+ } else {
714
+ throw new Error('Upload adapter not supported');
715
+ }
716
+ });
717
+ }
718
+
719
+ /**
720
+ * Download file
721
+ */
722
+ async download<T = any>(
723
+ options: RequestOptions & {
724
+ filePath?: string;
725
+ onProgress?: (progress: {
726
+ progress: number;
727
+ totalBytesWritten: number;
728
+ totalBytesExpectedToWrite: number;
729
+ }) => void;
730
+ },
731
+ ): Promise<T> {
732
+ return this.executeRequestWithCommonLogic<T>(options, 'GET', (processedConfig) => {
733
+ if (this.adapter.download) {
734
+ return this.adapter.download({
735
+ ...processedConfig,
736
+ filePath: options.filePath,
737
+ onProgress: options.onProgress,
738
+ });
739
+ } else {
740
+ throw new Error('Download adapter not supported');
741
+ }
742
+ });
743
+ }
744
+
745
+ /**
746
+ * Clear cache
747
+ */
748
+ clearCache(url?: string, params?: any): void {
749
+ if (url) {
750
+ const key = this.cache.generateKey(url, params);
751
+ this.cache.clear(key);
752
+ } else {
753
+ this.cache.clearAll();
754
+ }
755
+ }
756
+
757
+ /**
758
+ * Get cache statistics
759
+ */
760
+ getCacheStats() {
761
+ return this.cache.getStats();
762
+ }
763
+
764
+ /**
765
+ * Execute request with common logic
766
+ */
767
+ private async executeRequestWithCommonLogic<T = any>(
768
+ options: RequestOptions,
769
+ method: HttpMethod,
770
+ requestFn: (config: RequestConfig) => Promise<ResponseData<T>>,
771
+ cacheKey?: string,
772
+ ): Promise<T> {
773
+ // Build request config
774
+ const config: RequestConfig = this.buildRequestConfig(options, method);
775
+
776
+ // Perform security checks
777
+ this.performSecurityCheck(config, options);
778
+
779
+ // Execute request interceptors
780
+ const processedConfig = await this.executeRequestInterceptors(config, options);
781
+
782
+ // Create request function
783
+ const doRequest = async (): Promise<T> => {
784
+ let response: ResponseData<T>;
785
+
786
+ try {
787
+ response = await requestFn(processedConfig);
788
+ } catch (error) {
789
+ this.handleRequestError(error, processedConfig, options, method.toLowerCase());
790
+ }
791
+
792
+ // Execute response interceptors
793
+ const processedResponse = await this.executeResponseInterceptors(response, options);
794
+
795
+ // Check response status
796
+ this.handleResponseStatus(processedResponse, processedConfig, options);
797
+
798
+ // Call after response hook
799
+ options.hooks?.afterResponse?.(processedResponse);
800
+
801
+ return processedResponse.data;
802
+ };
803
+
804
+ // Determine retry config
805
+ const retryConfig: RetryConfig = {
806
+ ...this.defaultRetryConfig,
807
+ ...options.retry,
808
+ };
809
+
810
+ // Execute request with retry
811
+ const requestPromise = this.executeWithRetry(doRequest, retryConfig);
812
+
813
+ // Store pending request for de-duplication (not just for GET requests)
814
+ if (cacheKey) {
815
+ this.cache.setPendingRequest(cacheKey, requestPromise);
816
+ }
817
+
818
+ try {
819
+ return await requestPromise;
820
+ } catch (error) {
821
+ // Call error hook
822
+ options.hooks?.onError?.(error as Error);
823
+ throw error;
824
+ }
825
+ }
826
+
827
+ /**
828
+ * Set default headers
829
+ */
830
+ setDefaultHeaders(headers: Record<string, string>): this {
831
+ this.defaultHeaders = {
832
+ ...this.defaultHeaders,
833
+ ...headers,
834
+ };
835
+ return this;
836
+ }
837
+
838
+ /**
839
+ * Update default header
840
+ */
841
+ updateDefaultHeader(key: string, value: string): this {
842
+ this.defaultHeaders[key] = value;
843
+ return this;
844
+ }
845
+
846
+ /**
847
+ * Remove default header
848
+ */
849
+ removeDefaultHeader(key: string): this {
850
+ delete this.defaultHeaders[key];
851
+ return this;
852
+ }
853
+
854
+ /**
855
+ * Set default timeout
856
+ */
857
+ setDefaultTimeout(timeout: number): this {
858
+ this.defaultTimeout = timeout;
859
+ return this;
860
+ }
861
+
862
+ /**
863
+ * Enable/disable security features
864
+ */
865
+ setSecurityEnabled(enabled: boolean): this {
866
+ this.enableSecurity = enabled;
867
+ return this;
868
+ }
869
+
870
+ /**
871
+ * Enable/disable cache
872
+ */
873
+ setCacheEnabled(enabled: boolean): this {
874
+ this.enableCache = enabled;
875
+ return this;
876
+ }
877
+
878
+ /**
879
+ * Set default cache TTL
880
+ */
881
+ setDefaultCacheTTL(ttl: number): this {
882
+ this.cacheTTL = ttl;
883
+ return this;
884
+ }
885
+
886
+ /**
887
+ * Set authorization header with token
888
+ */
889
+ setAuthToken(token: string, type: string = 'Bearer'): this {
890
+ this.defaultHeaders['Authorization'] = `${type} ${token}`;
891
+ return this;
892
+ }
893
+
894
+ /**
895
+ * Clear authorization header
896
+ */
897
+ clearAuthToken(): this {
898
+ delete this.defaultHeaders['Authorization'];
899
+ return this;
900
+ }
901
+
902
+ /**
903
+ * Set base URL
904
+ */
905
+ setBaseURL(baseURL: string): this {
906
+ this.baseURL = baseURL;
907
+ return this;
908
+ }
909
+
910
+ /**
911
+ * Create a new client instance with merged config
912
+ */
913
+ createInstance(config: RequestInstanceConfig = {}): Request {
914
+ return new Request({
915
+ baseURL: config.baseURL || this.baseURL,
916
+ headers: { ...this.defaultHeaders, ...config.headers },
917
+ timeout: config.timeout || this.defaultTimeout,
918
+ adapter: config.adapter || this.adapter,
919
+ enableCache: config.enableCache !== undefined ? config.enableCache : this.enableCache,
920
+ cacheTTL: config.cacheTTL || this.cacheTTL,
921
+ retry: { ...this.defaultRetryConfig, ...config.retry },
922
+ enableSecurity: config.enableSecurity !== undefined ? config.enableSecurity : this.enableSecurity,
923
+ });
924
+ }
925
+
926
+ /**
927
+ * Quick GET request with simplified API
928
+ */
929
+ quickGet<T = any>(url: string, params?: Record<string, any>, headers?: Record<string, string>): Promise<T> {
930
+ return this.request<T>({
931
+ url,
932
+ method: 'GET',
933
+ params,
934
+ headers,
935
+ });
936
+ }
937
+
938
+ /**
939
+ * Quick POST request with simplified API
940
+ */
941
+ quickPost<T = any>(url: string, data?: any, headers?: Record<string, string>): Promise<T> {
942
+ return this.request<T>({
943
+ url,
944
+ method: 'POST',
945
+ data,
946
+ headers,
947
+ });
948
+ }
949
+ }
950
+
951
+ // Create default instance
952
+ export const request = new Request();
953
+
954
+ // Export for customization - removed default export to maintain naming consistency