@c9up/ream 0.2.28 → 0.2.30

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 (293) hide show
  1. package/README.md +5 -6
  2. package/dist/Codemods.d.ts +24 -0
  3. package/dist/Codemods.d.ts.map +1 -1
  4. package/dist/Codemods.js +211 -1
  5. package/dist/Codemods.js.map +1 -1
  6. package/dist/HttpKernel.d.ts.map +1 -1
  7. package/dist/HttpKernel.js +17 -5
  8. package/dist/HttpKernel.js.map +1 -1
  9. package/dist/Ignitor.d.ts +15 -2
  10. package/dist/Ignitor.d.ts.map +1 -1
  11. package/dist/Ignitor.js +33 -23
  12. package/dist/Ignitor.js.map +1 -1
  13. package/dist/bodyparser/BodyParserMiddleware.d.ts +4 -4
  14. package/dist/bodyparser/BodyParserMiddleware.d.ts.map +1 -1
  15. package/dist/bodyparser/BodyParserMiddleware.js +41 -11
  16. package/dist/bodyparser/BodyParserMiddleware.js.map +1 -1
  17. package/dist/bodyparser/MultipartFile.d.ts +21 -15
  18. package/dist/bodyparser/MultipartFile.d.ts.map +1 -1
  19. package/dist/bodyparser/MultipartFile.js +40 -58
  20. package/dist/bodyparser/MultipartFile.js.map +1 -1
  21. package/dist/bodyparser/parseSize.d.ts +7 -4
  22. package/dist/bodyparser/parseSize.d.ts.map +1 -1
  23. package/dist/bodyparser/parseSize.js +12 -17
  24. package/dist/bodyparser/parseSize.js.map +1 -1
  25. package/dist/bodyparser/qsParse.d.ts +20 -1
  26. package/dist/bodyparser/qsParse.d.ts.map +1 -1
  27. package/dist/bodyparser/qsParse.js +78 -25
  28. package/dist/bodyparser/qsParse.js.map +1 -1
  29. package/dist/codemods/source.d.ts +57 -0
  30. package/dist/codemods/source.d.ts.map +1 -0
  31. package/dist/codemods/source.js +282 -0
  32. package/dist/codemods/source.js.map +1 -0
  33. package/dist/console/prompts.d.ts.map +1 -1
  34. package/dist/console/prompts.js +3 -1
  35. package/dist/console/prompts.js.map +1 -1
  36. package/dist/container/Container.d.ts.map +1 -1
  37. package/dist/container/Container.js +61 -71
  38. package/dist/container/Container.js.map +1 -1
  39. package/dist/events/Emitter.d.ts +12 -0
  40. package/dist/events/Emitter.d.ts.map +1 -1
  41. package/dist/events/Emitter.js +36 -9
  42. package/dist/events/Emitter.js.map +1 -1
  43. package/dist/events/helix/helpers.d.ts.map +1 -1
  44. package/dist/events/helix/helpers.js +19 -9
  45. package/dist/events/helix/helpers.js.map +1 -1
  46. package/dist/graphql/GraphQLEngine.d.ts +8 -0
  47. package/dist/graphql/GraphQLEngine.d.ts.map +1 -1
  48. package/dist/graphql/GraphQLEngine.js +79 -12
  49. package/dist/graphql/GraphQLEngine.js.map +1 -1
  50. package/dist/helpers/MessageBuilder.d.ts +4 -2
  51. package/dist/helpers/MessageBuilder.d.ts.map +1 -1
  52. package/dist/helpers/MessageBuilder.js +4 -2
  53. package/dist/helpers/MessageBuilder.js.map +1 -1
  54. package/dist/helpers/errors.d.ts +11 -0
  55. package/dist/helpers/errors.d.ts.map +1 -0
  56. package/dist/helpers/errors.js +17 -0
  57. package/dist/helpers/errors.js.map +1 -0
  58. package/dist/helpers/napi-loader.d.ts.map +1 -1
  59. package/dist/helpers/napi-loader.js +3 -2
  60. package/dist/helpers/napi-loader.js.map +1 -1
  61. package/dist/http/Exception.d.ts +14 -1
  62. package/dist/http/Exception.d.ts.map +1 -1
  63. package/dist/http/Exception.js +10 -2
  64. package/dist/http/Exception.js.map +1 -1
  65. package/dist/http/HttpContext.d.ts +31 -2
  66. package/dist/http/HttpContext.d.ts.map +1 -1
  67. package/dist/http/HttpContext.js +12 -5
  68. package/dist/http/HttpContext.js.map +1 -1
  69. package/dist/http/RedirectBuilder.d.ts +9 -3
  70. package/dist/http/RedirectBuilder.d.ts.map +1 -1
  71. package/dist/http/RedirectBuilder.js +43 -37
  72. package/dist/http/RedirectBuilder.js.map +1 -1
  73. package/dist/http/Request.d.ts +42 -17
  74. package/dist/http/Request.d.ts.map +1 -1
  75. package/dist/http/Request.js +80 -58
  76. package/dist/http/Request.js.map +1 -1
  77. package/dist/http/Response.d.ts +7 -4
  78. package/dist/http/Response.d.ts.map +1 -1
  79. package/dist/http/Response.js +14 -6
  80. package/dist/http/Response.js.map +1 -1
  81. package/dist/http/SseStream.js +6 -7
  82. package/dist/http/SseStream.js.map +1 -1
  83. package/dist/http/permissions.d.ts +25 -0
  84. package/dist/http/permissions.d.ts.map +1 -0
  85. package/dist/http/permissions.js +38 -0
  86. package/dist/http/permissions.js.map +1 -0
  87. package/dist/http-client/HttpClient.d.ts +111 -0
  88. package/dist/http-client/HttpClient.d.ts.map +1 -0
  89. package/dist/http-client/HttpClient.js +386 -0
  90. package/dist/http-client/HttpClient.js.map +1 -0
  91. package/dist/http-client/addresses.d.ts +14 -0
  92. package/dist/http-client/addresses.d.ts.map +1 -0
  93. package/dist/http-client/addresses.js +57 -0
  94. package/dist/http-client/addresses.js.map +1 -0
  95. package/dist/http-client/fake.d.ts +50 -0
  96. package/dist/http-client/fake.d.ts.map +1 -0
  97. package/dist/http-client/fake.js +95 -0
  98. package/dist/http-client/fake.js.map +1 -0
  99. package/dist/http-client/index.d.ts +7 -0
  100. package/dist/http-client/index.d.ts.map +1 -0
  101. package/dist/http-client/index.js +7 -0
  102. package/dist/http-client/index.js.map +1 -0
  103. package/dist/index.d.ts +4 -3
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +1 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/middleware/Pipeline.d.ts.map +1 -1
  108. package/dist/middleware/Pipeline.js +6 -3
  109. package/dist/middleware/Pipeline.js.map +1 -1
  110. package/dist/native/generated.d.ts +93 -36
  111. package/dist/native/generated.d.ts.map +1 -1
  112. package/dist/profiler/Profiler.d.ts.map +1 -1
  113. package/dist/profiler/Profiler.js +4 -1
  114. package/dist/profiler/Profiler.js.map +1 -1
  115. package/dist/profiler/client.generated.d.ts +5 -0
  116. package/dist/profiler/client.generated.d.ts.map +1 -0
  117. package/dist/profiler/client.generated.js +6 -0
  118. package/dist/profiler/client.generated.js.map +1 -0
  119. package/dist/profiler/collectors.d.ts +92 -13
  120. package/dist/profiler/collectors.d.ts.map +1 -1
  121. package/dist/profiler/collectors.js +106 -19
  122. package/dist/profiler/collectors.js.map +1 -1
  123. package/dist/profiler/endpoint.d.ts +2 -1
  124. package/dist/profiler/endpoint.d.ts.map +1 -1
  125. package/dist/profiler/endpoint.js +25 -1
  126. package/dist/profiler/endpoint.js.map +1 -1
  127. package/dist/profiler/index.d.ts +8 -4
  128. package/dist/profiler/index.d.ts.map +1 -1
  129. package/dist/profiler/index.js +11 -4
  130. package/dist/profiler/index.js.map +1 -1
  131. package/dist/profiler/middleware.d.ts.map +1 -1
  132. package/dist/profiler/middleware.js +76 -10
  133. package/dist/profiler/middleware.js.map +1 -1
  134. package/dist/profiler/preview.d.ts +17 -0
  135. package/dist/profiler/preview.d.ts.map +1 -0
  136. package/dist/profiler/preview.js +100 -0
  137. package/dist/profiler/preview.js.map +1 -0
  138. package/dist/profiler/toolbar.d.ts +15 -23
  139. package/dist/profiler/toolbar.d.ts.map +1 -1
  140. package/dist/profiler/toolbar.js +25 -91
  141. package/dist/profiler/toolbar.js.map +1 -1
  142. package/dist/profiler/types.d.ts +6 -1
  143. package/dist/profiler/types.d.ts.map +1 -1
  144. package/dist/router/Router.d.ts +6 -0
  145. package/dist/router/Router.d.ts.map +1 -1
  146. package/dist/router/Router.js +115 -12
  147. package/dist/router/Router.js.map +1 -1
  148. package/dist/rpc/RpcProvider.d.ts +6 -0
  149. package/dist/rpc/RpcProvider.d.ts.map +1 -1
  150. package/dist/rpc/RpcProvider.js +4 -1
  151. package/dist/rpc/RpcProvider.js.map +1 -1
  152. package/dist/rpc/RpcRouter.d.ts +27 -14
  153. package/dist/rpc/RpcRouter.d.ts.map +1 -1
  154. package/dist/rpc/RpcRouter.js +97 -40
  155. package/dist/rpc/RpcRouter.js.map +1 -1
  156. package/dist/scheduler/Scheduler.js +1 -1
  157. package/dist/scheduler/Scheduler.js.map +1 -1
  158. package/dist/scheduler/locks/RedisLockBackend.d.ts.map +1 -1
  159. package/dist/scheduler/locks/RedisLockBackend.js +13 -2
  160. package/dist/scheduler/locks/RedisLockBackend.js.map +1 -1
  161. package/dist/security/CookieSigner.d.ts.map +1 -1
  162. package/dist/security/CookieSigner.js +32 -8
  163. package/dist/security/CookieSigner.js.map +1 -1
  164. package/dist/security/SignedUrl.d.ts.map +1 -1
  165. package/dist/security/SignedUrl.js +16 -19
  166. package/dist/security/SignedUrl.js.map +1 -1
  167. package/dist/server/Server.d.ts +44 -12
  168. package/dist/server/Server.d.ts.map +1 -1
  169. package/dist/server/Server.js +31 -10
  170. package/dist/server/Server.js.map +1 -1
  171. package/dist/server/nativeGrpc.d.ts +27 -0
  172. package/dist/server/nativeGrpc.d.ts.map +1 -0
  173. package/dist/server/nativeGrpc.js +47 -0
  174. package/dist/server/nativeGrpc.js.map +1 -0
  175. package/dist/server/nativeLimits.d.ts +57 -0
  176. package/dist/server/nativeLimits.d.ts.map +1 -0
  177. package/dist/server/nativeLimits.js +75 -0
  178. package/dist/server/nativeLimits.js.map +1 -0
  179. package/dist/session/Session.d.ts +12 -0
  180. package/dist/session/Session.d.ts.map +1 -1
  181. package/dist/session/Session.js +13 -4
  182. package/dist/session/Session.js.map +1 -1
  183. package/dist/session/SessionMiddleware.d.ts.map +1 -1
  184. package/dist/session/SessionMiddleware.js +28 -9
  185. package/dist/session/SessionMiddleware.js.map +1 -1
  186. package/dist/session/config.d.ts +5 -0
  187. package/dist/session/config.d.ts.map +1 -1
  188. package/dist/session/config.js +8 -0
  189. package/dist/session/config.js.map +1 -1
  190. package/dist/session/drivers/MemoryDriver.d.ts.map +1 -1
  191. package/dist/session/drivers/MemoryDriver.js +26 -7
  192. package/dist/session/drivers/MemoryDriver.js.map +1 -1
  193. package/dist/session/errors.d.ts +17 -0
  194. package/dist/session/errors.d.ts.map +1 -1
  195. package/dist/session/errors.js +21 -0
  196. package/dist/session/errors.js.map +1 -1
  197. package/dist/session/templateTags.d.ts +14 -0
  198. package/dist/session/templateTags.d.ts.map +1 -1
  199. package/dist/session/templateTags.js +45 -17
  200. package/dist/session/templateTags.js.map +1 -1
  201. package/dist/storage/StaticMiddleware.d.ts.map +1 -1
  202. package/dist/storage/StaticMiddleware.js +49 -27
  203. package/dist/storage/StaticMiddleware.js.map +1 -1
  204. package/dist/testing/ApiResponse.d.ts.map +1 -1
  205. package/dist/testing/ApiResponse.js +7 -1
  206. package/dist/testing/ApiResponse.js.map +1 -1
  207. package/dist/testing/TestClient.d.ts.map +1 -1
  208. package/dist/testing/TestClient.js +16 -4
  209. package/dist/testing/TestClient.js.map +1 -1
  210. package/dist/utils/Macroable.d.ts +1 -1
  211. package/dist/utils/Macroable.js +2 -2
  212. package/dist/utils/Macroable.js.map +1 -1
  213. package/events.darwin-arm64.node +0 -0
  214. package/events.darwin-x64.node +0 -0
  215. package/events.linux-arm64-gnu.node +0 -0
  216. package/events.linux-x64-gnu.node +0 -0
  217. package/events.win32-x64-msvc.node +0 -0
  218. package/index.darwin-arm64.node +0 -0
  219. package/index.darwin-x64.node +0 -0
  220. package/index.linux-arm64-gnu.node +0 -0
  221. package/index.linux-x64-gnu.node +0 -0
  222. package/index.win32-x64-msvc.node +0 -0
  223. package/package.json +11 -3
  224. package/scheduler.darwin-arm64.node +0 -0
  225. package/scheduler.darwin-x64.node +0 -0
  226. package/scheduler.linux-arm64-gnu.node +0 -0
  227. package/scheduler.linux-x64-gnu.node +0 -0
  228. package/scheduler.win32-x64-msvc.node +0 -0
  229. package/src/Codemods.ts +314 -3
  230. package/src/HttpKernel.ts +18 -7
  231. package/src/Ignitor.ts +58 -26
  232. package/src/bodyparser/BodyParserMiddleware.ts +45 -16
  233. package/src/bodyparser/MultipartFile.ts +46 -67
  234. package/src/bodyparser/parseSize.ts +17 -17
  235. package/src/bodyparser/qsParse.ts +96 -26
  236. package/src/codemods/source.ts +303 -0
  237. package/src/console/prompts.ts +3 -1
  238. package/src/container/Container.ts +97 -81
  239. package/src/events/Emitter.ts +37 -10
  240. package/src/events/helix/helpers.ts +24 -19
  241. package/src/graphql/GraphQLEngine.ts +91 -14
  242. package/src/helpers/MessageBuilder.ts +4 -2
  243. package/src/helpers/errors.ts +16 -0
  244. package/src/helpers/napi-loader.ts +3 -2
  245. package/src/http/Exception.ts +17 -2
  246. package/src/http/HttpContext.ts +34 -5
  247. package/src/http/RedirectBuilder.ts +51 -37
  248. package/src/http/Request.ts +102 -58
  249. package/src/http/Response.ts +15 -7
  250. package/src/http/SseStream.ts +6 -7
  251. package/src/http/permissions.ts +47 -0
  252. package/src/http-client/HttpClient.ts +505 -0
  253. package/src/http-client/addresses.ts +58 -0
  254. package/src/http-client/fake.ts +141 -0
  255. package/src/http-client/index.ts +21 -0
  256. package/src/index.ts +6 -1
  257. package/src/middleware/Pipeline.ts +7 -3
  258. package/src/native/generated.ts +106 -48
  259. package/src/profiler/Profiler.ts +4 -1
  260. package/src/profiler/client/toolbar.css +595 -0
  261. package/src/profiler/client/toolbar.js +1299 -0
  262. package/src/profiler/client.generated.ts +7 -0
  263. package/src/profiler/collectors.ts +156 -19
  264. package/src/profiler/endpoint.ts +27 -1
  265. package/src/profiler/index.ts +17 -3
  266. package/src/profiler/middleware.ts +74 -9
  267. package/src/profiler/preview.ts +104 -0
  268. package/src/profiler/toolbar.ts +28 -91
  269. package/src/profiler/types.ts +6 -1
  270. package/src/router/Router.ts +136 -13
  271. package/src/rpc/RpcProvider.ts +10 -1
  272. package/src/rpc/RpcRouter.ts +129 -45
  273. package/src/scheduler/Scheduler.ts +1 -1
  274. package/src/scheduler/locks/RedisLockBackend.ts +12 -4
  275. package/src/security/CookieSigner.ts +30 -8
  276. package/src/security/SignedUrl.ts +18 -17
  277. package/src/server/Server.ts +60 -12
  278. package/src/server/nativeGrpc.ts +59 -0
  279. package/src/server/nativeLimits.ts +111 -0
  280. package/src/session/Session.ts +29 -4
  281. package/src/session/SessionMiddleware.ts +30 -10
  282. package/src/session/config.ts +7 -0
  283. package/src/session/drivers/MemoryDriver.ts +26 -7
  284. package/src/session/errors.ts +31 -0
  285. package/src/session/templateTags.ts +57 -0
  286. package/src/storage/StaticMiddleware.ts +46 -27
  287. package/src/testing/ApiResponse.ts +7 -1
  288. package/src/testing/TestClient.ts +14 -5
  289. package/src/utils/Macroable.ts +2 -2
  290. package/scripts/build-napi-types.mjs +0 -69
  291. package/scripts/copy-napi.mjs +0 -121
  292. package/scripts/generate-mime.mjs +0 -119
  293. package/scripts/generate-napi-types.mjs +0 -187
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Outbound HTTP for server code that calls a URL it does not fully control —
3
+ * a customer's domain, a webhook target, a CMS the user pointed the app at.
4
+ *
5
+ * `fetch` connects wherever DNS says. A domain that resolves to `10.0.0.5`,
6
+ * `127.0.0.1` or `169.254.169.254` makes the server read its own network on
7
+ * the attacker's behalf (SSRF). Checking the address before the call is not
8
+ * enough: a DNS answer can change between the check and the connection
9
+ * (rebinding). So the check runs AT CONNECT TIME, inside the `lookup` of the
10
+ * client's own agents — the address that is checked is the address the
11
+ * socket connects to. The agents belong to this client alone, so a pooled
12
+ * socket opened by other code is never reused.
13
+ *
14
+ * Also bounded: one deadline for the whole exchange (redirects included), a
15
+ * maximum response size, and a few redirects, to the same host by default.
16
+ * Every failure is an {@link HttpClientError} with a `reason`.
17
+ *
18
+ * No AdonisJS counterpart: the framework ships no outbound client.
19
+ */
20
+ import { lookup as dnsLookup } from 'node:dns';
21
+ import http from 'node:http';
22
+ import https from 'node:https';
23
+ import { isIP } from 'node:net';
24
+ import { ReamError } from '../errors/ReamError.js';
25
+ import { isPublicAddress } from './addresses.js';
26
+ const CODES = {
27
+ invalidUrl: 'E_HTTP_CLIENT_INVALID_URL',
28
+ privateAddress: 'E_HTTP_CLIENT_PRIVATE_ADDRESS',
29
+ unreachable: 'E_HTTP_CLIENT_UNREACHABLE',
30
+ timedOut: 'E_HTTP_CLIENT_TIMED_OUT',
31
+ tooLarge: 'E_HTTP_CLIENT_TOO_LARGE',
32
+ redirect: 'E_HTTP_CLIENT_REDIRECT_REFUSED',
33
+ };
34
+ /**
35
+ * A failed outbound request. A `ReamError`, not an HTTP `Exception`: left
36
+ * uncaught in a handler it answers a generic 500 and its message, which names
37
+ * hosts and addresses, never reaches the client.
38
+ */
39
+ export class HttpClientError extends ReamError {
40
+ reason;
41
+ /** The target, without its query string (which may carry credentials). */
42
+ url;
43
+ constructor(reason, url, message, cause) {
44
+ const shown = describeUrl(url);
45
+ super(CODES[reason], message, { context: { url: shown } });
46
+ this.name = 'HttpClientError';
47
+ this.reason = reason;
48
+ this.url = shown;
49
+ if (cause !== undefined)
50
+ this.cause = cause;
51
+ }
52
+ }
53
+ /** A complete response, its body read and bounded. */
54
+ export class HttpResponse {
55
+ status;
56
+ headers;
57
+ /** The URL that answered, after redirects. */
58
+ url;
59
+ body;
60
+ constructor(status, headers, url, body) {
61
+ this.status = status;
62
+ this.headers = headers;
63
+ this.url = url;
64
+ this.body = body;
65
+ }
66
+ /** A 2xx status. */
67
+ get ok() {
68
+ return this.status >= 200 && this.status < 300;
69
+ }
70
+ /** A header's first value; names are case-insensitive. */
71
+ header(name) {
72
+ const value = this.headers[name.toLowerCase()];
73
+ return Array.isArray(value) ? value[0] : value;
74
+ }
75
+ text() {
76
+ return this.body.toString('utf8');
77
+ }
78
+ /** The body parsed as JSON — `unknown` until the caller validates it. */
79
+ json() {
80
+ return JSON.parse(this.text());
81
+ }
82
+ }
83
+ const DEFAULT_TIMEOUT = 10_000;
84
+ const DEFAULT_MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
85
+ const DEFAULT_MAX_REDIRECTS = 3;
86
+ const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
87
+ /** Headers that must not follow a redirect to another host. */
88
+ const CREDENTIAL_HEADERS = ['authorization', 'cookie', 'proxy-authorization'];
89
+ export class HttpClient {
90
+ #timeout;
91
+ #maxResponseBytes;
92
+ #maxRedirects;
93
+ #crossHostRedirects;
94
+ #headers;
95
+ #allowHosts;
96
+ #guardedAgents;
97
+ #exemptAgents;
98
+ constructor(options = {}) {
99
+ this.#timeout = options.timeout ?? DEFAULT_TIMEOUT;
100
+ this.#maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
101
+ this.#maxRedirects = options.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
102
+ this.#crossHostRedirects = options.crossHostRedirects ?? false;
103
+ this.#headers = {
104
+ 'user-agent': options.userAgent ?? '@c9up/ream',
105
+ ...lowercaseKeys(options.headers ?? {}),
106
+ };
107
+ this.#allowHosts = new Set((options.allowHosts ?? []).map((entry) => entry.toLowerCase()));
108
+ }
109
+ get(url, options = {}) {
110
+ return this.request(url, { ...options, method: 'GET' });
111
+ }
112
+ post(url, options = {}) {
113
+ return this.request(url, { ...options, method: 'POST' });
114
+ }
115
+ put(url, options = {}) {
116
+ return this.request(url, { ...options, method: 'PUT' });
117
+ }
118
+ patch(url, options = {}) {
119
+ return this.request(url, { ...options, method: 'PATCH' });
120
+ }
121
+ delete(url, options = {}) {
122
+ return this.request(url, { ...options, method: 'DELETE' });
123
+ }
124
+ /** Send a request, following redirects within the client's rules. */
125
+ async request(input, options = {}) {
126
+ let url = parseTarget(input);
127
+ let method = (options.method ?? 'GET').toUpperCase();
128
+ let headers = {
129
+ ...this.#headers,
130
+ ...lowercaseKeys(options.headers ?? {}),
131
+ };
132
+ let body = encodeBody(options, headers);
133
+ const maxResponseBytes = options.maxResponseBytes ?? this.#maxResponseBytes;
134
+ const timeout = options.timeout ?? this.#timeout;
135
+ const deadline = new AbortController();
136
+ const timer = setTimeout(() => {
137
+ deadline.abort(new HttpClientError('timedOut', url, `No complete response within ${timeout} ms.`));
138
+ }, timeout);
139
+ const signal = options.signal
140
+ ? AbortSignal.any([deadline.signal, options.signal])
141
+ : deadline.signal;
142
+ try {
143
+ for (let redirects = 0;; redirects++) {
144
+ this.#assertAddressLiteralAllowed(url);
145
+ if (body !== undefined)
146
+ headers['content-length'] = String(body.length);
147
+ const response = await this.send({ url, method, headers, body, signal, maxResponseBytes });
148
+ if (response.body.length > maxResponseBytes) {
149
+ throw new HttpClientError('tooLarge', url, `Response exceeds ${maxResponseBytes} bytes.`);
150
+ }
151
+ const location = response.header('location');
152
+ if (!REDIRECT_STATUSES.has(response.status) || location === undefined)
153
+ return response;
154
+ if (this.#maxRedirects === 0)
155
+ return response;
156
+ if (redirects >= this.#maxRedirects) {
157
+ throw new HttpClientError('redirect', url, `More than ${this.#maxRedirects} redirects.`);
158
+ }
159
+ const next = parseTarget(location, url);
160
+ if (url.protocol === 'https:' && next.protocol === 'http:') {
161
+ throw new HttpClientError('redirect', next, 'Refused a redirect from https to http.');
162
+ }
163
+ if (next.host !== url.host) {
164
+ if (!this.#crossHostRedirects) {
165
+ throw new HttpClientError('redirect', next, `Refused a redirect from ${url.host} to another host (${next.host}).`);
166
+ }
167
+ headers = withoutKeys(headers, CREDENTIAL_HEADERS);
168
+ }
169
+ // What browsers and fetch do: a 303 always becomes a GET, and a
170
+ // 301/302 does when it answered a POST. 307/308 keep method and body.
171
+ const becomesGet = response.status === 303 ||
172
+ ((response.status === 301 || response.status === 302) && method === 'POST');
173
+ if (becomesGet && method !== 'HEAD') {
174
+ method = 'GET';
175
+ body = undefined;
176
+ headers = withoutKeys(headers, ['content-type', 'content-length']);
177
+ }
178
+ url = next;
179
+ }
180
+ }
181
+ catch (error) {
182
+ // An abort from the deadline surfaces as whatever the transport threw;
183
+ // the deadline's own error says what happened.
184
+ if (deadline.signal.aborted && deadline.signal.reason instanceof HttpClientError) {
185
+ throw deadline.signal.reason;
186
+ }
187
+ throw error;
188
+ }
189
+ finally {
190
+ clearTimeout(timer);
191
+ }
192
+ }
193
+ /**
194
+ * Put one request on the wire and read its response, bounded. Redirects are
195
+ * not followed here. Overridden by the test fake.
196
+ */
197
+ send(request) {
198
+ const { url, method, headers, body, signal, maxResponseBytes } = request;
199
+ const agents = this.#agentsFor(url);
200
+ const transport = url.protocol === 'https:' ? https : http;
201
+ const agent = url.protocol === 'https:' ? agents.https : agents.http;
202
+ return new Promise((resolve, reject) => {
203
+ let settled = false;
204
+ const fail = (error) => {
205
+ if (settled)
206
+ return;
207
+ settled = true;
208
+ signal.removeEventListener('abort', onAbort);
209
+ req.destroy();
210
+ reject(toClientError(error, url, signal));
211
+ };
212
+ const onAbort = () => fail(signal.reason);
213
+ const req = transport.request(url, { method, headers, agent }, (res) => {
214
+ const declared = Number(res.headers['content-length']);
215
+ if (Number.isFinite(declared) && declared > maxResponseBytes) {
216
+ fail(new HttpClientError('tooLarge', url, `Response exceeds ${maxResponseBytes} bytes.`));
217
+ return;
218
+ }
219
+ const chunks = [];
220
+ let received = 0;
221
+ res.on('data', (chunk) => {
222
+ received += chunk.length;
223
+ if (received > maxResponseBytes) {
224
+ fail(new HttpClientError('tooLarge', url, `Response exceeds ${maxResponseBytes} bytes.`));
225
+ return;
226
+ }
227
+ chunks.push(chunk);
228
+ });
229
+ res.on('error', fail);
230
+ res.on('close', () => {
231
+ if (!res.complete)
232
+ fail(new Error('The response was cut short.'));
233
+ });
234
+ res.on('end', () => {
235
+ if (settled)
236
+ return;
237
+ settled = true;
238
+ signal.removeEventListener('abort', onAbort);
239
+ resolve(new HttpResponse(res.statusCode ?? 0, copyHeaders(res.headers), url.href, Buffer.concat(chunks)));
240
+ });
241
+ });
242
+ req.on('error', fail);
243
+ if (signal.aborted) {
244
+ fail(signal.reason);
245
+ return;
246
+ }
247
+ signal.addEventListener('abort', onAbort, { once: true });
248
+ req.end(body);
249
+ });
250
+ }
251
+ /** Release the pooled connections. The client stays usable; new ones are opened on demand. */
252
+ close() {
253
+ for (const agents of [this.#guardedAgents, this.#exemptAgents]) {
254
+ agents?.http.destroy();
255
+ agents?.https.destroy();
256
+ }
257
+ this.#guardedAgents = undefined;
258
+ this.#exemptAgents = undefined;
259
+ }
260
+ #isExempt(url) {
261
+ if (this.#allowHosts.size === 0)
262
+ return false;
263
+ const port = url.port || (url.protocol === 'https:' ? '443' : '80');
264
+ const hostname = url.hostname.toLowerCase();
265
+ return this.#allowHosts.has(hostname) || this.#allowHosts.has(`${hostname}:${port}`);
266
+ }
267
+ /**
268
+ * An address written in the URL is never looked up, so the connect-time
269
+ * check does not see it: it is judged here, on every hop.
270
+ */
271
+ #assertAddressLiteralAllowed(url) {
272
+ const address = url.hostname.replace(/^\[|\]$/g, '');
273
+ if (isIP(address) === 0 || this.#isExempt(url))
274
+ return;
275
+ if (!isPublicAddress(address)) {
276
+ throw new HttpClientError('privateAddress', url, `${address} is not a public address.`);
277
+ }
278
+ }
279
+ #agentsFor(url) {
280
+ if (this.#isExempt(url)) {
281
+ this.#exemptAgents ??= {
282
+ http: new http.Agent({ keepAlive: true }),
283
+ https: new https.Agent({ keepAlive: true }),
284
+ };
285
+ return this.#exemptAgents;
286
+ }
287
+ this.#guardedAgents ??= {
288
+ http: new http.Agent({ keepAlive: true, lookup: publicOnlyLookup }),
289
+ https: new https.Agent({ keepAlive: true, lookup: publicOnlyLookup }),
290
+ };
291
+ return this.#guardedAgents;
292
+ }
293
+ }
294
+ /**
295
+ * `dns.lookup`, refusing the connection when ANY address the name resolves
296
+ * to is not public — a name that answers both a public and a private address
297
+ * is a rebinding setup, not a choice to make on the attacker's behalf.
298
+ */
299
+ const publicOnlyLookup = (hostname, options, callback) => {
300
+ dnsLookup(hostname, { ...options, all: true }, (error, addresses) => {
301
+ if (error) {
302
+ callback(error, '');
303
+ return;
304
+ }
305
+ const refused = addresses.find((entry) => !isPublicAddress(entry.address));
306
+ const [first] = addresses;
307
+ if (refused !== undefined || first === undefined) {
308
+ callback(new HttpClientError('privateAddress', `http://${hostname}`, `${hostname} resolves to ${refused?.address ?? 'no address'}, which is not a public address.`), '');
309
+ return;
310
+ }
311
+ if (options.all)
312
+ callback(null, addresses);
313
+ else
314
+ callback(null, first.address, first.family);
315
+ });
316
+ };
317
+ function parseTarget(input, base) {
318
+ let url;
319
+ try {
320
+ url = new URL(input, base);
321
+ }
322
+ catch (error) {
323
+ throw new HttpClientError('invalidUrl', String(input), 'Not a valid URL.', error);
324
+ }
325
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
326
+ throw new HttpClientError('invalidUrl', url, `Only http and https URLs are allowed, not ${url.protocol}`);
327
+ }
328
+ if (url.username !== '' || url.password !== '') {
329
+ throw new HttpClientError('invalidUrl', url, 'Credentials in the URL are refused; send a header.');
330
+ }
331
+ return url;
332
+ }
333
+ function encodeBody(options, headers) {
334
+ if (options.json !== undefined) {
335
+ headers['content-type'] ??= 'application/json';
336
+ return Buffer.from(JSON.stringify(options.json), 'utf8');
337
+ }
338
+ if (options.body === undefined)
339
+ return undefined;
340
+ return typeof options.body === 'string'
341
+ ? Buffer.from(options.body, 'utf8')
342
+ : Buffer.from(options.body);
343
+ }
344
+ function toClientError(error, url, signal) {
345
+ if (error instanceof HttpClientError)
346
+ return error;
347
+ // An abort the caller asked for is theirs to recognise: hand back its reason.
348
+ if (signal.aborted && error === signal.reason)
349
+ return error;
350
+ const message = error instanceof Error ? error.message : String(error);
351
+ return new HttpClientError('unreachable', url, `Could not reach ${url.host}: ${message}`, error);
352
+ }
353
+ function lowercaseKeys(headers) {
354
+ const out = {};
355
+ for (const [name, value] of Object.entries(headers))
356
+ out[name.toLowerCase()] = value;
357
+ return out;
358
+ }
359
+ function withoutKeys(headers, names) {
360
+ const out = {};
361
+ for (const [name, value] of Object.entries(headers)) {
362
+ if (!names.includes(name))
363
+ out[name] = value;
364
+ }
365
+ return out;
366
+ }
367
+ function copyHeaders(headers) {
368
+ const out = {};
369
+ for (const [name, value] of Object.entries(headers)) {
370
+ if (value !== undefined)
371
+ out[name] = value;
372
+ }
373
+ return out;
374
+ }
375
+ function describeUrl(url) {
376
+ if (typeof url === 'string') {
377
+ try {
378
+ return describeUrl(new URL(url));
379
+ }
380
+ catch {
381
+ return url.split('?')[0] ?? url;
382
+ }
383
+ }
384
+ return `${url.origin}${url.pathname}`;
385
+ }
386
+ //# sourceMappingURL=HttpClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpClient.js","sourceRoot":"","sources":["../../src/http-client/HttpClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Which addresses an outbound request may connect to.
3
+ *
4
+ * Everything that is not a globally routable unicast address is refused:
5
+ * loopback, private and shared ranges, link-local (the cloud metadata
6
+ * endpoint `169.254.169.254` lives there), documentation and benchmarking
7
+ * ranges, multicast, and the IPv6 transition prefixes that tunnel to an IPv4
8
+ * address (IPv4-compatible, NAT64, 6to4, Teredo) — through those, a "public" IPv6 address can
9
+ * still reach `10.0.0.5`. An IPv4-mapped IPv6 address (`::ffff:10.0.0.5`) is
10
+ * judged by the IPv4 rules.
11
+ */
12
+ /** True for a globally routable unicast address; false for anything else, including a non-address. */
13
+ export declare function isPublicAddress(address: string): boolean;
14
+ //# sourceMappingURL=addresses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addresses.d.ts","sourceRoot":"","sources":["../../src/http-client/addresses.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA0CH,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAIxD"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Which addresses an outbound request may connect to.
3
+ *
4
+ * Everything that is not a globally routable unicast address is refused:
5
+ * loopback, private and shared ranges, link-local (the cloud metadata
6
+ * endpoint `169.254.169.254` lives there), documentation and benchmarking
7
+ * ranges, multicast, and the IPv6 transition prefixes that tunnel to an IPv4
8
+ * address (IPv4-compatible, NAT64, 6to4, Teredo) — through those, a "public" IPv6 address can
9
+ * still reach `10.0.0.5`. An IPv4-mapped IPv6 address (`::ffff:10.0.0.5`) is
10
+ * judged by the IPv4 rules.
11
+ */
12
+ import { BlockList, isIP } from 'node:net';
13
+ const NON_PUBLIC_V4 = [
14
+ ['0.0.0.0', 8], // "this network"
15
+ ['10.0.0.0', 8], // private
16
+ ['100.64.0.0', 10], // carrier-grade NAT
17
+ ['127.0.0.0', 8], // loopback
18
+ ['169.254.0.0', 16], // link-local, cloud metadata
19
+ ['172.16.0.0', 12], // private
20
+ ['192.0.0.0', 24], // IETF protocol assignments
21
+ ['192.0.2.0', 24], // TEST-NET-1
22
+ ['192.88.99.0', 24], // 6to4 relay anycast
23
+ ['192.168.0.0', 16], // private
24
+ ['198.18.0.0', 15], // benchmarking
25
+ ['198.51.100.0', 24], // TEST-NET-2
26
+ ['203.0.113.0', 24], // TEST-NET-3
27
+ ['224.0.0.0', 4], // multicast
28
+ ['240.0.0.0', 4], // reserved, broadcast
29
+ ];
30
+ const NON_PUBLIC_V6 = [
31
+ ['::', 128], // unspecified
32
+ ['::1', 128], // loopback
33
+ ['::', 96], // IPv4-compatible (deprecated), embeds an IPv4 address
34
+ ['64:ff9b::', 96], // NAT64, embeds an IPv4 address
35
+ ['64:ff9b:1::', 48], // local-use NAT64
36
+ ['100::', 64], // discard
37
+ ['2001::', 23], // IETF protocol assignments, Teredo included
38
+ ['2001:db8::', 32], // documentation
39
+ ['2002::', 16], // 6to4, embeds an IPv4 address
40
+ ['fc00::', 7], // unique local
41
+ ['fe80::', 10], // link-local
42
+ ['fec0::', 10], // site-local (deprecated)
43
+ ['ff00::', 8], // multicast
44
+ ];
45
+ const nonPublic = new BlockList();
46
+ for (const [network, prefix] of NON_PUBLIC_V4)
47
+ nonPublic.addSubnet(network, prefix, 'ipv4');
48
+ for (const [network, prefix] of NON_PUBLIC_V6)
49
+ nonPublic.addSubnet(network, prefix, 'ipv6');
50
+ /** True for a globally routable unicast address; false for anything else, including a non-address. */
51
+ export function isPublicAddress(address) {
52
+ const family = isIP(address);
53
+ if (family === 0)
54
+ return false;
55
+ return !nonPublic.check(address, family === 4 ? 'ipv4' : 'ipv6');
56
+ }
57
+ //# sourceMappingURL=addresses.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addresses.js","sourceRoot":"","sources":["../../src/http-client/addresses.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AAE1C,MAAM,aAAa,GAA6C;IAC9D,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,iBAAiB;IACjC,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,UAAU;IAC3B,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,oBAAoB;IACxC,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,WAAW;IAC7B,CAAC,aAAa,EAAE,EAAE,CAAC,EAAE,6BAA6B;IAClD,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,UAAU;IAC9B,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,4BAA4B;IAC/C,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,aAAa;IAChC,CAAC,aAAa,EAAE,EAAE,CAAC,EAAE,qBAAqB;IAC1C,CAAC,aAAa,EAAE,EAAE,CAAC,EAAE,UAAU;IAC/B,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,eAAe;IACnC,CAAC,cAAc,EAAE,EAAE,CAAC,EAAE,aAAa;IACnC,CAAC,aAAa,EAAE,EAAE,CAAC,EAAE,aAAa;IAClC,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,YAAY;IAC9B,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,sBAAsB;CACzC,CAAA;AAED,MAAM,aAAa,GAA6C;IAC9D,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,cAAc;IAC3B,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,WAAW;IACzB,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,uDAAuD;IACnE,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,gCAAgC;IACnD,CAAC,aAAa,EAAE,EAAE,CAAC,EAAE,kBAAkB;IACvC,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,UAAU;IACzB,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,6CAA6C;IAC7D,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,gBAAgB;IACpC,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,+BAA+B;IAC/C,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,eAAe;IAC9B,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,aAAa;IAC7B,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,0BAA0B;IAC1C,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,YAAY;CAC5B,CAAA;AAED,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAA;AACjC,KAAK,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,aAAa;IAAE,SAAS,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC3F,KAAK,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,aAAa;IAAE,SAAS,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAE3F,sGAAsG;AACtG,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAA;IAC5B,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC9B,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AAClE,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * An {@link HttpClient} that never touches the network, for tests.
3
+ *
4
+ * It is a real client with the transport swapped: URL validation, the
5
+ * address-literal rule, the size limit and the redirect policy all still
6
+ * apply, so a test sees the same refusals production would. Only the wire is
7
+ * replaced — canned responses in, recorded requests out.
8
+ *
9
+ * const http = fakeHttp()
10
+ * http.on('GET', 'https://cms.example.com/api/posts', { json: [{ id: 1 }] })
11
+ * await new CmsReader(http).posts()
12
+ * expect(http.requests).toHaveLength(1)
13
+ */
14
+ import { HttpClient, type HttpClientOptions, HttpResponse, type OutgoingRequest } from './HttpClient.js';
15
+ /** What a faked endpoint answers. */
16
+ export interface FakeResponse {
17
+ /** Default 200. */
18
+ status?: number;
19
+ headers?: Record<string, string | string[]>;
20
+ body?: string | Uint8Array;
21
+ /** Serialised as the body, with `content-type: application/json`. */
22
+ json?: unknown;
23
+ }
24
+ /** A request the fake received. */
25
+ export interface RecordedRequest {
26
+ method: string;
27
+ url: string;
28
+ headers: Readonly<Record<string, string>>;
29
+ body: Buffer | undefined;
30
+ }
31
+ /** An exact URL (with or without its query string), a pattern, or a predicate. */
32
+ export type FakeMatcher = string | RegExp | ((request: RecordedRequest) => boolean);
33
+ export declare class FakeHttpClient extends HttpClient {
34
+ #private;
35
+ /** Every request sent, in order — redirect hops included. */
36
+ readonly requests: RecordedRequest[];
37
+ /**
38
+ * Answer `method` + `match` with `response`. The most recent matching
39
+ * registration wins, so a test can override a default set up earlier.
40
+ */
41
+ on(method: string, match: FakeMatcher, response: FakeResponse | ((request: RecordedRequest) => FakeResponse)): this;
42
+ /** Answer `match` whatever the method. */
43
+ onAny(match: FakeMatcher, response: FakeResponse | ((request: RecordedRequest) => FakeResponse)): this;
44
+ /** Forget the registered responses and the recorded requests. */
45
+ reset(): void;
46
+ protected send(request: OutgoingRequest): Promise<HttpResponse>;
47
+ }
48
+ /** A fresh {@link FakeHttpClient}, with the same options a real client takes. */
49
+ export declare function fakeHttp(options?: HttpClientOptions): FakeHttpClient;
50
+ //# sourceMappingURL=fake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake.d.ts","sourceRoot":"","sources":["../../src/http-client/fake.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,UAAU,EAEV,KAAK,iBAAiB,EACtB,YAAY,EACZ,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAA;AAExB,qCAAqC;AACrC,MAAM,WAAW,YAAY;IAC3B,mBAAmB;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;IAC3C,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;IAC1B,qEAAqE;IACrE,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,mCAAmC;AACnC,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACzC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CACzB;AAED,kFAAkF;AAClF,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,CAAA;AAQnF,qBAAa,cAAe,SAAQ,UAAU;;IAC5C,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,eAAe,EAAE,CAAK;IAGzC;;;OAGG;IACH,EAAE,CACA,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,YAAY,GAAG,CAAC,CAAC,OAAO,EAAE,eAAe,KAAK,YAAY,CAAC,GACpE,IAAI;IAKP,0CAA0C;IAC1C,KAAK,CACH,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,YAAY,GAAG,CAAC,CAAC,OAAO,EAAE,eAAe,KAAK,YAAY,CAAC,GACpE,IAAI;IAKP,iEAAiE;IACjE,KAAK,IAAI,IAAI;cAKY,IAAI,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC;CA+C/E;AAQD,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,OAAO,GAAE,iBAAsB,GAAG,cAAc,CAExE"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * An {@link HttpClient} that never touches the network, for tests.
3
+ *
4
+ * It is a real client with the transport swapped: URL validation, the
5
+ * address-literal rule, the size limit and the redirect policy all still
6
+ * apply, so a test sees the same refusals production would. Only the wire is
7
+ * replaced — canned responses in, recorded requests out.
8
+ *
9
+ * const http = fakeHttp()
10
+ * http.on('GET', 'https://cms.example.com/api/posts', { json: [{ id: 1 }] })
11
+ * await new CmsReader(http).posts()
12
+ * expect(http.requests).toHaveLength(1)
13
+ */
14
+ import { HttpClient, HttpClientError, HttpResponse, } from './HttpClient.js';
15
+ export class FakeHttpClient extends HttpClient {
16
+ /** Every request sent, in order — redirect hops included. */
17
+ requests = [];
18
+ #routes = [];
19
+ /**
20
+ * Answer `method` + `match` with `response`. The most recent matching
21
+ * registration wins, so a test can override a default set up earlier.
22
+ */
23
+ on(method, match, response) {
24
+ this.#routes.push({ method: method.toUpperCase(), match, respond: response });
25
+ return this;
26
+ }
27
+ /** Answer `match` whatever the method. */
28
+ onAny(match, response) {
29
+ this.#routes.push({ method: undefined, match, respond: response });
30
+ return this;
31
+ }
32
+ /** Forget the registered responses and the recorded requests. */
33
+ reset() {
34
+ this.#routes.length = 0;
35
+ this.requests.length = 0;
36
+ }
37
+ async send(request) {
38
+ const recorded = {
39
+ method: request.method,
40
+ url: request.url.href,
41
+ headers: { ...request.headers },
42
+ body: request.body,
43
+ };
44
+ this.requests.push(recorded);
45
+ const route = this.#latestRoute(recorded, request.url);
46
+ if (route === undefined) {
47
+ throw new HttpClientError('unreachable', request.url, `No fake response for ${recorded.method} ${recorded.url}.`);
48
+ }
49
+ const answer = typeof route.respond === 'function' ? route.respond(recorded) : route.respond;
50
+ const headers = {};
51
+ const given = answer.headers ?? {};
52
+ for (const [name, value] of Object.entries(given)) {
53
+ headers[name.toLowerCase()] = value;
54
+ }
55
+ let body;
56
+ if (answer.json !== undefined) {
57
+ headers['content-type'] ??= 'application/json';
58
+ body = Buffer.from(JSON.stringify(answer.json), 'utf8');
59
+ }
60
+ else if (answer.body === undefined) {
61
+ body = Buffer.alloc(0);
62
+ }
63
+ else {
64
+ body =
65
+ typeof answer.body === 'string'
66
+ ? Buffer.from(answer.body, 'utf8')
67
+ : Buffer.from(answer.body);
68
+ }
69
+ return new HttpResponse(answer.status ?? 200, headers, request.url.href, body);
70
+ }
71
+ #latestRoute(request, url) {
72
+ for (let i = this.#routes.length - 1; i >= 0; i--) {
73
+ const candidate = this.#routes[i];
74
+ if (candidate === undefined)
75
+ continue;
76
+ if (candidate.method !== undefined && candidate.method !== request.method)
77
+ continue;
78
+ if (matches(candidate.match, request, url))
79
+ return candidate;
80
+ }
81
+ return undefined;
82
+ }
83
+ }
84
+ function matches(match, request, url) {
85
+ if (typeof match === 'function')
86
+ return match(request);
87
+ if (match instanceof RegExp)
88
+ return match.test(request.url);
89
+ return match === url.href || match === `${url.origin}${url.pathname}`;
90
+ }
91
+ /** A fresh {@link FakeHttpClient}, with the same options a real client takes. */
92
+ export function fakeHttp(options = {}) {
93
+ return new FakeHttpClient(options);
94
+ }
95
+ //# sourceMappingURL=fake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake.js","sourceRoot":"","sources":["../../src/http-client/fake.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,UAAU,EACV,eAAe,EAEf,YAAY,GAEb,MAAM,iBAAiB,CAAA;AA6BxB,MAAM,OAAO,cAAe,SAAQ,UAAU;IAC5C,6DAA6D;IACpD,QAAQ,GAAsB,EAAE,CAAA;IAChC,OAAO,GAAgB,EAAE,CAAA;IAElC;;;OAGG;IACH,EAAE,CACA,MAAc,EACd,KAAkB,EAClB,QAAqE;QAErE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;QAC7E,OAAO,IAAI,CAAA;IACb,CAAC;IAED,0CAA0C;IAC1C,KAAK,CACH,KAAkB,EAClB,QAAqE;QAErE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;QAClE,OAAO,IAAI,CAAA;IACb,CAAC;IAED,iEAAiE;IACjE,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;QACvB,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;IAC1B,CAAC;IAEkB,KAAK,CAAC,IAAI,CAAC,OAAwB;QACpD,MAAM,QAAQ,GAAoB;YAChC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI;YACrB,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE;YAC/B,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAA;QACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAE5B,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;QACtD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,eAAe,CACvB,aAAa,EACb,OAAO,CAAC,GAAG,EACX,wBAAwB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,GAAG,GAAG,CAC3D,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,KAAK,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAA;QAC5F,MAAM,OAAO,GAAsC,EAAE,CAAA;QACrD,MAAM,KAAK,GAAsC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAA;QACrE,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAA;QACrC,CAAC;QACD,IAAI,IAAY,CAAA;QAChB,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,CAAC,cAAc,CAAC,KAAK,kBAAkB,CAAA;YAC9C,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAA;QACzD,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACrC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACxB,CAAC;aAAM,CAAC;YACN,IAAI;gBACF,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;oBAC7B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC;oBAClC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAChC,CAAC;QACD,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAChF,CAAC;IAED,YAAY,CAAC,OAAwB,EAAE,GAAQ;QAC7C,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;YACjC,IAAI,SAAS,KAAK,SAAS;gBAAE,SAAQ;YACrC,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;gBAAE,SAAQ;YACnF,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC;gBAAE,OAAO,SAAS,CAAA;QAC9D,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;CACF;AAED,SAAS,OAAO,CAAC,KAAkB,EAAE,OAAwB,EAAE,GAAQ;IACrE,IAAI,OAAO,KAAK,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC,CAAA;IACtD,IAAI,KAAK,YAAY,MAAM;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC3D,OAAO,KAAK,KAAK,GAAG,CAAC,IAAI,IAAI,KAAK,KAAK,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAA;AACvE,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,QAAQ,CAAC,UAA6B,EAAE;IACtD,OAAO,IAAI,cAAc,CAAC,OAAO,CAAC,CAAA;AACpC,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `@c9up/ream/http-client` — outbound HTTP with an SSRF guard, and its test fake.
3
+ */
4
+ export { isPublicAddress } from './addresses.js';
5
+ export { FakeHttpClient, type FakeMatcher, type FakeResponse, fakeHttp, type RecordedRequest, } from './fake.js';
6
+ export { HttpClient, HttpClientError, type HttpClientFailure, type HttpClientOptions, type HttpRequestOptions, HttpResponse, type OutgoingRequest, } from './HttpClient.js';
7
+ //# sourceMappingURL=index.d.ts.map