recker 1.0.26 → 1.0.27
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.
- package/dist/browser/browser/cache.d.ts +40 -0
- package/dist/browser/browser/cache.js +199 -0
- package/dist/browser/browser/crypto.d.ts +24 -0
- package/dist/browser/browser/crypto.js +80 -0
- package/dist/browser/browser/index.d.ts +31 -0
- package/dist/browser/browser/index.js +31 -0
- package/dist/browser/browser/recker.d.ts +26 -0
- package/dist/browser/browser/recker.js +61 -0
- package/dist/browser/cache/basic-file-storage.d.ts +12 -0
- package/dist/browser/cache/basic-file-storage.js +50 -0
- package/dist/browser/cache/memory-limits.d.ts +20 -0
- package/dist/browser/cache/memory-limits.js +96 -0
- package/dist/browser/cache/memory-storage.d.ts +132 -0
- package/dist/browser/cache/memory-storage.js +454 -0
- package/dist/browser/cache.d.ts +40 -0
- package/dist/browser/cache.js +199 -0
- package/dist/browser/constants/http-status.d.ts +73 -0
- package/dist/browser/constants/http-status.js +156 -0
- package/dist/browser/cookies/memory-cookie-jar.d.ts +30 -0
- package/dist/browser/cookies/memory-cookie-jar.js +210 -0
- package/dist/browser/core/client.d.ts +118 -0
- package/dist/browser/core/client.js +667 -0
- package/dist/browser/core/errors.d.ts +142 -0
- package/dist/browser/core/errors.js +308 -0
- package/dist/browser/core/index.d.ts +5 -0
- package/dist/browser/core/index.js +5 -0
- package/dist/browser/core/request-promise.d.ts +23 -0
- package/dist/browser/core/request-promise.js +82 -0
- package/dist/browser/core/request.d.ts +20 -0
- package/dist/browser/core/request.js +76 -0
- package/dist/browser/core/response.d.ts +34 -0
- package/dist/browser/core/response.js +178 -0
- package/dist/browser/crypto.d.ts +24 -0
- package/dist/browser/crypto.js +80 -0
- package/dist/browser/index.d.ts +31 -0
- package/dist/browser/index.js +31 -0
- package/dist/browser/plugins/auth/api-key.d.ts +8 -0
- package/dist/browser/plugins/auth/api-key.js +27 -0
- package/dist/browser/plugins/auth/auth0.d.ts +33 -0
- package/dist/browser/plugins/auth/auth0.js +94 -0
- package/dist/browser/plugins/auth/aws-sigv4.d.ts +10 -0
- package/dist/browser/plugins/auth/aws-sigv4.js +88 -0
- package/dist/browser/plugins/auth/azure-ad.d.ts +48 -0
- package/dist/browser/plugins/auth/azure-ad.js +152 -0
- package/dist/browser/plugins/auth/basic.d.ts +7 -0
- package/dist/browser/plugins/auth/basic.js +13 -0
- package/dist/browser/plugins/auth/bearer.d.ts +8 -0
- package/dist/browser/plugins/auth/bearer.js +17 -0
- package/dist/browser/plugins/auth/cognito.d.ts +45 -0
- package/dist/browser/plugins/auth/cognito.js +208 -0
- package/dist/browser/plugins/auth/digest.d.ts +8 -0
- package/dist/browser/plugins/auth/digest.js +100 -0
- package/dist/browser/plugins/auth/firebase.d.ts +32 -0
- package/dist/browser/plugins/auth/firebase.js +195 -0
- package/dist/browser/plugins/auth/github-app.d.ts +36 -0
- package/dist/browser/plugins/auth/github-app.js +170 -0
- package/dist/browser/plugins/auth/google-service-account.d.ts +49 -0
- package/dist/browser/plugins/auth/google-service-account.js +172 -0
- package/dist/browser/plugins/auth/index.d.ts +15 -0
- package/dist/browser/plugins/auth/index.js +15 -0
- package/dist/browser/plugins/auth/mtls.d.ts +37 -0
- package/dist/browser/plugins/auth/mtls.js +140 -0
- package/dist/browser/plugins/auth/oauth2.d.ts +8 -0
- package/dist/browser/plugins/auth/oauth2.js +26 -0
- package/dist/browser/plugins/auth/oidc.d.ts +55 -0
- package/dist/browser/plugins/auth/oidc.js +222 -0
- package/dist/browser/plugins/auth/okta.d.ts +47 -0
- package/dist/browser/plugins/auth/okta.js +157 -0
- package/dist/browser/plugins/auth.d.ts +1 -0
- package/dist/browser/plugins/auth.js +1 -0
- package/dist/browser/plugins/cache.d.ts +15 -0
- package/dist/browser/plugins/cache.js +486 -0
- package/dist/browser/plugins/circuit-breaker.d.ts +13 -0
- package/dist/browser/plugins/circuit-breaker.js +100 -0
- package/dist/browser/plugins/compression.d.ts +4 -0
- package/dist/browser/plugins/compression.js +130 -0
- package/dist/browser/plugins/cookie-jar.d.ts +5 -0
- package/dist/browser/plugins/cookie-jar.js +72 -0
- package/dist/browser/plugins/dedup.d.ts +5 -0
- package/dist/browser/plugins/dedup.js +35 -0
- package/dist/browser/plugins/graphql.d.ts +13 -0
- package/dist/browser/plugins/graphql.js +58 -0
- package/dist/browser/plugins/grpc-web.d.ts +79 -0
- package/dist/browser/plugins/grpc-web.js +261 -0
- package/dist/browser/plugins/hls.d.ts +105 -0
- package/dist/browser/plugins/hls.js +395 -0
- package/dist/browser/plugins/jsonrpc.d.ts +75 -0
- package/dist/browser/plugins/jsonrpc.js +143 -0
- package/dist/browser/plugins/logger.d.ts +13 -0
- package/dist/browser/plugins/logger.js +108 -0
- package/dist/browser/plugins/odata.d.ts +181 -0
- package/dist/browser/plugins/odata.js +564 -0
- package/dist/browser/plugins/pagination.d.ts +16 -0
- package/dist/browser/plugins/pagination.js +105 -0
- package/dist/browser/plugins/rate-limit.d.ts +15 -0
- package/dist/browser/plugins/rate-limit.js +162 -0
- package/dist/browser/plugins/retry.d.ts +14 -0
- package/dist/browser/plugins/retry.js +116 -0
- package/dist/browser/plugins/scrape.d.ts +21 -0
- package/dist/browser/plugins/scrape.js +82 -0
- package/dist/browser/plugins/server-timing.d.ts +7 -0
- package/dist/browser/plugins/server-timing.js +24 -0
- package/dist/browser/plugins/soap.d.ts +72 -0
- package/dist/browser/plugins/soap.js +347 -0
- package/dist/browser/plugins/xml.d.ts +9 -0
- package/dist/browser/plugins/xml.js +194 -0
- package/dist/browser/plugins/xsrf.d.ts +9 -0
- package/dist/browser/plugins/xsrf.js +48 -0
- package/dist/browser/recker.d.ts +26 -0
- package/dist/browser/recker.js +61 -0
- package/dist/browser/runner/request-runner.d.ts +46 -0
- package/dist/browser/runner/request-runner.js +89 -0
- package/dist/browser/scrape/document.d.ts +44 -0
- package/dist/browser/scrape/document.js +210 -0
- package/dist/browser/scrape/element.d.ts +49 -0
- package/dist/browser/scrape/element.js +176 -0
- package/dist/browser/scrape/extractors.d.ts +16 -0
- package/dist/browser/scrape/extractors.js +356 -0
- package/dist/browser/scrape/types.d.ts +107 -0
- package/dist/browser/scrape/types.js +1 -0
- package/dist/browser/transport/fetch.d.ts +11 -0
- package/dist/browser/transport/fetch.js +143 -0
- package/dist/browser/transport/undici.d.ts +38 -0
- package/dist/browser/transport/undici.js +897 -0
- package/dist/browser/types/ai.d.ts +267 -0
- package/dist/browser/types/ai.js +1 -0
- package/dist/browser/types/index.d.ts +351 -0
- package/dist/browser/types/index.js +1 -0
- package/dist/browser/types/logger.d.ts +16 -0
- package/dist/browser/types/logger.js +66 -0
- package/dist/browser/types/udp.d.ts +138 -0
- package/dist/browser/types/udp.js +1 -0
- package/dist/browser/utils/agent-manager.d.ts +29 -0
- package/dist/browser/utils/agent-manager.js +160 -0
- package/dist/browser/utils/body.d.ts +10 -0
- package/dist/browser/utils/body.js +148 -0
- package/dist/browser/utils/charset.d.ts +15 -0
- package/dist/browser/utils/charset.js +169 -0
- package/dist/browser/utils/concurrency.d.ts +20 -0
- package/dist/browser/utils/concurrency.js +120 -0
- package/dist/browser/utils/dns.d.ts +6 -0
- package/dist/browser/utils/dns.js +26 -0
- package/dist/browser/utils/header-parser.d.ts +94 -0
- package/dist/browser/utils/header-parser.js +617 -0
- package/dist/browser/utils/html-cleaner.d.ts +1 -0
- package/dist/browser/utils/html-cleaner.js +21 -0
- package/dist/browser/utils/link-header.d.ts +69 -0
- package/dist/browser/utils/link-header.js +190 -0
- package/dist/browser/utils/optional-require.d.ts +19 -0
- package/dist/browser/utils/optional-require.js +105 -0
- package/dist/browser/utils/progress.d.ts +8 -0
- package/dist/browser/utils/progress.js +82 -0
- package/dist/browser/utils/request-pool.d.ts +22 -0
- package/dist/browser/utils/request-pool.js +101 -0
- package/dist/browser/utils/sse.d.ts +7 -0
- package/dist/browser/utils/sse.js +67 -0
- package/dist/browser/utils/streaming.d.ts +17 -0
- package/dist/browser/utils/streaming.js +84 -0
- package/dist/browser/utils/try-fn.d.ts +3 -0
- package/dist/browser/utils/try-fn.js +59 -0
- package/dist/browser/utils/user-agent.d.ts +44 -0
- package/dist/browser/utils/user-agent.js +100 -0
- package/dist/browser/utils/whois.d.ts +32 -0
- package/dist/browser/utils/whois.js +246 -0
- package/dist/browser/websocket/client.d.ts +65 -0
- package/dist/browser/websocket/client.js +313 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1 -0
- package/dist/transport/fetch.d.ts +7 -1
- package/dist/transport/fetch.js +58 -76
- package/package.json +34 -2
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
import { consoleLogger } from '../types/index.js';
|
|
2
|
+
import { HttpRequest } from './request.js';
|
|
3
|
+
import { UndiciTransport } from '../transport/undici.js';
|
|
4
|
+
import { RequestPromise } from './request-promise.js';
|
|
5
|
+
import { HttpError, MaxSizeExceededError, ConfigurationError, ValidationError, TimeoutError } from '../core/errors.js';
|
|
6
|
+
import { processBody, createFormData, isPlainObject } from '../utils/body.js';
|
|
7
|
+
import { AgentManager } from '../utils/agent-manager.js';
|
|
8
|
+
import { RequestPool } from '../utils/request-pool.js';
|
|
9
|
+
import { normalizeConcurrency } from '../utils/concurrency.js';
|
|
10
|
+
import { getDefaultUserAgent } from '../utils/user-agent.js';
|
|
11
|
+
import { paginate, streamPages } from '../plugins/pagination.js';
|
|
12
|
+
import { retryPlugin } from '../plugins/retry.js';
|
|
13
|
+
import { cachePlugin } from '../plugins/cache.js';
|
|
14
|
+
import { dedupPlugin } from '../plugins/dedup.js';
|
|
15
|
+
import { createXSRFMiddleware } from '../plugins/xsrf.js';
|
|
16
|
+
import { createCompressionMiddleware } from '../plugins/compression.js';
|
|
17
|
+
import { serializeXML } from '../plugins/xml.js';
|
|
18
|
+
import { MemoryStorage } from '../cache/memory-storage.js';
|
|
19
|
+
import { FileStorage } from '../cache/basic-file-storage.js';
|
|
20
|
+
import { RequestRunner } from '../runner/request-runner.js';
|
|
21
|
+
import { ReckerWebSocket } from '../websocket/client.js';
|
|
22
|
+
import { whois as performWhois, isDomainAvailable } from '../utils/whois.js';
|
|
23
|
+
import { MemoryCookieJar } from '../cookies/memory-cookie-jar.js';
|
|
24
|
+
import { scrape as scrapeHelper } from '../plugins/scrape.js';
|
|
25
|
+
import { HlsPromise } from '../plugins/hls.js';
|
|
26
|
+
export class Client {
|
|
27
|
+
baseUrl;
|
|
28
|
+
middlewares;
|
|
29
|
+
hooks;
|
|
30
|
+
transport;
|
|
31
|
+
defaultHeaders;
|
|
32
|
+
defaultParams;
|
|
33
|
+
paginationConfig;
|
|
34
|
+
handler;
|
|
35
|
+
logger;
|
|
36
|
+
debugEnabled;
|
|
37
|
+
agentManager;
|
|
38
|
+
concurrencyConfig;
|
|
39
|
+
requestPool;
|
|
40
|
+
maxResponseSize;
|
|
41
|
+
cookieJar;
|
|
42
|
+
cookieIgnoreInvalid = false;
|
|
43
|
+
defaultTimeout;
|
|
44
|
+
constructor(options = {}) {
|
|
45
|
+
this.baseUrl = options.baseUrl || '';
|
|
46
|
+
this.middlewares = options.middlewares || [];
|
|
47
|
+
this.defaultTimeout = options.timeout;
|
|
48
|
+
this.hooks = {
|
|
49
|
+
beforeRequest: options.hooks?.beforeRequest || [],
|
|
50
|
+
afterResponse: options.hooks?.afterResponse || [],
|
|
51
|
+
onError: options.hooks?.onError || [],
|
|
52
|
+
onRetry: options.hooks?.onRetry || [],
|
|
53
|
+
onUrlResolved: options.hooks?.onUrlResolved || [],
|
|
54
|
+
};
|
|
55
|
+
this.defaultHeaders = {
|
|
56
|
+
'User-Agent': getDefaultUserAgent(),
|
|
57
|
+
...(options.headers || {})
|
|
58
|
+
};
|
|
59
|
+
this.defaultParams = options.defaults?.params || {};
|
|
60
|
+
this.paginationConfig = options.pagination;
|
|
61
|
+
this.maxResponseSize = options.maxResponseSize;
|
|
62
|
+
this.debugEnabled = options.debug === true;
|
|
63
|
+
if (this.debugEnabled) {
|
|
64
|
+
this.logger = options.logger ?? consoleLogger;
|
|
65
|
+
}
|
|
66
|
+
else if (options.logger) {
|
|
67
|
+
this.logger = options.logger;
|
|
68
|
+
}
|
|
69
|
+
this.concurrencyConfig = normalizeConcurrency({
|
|
70
|
+
concurrency: options.concurrency,
|
|
71
|
+
http2: options.http2
|
|
72
|
+
});
|
|
73
|
+
if (options.transport) {
|
|
74
|
+
this.transport = options.transport;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
let http2Options;
|
|
78
|
+
if (options.http2) {
|
|
79
|
+
if (typeof options.http2 === 'boolean') {
|
|
80
|
+
http2Options = { enabled: options.http2 };
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
http2Options = options.http2;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
this.agentManager = new AgentManager(this.concurrencyConfig.agent);
|
|
87
|
+
this.transport = new UndiciTransport(this.baseUrl || undefined, {
|
|
88
|
+
proxy: options.proxy,
|
|
89
|
+
http2: http2Options,
|
|
90
|
+
dns: options.dns,
|
|
91
|
+
agent: this.agentManager,
|
|
92
|
+
socketPath: options.socketPath,
|
|
93
|
+
tls: options.tls,
|
|
94
|
+
observability: options.observability
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (options.retry) {
|
|
98
|
+
retryPlugin(options.retry)(this);
|
|
99
|
+
}
|
|
100
|
+
if (this.concurrencyConfig.max < Infinity || this.concurrencyConfig.requestsPerInterval < Infinity) {
|
|
101
|
+
this.requestPool = new RequestPool({
|
|
102
|
+
concurrency: this.concurrencyConfig.max,
|
|
103
|
+
requestsPerInterval: this.concurrencyConfig.requestsPerInterval,
|
|
104
|
+
interval: this.concurrencyConfig.interval
|
|
105
|
+
});
|
|
106
|
+
this.middlewares.unshift(this.requestPool.asMiddleware());
|
|
107
|
+
if (this.debugEnabled && this.logger) {
|
|
108
|
+
this.logger.debug(`Global concurrency limit: ${this.concurrencyConfig.max} concurrent requests`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
if (this.debugEnabled && this.logger) {
|
|
113
|
+
this.logger.debug('No global concurrency limit (allows unlimited parallel batches)');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (options.dedup) {
|
|
117
|
+
dedupPlugin(options.dedup)(this);
|
|
118
|
+
}
|
|
119
|
+
if (options.cache) {
|
|
120
|
+
let storage;
|
|
121
|
+
if (options.cache.storage) {
|
|
122
|
+
storage = options.cache.storage;
|
|
123
|
+
}
|
|
124
|
+
else if (options.cache.driver === 'file') {
|
|
125
|
+
storage = new FileStorage(options.cache.fileStoragePath);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
storage = new MemoryStorage();
|
|
129
|
+
}
|
|
130
|
+
cachePlugin({
|
|
131
|
+
...options.cache,
|
|
132
|
+
storage
|
|
133
|
+
})(this);
|
|
134
|
+
}
|
|
135
|
+
if (options.plugins) {
|
|
136
|
+
options.plugins.forEach((plugin) => plugin(this));
|
|
137
|
+
}
|
|
138
|
+
if (options.compression) {
|
|
139
|
+
const compressionMiddleware = createCompressionMiddleware(options.compression);
|
|
140
|
+
if (compressionMiddleware) {
|
|
141
|
+
this.middlewares.push(compressionMiddleware);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (options.xsrf) {
|
|
145
|
+
const xsrfMiddleware = createXSRFMiddleware(options.xsrf);
|
|
146
|
+
if (xsrfMiddleware) {
|
|
147
|
+
this.middlewares.push(xsrfMiddleware);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (options.cookies) {
|
|
151
|
+
this.setupCookieJar(options.cookies);
|
|
152
|
+
}
|
|
153
|
+
if (this.maxResponseSize !== undefined) {
|
|
154
|
+
this.middlewares.push(this.createMaxSizeMiddleware(this.maxResponseSize));
|
|
155
|
+
}
|
|
156
|
+
if (this.debugEnabled && this.logger) {
|
|
157
|
+
this.middlewares.unshift(this.createLoggingMiddleware(this.logger));
|
|
158
|
+
}
|
|
159
|
+
this.middlewares.push(this.httpErrorMiddleware);
|
|
160
|
+
this.handler = this.composeMiddlewares();
|
|
161
|
+
}
|
|
162
|
+
createLoggingMiddleware(logger) {
|
|
163
|
+
return async (req, next) => {
|
|
164
|
+
const startTime = Date.now();
|
|
165
|
+
logger.debug({ type: 'request', method: req.method, url: req.url }, `→ ${req.method} ${req.url}`);
|
|
166
|
+
try {
|
|
167
|
+
const response = await next(req);
|
|
168
|
+
const duration = Date.now() - startTime;
|
|
169
|
+
logger.debug({
|
|
170
|
+
type: 'response',
|
|
171
|
+
method: req.method,
|
|
172
|
+
url: req.url,
|
|
173
|
+
status: response.status,
|
|
174
|
+
duration,
|
|
175
|
+
timings: response.timings,
|
|
176
|
+
}, `← ${response.status} ${req.method} ${req.url} (${duration}ms)`);
|
|
177
|
+
return response;
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
const duration = Date.now() - startTime;
|
|
181
|
+
const err = error;
|
|
182
|
+
logger.error({
|
|
183
|
+
type: 'error',
|
|
184
|
+
method: req.method,
|
|
185
|
+
url: req.url,
|
|
186
|
+
error: err.message,
|
|
187
|
+
errorName: err.name,
|
|
188
|
+
duration,
|
|
189
|
+
}, `✖ ${req.method} ${req.url} - ${err.message}`);
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
createMaxSizeMiddleware(globalMaxSize) {
|
|
195
|
+
return async (req, next) => {
|
|
196
|
+
const response = await next(req);
|
|
197
|
+
const limit = req.maxResponseSize ?? globalMaxSize;
|
|
198
|
+
if (limit === undefined)
|
|
199
|
+
return response;
|
|
200
|
+
const contentLength = response.headers.get('Content-Length');
|
|
201
|
+
if (contentLength) {
|
|
202
|
+
const size = parseInt(contentLength, 10);
|
|
203
|
+
if (!isNaN(size) && size > limit) {
|
|
204
|
+
throw new MaxSizeExceededError(limit, size, req);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return response;
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
setupCookieJar(options) {
|
|
211
|
+
if (options === true) {
|
|
212
|
+
this.cookieJar = new MemoryCookieJar();
|
|
213
|
+
}
|
|
214
|
+
else if (typeof options === 'object') {
|
|
215
|
+
if (options.jar === true) {
|
|
216
|
+
this.cookieJar = new MemoryCookieJar();
|
|
217
|
+
}
|
|
218
|
+
else if (options.jar && typeof options.jar === 'object') {
|
|
219
|
+
this.cookieJar = options.jar;
|
|
220
|
+
}
|
|
221
|
+
this.cookieIgnoreInvalid = options.ignoreInvalid ?? false;
|
|
222
|
+
}
|
|
223
|
+
if (this.cookieJar) {
|
|
224
|
+
this.middlewares.push(this.createCookieMiddleware());
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
createCookieMiddleware() {
|
|
228
|
+
return async (req, next) => {
|
|
229
|
+
const jar = this.cookieJar;
|
|
230
|
+
try {
|
|
231
|
+
const cookieString = await jar.getCookieString(req.url);
|
|
232
|
+
if (cookieString) {
|
|
233
|
+
const existingCookie = req.headers.get('cookie');
|
|
234
|
+
const newCookie = existingCookie
|
|
235
|
+
? `${existingCookie}; ${cookieString}`
|
|
236
|
+
: cookieString;
|
|
237
|
+
req.headers.set('cookie', newCookie);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
if (!this.cookieIgnoreInvalid) {
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const response = await next(req);
|
|
246
|
+
const setCookieHeader = response.headers.get('set-cookie');
|
|
247
|
+
if (setCookieHeader) {
|
|
248
|
+
const cookies = this.splitSetCookieHeader(setCookieHeader);
|
|
249
|
+
for (const cookie of cookies) {
|
|
250
|
+
try {
|
|
251
|
+
await jar.setCookie(cookie, req.url);
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
if (!this.cookieIgnoreInvalid) {
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return response;
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
splitSetCookieHeader(header) {
|
|
264
|
+
return header.split(/,(?=\s*[a-zA-Z0-9_-]+=)/g).map(s => s.trim());
|
|
265
|
+
}
|
|
266
|
+
composeMiddlewares() {
|
|
267
|
+
const chain = [...this.middlewares];
|
|
268
|
+
const transportDispatch = this.transport.dispatch.bind(this.transport);
|
|
269
|
+
if (this.hooks.beforeRequest?.length || this.hooks.afterResponse?.length) {
|
|
270
|
+
chain.unshift(this.hooksMiddleware);
|
|
271
|
+
}
|
|
272
|
+
if (chain.length === 0) {
|
|
273
|
+
return transportDispatch;
|
|
274
|
+
}
|
|
275
|
+
return chain.reduceRight((next, middleware) => {
|
|
276
|
+
return (req) => middleware(req, next);
|
|
277
|
+
}, transportDispatch);
|
|
278
|
+
}
|
|
279
|
+
hooksMiddleware = async (req, next) => {
|
|
280
|
+
let modifiedReq = req;
|
|
281
|
+
if (this.hooks.beforeRequest && this.hooks.beforeRequest.length > 0) {
|
|
282
|
+
for (const hook of this.hooks.beforeRequest) {
|
|
283
|
+
const result = await hook(modifiedReq);
|
|
284
|
+
if (result) {
|
|
285
|
+
modifiedReq = result;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
let response = await next(modifiedReq);
|
|
291
|
+
if (this.hooks.afterResponse && this.hooks.afterResponse.length > 0) {
|
|
292
|
+
for (const hook of this.hooks.afterResponse) {
|
|
293
|
+
const result = await hook(modifiedReq, response);
|
|
294
|
+
if (result) {
|
|
295
|
+
response = result;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return response;
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
if (this.hooks.onError && this.hooks.onError.length > 0) {
|
|
303
|
+
for (const hook of this.hooks.onError) {
|
|
304
|
+
const result = await hook(error, modifiedReq);
|
|
305
|
+
if (result) {
|
|
306
|
+
return result;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
httpErrorMiddleware = async (req, next) => {
|
|
314
|
+
const response = await next(req);
|
|
315
|
+
if (req.throwHttpErrors !== false && !response.ok && response.status !== 304) {
|
|
316
|
+
throw new HttpError(response, req);
|
|
317
|
+
}
|
|
318
|
+
return response;
|
|
319
|
+
};
|
|
320
|
+
use(middleware) {
|
|
321
|
+
this.middlewares.push(middleware);
|
|
322
|
+
this.handler = this.composeMiddlewares();
|
|
323
|
+
return this;
|
|
324
|
+
}
|
|
325
|
+
beforeRequest(hook) {
|
|
326
|
+
if (!this.hooks.beforeRequest) {
|
|
327
|
+
this.hooks.beforeRequest = [];
|
|
328
|
+
}
|
|
329
|
+
this.hooks.beforeRequest.push(hook);
|
|
330
|
+
this.handler = this.composeMiddlewares();
|
|
331
|
+
return this;
|
|
332
|
+
}
|
|
333
|
+
afterResponse(hook) {
|
|
334
|
+
if (!this.hooks.afterResponse) {
|
|
335
|
+
this.hooks.afterResponse = [];
|
|
336
|
+
}
|
|
337
|
+
this.hooks.afterResponse.push(hook);
|
|
338
|
+
this.handler = this.composeMiddlewares();
|
|
339
|
+
return this;
|
|
340
|
+
}
|
|
341
|
+
onError(hook) {
|
|
342
|
+
if (!this.hooks.onError) {
|
|
343
|
+
this.hooks.onError = [];
|
|
344
|
+
}
|
|
345
|
+
this.hooks.onError.push(hook);
|
|
346
|
+
this.handler = this.composeMiddlewares();
|
|
347
|
+
return this;
|
|
348
|
+
}
|
|
349
|
+
buildUrl(path, requestParams) {
|
|
350
|
+
const hasRequestParams = requestParams && Object.keys(requestParams).length > 0;
|
|
351
|
+
const hasDefaultParams = Object.keys(this.defaultParams).length > 0;
|
|
352
|
+
if (!hasRequestParams && !hasDefaultParams) {
|
|
353
|
+
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
354
|
+
return path;
|
|
355
|
+
}
|
|
356
|
+
if (this.baseUrl) {
|
|
357
|
+
const base = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
|
|
358
|
+
const p = path.startsWith('/') ? path : '/' + path;
|
|
359
|
+
return base + p;
|
|
360
|
+
}
|
|
361
|
+
return path;
|
|
362
|
+
}
|
|
363
|
+
let finalPath = path;
|
|
364
|
+
const mergedParams = { ...this.defaultParams, ...requestParams };
|
|
365
|
+
const usedParams = new Set();
|
|
366
|
+
if (finalPath.includes(':')) {
|
|
367
|
+
finalPath = finalPath.replace(/:([a-zA-Z0-9_]+)/g, (match, paramName) => {
|
|
368
|
+
if (mergedParams && paramName in mergedParams) {
|
|
369
|
+
usedParams.add(paramName);
|
|
370
|
+
return encodeURIComponent(String(mergedParams[paramName]));
|
|
371
|
+
}
|
|
372
|
+
throw new ValidationError(`Missing required path parameter: ${paramName}`, {
|
|
373
|
+
field: paramName,
|
|
374
|
+
value: undefined,
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
let finalUrl;
|
|
379
|
+
if (finalPath.startsWith('http://') || finalPath.startsWith('https://')) {
|
|
380
|
+
finalUrl = finalPath;
|
|
381
|
+
}
|
|
382
|
+
else if (this.baseUrl) {
|
|
383
|
+
const base = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
|
|
384
|
+
const p = finalPath.startsWith('/') ? finalPath : '/' + finalPath;
|
|
385
|
+
finalUrl = base + p;
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
throw new ConfigurationError('Relative path provided without a baseUrl or explicit transport.', {
|
|
389
|
+
configKey: 'baseUrl',
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
const remainingKeys = Object.keys(mergedParams).filter((k) => !usedParams.has(k));
|
|
393
|
+
if (remainingKeys.length > 0) {
|
|
394
|
+
const queryParts = remainingKeys.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(String(mergedParams[key]))}`);
|
|
395
|
+
const separator = finalUrl.includes('?') ? '&' : '?';
|
|
396
|
+
return finalUrl + separator + queryParts.join('&');
|
|
397
|
+
}
|
|
398
|
+
return finalUrl;
|
|
399
|
+
}
|
|
400
|
+
request(path, options = {}) {
|
|
401
|
+
const url = this.buildUrl(path, options.params);
|
|
402
|
+
let mergedHeaders;
|
|
403
|
+
if (options.headers) {
|
|
404
|
+
mergedHeaders = this.defaultHeaders instanceof Headers
|
|
405
|
+
? new Headers(this.defaultHeaders)
|
|
406
|
+
: new Headers(this.defaultHeaders);
|
|
407
|
+
const optHeaders = options.headers instanceof Headers
|
|
408
|
+
? options.headers
|
|
409
|
+
: new Headers(options.headers);
|
|
410
|
+
optHeaders.forEach((value, key) => mergedHeaders.append(key, value));
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
mergedHeaders = this.defaultHeaders instanceof Headers
|
|
414
|
+
? this.defaultHeaders
|
|
415
|
+
: new Headers(this.defaultHeaders);
|
|
416
|
+
}
|
|
417
|
+
const needsController = options.timeout || options.signal || this.defaultTimeout;
|
|
418
|
+
let controller;
|
|
419
|
+
let signal = options.signal;
|
|
420
|
+
let timeoutId;
|
|
421
|
+
let externalAbortCleanup;
|
|
422
|
+
if (needsController) {
|
|
423
|
+
controller = new AbortController();
|
|
424
|
+
signal = controller.signal;
|
|
425
|
+
if (options.signal) {
|
|
426
|
+
const externalSignal = options.signal;
|
|
427
|
+
const abortHandler = () => controller.abort(externalSignal.reason);
|
|
428
|
+
if (externalSignal.aborted) {
|
|
429
|
+
abortHandler();
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
externalSignal.addEventListener('abort', abortHandler, { once: true });
|
|
433
|
+
externalAbortCleanup = () => externalSignal.removeEventListener('abort', abortHandler);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const timeout = options.timeout ?? this.defaultTimeout;
|
|
437
|
+
if (timeout) {
|
|
438
|
+
const totalTimeout = typeof timeout === 'number' ? timeout : timeout.request;
|
|
439
|
+
if (totalTimeout) {
|
|
440
|
+
timeoutId = setTimeout(() => controller.abort(new TimeoutError(req, {
|
|
441
|
+
phase: 'request',
|
|
442
|
+
timeout: totalTimeout,
|
|
443
|
+
})), totalTimeout);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const req = new HttpRequest(url, {
|
|
448
|
+
...options,
|
|
449
|
+
headers: mergedHeaders,
|
|
450
|
+
signal,
|
|
451
|
+
maxResponseSize: options.maxResponseSize ?? this.maxResponseSize
|
|
452
|
+
});
|
|
453
|
+
const responsePromise = this.handler(req);
|
|
454
|
+
if (timeoutId || externalAbortCleanup) {
|
|
455
|
+
responsePromise.finally(() => {
|
|
456
|
+
if (timeoutId)
|
|
457
|
+
clearTimeout(timeoutId);
|
|
458
|
+
externalAbortCleanup?.();
|
|
459
|
+
}).catch(() => {
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
return new RequestPromise(responsePromise, controller);
|
|
463
|
+
}
|
|
464
|
+
get(path, options = {}) {
|
|
465
|
+
return this.request(path, { ...options, method: 'GET' });
|
|
466
|
+
}
|
|
467
|
+
async batch(requests, options = {}) {
|
|
468
|
+
const mapResponse = options.mapResponse ?? ((res) => res);
|
|
469
|
+
const batchConcurrency = options.concurrency ?? this.concurrencyConfig.runner.concurrency;
|
|
470
|
+
const runner = new RequestRunner({
|
|
471
|
+
concurrency: batchConcurrency,
|
|
472
|
+
retries: this.concurrencyConfig.runner.retries,
|
|
473
|
+
retryDelay: this.concurrencyConfig.runner.retryDelay
|
|
474
|
+
});
|
|
475
|
+
const runnerResult = await runner.run(requests, async (item) => {
|
|
476
|
+
const res = await this.request(item.path, item.options);
|
|
477
|
+
return mapResponse(res);
|
|
478
|
+
});
|
|
479
|
+
return runnerResult;
|
|
480
|
+
}
|
|
481
|
+
multi(requests, options = {}) {
|
|
482
|
+
return this.batch(requests, options);
|
|
483
|
+
}
|
|
484
|
+
requestWithBody(method, path, bodyOrOptions, options) {
|
|
485
|
+
let actualBody = bodyOrOptions;
|
|
486
|
+
let actualOptions = options;
|
|
487
|
+
const isOptionsEmpty = actualOptions === undefined ||
|
|
488
|
+
(typeof actualOptions === 'object' && actualOptions !== null && Object.keys(actualOptions).length === 0);
|
|
489
|
+
if (isOptionsEmpty && isPlainObject(bodyOrOptions)) {
|
|
490
|
+
const potentialOptions = bodyOrOptions;
|
|
491
|
+
if (potentialOptions.json !== undefined ||
|
|
492
|
+
potentialOptions.form !== undefined ||
|
|
493
|
+
potentialOptions.xml !== undefined ||
|
|
494
|
+
potentialOptions.body !== undefined ||
|
|
495
|
+
potentialOptions.headers !== undefined ||
|
|
496
|
+
potentialOptions.timeout !== undefined ||
|
|
497
|
+
potentialOptions.retry !== undefined ||
|
|
498
|
+
potentialOptions.hooks !== undefined ||
|
|
499
|
+
potentialOptions.searchParams !== undefined ||
|
|
500
|
+
potentialOptions.params !== undefined) {
|
|
501
|
+
actualOptions = bodyOrOptions;
|
|
502
|
+
actualBody = undefined;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
actualOptions = actualOptions || {};
|
|
506
|
+
const { json, form, xml, ...restOptions } = actualOptions;
|
|
507
|
+
let finalBody = actualBody;
|
|
508
|
+
let explicitContentType;
|
|
509
|
+
if (form !== undefined) {
|
|
510
|
+
finalBody = createFormData(form);
|
|
511
|
+
explicitContentType = undefined;
|
|
512
|
+
}
|
|
513
|
+
else if (json !== undefined) {
|
|
514
|
+
finalBody = JSON.stringify(json);
|
|
515
|
+
explicitContentType = 'application/json';
|
|
516
|
+
}
|
|
517
|
+
else if (xml !== undefined) {
|
|
518
|
+
finalBody = '<?xml version="1.0" encoding="UTF-8"?>\n' + serializeXML(xml);
|
|
519
|
+
explicitContentType = 'application/xml';
|
|
520
|
+
}
|
|
521
|
+
else if (restOptions.body !== undefined) {
|
|
522
|
+
finalBody = restOptions.body;
|
|
523
|
+
}
|
|
524
|
+
const { body: processedBody, contentType } = processBody(finalBody);
|
|
525
|
+
const headers = new Headers(restOptions.headers);
|
|
526
|
+
const finalContentType = explicitContentType ?? contentType;
|
|
527
|
+
if (finalContentType && !headers.has('Content-Type')) {
|
|
528
|
+
headers.set('Content-Type', finalContentType);
|
|
529
|
+
}
|
|
530
|
+
return this.request(path, { ...restOptions, method, body: processedBody, headers });
|
|
531
|
+
}
|
|
532
|
+
post(path, body, options = {}) {
|
|
533
|
+
return this.requestWithBody('POST', path, body, options);
|
|
534
|
+
}
|
|
535
|
+
put(path, body, options = {}) {
|
|
536
|
+
return this.requestWithBody('PUT', path, body, options);
|
|
537
|
+
}
|
|
538
|
+
patch(path, body, options = {}) {
|
|
539
|
+
return this.requestWithBody('PATCH', path, body, options);
|
|
540
|
+
}
|
|
541
|
+
delete(path, options = {}) {
|
|
542
|
+
return this.request(path, { ...options, method: 'DELETE' });
|
|
543
|
+
}
|
|
544
|
+
head(path, options = {}) {
|
|
545
|
+
return this.request(path, { ...options, method: 'HEAD' });
|
|
546
|
+
}
|
|
547
|
+
options(path, options = {}) {
|
|
548
|
+
return this.request(path, { ...options, method: 'OPTIONS' });
|
|
549
|
+
}
|
|
550
|
+
trace(path, options = {}) {
|
|
551
|
+
return this.request(path, { ...options, method: 'TRACE' });
|
|
552
|
+
}
|
|
553
|
+
connect(path, options = {}) {
|
|
554
|
+
return this.request(path, { ...options, method: 'CONNECT' });
|
|
555
|
+
}
|
|
556
|
+
purge(path, options = {}) {
|
|
557
|
+
return this.request(path, { ...options, method: 'PURGE' });
|
|
558
|
+
}
|
|
559
|
+
propfind(path, body, options = {}) {
|
|
560
|
+
return this.requestWithBody('PROPFIND', path, body, options);
|
|
561
|
+
}
|
|
562
|
+
proppatch(path, body, options = {}) {
|
|
563
|
+
return this.requestWithBody('PROPPATCH', path, body, options);
|
|
564
|
+
}
|
|
565
|
+
mkcol(path, options = {}) {
|
|
566
|
+
return this.request(path, { ...options, method: 'MKCOL' });
|
|
567
|
+
}
|
|
568
|
+
copy(path, options = {}) {
|
|
569
|
+
return this.request(path, { ...options, method: 'COPY' });
|
|
570
|
+
}
|
|
571
|
+
move(path, options = {}) {
|
|
572
|
+
return this.request(path, { ...options, method: 'MOVE' });
|
|
573
|
+
}
|
|
574
|
+
lock(path, body, options = {}) {
|
|
575
|
+
return this.requestWithBody('LOCK', path, body, options);
|
|
576
|
+
}
|
|
577
|
+
unlock(path, options = {}) {
|
|
578
|
+
return this.request(path, { ...options, method: 'UNLOCK' });
|
|
579
|
+
}
|
|
580
|
+
link(path, body, options = {}) {
|
|
581
|
+
return this.requestWithBody('LINK', path, body, options);
|
|
582
|
+
}
|
|
583
|
+
unlink(path, body, options = {}) {
|
|
584
|
+
return this.requestWithBody('UNLINK', path, body, options);
|
|
585
|
+
}
|
|
586
|
+
scrape(path, options = {}) {
|
|
587
|
+
const method = options.method || 'GET';
|
|
588
|
+
const requestPromise = this.request(path, { ...options, method });
|
|
589
|
+
return scrapeHelper(requestPromise);
|
|
590
|
+
}
|
|
591
|
+
paginate(path, options = {}) {
|
|
592
|
+
const { getItems, getNextUrl, maxPages, pageParam, limitParam, resultsPath, nextCursorPath, ...reqOptions } = options;
|
|
593
|
+
const paginationOpts = {
|
|
594
|
+
getItems,
|
|
595
|
+
getNextUrl,
|
|
596
|
+
maxPages,
|
|
597
|
+
pageParam: pageParam || this.paginationConfig?.pageParam,
|
|
598
|
+
limitParam: limitParam || this.paginationConfig?.limitParam,
|
|
599
|
+
resultsPath: resultsPath || this.paginationConfig?.resultsPath,
|
|
600
|
+
nextCursorPath: nextCursorPath || this.paginationConfig?.nextCursorPath,
|
|
601
|
+
};
|
|
602
|
+
return paginate(this, path, reqOptions, paginationOpts);
|
|
603
|
+
}
|
|
604
|
+
pages(path, options = {}) {
|
|
605
|
+
const { getNextUrl, maxPages, pageParam, limitParam, resultsPath, nextCursorPath, ...reqOptions } = options;
|
|
606
|
+
const paginationOpts = {
|
|
607
|
+
getNextUrl,
|
|
608
|
+
maxPages,
|
|
609
|
+
pageParam: pageParam || this.paginationConfig?.pageParam,
|
|
610
|
+
limitParam: limitParam || this.paginationConfig?.limitParam,
|
|
611
|
+
nextCursorPath: nextCursorPath || this.paginationConfig?.nextCursorPath,
|
|
612
|
+
};
|
|
613
|
+
return streamPages(this, path, reqOptions, paginationOpts);
|
|
614
|
+
}
|
|
615
|
+
page(path, pageNumber, options = {}) {
|
|
616
|
+
const pageParam = options.pageParam || this.paginationConfig?.pageParam || 'page';
|
|
617
|
+
const url = new URL(path.startsWith('http') ? path : `http://base${path}`);
|
|
618
|
+
const params = { ...options.params, [pageParam]: pageNumber };
|
|
619
|
+
return this.request(path, { ...options, params });
|
|
620
|
+
}
|
|
621
|
+
async getAll(path, options = {}) {
|
|
622
|
+
const items = [];
|
|
623
|
+
for await (const item of this.paginate(path, options)) {
|
|
624
|
+
items.push(item);
|
|
625
|
+
}
|
|
626
|
+
return items;
|
|
627
|
+
}
|
|
628
|
+
websocket(path, options = {}) {
|
|
629
|
+
let wsUrl;
|
|
630
|
+
if (path.startsWith('ws://') || path.startsWith('wss://')) {
|
|
631
|
+
wsUrl = path;
|
|
632
|
+
}
|
|
633
|
+
else if (this.baseUrl) {
|
|
634
|
+
const base = this.baseUrl.replace(/^http/, 'ws');
|
|
635
|
+
wsUrl = new URL(path, base).toString();
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
throw new ConfigurationError('WebSocket requires either a full ws:// URL or a baseUrl', {
|
|
639
|
+
configKey: 'baseUrl',
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
const headersObj = {};
|
|
643
|
+
if (this.defaultHeaders) {
|
|
644
|
+
const headers = new Headers(this.defaultHeaders);
|
|
645
|
+
headers.forEach((value, key) => {
|
|
646
|
+
headersObj[key] = value;
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
const finalHeaders = { ...headersObj, ...options.headers };
|
|
650
|
+
return new ReckerWebSocket(wsUrl, { ...options, headers: finalHeaders });
|
|
651
|
+
}
|
|
652
|
+
ws(path, options = {}) {
|
|
653
|
+
return this.websocket(path, options);
|
|
654
|
+
}
|
|
655
|
+
async whois(query, options) {
|
|
656
|
+
return performWhois(query, options);
|
|
657
|
+
}
|
|
658
|
+
async isDomainAvailable(domain, options) {
|
|
659
|
+
return isDomainAvailable(domain, options);
|
|
660
|
+
}
|
|
661
|
+
hls(manifestUrl, options = {}) {
|
|
662
|
+
return new HlsPromise(this, manifestUrl, options);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
export function createClient(options = {}) {
|
|
666
|
+
return new Client(options);
|
|
667
|
+
}
|