nstarter-http-request 0.1.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.
Files changed (263) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +160 -0
  3. package/dist/cjs/adapter/axios.adapter.js +224 -0
  4. package/dist/cjs/adapter/axios.adapter.js.map +1 -0
  5. package/dist/cjs/adapter/base.js +134 -0
  6. package/dist/cjs/adapter/base.js.map +1 -0
  7. package/dist/cjs/adapter/index.js +20 -0
  8. package/dist/cjs/adapter/index.js.map +1 -0
  9. package/dist/cjs/adapter/undici.adapter.js +272 -0
  10. package/dist/cjs/adapter/undici.adapter.js.map +1 -0
  11. package/dist/cjs/client.js +409 -0
  12. package/dist/cjs/client.js.map +1 -0
  13. package/dist/cjs/config.js +69 -0
  14. package/dist/cjs/config.js.map +1 -0
  15. package/dist/cjs/const/config.const.js +53 -0
  16. package/dist/cjs/const/config.const.js.map +1 -0
  17. package/dist/cjs/const/dns.const.js +16 -0
  18. package/dist/cjs/const/dns.const.js.map +1 -0
  19. package/dist/cjs/const/enum.const.js +22 -0
  20. package/dist/cjs/const/enum.const.js.map +1 -0
  21. package/dist/cjs/const/index.js +21 -0
  22. package/dist/cjs/const/index.js.map +1 -0
  23. package/dist/cjs/const/ip.const.js +110 -0
  24. package/dist/cjs/const/ip.const.js.map +1 -0
  25. package/dist/cjs/index.js +25 -0
  26. package/dist/cjs/index.js.map +1 -0
  27. package/dist/cjs/logger/base.js +60 -0
  28. package/dist/cjs/logger/base.js.map +1 -0
  29. package/dist/cjs/logger/http_client.logger.js +209 -0
  30. package/dist/cjs/logger/http_client.logger.js.map +1 -0
  31. package/dist/cjs/logger/index.js +20 -0
  32. package/dist/cjs/logger/index.js.map +1 -0
  33. package/dist/cjs/logger/log.filter.js +126 -0
  34. package/dist/cjs/logger/log.filter.js.map +1 -0
  35. package/dist/cjs/security/dns.validator.js +137 -0
  36. package/dist/cjs/security/dns.validator.js.map +1 -0
  37. package/dist/cjs/security/index.js +21 -0
  38. package/dist/cjs/security/index.js.map +1 -0
  39. package/dist/cjs/security/ip.validator.js +107 -0
  40. package/dist/cjs/security/ip.validator.js.map +1 -0
  41. package/dist/cjs/security/ssrf.guard.js +180 -0
  42. package/dist/cjs/security/ssrf.guard.js.map +1 -0
  43. package/dist/cjs/security/url.validator.js +170 -0
  44. package/dist/cjs/security/url.validator.js.map +1 -0
  45. package/dist/cjs/tsconfig.tsbuildinfo +1 -0
  46. package/dist/cjs/types/adapter.js +6 -0
  47. package/dist/cjs/types/adapter.js.map +1 -0
  48. package/dist/cjs/types/client.js +6 -0
  49. package/dist/cjs/types/client.js.map +1 -0
  50. package/dist/cjs/types/config.js +6 -0
  51. package/dist/cjs/types/config.js.map +1 -0
  52. package/dist/cjs/types/errors.js +35 -0
  53. package/dist/cjs/types/errors.js.map +1 -0
  54. package/dist/cjs/types/index.js +35 -0
  55. package/dist/cjs/types/index.js.map +1 -0
  56. package/dist/cjs/types/ip.js +6 -0
  57. package/dist/cjs/types/ip.js.map +1 -0
  58. package/dist/cjs/types/logger.js +6 -0
  59. package/dist/cjs/types/logger.js.map +1 -0
  60. package/dist/cjs/types/request_response.js +6 -0
  61. package/dist/cjs/types/request_response.js.map +1 -0
  62. package/dist/cjs/types/security.js +6 -0
  63. package/dist/cjs/types/security.js.map +1 -0
  64. package/dist/cjs/types/trace.js +14 -0
  65. package/dist/cjs/types/trace.js.map +1 -0
  66. package/dist/cjs/utils/common.js +31 -0
  67. package/dist/cjs/utils/common.js.map +1 -0
  68. package/dist/cjs/utils/domain.js +79 -0
  69. package/dist/cjs/utils/domain.js.map +1 -0
  70. package/dist/cjs/utils/index.js +44 -0
  71. package/dist/cjs/utils/index.js.map +1 -0
  72. package/dist/cjs/utils/ip.range.js +200 -0
  73. package/dist/cjs/utils/ip.range.js.map +1 -0
  74. package/dist/cjs/utils/trace.context.js +213 -0
  75. package/dist/cjs/utils/trace.context.js.map +1 -0
  76. package/dist/esm/adapter/axios.adapter.js +184 -0
  77. package/dist/esm/adapter/axios.adapter.js.map +1 -0
  78. package/dist/esm/adapter/base.js +130 -0
  79. package/dist/esm/adapter/base.js.map +1 -0
  80. package/dist/esm/adapter/index.js +4 -0
  81. package/dist/esm/adapter/index.js.map +1 -0
  82. package/dist/esm/adapter/undici.adapter.js +235 -0
  83. package/dist/esm/adapter/undici.adapter.js.map +1 -0
  84. package/dist/esm/client.js +405 -0
  85. package/dist/esm/client.js.map +1 -0
  86. package/dist/esm/config.js +65 -0
  87. package/dist/esm/config.js.map +1 -0
  88. package/dist/esm/const/config.const.js +50 -0
  89. package/dist/esm/const/config.const.js.map +1 -0
  90. package/dist/esm/const/dns.const.js +13 -0
  91. package/dist/esm/const/dns.const.js.map +1 -0
  92. package/dist/esm/const/enum.const.js +19 -0
  93. package/dist/esm/const/enum.const.js.map +1 -0
  94. package/dist/esm/const/index.js +5 -0
  95. package/dist/esm/const/index.js.map +1 -0
  96. package/dist/esm/const/ip.const.js +107 -0
  97. package/dist/esm/const/ip.const.js.map +1 -0
  98. package/dist/esm/index.js +9 -0
  99. package/dist/esm/index.js.map +1 -0
  100. package/dist/esm/logger/base.js +55 -0
  101. package/dist/esm/logger/base.js.map +1 -0
  102. package/dist/esm/logger/http_client.logger.js +205 -0
  103. package/dist/esm/logger/http_client.logger.js.map +1 -0
  104. package/dist/esm/logger/index.js +4 -0
  105. package/dist/esm/logger/index.js.map +1 -0
  106. package/dist/esm/logger/log.filter.js +122 -0
  107. package/dist/esm/logger/log.filter.js.map +1 -0
  108. package/dist/esm/security/dns.validator.js +133 -0
  109. package/dist/esm/security/dns.validator.js.map +1 -0
  110. package/dist/esm/security/index.js +5 -0
  111. package/dist/esm/security/index.js.map +1 -0
  112. package/dist/esm/security/ip.validator.js +103 -0
  113. package/dist/esm/security/ip.validator.js.map +1 -0
  114. package/dist/esm/security/ssrf.guard.js +176 -0
  115. package/dist/esm/security/ssrf.guard.js.map +1 -0
  116. package/dist/esm/security/url.validator.js +166 -0
  117. package/dist/esm/security/url.validator.js.map +1 -0
  118. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
  119. package/dist/esm/types/adapter.js +5 -0
  120. package/dist/esm/types/adapter.js.map +1 -0
  121. package/dist/esm/types/client.js +5 -0
  122. package/dist/esm/types/client.js.map +1 -0
  123. package/dist/esm/types/config.js +5 -0
  124. package/dist/esm/types/config.js.map +1 -0
  125. package/dist/esm/types/errors.js +30 -0
  126. package/dist/esm/types/errors.js.map +1 -0
  127. package/dist/esm/types/index.js +19 -0
  128. package/dist/esm/types/index.js.map +1 -0
  129. package/dist/esm/types/ip.js +5 -0
  130. package/dist/esm/types/ip.js.map +1 -0
  131. package/dist/esm/types/logger.js +5 -0
  132. package/dist/esm/types/logger.js.map +1 -0
  133. package/dist/esm/types/request_response.js +5 -0
  134. package/dist/esm/types/request_response.js.map +1 -0
  135. package/dist/esm/types/security.js +5 -0
  136. package/dist/esm/types/security.js.map +1 -0
  137. package/dist/esm/types/trace.js +11 -0
  138. package/dist/esm/types/trace.js.map +1 -0
  139. package/dist/esm/utils/common.js +27 -0
  140. package/dist/esm/utils/common.js.map +1 -0
  141. package/dist/esm/utils/domain.js +71 -0
  142. package/dist/esm/utils/domain.js.map +1 -0
  143. package/dist/esm/utils/index.js +7 -0
  144. package/dist/esm/utils/index.js.map +1 -0
  145. package/dist/esm/utils/ip.range.js +187 -0
  146. package/dist/esm/utils/ip.range.js.map +1 -0
  147. package/dist/esm/utils/trace.context.js +199 -0
  148. package/dist/esm/utils/trace.context.js.map +1 -0
  149. package/dist/types/adapter/axios.adapter.d.ts +51 -0
  150. package/dist/types/adapter/axios.adapter.d.ts.map +1 -0
  151. package/dist/types/adapter/base.d.ts +56 -0
  152. package/dist/types/adapter/base.d.ts.map +1 -0
  153. package/dist/types/adapter/index.d.ts +4 -0
  154. package/dist/types/adapter/index.d.ts.map +1 -0
  155. package/dist/types/adapter/undici.adapter.d.ts +68 -0
  156. package/dist/types/adapter/undici.adapter.d.ts.map +1 -0
  157. package/dist/types/client.d.ts +105 -0
  158. package/dist/types/client.d.ts.map +1 -0
  159. package/dist/types/config.d.ts +14 -0
  160. package/dist/types/config.d.ts.map +1 -0
  161. package/dist/types/const/config.const.d.ts +23 -0
  162. package/dist/types/const/config.const.d.ts.map +1 -0
  163. package/dist/types/const/dns.const.d.ts +13 -0
  164. package/dist/types/const/dns.const.d.ts.map +1 -0
  165. package/dist/types/const/enum.const.d.ts +17 -0
  166. package/dist/types/const/enum.const.d.ts.map +1 -0
  167. package/dist/types/const/index.d.ts +5 -0
  168. package/dist/types/const/index.d.ts.map +1 -0
  169. package/dist/types/const/ip.const.d.ts +42 -0
  170. package/dist/types/const/ip.const.d.ts.map +1 -0
  171. package/dist/types/index.d.ts +9 -0
  172. package/dist/types/index.d.ts.map +1 -0
  173. package/dist/types/logger/base.d.ts +42 -0
  174. package/dist/types/logger/base.d.ts.map +1 -0
  175. package/dist/types/logger/http_client.logger.d.ts +49 -0
  176. package/dist/types/logger/http_client.logger.d.ts.map +1 -0
  177. package/dist/types/logger/index.d.ts +4 -0
  178. package/dist/types/logger/index.d.ts.map +1 -0
  179. package/dist/types/logger/log.filter.d.ts +56 -0
  180. package/dist/types/logger/log.filter.d.ts.map +1 -0
  181. package/dist/types/security/dns.validator.d.ts +61 -0
  182. package/dist/types/security/dns.validator.d.ts.map +1 -0
  183. package/dist/types/security/index.d.ts +5 -0
  184. package/dist/types/security/index.d.ts.map +1 -0
  185. package/dist/types/security/ip.validator.d.ts +31 -0
  186. package/dist/types/security/ip.validator.d.ts.map +1 -0
  187. package/dist/types/security/ssrf.guard.d.ts +54 -0
  188. package/dist/types/security/ssrf.guard.d.ts.map +1 -0
  189. package/dist/types/security/url.validator.d.ts +76 -0
  190. package/dist/types/security/url.validator.d.ts.map +1 -0
  191. package/dist/types/types/adapter.d.ts +30 -0
  192. package/dist/types/types/adapter.d.ts.map +1 -0
  193. package/dist/types/types/client.d.ts +85 -0
  194. package/dist/types/types/client.d.ts.map +1 -0
  195. package/dist/types/types/config.d.ts +99 -0
  196. package/dist/types/types/config.d.ts.map +1 -0
  197. package/dist/types/types/errors.d.ts +23 -0
  198. package/dist/types/types/errors.d.ts.map +1 -0
  199. package/dist/types/types/index.d.ts +10 -0
  200. package/dist/types/types/index.d.ts.map +1 -0
  201. package/dist/types/types/ip.d.ts +32 -0
  202. package/dist/types/types/ip.d.ts.map +1 -0
  203. package/dist/types/types/logger.d.ts +136 -0
  204. package/dist/types/types/logger.d.ts.map +1 -0
  205. package/dist/types/types/request_response.d.ts +54 -0
  206. package/dist/types/types/request_response.d.ts.map +1 -0
  207. package/dist/types/types/security.d.ts +115 -0
  208. package/dist/types/types/security.d.ts.map +1 -0
  209. package/dist/types/types/trace.d.ts +34 -0
  210. package/dist/types/types/trace.d.ts.map +1 -0
  211. package/dist/types/utils/common.d.ts +14 -0
  212. package/dist/types/utils/common.d.ts.map +1 -0
  213. package/dist/types/utils/domain.d.ts +39 -0
  214. package/dist/types/utils/domain.d.ts.map +1 -0
  215. package/dist/types/utils/index.d.ts +6 -0
  216. package/dist/types/utils/index.d.ts.map +1 -0
  217. package/dist/types/utils/ip.range.d.ts +61 -0
  218. package/dist/types/utils/ip.range.d.ts.map +1 -0
  219. package/dist/types/utils/trace.context.d.ts +106 -0
  220. package/dist/types/utils/trace.context.d.ts.map +1 -0
  221. package/docs/adapters.md +53 -0
  222. package/docs/configuration.md +149 -0
  223. package/docs/logging.md +70 -0
  224. package/docs/proxy.md +44 -0
  225. package/docs/security.md +56 -0
  226. package/docs/trace-context.md +436 -0
  227. package/package.json +50 -0
  228. package/src/adapter/axios.adapter.ts +228 -0
  229. package/src/adapter/base.ts +180 -0
  230. package/src/adapter/index.ts +3 -0
  231. package/src/adapter/undici.adapter.ts +282 -0
  232. package/src/client.ts +552 -0
  233. package/src/config.ts +86 -0
  234. package/src/const/config.const.ts +60 -0
  235. package/src/const/dns.const.ts +15 -0
  236. package/src/const/enum.const.ts +17 -0
  237. package/src/const/index.ts +4 -0
  238. package/src/const/ip.const.ts +139 -0
  239. package/src/index.ts +8 -0
  240. package/src/logger/base.ts +75 -0
  241. package/src/logger/http_client.logger.ts +272 -0
  242. package/src/logger/index.ts +3 -0
  243. package/src/logger/log.filter.ts +149 -0
  244. package/src/security/dns.validator.ts +170 -0
  245. package/src/security/index.ts +4 -0
  246. package/src/security/ip.validator.ts +124 -0
  247. package/src/security/ssrf.guard.ts +224 -0
  248. package/src/security/url.validator.ts +192 -0
  249. package/src/types/adapter.ts +38 -0
  250. package/src/types/client.ts +119 -0
  251. package/src/types/config.ts +110 -0
  252. package/src/types/errors.ts +38 -0
  253. package/src/types/index.ts +27 -0
  254. package/src/types/ip.ts +34 -0
  255. package/src/types/logger.ts +150 -0
  256. package/src/types/request_response.ts +65 -0
  257. package/src/types/security.ts +126 -0
  258. package/src/types/trace.ts +35 -0
  259. package/src/utils/common.ts +28 -0
  260. package/src/utils/domain.ts +78 -0
  261. package/src/utils/index.ts +7 -0
  262. package/src/utils/ip.range.ts +218 -0
  263. package/src/utils/trace.context.ts +240 -0
