recker 1.0.2-0 → 1.0.4

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 (287) hide show
  1. package/LICENSE +0 -2
  2. package/README.md +121 -72
  3. package/dist/cache/memory-storage.d.ts.map +1 -1
  4. package/dist/cache/memory-storage.js +7 -1
  5. package/dist/constants/http-status.d.ts +74 -0
  6. package/dist/constants/http-status.d.ts.map +1 -0
  7. package/dist/constants/http-status.js +156 -0
  8. package/dist/constants.d.ts.map +1 -1
  9. package/dist/constants.js +6 -6
  10. package/dist/cookies/memory-cookie-jar.d.ts +31 -0
  11. package/dist/cookies/memory-cookie-jar.d.ts.map +1 -0
  12. package/dist/cookies/memory-cookie-jar.js +210 -0
  13. package/dist/core/client.d.ts +9 -0
  14. package/dist/core/client.d.ts.map +1 -1
  15. package/dist/core/client.js +252 -53
  16. package/dist/core/errors.d.ts +18 -2
  17. package/dist/core/errors.d.ts.map +1 -1
  18. package/dist/core/errors.js +66 -5
  19. package/dist/core/index.d.ts +6 -0
  20. package/dist/core/index.d.ts.map +1 -0
  21. package/dist/core/index.js +5 -0
  22. package/dist/core/request-promise.d.ts.map +1 -1
  23. package/dist/core/request-promise.js +8 -2
  24. package/dist/core/request.d.ts +7 -1
  25. package/dist/core/request.d.ts.map +1 -1
  26. package/dist/core/request.js +32 -0
  27. package/dist/core/response.d.ts +2 -0
  28. package/dist/core/response.d.ts.map +1 -1
  29. package/dist/core/response.js +44 -19
  30. package/dist/events/request-events.d.ts +48 -0
  31. package/dist/events/request-events.d.ts.map +1 -0
  32. package/dist/events/request-events.js +85 -0
  33. package/dist/index.d.ts +28 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +28 -2
  36. package/dist/mcp/client.d.ts.map +1 -1
  37. package/dist/mcp/client.js +16 -5
  38. package/dist/mcp/contract.d.ts +77 -0
  39. package/dist/mcp/contract.d.ts.map +1 -0
  40. package/dist/mcp/contract.js +278 -0
  41. package/dist/mcp/types.d.ts +1 -0
  42. package/dist/mcp/types.d.ts.map +1 -1
  43. package/dist/plugins/auth.d.ts +45 -0
  44. package/dist/plugins/auth.d.ts.map +1 -0
  45. package/dist/plugins/auth.js +268 -0
  46. package/dist/plugins/cache.d.ts +7 -1
  47. package/dist/plugins/cache.d.ts.map +1 -1
  48. package/dist/plugins/cache.js +470 -49
  49. package/dist/plugins/circuit-breaker.js +1 -1
  50. package/dist/plugins/compression.d.ts.map +1 -1
  51. package/dist/plugins/compression.js +3 -3
  52. package/dist/plugins/dedup.d.ts.map +1 -1
  53. package/dist/plugins/dedup.js +2 -1
  54. package/dist/plugins/graphql.d.ts +4 -3
  55. package/dist/plugins/graphql.d.ts.map +1 -1
  56. package/dist/plugins/graphql.js +24 -5
  57. package/dist/plugins/grpc-web.d.ts +80 -0
  58. package/dist/plugins/grpc-web.d.ts.map +1 -0
  59. package/dist/plugins/grpc-web.js +261 -0
  60. package/dist/plugins/har-player.d.ts.map +1 -1
  61. package/dist/plugins/har-player.js +11 -2
  62. package/dist/plugins/hls.d.ts +33 -0
  63. package/dist/plugins/hls.d.ts.map +1 -0
  64. package/dist/plugins/hls.js +225 -0
  65. package/dist/plugins/http2-push.d.ts +64 -0
  66. package/dist/plugins/http2-push.d.ts.map +1 -0
  67. package/dist/plugins/http2-push.js +274 -0
  68. package/dist/plugins/http3.d.ts +76 -0
  69. package/dist/plugins/http3.d.ts.map +1 -0
  70. package/dist/plugins/http3.js +231 -0
  71. package/dist/plugins/interface-rotator.d.ts +10 -0
  72. package/dist/plugins/interface-rotator.d.ts.map +1 -0
  73. package/dist/plugins/interface-rotator.js +57 -0
  74. package/dist/plugins/jsonrpc.d.ts +76 -0
  75. package/dist/plugins/jsonrpc.d.ts.map +1 -0
  76. package/dist/plugins/jsonrpc.js +143 -0
  77. package/dist/plugins/logger.d.ts +8 -5
  78. package/dist/plugins/logger.d.ts.map +1 -1
  79. package/dist/plugins/logger.js +66 -30
  80. package/dist/plugins/odata.d.ts +182 -0
  81. package/dist/plugins/odata.d.ts.map +1 -0
  82. package/dist/plugins/odata.js +561 -0
  83. package/dist/plugins/retry.d.ts +1 -0
  84. package/dist/plugins/retry.d.ts.map +1 -1
  85. package/dist/plugins/retry.js +26 -2
  86. package/dist/plugins/scrape.d.ts +22 -0
  87. package/dist/plugins/scrape.d.ts.map +1 -0
  88. package/dist/plugins/scrape.js +87 -0
  89. package/dist/plugins/soap.d.ts +73 -0
  90. package/dist/plugins/soap.d.ts.map +1 -0
  91. package/dist/plugins/soap.js +347 -0
  92. package/dist/plugins/user-agent.d.ts +8 -0
  93. package/dist/plugins/user-agent.d.ts.map +1 -0
  94. package/dist/plugins/user-agent.js +46 -0
  95. package/dist/plugins/xml.d.ts +10 -0
  96. package/dist/plugins/xml.d.ts.map +1 -0
  97. package/dist/plugins/xml.js +194 -0
  98. package/dist/presets/anthropic.d.ts +7 -0
  99. package/dist/presets/anthropic.d.ts.map +1 -0
  100. package/dist/presets/anthropic.js +17 -0
  101. package/dist/presets/azure-openai.d.ts +9 -0
  102. package/dist/presets/azure-openai.d.ts.map +1 -0
  103. package/dist/presets/azure-openai.js +25 -0
  104. package/dist/presets/cloudflare.d.ts +13 -0
  105. package/dist/presets/cloudflare.d.ts.map +1 -0
  106. package/dist/presets/cloudflare.js +39 -0
  107. package/dist/presets/cohere.d.ts +6 -0
  108. package/dist/presets/cohere.d.ts.map +1 -0
  109. package/dist/presets/cohere.js +16 -0
  110. package/dist/presets/deepseek.d.ts +6 -0
  111. package/dist/presets/deepseek.d.ts.map +1 -0
  112. package/dist/presets/deepseek.js +16 -0
  113. package/dist/presets/digitalocean.d.ts +6 -0
  114. package/dist/presets/digitalocean.d.ts.map +1 -0
  115. package/dist/presets/digitalocean.js +16 -0
  116. package/dist/presets/discord.d.ts +7 -0
  117. package/dist/presets/discord.d.ts.map +1 -0
  118. package/dist/presets/discord.js +17 -0
  119. package/dist/presets/fireworks.d.ts +6 -0
  120. package/dist/presets/fireworks.d.ts.map +1 -0
  121. package/dist/presets/fireworks.js +16 -0
  122. package/dist/presets/gemini.d.ts +6 -0
  123. package/dist/presets/gemini.d.ts.map +1 -0
  124. package/dist/presets/gemini.js +16 -0
  125. package/dist/presets/github.d.ts +7 -0
  126. package/dist/presets/github.d.ts.map +1 -0
  127. package/dist/presets/github.js +17 -0
  128. package/dist/presets/gitlab.d.ts +7 -0
  129. package/dist/presets/gitlab.d.ts.map +1 -0
  130. package/dist/presets/gitlab.js +16 -0
  131. package/dist/presets/groq.d.ts +6 -0
  132. package/dist/presets/groq.d.ts.map +1 -0
  133. package/dist/presets/groq.js +16 -0
  134. package/dist/presets/huggingface.d.ts +6 -0
  135. package/dist/presets/huggingface.d.ts.map +1 -0
  136. package/dist/presets/huggingface.js +16 -0
  137. package/dist/presets/index.d.ts +28 -0
  138. package/dist/presets/index.d.ts.map +1 -0
  139. package/dist/presets/index.js +27 -0
  140. package/dist/presets/linear.d.ts +6 -0
  141. package/dist/presets/linear.d.ts.map +1 -0
  142. package/dist/presets/linear.js +16 -0
  143. package/dist/presets/mistral.d.ts +6 -0
  144. package/dist/presets/mistral.d.ts.map +1 -0
  145. package/dist/presets/mistral.js +16 -0
  146. package/dist/presets/notion.d.ts +7 -0
  147. package/dist/presets/notion.d.ts.map +1 -0
  148. package/dist/presets/notion.js +17 -0
  149. package/dist/presets/openai.d.ts +8 -0
  150. package/dist/presets/openai.d.ts.map +1 -0
  151. package/dist/presets/openai.js +23 -0
  152. package/dist/presets/perplexity.d.ts +6 -0
  153. package/dist/presets/perplexity.d.ts.map +1 -0
  154. package/dist/presets/perplexity.js +16 -0
  155. package/dist/presets/registry.d.ts +20 -0
  156. package/dist/presets/registry.d.ts.map +1 -0
  157. package/dist/presets/registry.js +311 -0
  158. package/dist/presets/replicate.d.ts +6 -0
  159. package/dist/presets/replicate.d.ts.map +1 -0
  160. package/dist/presets/replicate.js +16 -0
  161. package/dist/presets/slack.d.ts +6 -0
  162. package/dist/presets/slack.d.ts.map +1 -0
  163. package/dist/presets/slack.js +16 -0
  164. package/dist/presets/stripe.d.ts +8 -0
  165. package/dist/presets/stripe.d.ts.map +1 -0
  166. package/dist/presets/stripe.js +23 -0
  167. package/dist/presets/supabase.d.ts +7 -0
  168. package/dist/presets/supabase.d.ts.map +1 -0
  169. package/dist/presets/supabase.js +18 -0
  170. package/dist/presets/together.d.ts +6 -0
  171. package/dist/presets/together.d.ts.map +1 -0
  172. package/dist/presets/together.js +16 -0
  173. package/dist/presets/twilio.d.ts +7 -0
  174. package/dist/presets/twilio.d.ts.map +1 -0
  175. package/dist/presets/twilio.js +17 -0
  176. package/dist/presets/vercel.d.ts +7 -0
  177. package/dist/presets/vercel.d.ts.map +1 -0
  178. package/dist/presets/vercel.js +23 -0
  179. package/dist/presets/xai.d.ts +7 -0
  180. package/dist/presets/xai.d.ts.map +1 -0
  181. package/dist/presets/xai.js +17 -0
  182. package/dist/protocols/ftp.d.ts +63 -0
  183. package/dist/protocols/ftp.d.ts.map +1 -0
  184. package/dist/protocols/ftp.js +388 -0
  185. package/dist/protocols/index.d.ts +4 -0
  186. package/dist/protocols/index.d.ts.map +1 -0
  187. package/dist/protocols/index.js +3 -0
  188. package/dist/protocols/sftp.d.ts +65 -0
  189. package/dist/protocols/sftp.d.ts.map +1 -0
  190. package/dist/protocols/sftp.js +346 -0
  191. package/dist/protocols/telnet.d.ts +50 -0
  192. package/dist/protocols/telnet.d.ts.map +1 -0
  193. package/dist/protocols/telnet.js +139 -0
  194. package/dist/runner/request-runner.d.ts.map +1 -1
  195. package/dist/runner/request-runner.js +1 -0
  196. package/dist/scrape/document.d.ts +44 -0
  197. package/dist/scrape/document.d.ts.map +1 -0
  198. package/dist/scrape/document.js +198 -0
  199. package/dist/scrape/element.d.ts +50 -0
  200. package/dist/scrape/element.d.ts.map +1 -0
  201. package/dist/scrape/element.js +176 -0
  202. package/dist/scrape/extractors.d.ts +17 -0
  203. package/dist/scrape/extractors.d.ts.map +1 -0
  204. package/dist/scrape/extractors.js +356 -0
  205. package/dist/scrape/index.d.ts +5 -0
  206. package/dist/scrape/index.d.ts.map +1 -0
  207. package/dist/scrape/index.js +3 -0
  208. package/dist/scrape/types.d.ts +108 -0
  209. package/dist/scrape/types.d.ts.map +1 -0
  210. package/dist/scrape/types.js +1 -0
  211. package/dist/testing/index.d.ts +3 -0
  212. package/dist/testing/index.d.ts.map +1 -0
  213. package/dist/testing/index.js +1 -0
  214. package/dist/testing/mock.d.ts +58 -0
  215. package/dist/testing/mock.d.ts.map +1 -0
  216. package/dist/testing/mock.js +252 -0
  217. package/dist/transport/fetch.d.ts.map +1 -1
  218. package/dist/transport/fetch.js +12 -4
  219. package/dist/transport/undici.d.ts +17 -1
  220. package/dist/transport/undici.d.ts.map +1 -1
  221. package/dist/transport/undici.js +708 -47
  222. package/dist/types/index.d.ts +111 -10
  223. package/dist/types/index.d.ts.map +1 -1
  224. package/dist/types/index.js +1 -1
  225. package/dist/types/logger.d.ts +17 -0
  226. package/dist/types/logger.d.ts.map +1 -0
  227. package/dist/types/logger.js +66 -0
  228. package/dist/utils/agent-manager.d.ts.map +1 -1
  229. package/dist/utils/agent-manager.js +20 -4
  230. package/dist/utils/body.d.ts.map +1 -1
  231. package/dist/utils/body.js +14 -2
  232. package/dist/utils/charset.d.ts +16 -0
  233. package/dist/utils/charset.d.ts.map +1 -0
  234. package/dist/utils/charset.js +169 -0
  235. package/dist/utils/client-pool.d.ts +21 -0
  236. package/dist/utils/client-pool.d.ts.map +1 -0
  237. package/dist/utils/client-pool.js +49 -0
  238. package/dist/utils/concurrency.d.ts.map +1 -1
  239. package/dist/utils/concurrency.js +8 -4
  240. package/dist/utils/dns-toolkit.d.ts +13 -0
  241. package/dist/utils/dns-toolkit.d.ts.map +1 -0
  242. package/dist/utils/dns-toolkit.js +48 -0
  243. package/dist/utils/doh.d.ts.map +1 -1
  244. package/dist/utils/doh.js +16 -3
  245. package/dist/utils/download.d.ts +15 -0
  246. package/dist/utils/download.d.ts.map +1 -0
  247. package/dist/utils/download.js +44 -0
  248. package/dist/utils/env-proxy.d.ts +13 -0
  249. package/dist/utils/env-proxy.d.ts.map +1 -0
  250. package/dist/utils/env-proxy.js +105 -0
  251. package/dist/utils/header-parser.d.ts +15 -1
  252. package/dist/utils/header-parser.d.ts.map +1 -1
  253. package/dist/utils/header-parser.js +161 -1
  254. package/dist/utils/link-header.d.ts +70 -0
  255. package/dist/utils/link-header.d.ts.map +1 -0
  256. package/dist/utils/link-header.js +190 -0
  257. package/dist/utils/progress.d.ts +7 -2
  258. package/dist/utils/progress.d.ts.map +1 -1
  259. package/dist/utils/progress.js +48 -15
  260. package/dist/utils/rdap.d.ts +17 -0
  261. package/dist/utils/rdap.d.ts.map +1 -0
  262. package/dist/utils/rdap.js +32 -0
  263. package/dist/utils/request-pool.d.ts.map +1 -1
  264. package/dist/utils/request-pool.js +4 -3
  265. package/dist/utils/sse.d.ts.map +1 -1
  266. package/dist/utils/sse.js +8 -2
  267. package/dist/utils/status-codes.d.ts +84 -0
  268. package/dist/utils/status-codes.d.ts.map +1 -0
  269. package/dist/utils/status-codes.js +204 -0
  270. package/dist/utils/streaming.d.ts.map +1 -1
  271. package/dist/utils/streaming.js +1 -0
  272. package/dist/utils/tls-inspector.d.ts +21 -0
  273. package/dist/utils/tls-inspector.d.ts.map +1 -0
  274. package/dist/utils/tls-inspector.js +39 -0
  275. package/dist/utils/try-fn.d.ts.map +1 -1
  276. package/dist/utils/try-fn.js +11 -5
  277. package/dist/utils/upload.d.ts +1 -0
  278. package/dist/utils/upload.d.ts.map +1 -1
  279. package/dist/utils/upload.js +20 -3
  280. package/dist/utils/user-agent.d.ts +9 -9
  281. package/dist/utils/user-agent.js +9 -9
  282. package/dist/utils/whois.d.ts.map +1 -1
  283. package/dist/utils/whois.js +11 -2
  284. package/dist/websocket/client.d.ts +29 -1
  285. package/dist/websocket/client.d.ts.map +1 -1
  286. package/dist/websocket/client.js +145 -13
  287. package/package.json +45 -8
