wreq-js 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -182
- package/dist/test/helpers/local-test-server.d.ts +7 -0
- package/dist/test/helpers/local-test-server.d.ts.map +1 -0
- package/dist/test/helpers/local-test-server.js +302 -0
- package/dist/test/helpers/local-test-server.js.map +1 -0
- package/dist/test/http.spec.js +110 -12
- package/dist/test/http.spec.js.map +1 -1
- package/dist/test/run-with-local-server.d.ts +2 -0
- package/dist/test/run-with-local-server.d.ts.map +1 -0
- package/dist/test/run-with-local-server.js +61 -0
- package/dist/test/run-with-local-server.js.map +1 -0
- package/dist/test/websocket.spec.js +13 -16
- package/dist/test/websocket.spec.js.map +1 -1
- package/dist/types.d.ts +271 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -1
- package/dist/wreq-js.d.ts +79 -54
- package/dist/wreq-js.d.ts.map +1 -1
- package/dist/wreq-js.js +559 -63
- package/dist/wreq-js.js.map +1 -1
- package/package.json +2 -2
- package/rust/wreq-js.darwin-arm64.node +0 -0
- package/rust/wreq-js.darwin-x64.node +0 -0
- package/rust/wreq-js.linux-arm64-gnu.node +0 -0
- package/rust/wreq-js.linux-x64-gnu.node +0 -0
- package/rust/wreq-js.win32-x64-msvc.node +0 -0
package/dist/wreq-js.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WebSocket = void 0;
|
|
3
|
+
exports.RequestError = exports.WebSocket = exports.Session = exports.Response = exports.Headers = void 0;
|
|
4
|
+
exports.fetch = fetch;
|
|
5
|
+
exports.createSession = createSession;
|
|
6
|
+
exports.withSession = withSession;
|
|
4
7
|
exports.request = request;
|
|
5
8
|
exports.getProfiles = getProfiles;
|
|
6
9
|
exports.get = get;
|
|
7
10
|
exports.post = post;
|
|
8
11
|
exports.websocket = websocket;
|
|
12
|
+
const node_crypto_1 = require("node:crypto");
|
|
13
|
+
const node_http_1 = require("node:http");
|
|
9
14
|
const types_1 = require("./types");
|
|
15
|
+
Object.defineProperty(exports, "RequestError", { enumerable: true, get: function () { return types_1.RequestError; } });
|
|
10
16
|
let nativeBinding;
|
|
11
17
|
let cachedProfiles;
|
|
12
18
|
function loadNativeBinding() {
|
|
@@ -55,44 +61,550 @@ const websocketFinalizer = typeof FinalizationRegistry === "function"
|
|
|
55
61
|
void nativeBinding.websocketClose(connection).catch(() => undefined);
|
|
56
62
|
})
|
|
57
63
|
: undefined;
|
|
64
|
+
const DEFAULT_BROWSER = "chrome_142";
|
|
65
|
+
function generateSessionId() {
|
|
66
|
+
const cryptoGlobal = globalThis.crypto;
|
|
67
|
+
if (cryptoGlobal?.randomUUID) {
|
|
68
|
+
return cryptoGlobal.randomUUID();
|
|
69
|
+
}
|
|
70
|
+
return (0, node_crypto_1.randomBytes)(16).toString("hex");
|
|
71
|
+
}
|
|
72
|
+
function normalizeSessionOptions(options) {
|
|
73
|
+
const sessionId = options?.sessionId ?? generateSessionId();
|
|
74
|
+
const defaults = {
|
|
75
|
+
browser: options?.browser ?? DEFAULT_BROWSER,
|
|
76
|
+
};
|
|
77
|
+
if (options?.proxy !== undefined) {
|
|
78
|
+
defaults.proxy = options.proxy;
|
|
79
|
+
}
|
|
80
|
+
if (options?.timeout !== undefined) {
|
|
81
|
+
defaults.timeout = options.timeout;
|
|
82
|
+
}
|
|
83
|
+
return { sessionId, defaults };
|
|
84
|
+
}
|
|
85
|
+
function isIterable(value) {
|
|
86
|
+
return Boolean(value) && typeof value[Symbol.iterator] === "function";
|
|
87
|
+
}
|
|
88
|
+
function isPlainObject(value) {
|
|
89
|
+
if (typeof value !== "object" || value === null) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const proto = Object.getPrototypeOf(value);
|
|
93
|
+
return proto === Object.prototype || proto === null;
|
|
94
|
+
}
|
|
95
|
+
function coerceHeaderValue(value) {
|
|
96
|
+
return String(value);
|
|
97
|
+
}
|
|
98
|
+
class Headers {
|
|
99
|
+
constructor(init) {
|
|
100
|
+
this.store = new Map();
|
|
101
|
+
if (init) {
|
|
102
|
+
this.applyInit(init);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
applyInit(init) {
|
|
106
|
+
if (init instanceof Headers) {
|
|
107
|
+
for (const [name, value] of init) {
|
|
108
|
+
this.append(name, value);
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(init) || isIterable(init)) {
|
|
113
|
+
for (const tuple of init) {
|
|
114
|
+
if (!tuple) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const [name, value] = tuple;
|
|
118
|
+
this.append(name, value);
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (isPlainObject(init)) {
|
|
123
|
+
for (const [name, value] of Object.entries(init)) {
|
|
124
|
+
if (value === undefined || value === null) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
this.set(name, coerceHeaderValue(value));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
normalizeName(name) {
|
|
132
|
+
if (typeof name !== "string") {
|
|
133
|
+
throw new TypeError("Header name must be a string");
|
|
134
|
+
}
|
|
135
|
+
const trimmed = name.trim();
|
|
136
|
+
if (!trimmed) {
|
|
137
|
+
throw new TypeError("Header name must not be empty");
|
|
138
|
+
}
|
|
139
|
+
return { key: trimmed.toLowerCase(), display: trimmed };
|
|
140
|
+
}
|
|
141
|
+
assertValue(value) {
|
|
142
|
+
if (value === undefined || value === null) {
|
|
143
|
+
throw new TypeError("Header value must not be null or undefined");
|
|
144
|
+
}
|
|
145
|
+
return coerceHeaderValue(value);
|
|
146
|
+
}
|
|
147
|
+
append(name, value) {
|
|
148
|
+
const normalized = this.normalizeName(name);
|
|
149
|
+
const existing = this.store.get(normalized.key);
|
|
150
|
+
const coercedValue = this.assertValue(value);
|
|
151
|
+
if (existing) {
|
|
152
|
+
existing.values.push(coercedValue);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.store.set(normalized.key, {
|
|
156
|
+
name: normalized.display,
|
|
157
|
+
values: [coercedValue],
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
set(name, value) {
|
|
161
|
+
const normalized = this.normalizeName(name);
|
|
162
|
+
const coercedValue = this.assertValue(value);
|
|
163
|
+
this.store.set(normalized.key, {
|
|
164
|
+
name: normalized.display,
|
|
165
|
+
values: [coercedValue],
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
get(name) {
|
|
169
|
+
const normalized = this.normalizeName(name);
|
|
170
|
+
const entry = this.store.get(normalized.key);
|
|
171
|
+
return entry ? entry.values.join(", ") : null;
|
|
172
|
+
}
|
|
173
|
+
has(name) {
|
|
174
|
+
const normalized = this.normalizeName(name);
|
|
175
|
+
return this.store.has(normalized.key);
|
|
176
|
+
}
|
|
177
|
+
delete(name) {
|
|
178
|
+
const normalized = this.normalizeName(name);
|
|
179
|
+
this.store.delete(normalized.key);
|
|
180
|
+
}
|
|
181
|
+
entries() {
|
|
182
|
+
return this[Symbol.iterator]();
|
|
183
|
+
}
|
|
184
|
+
*keys() {
|
|
185
|
+
for (const [name] of this) {
|
|
186
|
+
yield name;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
*values() {
|
|
190
|
+
for (const [, value] of this) {
|
|
191
|
+
yield value;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
forEach(callback, thisArg) {
|
|
195
|
+
for (const [name, value] of this) {
|
|
196
|
+
callback.call(thisArg, value, name, this);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
[Symbol.iterator]() {
|
|
200
|
+
const generator = function* (store) {
|
|
201
|
+
for (const entry of store.values()) {
|
|
202
|
+
yield [entry.name, entry.values.join(", ")];
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
return generator(this.store);
|
|
206
|
+
}
|
|
207
|
+
toObject() {
|
|
208
|
+
const result = {};
|
|
209
|
+
for (const [name, value] of this) {
|
|
210
|
+
result[name] = value;
|
|
211
|
+
}
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
exports.Headers = Headers;
|
|
216
|
+
function cloneNativeResponse(payload) {
|
|
217
|
+
return {
|
|
218
|
+
status: payload.status,
|
|
219
|
+
headers: { ...payload.headers },
|
|
220
|
+
body: payload.body,
|
|
221
|
+
cookies: { ...payload.cookies },
|
|
222
|
+
url: payload.url,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
class Response {
|
|
226
|
+
constructor(payload, requestUrl) {
|
|
227
|
+
this.type = "basic";
|
|
228
|
+
this.bodyUsed = false;
|
|
229
|
+
this.payload = cloneNativeResponse(payload);
|
|
230
|
+
this.requestUrl = requestUrl;
|
|
231
|
+
this.status = payload.status;
|
|
232
|
+
this.statusText = node_http_1.STATUS_CODES[payload.status] ?? "";
|
|
233
|
+
this.ok = this.status >= 200 && this.status < 300;
|
|
234
|
+
this.headers = new Headers(payload.headers);
|
|
235
|
+
this.url = payload.url;
|
|
236
|
+
this.redirected = this.url !== requestUrl;
|
|
237
|
+
this.cookies = { ...payload.cookies };
|
|
238
|
+
this.body = payload.body;
|
|
239
|
+
}
|
|
240
|
+
async json() {
|
|
241
|
+
const text = await this.text();
|
|
242
|
+
return JSON.parse(text);
|
|
243
|
+
}
|
|
244
|
+
async text() {
|
|
245
|
+
this.assertBodyAvailable();
|
|
246
|
+
this.bodyUsed = true;
|
|
247
|
+
return this.body;
|
|
248
|
+
}
|
|
249
|
+
clone() {
|
|
250
|
+
if (this.bodyUsed) {
|
|
251
|
+
throw new TypeError("Cannot clone a Response whose body is already used");
|
|
252
|
+
}
|
|
253
|
+
return new Response(cloneNativeResponse(this.payload), this.requestUrl);
|
|
254
|
+
}
|
|
255
|
+
assertBodyAvailable() {
|
|
256
|
+
if (this.bodyUsed) {
|
|
257
|
+
throw new TypeError("Response body is already used");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
exports.Response = Response;
|
|
262
|
+
class Session {
|
|
263
|
+
constructor(id, defaults) {
|
|
264
|
+
this.disposed = false;
|
|
265
|
+
this.id = id;
|
|
266
|
+
this.defaults = defaults;
|
|
267
|
+
}
|
|
268
|
+
get closed() {
|
|
269
|
+
return this.disposed;
|
|
270
|
+
}
|
|
271
|
+
ensureActive() {
|
|
272
|
+
if (this.disposed) {
|
|
273
|
+
throw new types_1.RequestError("Session has been closed");
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
enforceBrowser(browser) {
|
|
277
|
+
const resolved = browser ?? this.defaults.browser;
|
|
278
|
+
if (resolved !== this.defaults.browser) {
|
|
279
|
+
throw new types_1.RequestError("Session browser cannot be changed after creation");
|
|
280
|
+
}
|
|
281
|
+
return resolved;
|
|
282
|
+
}
|
|
283
|
+
enforceProxy(proxy) {
|
|
284
|
+
if (proxy === undefined) {
|
|
285
|
+
return this.defaults.proxy;
|
|
286
|
+
}
|
|
287
|
+
if ((this.defaults.proxy ?? null) !== (proxy ?? null)) {
|
|
288
|
+
throw new types_1.RequestError("Session proxy cannot be changed after creation");
|
|
289
|
+
}
|
|
290
|
+
return proxy;
|
|
291
|
+
}
|
|
292
|
+
async fetch(input, init) {
|
|
293
|
+
this.ensureActive();
|
|
294
|
+
const config = {
|
|
295
|
+
...(init ?? {}),
|
|
296
|
+
session: this,
|
|
297
|
+
cookieMode: "session",
|
|
298
|
+
};
|
|
299
|
+
config.browser = this.enforceBrowser(config.browser);
|
|
300
|
+
const proxy = this.enforceProxy(config.proxy);
|
|
301
|
+
if (proxy !== undefined || config.proxy !== undefined) {
|
|
302
|
+
if (proxy === undefined) {
|
|
303
|
+
delete config.proxy;
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
config.proxy = proxy;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (config.timeout === undefined && this.defaults.timeout !== undefined) {
|
|
310
|
+
config.timeout = this.defaults.timeout;
|
|
311
|
+
}
|
|
312
|
+
return fetch(input, config);
|
|
313
|
+
}
|
|
314
|
+
async clearCookies() {
|
|
315
|
+
this.ensureActive();
|
|
316
|
+
try {
|
|
317
|
+
nativeBinding.clearSession(this.id);
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
throw new types_1.RequestError(String(error));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async close() {
|
|
324
|
+
if (this.disposed) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
this.disposed = true;
|
|
328
|
+
try {
|
|
329
|
+
nativeBinding.dropSession(this.id);
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
throw new types_1.RequestError(String(error));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
exports.Session = Session;
|
|
337
|
+
function resolveSessionContext(config) {
|
|
338
|
+
const requestedMode = config.cookieMode ?? "ephemeral";
|
|
339
|
+
const sessionCandidate = config.session;
|
|
340
|
+
const providedSessionId = typeof config.sessionId === "string" ? config.sessionId.trim() : undefined;
|
|
341
|
+
if (sessionCandidate && providedSessionId) {
|
|
342
|
+
throw new types_1.RequestError("Provide either `session` or `sessionId`, not both.");
|
|
343
|
+
}
|
|
344
|
+
if (sessionCandidate) {
|
|
345
|
+
if (!(sessionCandidate instanceof Session)) {
|
|
346
|
+
throw new types_1.RequestError("`session` must be created via createSession()");
|
|
347
|
+
}
|
|
348
|
+
if (sessionCandidate.closed) {
|
|
349
|
+
throw new types_1.RequestError("Session has been closed");
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
sessionId: sessionCandidate.id,
|
|
353
|
+
cookieMode: "session",
|
|
354
|
+
dropAfterRequest: false,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
if (providedSessionId) {
|
|
358
|
+
if (!providedSessionId) {
|
|
359
|
+
throw new types_1.RequestError("sessionId must not be empty");
|
|
360
|
+
}
|
|
361
|
+
if (requestedMode === "ephemeral") {
|
|
362
|
+
throw new types_1.RequestError("cookieMode 'ephemeral' cannot be combined with sessionId");
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
sessionId: providedSessionId,
|
|
366
|
+
cookieMode: "session",
|
|
367
|
+
dropAfterRequest: false,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
if (requestedMode === "session") {
|
|
371
|
+
throw new types_1.RequestError("cookieMode 'session' requires a session or sessionId");
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
sessionId: generateSessionId(),
|
|
375
|
+
cookieMode: "ephemeral",
|
|
376
|
+
dropAfterRequest: true,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
function createAbortError(reason) {
|
|
380
|
+
const fallbackMessage = typeof reason === "string" ? reason : "The operation was aborted";
|
|
381
|
+
if (typeof DOMException !== "undefined" && reason instanceof DOMException) {
|
|
382
|
+
return reason.name === "AbortError" ? reason : new DOMException(reason.message || fallbackMessage, "AbortError");
|
|
383
|
+
}
|
|
384
|
+
if (reason instanceof Error) {
|
|
385
|
+
reason.name = "AbortError";
|
|
386
|
+
return reason;
|
|
387
|
+
}
|
|
388
|
+
if (typeof DOMException !== "undefined") {
|
|
389
|
+
return new DOMException(fallbackMessage, "AbortError");
|
|
390
|
+
}
|
|
391
|
+
const error = new Error(fallbackMessage);
|
|
392
|
+
error.name = "AbortError";
|
|
393
|
+
return error;
|
|
394
|
+
}
|
|
395
|
+
function isAbortError(error) {
|
|
396
|
+
return Boolean(error) && typeof error.name === "string" && error.name === "AbortError";
|
|
397
|
+
}
|
|
398
|
+
function setupAbort(signal) {
|
|
399
|
+
if (!signal) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
if (signal.aborted) {
|
|
403
|
+
throw createAbortError(signal.reason);
|
|
404
|
+
}
|
|
405
|
+
let onAbort;
|
|
406
|
+
const promise = new Promise((_, reject) => {
|
|
407
|
+
onAbort = () => {
|
|
408
|
+
reject(createAbortError(signal.reason));
|
|
409
|
+
};
|
|
410
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
411
|
+
});
|
|
412
|
+
const cleanup = () => {
|
|
413
|
+
if (onAbort) {
|
|
414
|
+
signal.removeEventListener("abort", onAbort);
|
|
415
|
+
onAbort = undefined;
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
return { promise, cleanup };
|
|
419
|
+
}
|
|
420
|
+
function normalizeUrlInput(input) {
|
|
421
|
+
const value = typeof input === "string" ? input : input.toString();
|
|
422
|
+
if (!value) {
|
|
423
|
+
throw new types_1.RequestError("URL is required");
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
return new URL(value).toString();
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
throw new types_1.RequestError(`Invalid URL: ${value}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function validateRedirectMode(mode) {
|
|
433
|
+
if (!mode || mode === "follow") {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
throw new types_1.RequestError(`Redirect mode '${mode}' is not supported`);
|
|
437
|
+
}
|
|
438
|
+
function serializeBody(body) {
|
|
439
|
+
if (body === null || body === undefined) {
|
|
440
|
+
return undefined;
|
|
441
|
+
}
|
|
442
|
+
if (typeof body === "string") {
|
|
443
|
+
return body;
|
|
444
|
+
}
|
|
445
|
+
if (Buffer.isBuffer(body)) {
|
|
446
|
+
return body.toString();
|
|
447
|
+
}
|
|
448
|
+
if (body instanceof URLSearchParams) {
|
|
449
|
+
return body.toString();
|
|
450
|
+
}
|
|
451
|
+
if (body instanceof ArrayBuffer) {
|
|
452
|
+
return Buffer.from(body).toString();
|
|
453
|
+
}
|
|
454
|
+
if (ArrayBuffer.isView(body)) {
|
|
455
|
+
return Buffer.from(body.buffer, body.byteOffset, body.byteLength).toString();
|
|
456
|
+
}
|
|
457
|
+
throw new TypeError("Unsupported body type; expected string, Buffer, ArrayBuffer, or URLSearchParams");
|
|
458
|
+
}
|
|
459
|
+
const SUPPORTED_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD"];
|
|
460
|
+
function ensureMethod(method) {
|
|
461
|
+
const normalized = method?.trim().toUpperCase();
|
|
462
|
+
return normalized && normalized.length > 0 ? normalized : "GET";
|
|
463
|
+
}
|
|
464
|
+
function assertSupportedMethod(method) {
|
|
465
|
+
if (!SUPPORTED_METHODS.includes(method)) {
|
|
466
|
+
throw new types_1.RequestError(`Unsupported HTTP method: ${method}`);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function ensureBodyAllowed(method, body) {
|
|
470
|
+
if (!body) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (method === "GET" || method === "HEAD") {
|
|
474
|
+
throw new types_1.RequestError(`Request with ${method} method cannot have a body`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
function validateBrowserProfile(browser) {
|
|
478
|
+
if (!browser) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const profiles = getProfiles();
|
|
482
|
+
if (!profiles.includes(browser)) {
|
|
483
|
+
throw new types_1.RequestError(`Invalid browser profile: ${browser}. Available profiles: ${profiles.join(", ")}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
async function dispatchRequest(options, requestUrl, signal) {
|
|
487
|
+
const abortHandler = setupAbort(signal);
|
|
488
|
+
const nativePromise = nativeBinding.request(options);
|
|
489
|
+
const pending = abortHandler ? Promise.race([nativePromise, abortHandler.promise]) : nativePromise;
|
|
490
|
+
let payload;
|
|
491
|
+
try {
|
|
492
|
+
payload = (await pending);
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
if (isAbortError(error)) {
|
|
496
|
+
throw error;
|
|
497
|
+
}
|
|
498
|
+
if (error instanceof types_1.RequestError) {
|
|
499
|
+
throw error;
|
|
500
|
+
}
|
|
501
|
+
throw new types_1.RequestError(String(error));
|
|
502
|
+
}
|
|
503
|
+
finally {
|
|
504
|
+
abortHandler?.cleanup();
|
|
505
|
+
}
|
|
506
|
+
return new Response(payload, requestUrl);
|
|
507
|
+
}
|
|
58
508
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* @param options - Request options
|
|
62
|
-
* @returns Promise that resolves to the response
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```typescript
|
|
66
|
-
* import { request } from 'wreq-js';
|
|
509
|
+
* Fetch-compatible entry point that adds browser impersonation controls.
|
|
67
510
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* browser: 'chrome_137',
|
|
71
|
-
* headers: {
|
|
72
|
-
* 'Custom-Header': 'value'
|
|
73
|
-
* }
|
|
74
|
-
* });
|
|
75
|
-
*
|
|
76
|
-
* console.log(response.status); // 200
|
|
77
|
-
* console.log(response.body); // Response body
|
|
78
|
-
* ```
|
|
511
|
+
* @param input - Request URL (string or URL instance)
|
|
512
|
+
* @param init - Fetch-compatible init options
|
|
79
513
|
*/
|
|
80
|
-
async function
|
|
81
|
-
|
|
82
|
-
|
|
514
|
+
async function fetch(input, init) {
|
|
515
|
+
const url = normalizeUrlInput(input);
|
|
516
|
+
const config = init ?? {};
|
|
517
|
+
const sessionContext = resolveSessionContext(config);
|
|
518
|
+
validateRedirectMode(config.redirect);
|
|
519
|
+
validateBrowserProfile(config.browser);
|
|
520
|
+
const headers = new Headers(config.headers);
|
|
521
|
+
const method = ensureMethod(config.method);
|
|
522
|
+
assertSupportedMethod(method);
|
|
523
|
+
const body = serializeBody(config.body ?? null);
|
|
524
|
+
ensureBodyAllowed(method, body);
|
|
525
|
+
const headerRecord = headers.toObject();
|
|
526
|
+
const hasHeaders = Object.keys(headerRecord).length > 0;
|
|
527
|
+
const requestOptions = {
|
|
528
|
+
url,
|
|
529
|
+
method,
|
|
530
|
+
...(config.browser && { browser: config.browser }),
|
|
531
|
+
...(hasHeaders && { headers: headerRecord }),
|
|
532
|
+
...(body !== undefined && { body }),
|
|
533
|
+
...(config.proxy !== undefined && { proxy: config.proxy }),
|
|
534
|
+
...(config.timeout !== undefined && { timeout: config.timeout }),
|
|
535
|
+
sessionId: sessionContext.sessionId,
|
|
536
|
+
ephemeral: sessionContext.dropAfterRequest,
|
|
537
|
+
};
|
|
538
|
+
try {
|
|
539
|
+
return await dispatchRequest(requestOptions, url, config.signal ?? null);
|
|
83
540
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
541
|
+
finally {
|
|
542
|
+
if (sessionContext.dropAfterRequest) {
|
|
543
|
+
try {
|
|
544
|
+
nativeBinding.dropSession(sessionContext.sessionId);
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
// ignore cleanup errors for ephemeral sessions
|
|
548
|
+
}
|
|
88
549
|
}
|
|
89
550
|
}
|
|
551
|
+
}
|
|
552
|
+
async function createSession(options) {
|
|
553
|
+
const { sessionId, defaults } = normalizeSessionOptions(options);
|
|
554
|
+
validateBrowserProfile(defaults.browser);
|
|
555
|
+
let createdId;
|
|
90
556
|
try {
|
|
91
|
-
|
|
557
|
+
createdId = nativeBinding.createSession({
|
|
558
|
+
sessionId,
|
|
559
|
+
browser: defaults.browser,
|
|
560
|
+
...(defaults.proxy !== undefined && { proxy: defaults.proxy }),
|
|
561
|
+
});
|
|
92
562
|
}
|
|
93
563
|
catch (error) {
|
|
94
564
|
throw new types_1.RequestError(String(error));
|
|
95
565
|
}
|
|
566
|
+
return new Session(createdId, defaults);
|
|
567
|
+
}
|
|
568
|
+
async function withSession(fn, options) {
|
|
569
|
+
const session = await createSession(options);
|
|
570
|
+
try {
|
|
571
|
+
return await fn(session);
|
|
572
|
+
}
|
|
573
|
+
finally {
|
|
574
|
+
await session.close();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* @deprecated Use {@link fetch} instead.
|
|
579
|
+
*/
|
|
580
|
+
async function request(options) {
|
|
581
|
+
if (!options.url) {
|
|
582
|
+
throw new types_1.RequestError("URL is required");
|
|
583
|
+
}
|
|
584
|
+
const { url, ...rest } = options;
|
|
585
|
+
const init = {};
|
|
586
|
+
if (rest.method !== undefined) {
|
|
587
|
+
init.method = rest.method;
|
|
588
|
+
}
|
|
589
|
+
if (rest.headers !== undefined) {
|
|
590
|
+
init.headers = rest.headers;
|
|
591
|
+
}
|
|
592
|
+
if (rest.body !== undefined) {
|
|
593
|
+
init.body = rest.body;
|
|
594
|
+
}
|
|
595
|
+
if (rest.browser !== undefined) {
|
|
596
|
+
init.browser = rest.browser;
|
|
597
|
+
}
|
|
598
|
+
if (rest.proxy !== undefined) {
|
|
599
|
+
init.proxy = rest.proxy;
|
|
600
|
+
}
|
|
601
|
+
if (rest.timeout !== undefined) {
|
|
602
|
+
init.timeout = rest.timeout;
|
|
603
|
+
}
|
|
604
|
+
if (rest.sessionId !== undefined) {
|
|
605
|
+
init.sessionId = rest.sessionId;
|
|
606
|
+
}
|
|
607
|
+
return fetch(url, init);
|
|
96
608
|
}
|
|
97
609
|
/**
|
|
98
610
|
* Get list of available browser profiles
|
|
@@ -114,43 +626,21 @@ function getProfiles() {
|
|
|
114
626
|
return cachedProfiles;
|
|
115
627
|
}
|
|
116
628
|
/**
|
|
117
|
-
* Convenience
|
|
118
|
-
*
|
|
119
|
-
* @param url - URL to request
|
|
120
|
-
* @param options - Additional request options
|
|
121
|
-
* @returns Promise that resolves to the response
|
|
122
|
-
*
|
|
123
|
-
* @example
|
|
124
|
-
* ```typescript
|
|
125
|
-
* import { get } from 'wreq-js';
|
|
126
|
-
*
|
|
127
|
-
* const response = await get('https://example.com/api');
|
|
128
|
-
* ```
|
|
629
|
+
* Convenience helper for GET requests using {@link fetch}.
|
|
129
630
|
*/
|
|
130
|
-
async function get(url,
|
|
131
|
-
return
|
|
631
|
+
async function get(url, init) {
|
|
632
|
+
return fetch(url, { ...(init ?? {}), method: "GET" });
|
|
132
633
|
}
|
|
133
634
|
/**
|
|
134
|
-
* Convenience
|
|
135
|
-
*
|
|
136
|
-
* @param url - URL to request
|
|
137
|
-
* @param body - Request body
|
|
138
|
-
* @param options - Additional request options
|
|
139
|
-
* @returns Promise that resolves to the response
|
|
140
|
-
*
|
|
141
|
-
* @example
|
|
142
|
-
* ```typescript
|
|
143
|
-
* import { post } from 'wreq-js';
|
|
144
|
-
*
|
|
145
|
-
* const response = await post(
|
|
146
|
-
* 'https://example.com/api',
|
|
147
|
-
* JSON.stringify({ foo: 'bar' }),
|
|
148
|
-
* { headers: { 'Content-Type': 'application/json' } }
|
|
149
|
-
* );
|
|
150
|
-
* ```
|
|
635
|
+
* Convenience helper for POST requests using {@link fetch}.
|
|
151
636
|
*/
|
|
152
|
-
async function post(url, body,
|
|
153
|
-
|
|
637
|
+
async function post(url, body, init) {
|
|
638
|
+
const config = {
|
|
639
|
+
...(init ?? {}),
|
|
640
|
+
method: "POST",
|
|
641
|
+
...(body !== undefined ? { body } : {}),
|
|
642
|
+
};
|
|
643
|
+
return fetch(url, config);
|
|
154
644
|
}
|
|
155
645
|
/**
|
|
156
646
|
* WebSocket connection class
|
|
@@ -161,7 +651,7 @@ async function post(url, body, options) {
|
|
|
161
651
|
*
|
|
162
652
|
* const ws = await websocket({
|
|
163
653
|
* url: 'wss://echo.websocket.org',
|
|
164
|
-
* browser: '
|
|
654
|
+
* browser: 'chrome_142',
|
|
165
655
|
* onMessage: (data) => {
|
|
166
656
|
* console.log('Received:', data);
|
|
167
657
|
* },
|
|
@@ -246,7 +736,7 @@ async function websocket(options) {
|
|
|
246
736
|
try {
|
|
247
737
|
const connection = await nativeBinding.websocketConnect({
|
|
248
738
|
url: options.url,
|
|
249
|
-
browser: options.browser ||
|
|
739
|
+
browser: options.browser || DEFAULT_BROWSER,
|
|
250
740
|
headers: options.headers || {},
|
|
251
741
|
...(options.proxy !== undefined && { proxy: options.proxy }),
|
|
252
742
|
onMessage: options.onMessage,
|
|
@@ -260,11 +750,17 @@ async function websocket(options) {
|
|
|
260
750
|
}
|
|
261
751
|
}
|
|
262
752
|
exports.default = {
|
|
753
|
+
fetch,
|
|
263
754
|
request,
|
|
264
755
|
get,
|
|
265
756
|
post,
|
|
266
757
|
getProfiles,
|
|
758
|
+
createSession,
|
|
759
|
+
withSession,
|
|
267
760
|
websocket,
|
|
268
761
|
WebSocket,
|
|
762
|
+
Headers,
|
|
763
|
+
Response,
|
|
764
|
+
Session,
|
|
269
765
|
};
|
|
270
766
|
//# sourceMappingURL=wreq-js.js.map
|