@@ -0,0 +1,17 @@
1
+ /**
2
+ * HTTP请求客户端适配器枚举.
3
+ */
4
+ export enum EClientAdapter {
5
+ 'axios' = 'axios',
6
+ 'undici' = 'undici',
7
+ }
8
+
9
+ /**
10
+ * 日志级别
11
+ */
12
+ export enum ELogLevel {
13
+ DEBUG = 'debug',
14
+ INFO = 'info',
15
+ WARN = 'warn',
16
+ ERROR = 'error'
17
+ }
@@ -0,0 +1,4 @@
1
+ export * from './ip.const';
2
+ export * from './config.const';
3
+ export * from './enum.const';
4
+ export * from './dns.const';
@@ -0,0 +1,139 @@
1
+ import {
2
+ compileIpv4Ranges,
3
+ compileIpv6Ranges,
4
+ } from '../utils/ip.range';
5
+ import type {
6
+ CompiledIpv4Range,
7
+ CompiledIpv6Range
8
+ } from '../types';
9
+
10
+ /**
11
+ * 私有 IP 地址范围(RFC 1918)
12
+ */
13
+ const PRIVATE_IPV4_RANGES = [
14
+ '10.0.0.0/8', // 10.0.0.0 - 10.255.255.255
15
+ '172.16.0.0/12', // 172.16.0.0 - 172.31.255.255
16
+ '192.168.0.0/16', // 192.168.0.0 - 192.168.255.255
17
+ '100.64.0.0/10' // 100.64.0.0 - 100.127.255.255 (Shared Address Space, RFC 6598)
18
+ ];
19
+
20
+ /**
21
+ * 私有 IPv6 地址范围
22
+ */
23
+ const PRIVATE_IPV6_RANGES = [
24
+ 'fc00::/7', // Unique Local Address (ULA)
25
+ 'fd00::/8' // Unique Local Address (ULA)
26
+ ];
27
+
28
+ /**
29
+ * 本地回环地址范围
30
+ */
31
+ const LOOPBACK_IPV4_RANGES = [
32
+ '127.0.0.0/8' // 127.0.0.0 - 127.255.255.255
33
+ ];
34
+
35
+ const LOOPBACK_IPV6_RANGES = [
36
+ '::1/128' // ::1
37
+ ];
38
+
39
+ /**
40
+ * Link-Local 地址范围
41
+ */
42
+ const LINK_LOCAL_IPV4_RANGES = [
43
+ '169.254.0.0/16' // 169.254.0.0 - 169.254.255.255
44
+ ];
45
+
46
+ const LINK_LOCAL_IPV6_RANGES = [
47
+ 'fe80::/10' // fe80:: - febf::
48
+ ];
49
+
50
+ /**
51
+ * 组播地址范围
52
+ */
53
+ const MULTICAST_IPV4_RANGES = [
54
+ '224.0.0.0/4' // 224.0.0.0 - 239.255.255.255
55
+ ];
56
+
57
+ const MULTICAST_IPV6_RANGES = [
58
+ 'ff00::/8' // ff00:: - ffff::
59
+ ];
60
+
61
+ /**
62
+ * 其他保留地址范围
63
+ */
64
+ const RESERVED_IPV4_RANGES = [
65
+ '0.0.0.0/8', // Current network (RFC 1122)
66
+ '192.0.0.0/24', // IETF Protocol Assignments (RFC 6890)
67
+ '192.0.2.0/24', // TEST-NET-1 (RFC 5737)
68
+ '198.18.0.0/15', // Benchmarking (RFC 2544)
69
+ '198.51.100.0/24', // TEST-NET-2 (RFC 5737)
70
+ '203.0.113.0/24', // TEST-NET-3 (RFC 5737)
71
+ '240.0.0.0/4', // Reserved (RFC 1112)
72
+ '255.255.255.255/32' // Broadcast
73
+ ];
74
+
75
+ const RESERVED_IPV6_RANGES = [
76
+ '::/128', // Unspecified
77
+ '::ffff:0:0/96', // IPv4-mapped IPv6
78
+ '64:ff9b::/96', // IPv4/IPv6 translation (RFC 6052)
79
+ '100::/64', // Discard prefix (RFC 6666)
80
+ '2001::/32', // TEREDO (RFC 4380)
81
+ '2001:10::/28', // Deprecated (RFC 4843)
82
+ '2001:db8::/32' // Documentation (RFC 3849)
83
+ ];
84
+
85
+ /**
86
+ * IPV4 校验地址范围(原始 CIDR 字符串)
87
+ */
88
+ export const IPV4_RANGES = {
89
+ private: PRIVATE_IPV4_RANGES,
90
+ loopback: LOOPBACK_IPV4_RANGES,
91
+ linkLocal: LINK_LOCAL_IPV4_RANGES,
92
+ multicast: MULTICAST_IPV4_RANGES,
93
+ reserved: RESERVED_IPV4_RANGES
94
+ };
95
+
96
+ /**
97
+ * IPV6 校验地址范围(原始 CIDR 字符串)
98
+ */
99
+ export const IPV6_RANGES = {
100
+ private: PRIVATE_IPV6_RANGES,
101
+ loopback: LOOPBACK_IPV6_RANGES,
102
+ linkLocal: LINK_LOCAL_IPV6_RANGES,
103
+ multicast: MULTICAST_IPV6_RANGES,
104
+ reserved: RESERVED_IPV6_RANGES
105
+ };
106
+
107
+ /**
108
+ * 预编译的 IPV4 校验地址范围(高性能版本)
109
+ */
110
+ export const COMPILED_IPV4_RANGES: {
111
+ private: CompiledIpv4Range[],
112
+ loopback: CompiledIpv4Range[],
113
+ linkLocal: CompiledIpv4Range[],
114
+ multicast: CompiledIpv4Range[],
115
+ reserved: CompiledIpv4Range[]
116
+ } = {
117
+ private: compileIpv4Ranges(PRIVATE_IPV4_RANGES),
118
+ loopback: compileIpv4Ranges(LOOPBACK_IPV4_RANGES),
119
+ linkLocal: compileIpv4Ranges(LINK_LOCAL_IPV4_RANGES),
120
+ multicast: compileIpv4Ranges(MULTICAST_IPV4_RANGES),
121
+ reserved: compileIpv4Ranges(RESERVED_IPV4_RANGES)
122
+ };
123
+
124
+ /**
125
+ * 预编译的 IPV6 校验地址范围(高性能版本)
126
+ */
127
+ export const COMPILED_IPV6_RANGES: {
128
+ private: CompiledIpv6Range[],
129
+ loopback: CompiledIpv6Range[],
130
+ linkLocal: CompiledIpv6Range[],
131
+ multicast: CompiledIpv6Range[],
132
+ reserved: CompiledIpv6Range[]
133
+ } = {
134
+ private: compileIpv6Ranges(PRIVATE_IPV6_RANGES),
135
+ loopback: compileIpv6Ranges(LOOPBACK_IPV6_RANGES),
136
+ linkLocal: compileIpv6Ranges(LINK_LOCAL_IPV6_RANGES),
137
+ multicast: compileIpv6Ranges(MULTICAST_IPV6_RANGES),
138
+ reserved: compileIpv6Ranges(RESERVED_IPV6_RANGES)
139
+ };
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from './adapter';
2
+ export * from './client';
3
+ export * from './config';
4
+ export * from './logger';
5
+ export * from './security';
6
+ export * from './types';
7
+ export * from './utils';
8
+ export * from './const';
@@ -0,0 +1,75 @@
1
+ /**
2
+ * 日志记录器基础接口和实现
3
+ */
4
+
5
+ import type { ILogger } from '../types';
6
+ import { ELogLevel } from '../const';
7
+
8
+ /**
9
+ * 抽象日志记录器基类
10
+ */
11
+ export abstract class BaseLogger implements ILogger {
12
+ protected minLevel: ELogLevel;
13
+
14
+ constructor(minLevel: ELogLevel = ELogLevel.INFO) {
15
+ this.minLevel = minLevel;
16
+ }
17
+
18
+ /**
19
+ * 检查日志级别是否应该记录
20
+ * @param level 日志级别
21
+ * @returns 是否应该记录
22
+ */
23
+ protected shouldLog(level: ELogLevel): boolean {
24
+ const levels = [ELogLevel.DEBUG, ELogLevel.INFO, ELogLevel.WARN, ELogLevel.ERROR];
25
+ const minLevelIndex = levels.indexOf(this.minLevel);
26
+ const currentLevelIndex = levels.indexOf(level);
27
+ return currentLevelIndex >= minLevelIndex;
28
+ }
29
+
30
+ /**
31
+ * 设置最小日志级别
32
+ * @param level 最小日志级别
33
+ */
34
+ public setMinLevel(level: ELogLevel): void {
35
+ this.minLevel = level;
36
+ }
37
+
38
+ /**
39
+ * 获取当前最小日志级别
40
+ * @returns 最小日志级别
41
+ */
42
+ public getMinLevel(): ELogLevel {
43
+ return this.minLevel;
44
+ }
45
+
46
+ public abstract debug(message: string, meta?: Record<string, any>): void;
47
+
48
+ public abstract info(message: string, meta?: Record<string, any>): void;
49
+
50
+ public abstract warn(message: string, meta?: Record<string, any>): void;
51
+
52
+ public abstract error(message: string, meta?: Record<string, any>): void;
53
+ }
54
+
55
+ /**
56
+ * 空日志记录器(不记录任何日志)
57
+ */
58
+ export class NullLogger implements ILogger {
59
+ public debug(): void {
60
+ // 不做任何事
61
+ }
62
+
63
+ public info(): void {
64
+ // 不做任何事
65
+ }
66
+
67
+ public warn(): void {
68
+ // 不做任何事
69
+ }
70
+
71
+ public error(): void {
72
+ // 不做任何事
73
+ }
74
+ }
75
+
@@ -0,0 +1,272 @@
1
+ /**
2
+ * HTTP 客户端日志记录器实现
3
+ */
4
+
5
+ import type {
6
+ IHttpClientLogger,
7
+ ILogger,
8
+ ILogFilter,
9
+ IResFailedOptions,
10
+ IResSuccessOptions,
11
+ LogFilterConfig,
12
+ HttpClientLoggerConfig
13
+ } from '../types';
14
+ import { LogFilter } from './log.filter';
15
+
16
+ /**
17
+ * HTTP 客户端日志记录器
18
+ * 将通用 ILogger 包装成专门的 IHttpClientLogger
19
+ */
20
+ export class NsHttpClientLogger implements IHttpClientLogger {
21
+ private logger: ILogger;
22
+ private filter?: ILogFilter;
23
+ private logRequestBody: boolean;
24
+ private logResponseBody: boolean;
25
+ private bodyMaxLength: number;
26
+
27
+ constructor(config: HttpClientLoggerConfig) {
28
+ this.logger = config.logger;
29
+ this.filter = config.filter;
30
+ this.logRequestBody = config.logRequestBody ?? false;
31
+ this.logResponseBody = config.logResponseBody ?? false;
32
+ this.bodyMaxLength = config.bodyMaxLength ?? 1024;
33
+ }
34
+
35
+ /**
36
+ * 记录失败的请求
37
+ */
38
+ public logFailed(
39
+ resOptions: Omit<IResFailedOptions, 'isSuccess' | 'timestamp'>
40
+ ): void {
41
+ const fullOptions: IResFailedOptions = {
42
+ ...resOptions,
43
+ isSuccess: false,
44
+ timestamp: Date.now()
45
+ };
46
+
47
+ // 应用过滤器
48
+ if (this.filter && !this.filter.shouldLog(fullOptions)) {
49
+ return;
50
+ }
51
+
52
+ const meta = this.buildLogMeta(fullOptions);
53
+
54
+ this.logger.error(
55
+ `HTTP request failed: ${ fullOptions.request.method } ${ fullOptions.request.url }`,
56
+ meta
57
+ );
58
+ }
59
+
60
+ /**
61
+ * 记录成功的请求
62
+ */
63
+ public logSuccess(
64
+ resOptions: Omit<IResSuccessOptions, 'isSuccess' | 'timestamp'>
65
+ ): void {
66
+ const fullOptions: IResSuccessOptions = {
67
+ ...resOptions,
68
+ isSuccess: true,
69
+ timestamp: Date.now()
70
+ };
71
+
72
+ // 应用过滤器
73
+ if (this.filter && !this.filter.shouldLog(fullOptions)) {
74
+ return;
75
+ }
76
+
77
+ const meta = this.buildLogMeta(fullOptions);
78
+
79
+ this.logger.info(
80
+ `HTTP request succeeded: ${ fullOptions.request.method } ${ fullOptions.request.url }`,
81
+ meta
82
+ );
83
+ }
84
+
85
+ /**
86
+ * 构建日志元数据
87
+ */
88
+ private buildLogMeta(
89
+ options: IResSuccessOptions | IResFailedOptions
90
+ ): Record<string, any> {
91
+ const meta: Record<string, any> = {
92
+ method: options.request.method,
93
+ url: options.request.url,
94
+ duration: options.duration,
95
+ timestamp: options.timestamp,
96
+ retryCount: options.retryCount
97
+ };
98
+
99
+ // 添加场景信息
100
+ if (options.request.scenario) {
101
+ meta.scenario = options.request.scenario;
102
+ }
103
+
104
+ // 添加请求元数据
105
+ if (options.request.meta) {
106
+ meta.requestMeta = options.request.meta;
107
+ }
108
+
109
+ if (options.isSuccess) {
110
+ // 成功的请求
111
+ meta.status = options.response.status;
112
+ meta.statusText = options.response.statusText;
113
+
114
+ // 记录流式响应指标
115
+ if (options.streamMetrics) {
116
+ meta.streamMetrics = {
117
+ ttfb: options.streamMetrics.ttfb,
118
+ completed: options.streamMetrics.completed
119
+ };
120
+
121
+ if (options.streamMetrics.completed) {
122
+ meta.streamMetrics.totalDuration = options.streamMetrics.totalDuration;
123
+ meta.streamMetrics.streamDuration =
124
+ (options.streamMetrics.totalDuration ?? 0) - options.streamMetrics.ttfb;
125
+
126
+ if (options.streamMetrics.bytesTransferred !== undefined) {
127
+ meta.streamMetrics.bytesTransferred = options.streamMetrics.bytesTransferred;
128
+ meta.streamMetrics.bytesTransferredHuman =
129
+ this.formatBytes(options.streamMetrics.bytesTransferred);
130
+
131
+ // 计算吞吐量
132
+ const streamDuration = meta.streamMetrics.streamDuration;
133
+ if (streamDuration > 0) {
134
+ const throughputBps =
135
+ options.streamMetrics.bytesTransferred / (streamDuration / 1000);
136
+ meta.streamMetrics.throughput = `${ (throughputBps / 1024).toFixed(2) } KB/s`;
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ // 记录响应体(如果启用,且不是流式响应)
143
+ if (this.logResponseBody && !options.streamMetrics) {
144
+ meta.responseBody = this.truncateBody(options.response.data);
145
+ } else if (options.streamMetrics && !options.streamMetrics.completed) {
146
+ // 流式响应但未完成,标记为流
147
+ meta.responseBody = '# --- stream (in progress) --- #';
148
+ } else if (options.streamMetrics && options.streamMetrics.completed) {
149
+ // 流式响应已完成
150
+ meta.responseBody = '# --- stream (completed) --- #';
151
+ }
152
+ } else {
153
+ // 失败的请求
154
+ meta.error = options.error.message;
155
+ meta.errorCode = options.errorCode;
156
+
157
+ if (options.status !== undefined) {
158
+ meta.status = options.status;
159
+ }
160
+
161
+ if (options.response) {
162
+ meta.responseBody = this.truncateBody(options.response.data);
163
+ }
164
+ }
165
+
166
+ // 记录请求体(如果启用)
167
+ if (this.logRequestBody) {
168
+ meta.requestBody = this.truncateBody(options.request.body);
169
+ }
170
+
171
+ return meta;
172
+ }
173
+
174
+ /**
175
+ * 格式化字节数为人类可读格式
176
+ */
177
+ private formatBytes(bytes: number): string {
178
+ if (bytes === 0) {
179
+ return '0 Bytes';
180
+ }
181
+
182
+ const k = 1024;
183
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
184
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
185
+
186
+ return `${ parseFloat((bytes / Math.pow(k, i)).toFixed(2)) } ${ sizes[i] }`;
187
+ }
188
+
189
+ /**
190
+ * 截断请求/响应体到指定长度
191
+ * 对于特殊类型(Stream、Buffer等)返回标记文本
192
+ */
193
+ private truncateBody(body: any): any {
194
+ // 处理 null 和 undefined
195
+ if (body === null) {
196
+ return '# --- null --- #';
197
+ }
198
+ if (body === undefined) {
199
+ return '# --- undefined --- #';
200
+ }
201
+
202
+ // 处理基本类型
203
+ if (typeof body === 'number' || typeof body === 'boolean') {
204
+ return body;
205
+ }
206
+
207
+ // 处理字符串
208
+ if (typeof body === 'string') {
209
+ if (body.length <= this.bodyMaxLength) {
210
+ return body;
211
+ }
212
+ return body.substring(0, this.bodyMaxLength) + '... (truncated)';
213
+ }
214
+
215
+ // 处理 Buffer
216
+ if (Buffer.isBuffer(body)) {
217
+ return `# --- buffer (${ body.length } bytes) --- #`;
218
+ }
219
+
220
+ // 处理 Stream
221
+ if (typeof body === 'object' && typeof body.pipe === 'function') {
222
+ return '# --- stream --- #';
223
+ }
224
+
225
+ // 处理 ArrayBuffer
226
+ if (body instanceof ArrayBuffer ||
227
+ (typeof body === 'object' && body.constructor?.name === 'ArrayBuffer')) {
228
+ return `# --- arraybuffer (${ body.byteLength } bytes) --- #`;
229
+ }
230
+
231
+ // 处理 Blob (浏览器环境)
232
+ if (typeof body === 'object' && body.constructor?.name === 'Blob') {
233
+ return `# --- blob (${ body.size } bytes) --- #`;
234
+ }
235
+
236
+ // 处理普通对象和数组
237
+ try {
238
+ const bodyStr = JSON.stringify(body);
239
+ if (bodyStr.length <= this.bodyMaxLength) {
240
+ return body;
241
+ }
242
+ return bodyStr.substring(0, this.bodyMaxLength) + '... (truncated)';
243
+ } catch {
244
+ return '[unable to serialize body]';
245
+ }
246
+ }
247
+
248
+ /**
249
+ * 创建 HTTP 客户端日志记录器
250
+ * @param logger 底层日志记录器
251
+ * @param filterConfig 过滤器配置
252
+ * @param options 其他选项
253
+ */
254
+ public static create(
255
+ logger: ILogger,
256
+ filterConfig?: LogFilterConfig,
257
+ options?: {
258
+ logRequestBody?: boolean,
259
+ logResponseBody?: boolean,
260
+ bodyMaxLength?: number
261
+ }
262
+ ): NsHttpClientLogger {
263
+ return new NsHttpClientLogger({
264
+ logger,
265
+ filter: filterConfig ? new LogFilter(filterConfig) : undefined,
266
+ logRequestBody: options?.logRequestBody,
267
+ logResponseBody: options?.logResponseBody,
268
+ bodyMaxLength: options?.bodyMaxLength
269
+ });
270
+ }
271
+ }
272
+
@@ -0,0 +1,3 @@
1
+ export * from './base';
2
+ export * from './http_client.logger';
3
+ export * from './log.filter';
@@ -0,0 +1,149 @@
1
+ /**
2
+ * 日志过滤器实现
3
+ */
4
+
5
+ import type {
6
+ ILogFilter,
7
+ LogFilterConfig,
8
+ IResSuccessOptions,
9
+ IResFailedOptions
10
+ } from '../types';
11
+
12
+ /**
13
+ * 默认日志过滤器
14
+ */
15
+ export class LogFilter implements ILogFilter {
16
+ private config: LogFilterConfig;
17
+
18
+ constructor(config: LogFilterConfig = {}) {
19
+ this.config = config;
20
+ }
21
+
22
+ /**
23
+ * 判断是否应该记录此日志
24
+ * @param options 日志选项(成功或失败)
25
+ * @returns 是否应该记录
26
+ */
27
+ public shouldLog(options: IResSuccessOptions | IResFailedOptions): boolean {
28
+ return (
29
+ this.checkSuccessOrFailedFilter(options) &&
30
+ this.checkMethodFilter(options) &&
31
+ this.checkUrlPatternFilter(options) &&
32
+ this.checkScenarioFilter(options) &&
33
+ this.checkStatusCodeFilter(options) &&
34
+ this.checkDurationFilter(options) &&
35
+ this.checkCustomFilter(options)
36
+ );
37
+ }
38
+
39
+ /**
40
+ * 检查成功/失败过滤
41
+ */
42
+ private checkSuccessOrFailedFilter(options: IResSuccessOptions | IResFailedOptions): boolean {
43
+ if (this.config.onlyFailed && options.isSuccess) {
44
+ return false;
45
+ }
46
+ if (this.config.onlySuccess && !options.isSuccess) {
47
+ return false;
48
+ }
49
+ return true;
50
+ }
51
+
52
+ /**
53
+ * 检查 HTTP 方法过滤
54
+ */
55
+ private checkMethodFilter(options: IResSuccessOptions | IResFailedOptions): boolean {
56
+ if (this.config.methods && this.config.methods.length > 0) {
57
+ return this.config.methods.includes(options.request.method);
58
+ }
59
+ return true;
60
+ }
61
+
62
+ /**
63
+ * 检查 URL 模式过滤
64
+ */
65
+ private checkUrlPatternFilter(options: IResSuccessOptions | IResFailedOptions): boolean {
66
+ if (this.config.urlPatterns && this.config.urlPatterns.length > 0) {
67
+ return this.config.urlPatterns.some(pattern => {
68
+ if (typeof pattern === 'string') {
69
+ return options.request.url.includes(pattern);
70
+ }
71
+ return pattern.test(options.request.url);
72
+ });
73
+ }
74
+ return true;
75
+ }
76
+
77
+ /**
78
+ * 检查场景过滤
79
+ */
80
+ private checkScenarioFilter(options: IResSuccessOptions | IResFailedOptions): boolean {
81
+ if (this.config.scenarios && this.config.scenarios.length > 0) {
82
+ return !!options.request.scenario && this.config.scenarios.includes(options.request.scenario);
83
+ }
84
+ return true;
85
+ }
86
+
87
+ /**
88
+ * 检查状态码过滤
89
+ */
90
+ private checkStatusCodeFilter(options: IResSuccessOptions | IResFailedOptions): boolean {
91
+ const status = options.isSuccess ? options.response.status : options.status;
92
+ if (status === undefined) {
93
+ return true;
94
+ }
95
+
96
+ // 检查特定状态码
97
+ if (this.config.statusCodes && this.config.statusCodes.length > 0) {
98
+ if (!this.config.statusCodes.includes(status)) {
99
+ return false;
100
+ }
101
+ }
102
+
103
+ // 检查状态码范围
104
+ if (this.config.statusCodeRanges && this.config.statusCodeRanges.length > 0) {
105
+ return this.config.statusCodeRanges.some(
106
+ range => status >= range.min && status <= range.max
107
+ );
108
+ }
109
+
110
+ return true;
111
+ }
112
+
113
+ /**
114
+ * 检查请求耗时过滤
115
+ */
116
+ private checkDurationFilter(options: IResSuccessOptions | IResFailedOptions): boolean {
117
+ if (this.config.minDuration !== undefined && options.duration !== undefined) {
118
+ return options.duration >= this.config.minDuration;
119
+ }
120
+ return true;
121
+ }
122
+
123
+ /**
124
+ * 检查自定义过滤函数
125
+ */
126
+ private checkCustomFilter(options: IResSuccessOptions | IResFailedOptions): boolean {
127
+ if (this.config.customFilter) {
128
+ return this.config.customFilter(options);
129
+ }
130
+ return true;
131
+ }
132
+
133
+ /**
134
+ * 更新过滤器配置
135
+ * @param config 新的配置
136
+ */
137
+ public updateConfig(config: LogFilterConfig): void {
138
+ this.config = { ...this.config, ...config };
139
+ }
140
+
141
+ /**
142
+ * 获取当前配置
143
+ * @returns 当前配置
144
+ */
145
+ public getConfig(): LogFilterConfig {
146
+ return { ...this.config };
147
+ }
148
+ }
149
+