@@ -0,0 +1,64 @@
1
+ import type { Client } from '../core/client.js';
2
+ import * as http2 from 'node:http2';
3
+ import { EventEmitter } from 'node:events';
4
+ export interface PushPromise {
5
+ path: string;
6
+ method: string;
7
+ headers: Record<string, string | string[]>;
8
+ authority: string;
9
+ scheme: string;
10
+ }
11
+ export interface PushedResource {
12
+ promise: PushPromise;
13
+ status: number;
14
+ headers: Record<string, string | string[]>;
15
+ body: Buffer;
16
+ receivedAt: Date;
17
+ }
18
+ export interface Http2PushOptions {
19
+ enabled?: boolean;
20
+ maxConcurrentPushes?: number;
21
+ pushTimeout?: number;
22
+ cachePushes?: boolean;
23
+ maxCacheSize?: number;
24
+ cacheTtl?: number;
25
+ filter?: (promise: PushPromise) => boolean;
26
+ onPush?: (resource: PushedResource) => void;
27
+ }
28
+ export declare class Http2PushManager extends EventEmitter {
29
+ private options;
30
+ private sessions;
31
+ private pushCache;
32
+ private pendingPushes;
33
+ constructor(options?: Http2PushOptions);
34
+ connect(url: string): Promise<http2.ClientHttp2Session>;
35
+ request(url: string, options?: {
36
+ method?: string;
37
+ headers?: Record<string, string>;
38
+ body?: Buffer | string;
39
+ }): Promise<{
40
+ status: number;
41
+ headers: Record<string, string | string[]>;
42
+ body: Buffer;
43
+ }>;
44
+ getCachedPush(url: string): PushedResource | null;
45
+ waitForPush(url: string, timeout?: number): Promise<PushedResource | null>;
46
+ getCachedPushes(): Map<string, PushedResource>;
47
+ clearCache(): void;
48
+ close(): Promise<void>;
49
+ getSession(origin: string): http2.ClientHttp2Session | undefined;
50
+ private setupPushHandling;
51
+ private handlePushStream;
52
+ private cacheResource;
53
+ private getCacheKey;
54
+ }
55
+ export declare function http2Push(options: {
56
+ manager: Http2PushManager;
57
+ }): (client: Client) => void;
58
+ declare module '../core/client.js' {
59
+ interface Client {
60
+ getPushManager(): Http2PushManager;
61
+ }
62
+ }
63
+ export { http2 };
64
+ //# sourceMappingURL=http2-push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http2-push.d.ts","sourceRoot":"","sources":["../../src/plugins/http2-push.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,274 @@
1
+ import * as http2 from 'node:http2';
2
+ import { EventEmitter } from 'node:events';
3
+ export class Http2PushManager extends EventEmitter {
4
+ options;
5
+ sessions = new Map();
6
+ pushCache = new Map();
7
+ pendingPushes = new Map();
8
+ constructor(options = {}) {
9
+ super();
10
+ this.options = {
11
+ enabled: options.enabled ?? true,
12
+ maxConcurrentPushes: options.maxConcurrentPushes ?? 100,
13
+ pushTimeout: options.pushTimeout ?? 30000,
14
+ cachePushes: options.cachePushes ?? true,
15
+ maxCacheSize: options.maxCacheSize ?? 100,
16
+ cacheTtl: options.cacheTtl ?? 60000,
17
+ filter: options.filter ?? (() => true),
18
+ onPush: options.onPush ?? (() => { }),
19
+ };
20
+ }
21
+ async connect(url) {
22
+ const parsedUrl = new URL(url);
23
+ const origin = parsedUrl.origin;
24
+ const existingSession = this.sessions.get(origin);
25
+ if (existingSession && !existingSession.closed && !existingSession.destroyed) {
26
+ return existingSession;
27
+ }
28
+ return new Promise((resolve, reject) => {
29
+ const session = http2.connect(origin, {
30
+ settings: {
31
+ enablePush: this.options.enabled,
32
+ maxConcurrentStreams: this.options.maxConcurrentPushes,
33
+ },
34
+ });
35
+ session.on('error', (err) => {
36
+ this.emit('error', err);
37
+ reject(err);
38
+ });
39
+ session.on('connect', () => {
40
+ this.sessions.set(origin, session);
41
+ this.setupPushHandling(session, origin);
42
+ resolve(session);
43
+ });
44
+ session.on('close', () => {
45
+ this.sessions.delete(origin);
46
+ this.emit('sessionClosed', origin);
47
+ });
48
+ });
49
+ }
50
+ async request(url, options = {}) {
51
+ const parsedUrl = new URL(url);
52
+ const origin = parsedUrl.origin;
53
+ const path = parsedUrl.pathname + parsedUrl.search;
54
+ const cached = this.getCachedPush(url);
55
+ if (cached) {
56
+ this.emit('cacheHit', url, cached);
57
+ return {
58
+ status: cached.status,
59
+ headers: cached.headers,
60
+ body: cached.body,
61
+ };
62
+ }
63
+ const session = await this.connect(origin);
64
+ return new Promise((resolve, reject) => {
65
+ const reqHeaders = {
66
+ ':method': options.method ?? 'GET',
67
+ ':path': path,
68
+ ':scheme': parsedUrl.protocol.replace(':', ''),
69
+ ':authority': parsedUrl.host,
70
+ ...options.headers,
71
+ };
72
+ const stream = session.request(reqHeaders);
73
+ const chunks = [];
74
+ let responseHeaders = {};
75
+ let status = 0;
76
+ stream.on('response', (headers) => {
77
+ status = headers[':status'];
78
+ responseHeaders = { ...headers };
79
+ delete responseHeaders[':status'];
80
+ });
81
+ stream.on('data', (chunk) => {
82
+ chunks.push(chunk);
83
+ });
84
+ stream.on('end', () => {
85
+ resolve({
86
+ status,
87
+ headers: responseHeaders,
88
+ body: Buffer.concat(chunks),
89
+ });
90
+ });
91
+ stream.on('error', reject);
92
+ if (options.body) {
93
+ stream.write(options.body);
94
+ }
95
+ stream.end();
96
+ });
97
+ }
98
+ getCachedPush(url) {
99
+ const cacheKey = this.getCacheKey(url);
100
+ const entry = this.pushCache.get(cacheKey);
101
+ if (!entry)
102
+ return null;
103
+ if (Date.now() > entry.expiresAt) {
104
+ this.pushCache.delete(cacheKey);
105
+ return null;
106
+ }
107
+ return entry.resource;
108
+ }
109
+ async waitForPush(url, timeout) {
110
+ const cacheKey = this.getCacheKey(url);
111
+ const cached = this.getCachedPush(url);
112
+ if (cached)
113
+ return cached;
114
+ const pending = this.pendingPushes.get(cacheKey);
115
+ if (pending)
116
+ return pending;
117
+ return new Promise((resolve) => {
118
+ const timeoutMs = timeout ?? this.options.pushTimeout;
119
+ const timer = setTimeout(() => {
120
+ this.removeListener('push', handler);
121
+ resolve(null);
122
+ }, timeoutMs);
123
+ const handler = (resource) => {
124
+ const resourceKey = this.getCacheKey(`${resource.promise.scheme}://${resource.promise.authority}${resource.promise.path}`);
125
+ if (resourceKey === cacheKey) {
126
+ clearTimeout(timer);
127
+ this.removeListener('push', handler);
128
+ resolve(resource);
129
+ }
130
+ };
131
+ this.on('push', handler);
132
+ });
133
+ }
134
+ getCachedPushes() {
135
+ const result = new Map();
136
+ const now = Date.now();
137
+ for (const [key, entry] of this.pushCache.entries()) {
138
+ if (now <= entry.expiresAt) {
139
+ result.set(key, entry.resource);
140
+ }
141
+ }
142
+ return result;
143
+ }
144
+ clearCache() {
145
+ this.pushCache.clear();
146
+ }
147
+ async close() {
148
+ const closePromises = [];
149
+ for (const session of this.sessions.values()) {
150
+ closePromises.push(new Promise((resolve) => {
151
+ session.close(() => resolve());
152
+ }));
153
+ }
154
+ await Promise.all(closePromises);
155
+ this.sessions.clear();
156
+ }
157
+ getSession(origin) {
158
+ return this.sessions.get(origin);
159
+ }
160
+ setupPushHandling(session, origin) {
161
+ session.on('stream', (pushedStream, requestHeaders) => {
162
+ const promise = {
163
+ path: requestHeaders[':path'],
164
+ method: requestHeaders[':method'] ?? 'GET',
165
+ authority: requestHeaders[':authority'] ?? new URL(origin).host,
166
+ scheme: requestHeaders[':scheme'] ?? 'https',
167
+ headers: { ...requestHeaders },
168
+ };
169
+ if (!this.options.filter(promise)) {
170
+ pushedStream.close(http2.constants.NGHTTP2_CANCEL);
171
+ this.emit('pushRejected', promise);
172
+ return;
173
+ }
174
+ this.emit('pushPromise', promise);
175
+ const cacheKey = this.getCacheKey(`${promise.scheme}://${promise.authority}${promise.path}`);
176
+ const pushPromise = this.handlePushStream(pushedStream, promise);
177
+ this.pendingPushes.set(cacheKey, pushPromise);
178
+ pushPromise
179
+ .then((resource) => {
180
+ this.pendingPushes.delete(cacheKey);
181
+ if (this.options.cachePushes) {
182
+ this.cacheResource(cacheKey, resource);
183
+ }
184
+ this.options.onPush(resource);
185
+ this.emit('push', resource);
186
+ })
187
+ .catch((err) => {
188
+ this.pendingPushes.delete(cacheKey);
189
+ this.emit('pushError', err, promise);
190
+ });
191
+ });
192
+ }
193
+ handlePushStream(stream, promise) {
194
+ return new Promise((resolve, reject) => {
195
+ const chunks = [];
196
+ let responseHeaders = {};
197
+ let status = 0;
198
+ const timeout = setTimeout(() => {
199
+ stream.close(http2.constants.NGHTTP2_CANCEL);
200
+ reject(new Error(`Push stream timeout for ${promise.path}`));
201
+ }, this.options.pushTimeout);
202
+ stream.on('response', (headers) => {
203
+ status = headers[':status'];
204
+ responseHeaders = { ...headers };
205
+ delete responseHeaders[':status'];
206
+ });
207
+ stream.on('data', (chunk) => {
208
+ chunks.push(chunk);
209
+ });
210
+ stream.on('end', () => {
211
+ clearTimeout(timeout);
212
+ resolve({
213
+ promise,
214
+ status,
215
+ headers: responseHeaders,
216
+ body: Buffer.concat(chunks),
217
+ receivedAt: new Date(),
218
+ });
219
+ });
220
+ stream.on('error', (err) => {
221
+ clearTimeout(timeout);
222
+ reject(err);
223
+ });
224
+ });
225
+ }
226
+ cacheResource(key, resource) {
227
+ if (this.pushCache.size >= this.options.maxCacheSize) {
228
+ const oldestKey = this.pushCache.keys().next().value;
229
+ if (oldestKey) {
230
+ this.pushCache.delete(oldestKey);
231
+ }
232
+ }
233
+ this.pushCache.set(key, {
234
+ resource,
235
+ expiresAt: Date.now() + this.options.cacheTtl,
236
+ });
237
+ }
238
+ getCacheKey(url) {
239
+ try {
240
+ const parsed = new URL(url);
241
+ return `${parsed.origin}${parsed.pathname}${parsed.search}`;
242
+ }
243
+ catch {
244
+ return url;
245
+ }
246
+ }
247
+ }
248
+ export function http2Push(options) {
249
+ const { manager } = options;
250
+ const middleware = async (req, next) => {
251
+ const cached = manager.getCachedPush(req.url);
252
+ if (cached && req.method === 'GET') {
253
+ const headers = new Headers();
254
+ for (const [key, value] of Object.entries(cached.headers)) {
255
+ if (!key.startsWith(':')) {
256
+ headers.set(key, Array.isArray(value) ? value.join(', ') : value);
257
+ }
258
+ }
259
+ headers.set('X-Push-Cache', 'hit');
260
+ const response = new Response(new Uint8Array(cached.body), {
261
+ status: cached.status,
262
+ headers,
263
+ });
264
+ const { HttpResponse } = await import('../core/response.js');
265
+ return new HttpResponse(response);
266
+ }
267
+ return next(req);
268
+ };
269
+ return (client) => {
270
+ client.use(middleware);
271
+ client.getPushManager = () => manager;
272
+ };
273
+ }
274
+ export { http2 };
@@ -0,0 +1,76 @@
1
+ import type { Client } from '../core/client.js';
2
+ import { EventEmitter } from 'node:events';
3
+ export interface Http3Options {
4
+ enabled?: boolean;
5
+ preferHttp3?: boolean;
6
+ fallback?: boolean;
7
+ connectTimeout?: number;
8
+ cacheAltSvc?: boolean;
9
+ altSvcCacheTtl?: number;
10
+ enable0RTT?: boolean;
11
+ alpnProtocols?: string[];
12
+ onHttp3?: (url: string) => void;
13
+ onFallback?: (url: string, reason: string) => void;
14
+ }
15
+ interface AltSvcEntry {
16
+ protocol: string;
17
+ host: string;
18
+ port: number;
19
+ maxAge: number;
20
+ expiresAt: number;
21
+ }
22
+ export declare class Http3Manager extends EventEmitter {
23
+ private options;
24
+ private altSvcCache;
25
+ private http3Supported;
26
+ private quicAvailable;
27
+ constructor(options?: Http3Options);
28
+ checkQuicSupport(): Promise<boolean>;
29
+ recordAltSvc(origin: string, altSvcHeader: string): void;
30
+ isHttp3Available(url: string): boolean;
31
+ getHttp3Endpoint(url: string): {
32
+ host: string;
33
+ port: number;
34
+ protocol: string;
35
+ } | null;
36
+ getKnownEndpoints(): Map<string, AltSvcEntry[]>;
37
+ clearCache(): void;
38
+ markUnsupported(origin: string): void;
39
+ getConnectionInfo(url: string): {
40
+ supportsHttp3: boolean;
41
+ endpoint: {
42
+ host: string;
43
+ port: number;
44
+ protocol: string;
45
+ } | null;
46
+ nativeQuicAvailable: boolean;
47
+ };
48
+ }
49
+ export declare function http3(options: {
50
+ manager: Http3Manager;
51
+ }): (client: Client) => void;
52
+ export declare function detectHttp3Support(client: Client, url: string): Promise<{
53
+ supported: boolean;
54
+ protocols: string[];
55
+ endpoint: {
56
+ host: string;
57
+ port: number;
58
+ } | null;
59
+ altSvcHeader: string | null;
60
+ }>;
61
+ declare module '../core/client.js' {
62
+ interface Client {
63
+ getHttp3Manager(): Http3Manager;
64
+ http3Info(url: string): {
65
+ supportsHttp3: boolean;
66
+ endpoint: {
67
+ host: string;
68
+ port: number;
69
+ protocol: string;
70
+ } | null;
71
+ nativeQuicAvailable: boolean;
72
+ };
73
+ }
74
+ }
75
+ export {};
76
+ //# sourceMappingURL=http3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http3.d.ts","sourceRoot":"","sources":["../../src/plugins/http3.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,231 @@
1
+ import { EventEmitter } from 'node:events';
2
+ function parseAltSvc(header, origin) {
3
+ const entries = [];
4
+ const parts = header.split(',').map((p) => p.trim());
5
+ const originUrl = new URL(origin);
6
+ for (const part of parts) {
7
+ const match = part.match(/^([\w-]+)="?([^";\s]*)"?(?:;\s*ma=(\d+))?/);
8
+ if (!match)
9
+ continue;
10
+ const [, protocol, hostPort, maxAgeStr] = match;
11
+ const maxAge = maxAgeStr ? parseInt(maxAgeStr, 10) : 86400;
12
+ let host = originUrl.hostname;
13
+ let port = parseInt(originUrl.port || '443', 10);
14
+ if (hostPort) {
15
+ if (hostPort.startsWith(':')) {
16
+ port = parseInt(hostPort.slice(1), 10);
17
+ }
18
+ else {
19
+ const [h, p] = hostPort.split(':');
20
+ if (h)
21
+ host = h;
22
+ if (p)
23
+ port = parseInt(p, 10);
24
+ }
25
+ }
26
+ entries.push({
27
+ protocol,
28
+ host,
29
+ port,
30
+ maxAge,
31
+ expiresAt: Date.now() + maxAge * 1000,
32
+ });
33
+ }
34
+ return entries;
35
+ }
36
+ export class Http3Manager extends EventEmitter {
37
+ options;
38
+ altSvcCache = new Map();
39
+ http3Supported = new Map();
40
+ quicAvailable = null;
41
+ constructor(options = {}) {
42
+ super();
43
+ this.options = {
44
+ enabled: options.enabled ?? true,
45
+ preferHttp3: options.preferHttp3 ?? true,
46
+ fallback: options.fallback ?? true,
47
+ connectTimeout: options.connectTimeout ?? 5000,
48
+ cacheAltSvc: options.cacheAltSvc ?? true,
49
+ altSvcCacheTtl: options.altSvcCacheTtl ?? 86400000,
50
+ enable0RTT: options.enable0RTT ?? false,
51
+ alpnProtocols: options.alpnProtocols ?? ['h3', 'h3-29', 'h2', 'http/1.1'],
52
+ onHttp3: options.onHttp3 ?? (() => { }),
53
+ onFallback: options.onFallback ?? (() => { }),
54
+ };
55
+ }
56
+ async checkQuicSupport() {
57
+ if (this.quicAvailable !== null) {
58
+ return this.quicAvailable;
59
+ }
60
+ try {
61
+ const [major] = process.versions.node.split('.').map(Number);
62
+ if (major < 23) {
63
+ this.quicAvailable = false;
64
+ return false;
65
+ }
66
+ await import('node:quic').catch(() => null);
67
+ this.quicAvailable = true;
68
+ this.emit('quicAvailable');
69
+ return true;
70
+ }
71
+ catch {
72
+ this.quicAvailable = false;
73
+ return false;
74
+ }
75
+ }
76
+ recordAltSvc(origin, altSvcHeader) {
77
+ if (!this.options.cacheAltSvc)
78
+ return;
79
+ const entries = parseAltSvc(altSvcHeader, origin);
80
+ const http3Entries = entries.filter((e) => e.protocol.startsWith('h3') || e.protocol === 'quic');
81
+ if (http3Entries.length > 0) {
82
+ const maxExpiresAt = Date.now() + this.options.altSvcCacheTtl;
83
+ const cappedEntries = http3Entries.map((e) => ({
84
+ ...e,
85
+ expiresAt: Math.min(e.expiresAt, maxExpiresAt),
86
+ }));
87
+ this.altSvcCache.set(origin, cappedEntries);
88
+ this.http3Supported.set(origin, true);
89
+ this.emit('http3Discovered', origin, cappedEntries);
90
+ }
91
+ }
92
+ isHttp3Available(url) {
93
+ if (!this.options.enabled)
94
+ return false;
95
+ try {
96
+ const origin = new URL(url).origin;
97
+ const cached = this.altSvcCache.get(origin);
98
+ if (!cached)
99
+ return false;
100
+ const now = Date.now();
101
+ const valid = cached.filter((e) => e.expiresAt > now);
102
+ if (valid.length === 0) {
103
+ this.altSvcCache.delete(origin);
104
+ this.http3Supported.delete(origin);
105
+ return false;
106
+ }
107
+ return true;
108
+ }
109
+ catch {
110
+ return false;
111
+ }
112
+ }
113
+ getHttp3Endpoint(url) {
114
+ try {
115
+ const origin = new URL(url).origin;
116
+ const cached = this.altSvcCache.get(origin);
117
+ if (!cached)
118
+ return null;
119
+ const now = Date.now();
120
+ const valid = cached.filter((e) => e.expiresAt > now);
121
+ const sorted = valid.sort((a, b) => {
122
+ if (a.protocol === 'h3' && b.protocol !== 'h3')
123
+ return -1;
124
+ if (a.protocol !== 'h3' && b.protocol === 'h3')
125
+ return 1;
126
+ return 0;
127
+ });
128
+ const entry = sorted[0];
129
+ if (!entry)
130
+ return null;
131
+ return {
132
+ host: entry.host,
133
+ port: entry.port,
134
+ protocol: entry.protocol,
135
+ };
136
+ }
137
+ catch {
138
+ return null;
139
+ }
140
+ }
141
+ getKnownEndpoints() {
142
+ const now = Date.now();
143
+ const result = new Map();
144
+ for (const [origin, entries] of this.altSvcCache) {
145
+ const valid = entries.filter((e) => e.expiresAt > now);
146
+ if (valid.length > 0) {
147
+ result.set(origin, valid);
148
+ }
149
+ }
150
+ return result;
151
+ }
152
+ clearCache() {
153
+ this.altSvcCache.clear();
154
+ this.http3Supported.clear();
155
+ }
156
+ markUnsupported(origin) {
157
+ this.altSvcCache.delete(origin);
158
+ this.http3Supported.set(origin, false);
159
+ this.emit('http3Unsupported', origin);
160
+ }
161
+ getConnectionInfo(url) {
162
+ return {
163
+ supportsHttp3: this.isHttp3Available(url),
164
+ endpoint: this.getHttp3Endpoint(url),
165
+ nativeQuicAvailable: this.quicAvailable ?? false,
166
+ };
167
+ }
168
+ }
169
+ export function http3(options) {
170
+ const { manager } = options;
171
+ const middleware = async (req, next) => {
172
+ const response = await next(req);
173
+ const altSvc = response.headers.get('alt-svc');
174
+ if (altSvc) {
175
+ try {
176
+ const origin = new URL(req.url).origin;
177
+ manager.recordAltSvc(origin, altSvc);
178
+ }
179
+ catch {
180
+ }
181
+ }
182
+ return response;
183
+ };
184
+ return (client) => {
185
+ client.use(middleware);
186
+ client.getHttp3Manager = () => manager;
187
+ client.http3Info = (url) => {
188
+ return manager.getConnectionInfo(url);
189
+ };
190
+ };
191
+ }
192
+ export async function detectHttp3Support(client, url) {
193
+ try {
194
+ const response = await client.head(url);
195
+ const altSvc = response.headers.get('alt-svc');
196
+ if (!altSvc) {
197
+ return {
198
+ supported: false,
199
+ protocols: [],
200
+ endpoint: null,
201
+ altSvcHeader: null,
202
+ };
203
+ }
204
+ const entries = parseAltSvc(altSvc, url);
205
+ const http3Entries = entries.filter((e) => e.protocol.startsWith('h3') || e.protocol === 'quic');
206
+ if (http3Entries.length === 0) {
207
+ return {
208
+ supported: false,
209
+ protocols: [],
210
+ endpoint: null,
211
+ altSvcHeader: altSvc,
212
+ };
213
+ }
214
+ const protocols = [...new Set(http3Entries.map((e) => e.protocol))];
215
+ const primary = http3Entries[0];
216
+ return {
217
+ supported: true,
218
+ protocols,
219
+ endpoint: primary ? { host: primary.host, port: primary.port } : null,
220
+ altSvcHeader: altSvc,
221
+ };
222
+ }
223
+ catch {
224
+ return {
225
+ supported: false,
226
+ protocols: [],
227
+ endpoint: null,
228
+ altSvcHeader: null,
229
+ };
230
+ }
231
+ }
@@ -0,0 +1,10 @@
1
+ import { Plugin } from '../types/index.js';
2
+ export interface InterfaceRotatorOptions {
3
+ strategy?: 'round-robin' | 'random';
4
+ interface?: string | RegExp;
5
+ family?: 'IPv4' | 'IPv6' | 'both';
6
+ excludeInternal?: boolean;
7
+ ips?: string[];
8
+ }
9
+ export declare function interfaceRotator(options?: InterfaceRotatorOptions): Plugin;
10
+ //# sourceMappingURL=interface-rotator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface-rotator.d.ts","sourceRoot":"","sources":["../../src/plugins/interface-rotator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAiB,MAAM,mBAAmB,CAAC;AAI1D,MAAM,WAAW,uBAAuB;IAKtC,QAAQ,CAAC,EAAE,aAAa,GAAG,QAAQ,CAAC;IAMpC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAM5B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAMlC,eAAe,CAAC,EAAE,OAAO,CAAC;IAM1B,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,MAAM,CAyE9E"}