curl-cffi-node 0.1.7

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 (46) hide show
  1. package/README.md +716 -0
  2. package/curl-cffi-node.darwin-arm64.node +0 -0
  3. package/curl-cffi-node.darwin-x64.node +0 -0
  4. package/curl-cffi-node.linux-arm64-gnu.node +0 -0
  5. package/curl-cffi-node.linux-x64-gnu.node +0 -0
  6. package/curl-cffi-node.win32-x64-msvc.node +0 -0
  7. package/dist/binding.d.ts +2 -0
  8. package/dist/binding.d.ts.map +1 -0
  9. package/dist/binding.js +90 -0
  10. package/dist/binding.js.map +1 -0
  11. package/dist/enums.d.ts +87 -0
  12. package/dist/enums.d.ts.map +1 -0
  13. package/dist/enums.js +101 -0
  14. package/dist/enums.js.map +1 -0
  15. package/dist/errors.d.ts +61 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +116 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/index.d.ts +77 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +88 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/response.d.ts +86 -0
  24. package/dist/response.d.ts.map +1 -0
  25. package/dist/response.js +137 -0
  26. package/dist/response.js.map +1 -0
  27. package/dist/session.d.ts +93 -0
  28. package/dist/session.d.ts.map +1 -0
  29. package/dist/session.js +228 -0
  30. package/dist/session.js.map +1 -0
  31. package/dist/websocket.d.ts +68 -0
  32. package/dist/websocket.d.ts.map +1 -0
  33. package/dist/websocket.js +176 -0
  34. package/dist/websocket.js.map +1 -0
  35. package/npm/darwin-arm64/curl-cffi-node.darwin-arm64.node +0 -0
  36. package/npm/darwin-arm64/package.json +19 -0
  37. package/npm/darwin-x64/curl-cffi-node.darwin-x64.node +0 -0
  38. package/npm/darwin-x64/package.json +19 -0
  39. package/npm/linux-arm64-gnu/curl-cffi-node.linux-arm64-gnu.node +0 -0
  40. package/npm/linux-arm64-gnu/package.json +22 -0
  41. package/npm/linux-x64-gnu/curl-cffi-node.linux-x64-gnu.node +0 -0
  42. package/npm/linux-x64-gnu/package.json +22 -0
  43. package/npm/linux-x64-musl/package.json +14 -0
  44. package/npm/win32-x64-msvc/curl-cffi-node.win32-x64-msvc.node +0 -0
  45. package/npm/win32-x64-msvc/package.json +19 -0
  46. package/package.json +83 -0
package/dist/index.js ADDED
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * curl-cffi-node — Node.js binding for curl-impersonate
4
+ *
5
+ * Provides browser-impersonating HTTP client with TLS/JA3/HTTP2 fingerprinting.
6
+ *
7
+ * @module curl-cffi-node
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.Curl = exports.BrowserType = exports.CurlInfo = exports.CurlOpt = exports.curlVersion = exports.nativeVersion = exports.hello = exports.WS_CLOSE = exports.WS_BINARY = exports.WS_TEXT = exports.CurlWebSocket = exports.Session = exports.parseCurlError = exports.CurlCode = exports.ProxyError = exports.TLSError = exports.ConnectionError = exports.TimeoutError = exports.CurlError = exports.Headers = exports.Response = void 0;
11
+ exports.get = get;
12
+ exports.post = post;
13
+ exports.put = put;
14
+ exports.del = del;
15
+ exports.head = head;
16
+ exports.patch = patch;
17
+ const binding_js_1 = require("./binding.js");
18
+ // ─── Re-exports ──────────────────────────────────────────────────────────────
19
+ var response_js_1 = require("./response.js");
20
+ Object.defineProperty(exports, "Response", { enumerable: true, get: function () { return response_js_1.Response; } });
21
+ Object.defineProperty(exports, "Headers", { enumerable: true, get: function () { return response_js_1.Headers; } });
22
+ var errors_js_1 = require("./errors.js");
23
+ Object.defineProperty(exports, "CurlError", { enumerable: true, get: function () { return errors_js_1.CurlError; } });
24
+ Object.defineProperty(exports, "TimeoutError", { enumerable: true, get: function () { return errors_js_1.TimeoutError; } });
25
+ Object.defineProperty(exports, "ConnectionError", { enumerable: true, get: function () { return errors_js_1.ConnectionError; } });
26
+ Object.defineProperty(exports, "TLSError", { enumerable: true, get: function () { return errors_js_1.TLSError; } });
27
+ Object.defineProperty(exports, "ProxyError", { enumerable: true, get: function () { return errors_js_1.ProxyError; } });
28
+ Object.defineProperty(exports, "CurlCode", { enumerable: true, get: function () { return errors_js_1.CurlCode; } });
29
+ Object.defineProperty(exports, "parseCurlError", { enumerable: true, get: function () { return errors_js_1.parseCurlError; } });
30
+ var session_js_1 = require("./session.js");
31
+ Object.defineProperty(exports, "Session", { enumerable: true, get: function () { return session_js_1.Session; } });
32
+ var websocket_js_1 = require("./websocket.js");
33
+ Object.defineProperty(exports, "CurlWebSocket", { enumerable: true, get: function () { return websocket_js_1.CurlWebSocket; } });
34
+ Object.defineProperty(exports, "WS_TEXT", { enumerable: true, get: function () { return websocket_js_1.WS_TEXT; } });
35
+ Object.defineProperty(exports, "WS_BINARY", { enumerable: true, get: function () { return websocket_js_1.WS_BINARY; } });
36
+ Object.defineProperty(exports, "WS_CLOSE", { enumerable: true, get: function () { return websocket_js_1.WS_CLOSE; } });
37
+ // ─── Native Bindings ─────────────────────────────────────────────────────────
38
+ /**
39
+ * Returns a greeting from the native module to verify binding works.
40
+ */
41
+ exports.hello = binding_js_1.nativeBinding.hello;
42
+ /**
43
+ * Returns the version of the native Rust module.
44
+ */
45
+ exports.nativeVersion = binding_js_1.nativeBinding.nativeVersion;
46
+ /**
47
+ * Returns the version string from the linked libcurl-impersonate library.
48
+ */
49
+ exports.curlVersion = binding_js_1.nativeBinding.curlVersion;
50
+ // ─── Curl Handle Class (Low-level) ──────────────────────────────────────────
51
+ var enums_js_1 = require("./enums.js");
52
+ Object.defineProperty(exports, "CurlOpt", { enumerable: true, get: function () { return enums_js_1.CurlOpt; } });
53
+ Object.defineProperty(exports, "CurlInfo", { enumerable: true, get: function () { return enums_js_1.CurlInfo; } });
54
+ Object.defineProperty(exports, "BrowserType", { enumerable: true, get: function () { return enums_js_1.BrowserType; } });
55
+ exports.Curl = binding_js_1.nativeBinding.Curl;
56
+ // ─── Shorthand Functions (Story 3.5) ────────────────────────────────────────
57
+ const session_js_2 = require("./session.js");
58
+ /** Shorthand for a GET request. Creates a temporary session. */
59
+ async function get(url, options) {
60
+ const session = new session_js_2.Session({ impersonate: options?.impersonate });
61
+ return session.get(url, options);
62
+ }
63
+ /** Shorthand for a POST request. */
64
+ async function post(url, options) {
65
+ const session = new session_js_2.Session({ impersonate: options?.impersonate });
66
+ return session.post(url, options);
67
+ }
68
+ /** Shorthand for a PUT request. */
69
+ async function put(url, options) {
70
+ const session = new session_js_2.Session({ impersonate: options?.impersonate });
71
+ return session.put(url, options);
72
+ }
73
+ /** Shorthand for a DELETE request. */
74
+ async function del(url, options) {
75
+ const session = new session_js_2.Session({ impersonate: options?.impersonate });
76
+ return session.delete(url, options);
77
+ }
78
+ /** Shorthand for a HEAD request. */
79
+ async function head(url, options) {
80
+ const session = new session_js_2.Session({ impersonate: options?.impersonate });
81
+ return session.head(url, options);
82
+ }
83
+ /** Shorthand for a PATCH request. */
84
+ async function patch(url, options) {
85
+ const session = new session_js_2.Session({ impersonate: options?.impersonate });
86
+ return session.patch(url, options);
87
+ }
88
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Headers wrapper with case-insensitive access.
3
+ *
4
+ * Parses raw header string into a Map for efficient lookup.
5
+ */
6
+ export declare class Headers {
7
+ private _map;
8
+ constructor(raw: string);
9
+ /** Get the first value for a header (case-insensitive). */
10
+ get(name: string): string | null;
11
+ /** Check if a header exists (case-insensitive). */
12
+ has(name: string): boolean;
13
+ /** Get all values for a header. */
14
+ getAll(name: string): string[];
15
+ /** Iterate over all headers. */
16
+ forEach(callback: (value: string, key: string) => void): void;
17
+ /** Get all header keys. */
18
+ keys(): IterableIterator<string>;
19
+ /** Get all entries as [key, value] pairs (first value only). */
20
+ entries(): IterableIterator<[string, string]>;
21
+ }
22
+ /**
23
+ * Response timing information in milliseconds.
24
+ */
25
+ export interface Timing {
26
+ /** DNS resolution time in ms. */
27
+ dns: number;
28
+ /** TCP connection time in ms. */
29
+ connect: number;
30
+ /** TLS handshake time in ms. */
31
+ tls: number;
32
+ /** Total request time in ms. */
33
+ total: number;
34
+ }
35
+ /**
36
+ * High-level Response class wrapping the raw PerformResult.
37
+ *
38
+ * Provides convenient access to status, headers, body, and timing.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const response = await session.get('https://httpbin.org/get');
43
+ * console.log(response.status); // 200
44
+ * console.log(response.ok); // true
45
+ * console.log(response.headers.get('content-type')); // 'application/json'
46
+ * const data = response.json(); // parsed JSON
47
+ * console.log(response.timing); // { dns: 5.2, connect: 12.1, tls: 45.3, total: 123.4 }
48
+ * ```
49
+ */
50
+ export declare class Response {
51
+ /** HTTP status code (e.g., 200, 404, 500). */
52
+ readonly status: number;
53
+ /** Parsed response headers with case-insensitive access. */
54
+ readonly headers: Headers;
55
+ /** Raw response headers string. */
56
+ readonly rawHeaders: string;
57
+ /** The effective URL after any redirects. */
58
+ readonly url: string;
59
+ /** Raw response body as a Buffer. */
60
+ readonly content: Buffer;
61
+ /** Response timing information in milliseconds. */
62
+ readonly timing: Timing;
63
+ /** Total elapsed time in milliseconds. */
64
+ readonly elapsed: number;
65
+ constructor(raw: {
66
+ body: Buffer;
67
+ headers: string;
68
+ statusCode: number;
69
+ effectiveUrl: string;
70
+ dnsTimeMs: number;
71
+ connectTimeMs: number;
72
+ tlsTimeMs: number;
73
+ totalTimeMs: number;
74
+ });
75
+ /** Returns true if status is 200-299. */
76
+ get ok(): boolean;
77
+ /** Returns the response body as a UTF-8 string. */
78
+ text(): string;
79
+ /** Parses the response body as JSON. */
80
+ json<T = unknown>(): T;
81
+ /** Returns the raw response body as a Buffer. */
82
+ buffer(): Buffer;
83
+ /** Returns the response body as a Node.js Readable stream. */
84
+ stream(): import('stream').Readable;
85
+ }
86
+ //# sourceMappingURL=response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../lib/response.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,IAAI,CAAoC;gBAEpC,GAAG,EAAE,MAAM;IAgBvB,2DAA2D;IAC3D,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKhC,mDAAmD;IACnD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,mCAAmC;IACnC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAI9B,gCAAgC;IAChC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAQ7D,2BAA2B;IAC3B,IAAI,IAAI,gBAAgB,CAAC,MAAM,CAAC;IAIhC,gEAAgE;IAChE,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAO9C;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,QAAQ;IACnB,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B,mCAAmC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B,6CAA6C;IAC7C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,GAAG,EAAE;QACf,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;KACrB;IAeD,yCAAyC;IACzC,IAAI,EAAE,IAAI,OAAO,CAEhB;IAED,mDAAmD;IACnD,IAAI,IAAI,MAAM;IAId,wCAAwC;IACxC,IAAI,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC;IAUtB,iDAAiD;IACjD,MAAM,IAAI,MAAM;IAIhB,8DAA8D;IAC9D,MAAM,IAAI,OAAO,QAAQ,EAAE,QAAQ;CAKpC"}
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Response = exports.Headers = void 0;
4
+ /**
5
+ * Headers wrapper with case-insensitive access.
6
+ *
7
+ * Parses raw header string into a Map for efficient lookup.
8
+ */
9
+ class Headers {
10
+ _map = new Map();
11
+ constructor(raw) {
12
+ for (const line of raw.split('\r\n')) {
13
+ const idx = line.indexOf(':');
14
+ if (idx === -1)
15
+ continue;
16
+ const key = line.slice(0, idx).trim().toLowerCase();
17
+ const value = line.slice(idx + 1).trim();
18
+ if (!key)
19
+ continue;
20
+ const existing = this._map.get(key);
21
+ if (existing) {
22
+ existing.push(value);
23
+ }
24
+ else {
25
+ this._map.set(key, [value]);
26
+ }
27
+ }
28
+ }
29
+ /** Get the first value for a header (case-insensitive). */
30
+ get(name) {
31
+ const values = this._map.get(name.toLowerCase());
32
+ return values ? values[0] : null;
33
+ }
34
+ /** Check if a header exists (case-insensitive). */
35
+ has(name) {
36
+ return this._map.has(name.toLowerCase());
37
+ }
38
+ /** Get all values for a header. */
39
+ getAll(name) {
40
+ return this._map.get(name.toLowerCase()) || [];
41
+ }
42
+ /** Iterate over all headers. */
43
+ forEach(callback) {
44
+ this._map.forEach((values, key) => {
45
+ for (const v of values) {
46
+ callback(v, key);
47
+ }
48
+ });
49
+ }
50
+ /** Get all header keys. */
51
+ keys() {
52
+ return this._map.keys();
53
+ }
54
+ /** Get all entries as [key, value] pairs (first value only). */
55
+ entries() {
56
+ const entries = [];
57
+ this._map.forEach((values, key) => {
58
+ entries.push([key, values[0]]);
59
+ });
60
+ return entries[Symbol.iterator]();
61
+ }
62
+ }
63
+ exports.Headers = Headers;
64
+ /**
65
+ * High-level Response class wrapping the raw PerformResult.
66
+ *
67
+ * Provides convenient access to status, headers, body, and timing.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * const response = await session.get('https://httpbin.org/get');
72
+ * console.log(response.status); // 200
73
+ * console.log(response.ok); // true
74
+ * console.log(response.headers.get('content-type')); // 'application/json'
75
+ * const data = response.json(); // parsed JSON
76
+ * console.log(response.timing); // { dns: 5.2, connect: 12.1, tls: 45.3, total: 123.4 }
77
+ * ```
78
+ */
79
+ class Response {
80
+ /** HTTP status code (e.g., 200, 404, 500). */
81
+ status;
82
+ /** Parsed response headers with case-insensitive access. */
83
+ headers;
84
+ /** Raw response headers string. */
85
+ rawHeaders;
86
+ /** The effective URL after any redirects. */
87
+ url;
88
+ /** Raw response body as a Buffer. */
89
+ content;
90
+ /** Response timing information in milliseconds. */
91
+ timing;
92
+ /** Total elapsed time in milliseconds. */
93
+ elapsed;
94
+ constructor(raw) {
95
+ this.status = raw.statusCode;
96
+ this.rawHeaders = raw.headers;
97
+ this.headers = new Headers(raw.headers);
98
+ this.url = raw.effectiveUrl;
99
+ this.content = raw.body;
100
+ this.elapsed = raw.totalTimeMs;
101
+ this.timing = {
102
+ dns: raw.dnsTimeMs,
103
+ connect: raw.connectTimeMs,
104
+ tls: raw.tlsTimeMs,
105
+ total: raw.totalTimeMs,
106
+ };
107
+ }
108
+ /** Returns true if status is 200-299. */
109
+ get ok() {
110
+ return this.status >= 200 && this.status < 300;
111
+ }
112
+ /** Returns the response body as a UTF-8 string. */
113
+ text() {
114
+ return this.content.toString('utf8');
115
+ }
116
+ /** Parses the response body as JSON. */
117
+ json() {
118
+ try {
119
+ return JSON.parse(this.text());
120
+ }
121
+ catch (err) {
122
+ throw new Error(`Failed to parse response as JSON: ${err instanceof Error ? err.message : String(err)}`);
123
+ }
124
+ }
125
+ /** Returns the raw response body as a Buffer. */
126
+ buffer() {
127
+ return this.content;
128
+ }
129
+ /** Returns the response body as a Node.js Readable stream. */
130
+ stream() {
131
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
132
+ const { Readable } = require('stream');
133
+ return Readable.from(this.content);
134
+ }
135
+ }
136
+ exports.Response = Response;
137
+ //# sourceMappingURL=response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response.js","sourceRoot":"","sources":["../lib/response.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * High-level Session class for making HTTP requests with browser impersonation.
3
+ *
4
+ * Uses a persistent curl handle for cookie persistence and connection reuse.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const session = new Session({ impersonate: 'chrome116' });
9
+ * const response = await session.get('https://httpbin.org/get');
10
+ * console.log(response.json());
11
+ * ```
12
+ */
13
+ import { Response } from './response.js';
14
+ /** Options for creating a Session. */
15
+ export interface SessionOptions {
16
+ /** Browser to impersonate (e.g., 'chrome116', 'safari15_5'). */
17
+ impersonate?: string;
18
+ /** Default headers for all requests. */
19
+ headers?: Record<string, string>;
20
+ /** Default timeout in seconds. */
21
+ timeout?: number;
22
+ /** Whether to follow redirects (default: true). */
23
+ followRedirects?: boolean;
24
+ /** Maximum number of redirects (default: 30). */
25
+ maxRedirects?: number;
26
+ /** Proxy URL (e.g., 'http://host:port', 'socks5://user:pass@host:port'). */
27
+ proxy?: string;
28
+ /** Whether to verify SSL certificates (default: true). */
29
+ verify?: boolean;
30
+ /** Whether to apply default browser headers (default: true). */
31
+ defaultHeaders?: boolean;
32
+ /** Pre-existing cookies in Netscape format to restore. */
33
+ cookies?: string[];
34
+ }
35
+ /** Options for individual requests. */
36
+ export interface RequestOptions {
37
+ /** Custom headers for this request. */
38
+ headers?: Record<string, string>;
39
+ /** Request body as string, Buffer, or JSON object. */
40
+ data?: string | Buffer | Record<string, unknown>;
41
+ /** URL query parameters. */
42
+ params?: Record<string, string | number | boolean>;
43
+ /** Timeout in seconds (overrides session default). */
44
+ timeout?: number;
45
+ /** Whether to follow redirects (overrides session default). */
46
+ followRedirects?: boolean;
47
+ /** Maximum redirects (overrides session default). */
48
+ maxRedirects?: number;
49
+ /** Proxy URL (overrides session default). */
50
+ proxy?: string;
51
+ /** Whether to verify SSL (overrides session default). */
52
+ verify?: boolean;
53
+ /** Browser to impersonate (overrides session default). */
54
+ impersonate?: string;
55
+ }
56
+ export declare class Session {
57
+ private _options;
58
+ /** Persistent handle for connection reuse and cookie persistence. */
59
+ private _handle;
60
+ constructor(options?: SessionOptions);
61
+ get(url: string, options?: RequestOptions): Promise<Response>;
62
+ post(url: string, options?: RequestOptions): Promise<Response>;
63
+ put(url: string, options?: RequestOptions): Promise<Response>;
64
+ delete(url: string, options?: RequestOptions): Promise<Response>;
65
+ head(url: string, options?: RequestOptions): Promise<Response>;
66
+ patch(url: string, options?: RequestOptions): Promise<Response>;
67
+ options(url: string, options?: RequestOptions): Promise<Response>;
68
+ /** Get all cookies in Netscape format. */
69
+ get cookies(): string[];
70
+ /** Export cookies as serialized Netscape-format strings (for persistence). */
71
+ exportCookies(): string[];
72
+ /** Import cookies from Netscape-format strings. */
73
+ importCookies(cookies: string[]): void;
74
+ /** Clear all cookies. */
75
+ clearCookies(): void;
76
+ /** Clear cookies for a specific domain. */
77
+ clearCookies(domain: string): void;
78
+ /**
79
+ * Core request method — uses the persistent handle directly for cookie
80
+ * persistence and connection reuse. Per-request options are set on the
81
+ * handle before each perform call.
82
+ *
83
+ * **WARNING**: This method calls `curl.perform()` which is a synchronous
84
+ * blocking FFI call. The Node.js event loop is blocked for the duration
85
+ * of the HTTP request. For high-concurrency server environments, consider
86
+ * using the low-level `Curl` API with `performAsync()` instead.
87
+ *
88
+ * A non-blocking Session implementation using `curl_multi` + libuv polling
89
+ * is planned for a future release.
90
+ */
91
+ private _request;
92
+ }
93
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../lib/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ /**
3
+ * High-level Session class for making HTTP requests with browser impersonation.
4
+ *
5
+ * Uses a persistent curl handle for cookie persistence and connection reuse.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const session = new Session({ impersonate: 'chrome116' });
10
+ * const response = await session.get('https://httpbin.org/get');
11
+ * console.log(response.json());
12
+ * ```
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.Session = void 0;
16
+ const binding_js_1 = require("./binding.js");
17
+ const response_js_1 = require("./response.js");
18
+ const errors_js_1 = require("./errors.js");
19
+ const NativeCurl = binding_js_1.nativeBinding.Curl;
20
+ const NativeCurlOpt = binding_js_1.nativeBinding.CurlOpt;
21
+ class Session {
22
+ _options;
23
+ /** Persistent handle for connection reuse and cookie persistence. */
24
+ _handle;
25
+ constructor(options = {}) {
26
+ this._options = {
27
+ followRedirects: true,
28
+ maxRedirects: 30,
29
+ verify: true,
30
+ defaultHeaders: true,
31
+ timeout: 0,
32
+ ...options,
33
+ };
34
+ // Create the persistent handle
35
+ this._handle = new NativeCurl();
36
+ // Enable cookie engine
37
+ this._handle.enableCookies();
38
+ // Set impersonation once on the persistent handle
39
+ if (this._options.impersonate) {
40
+ this._handle.impersonateStr(this._options.impersonate, this._options.defaultHeaders);
41
+ }
42
+ // Import pre-existing cookies
43
+ if (options.cookies) {
44
+ for (const line of options.cookies) {
45
+ this._handle.addCookie(line);
46
+ }
47
+ }
48
+ }
49
+ // ─── HTTP Methods ─────────────────────────────────────────────────────
50
+ async get(url, options) {
51
+ return this._request('GET', url, options);
52
+ }
53
+ async post(url, options) {
54
+ return this._request('POST', url, options);
55
+ }
56
+ async put(url, options) {
57
+ return this._request('PUT', url, options);
58
+ }
59
+ async delete(url, options) {
60
+ return this._request('DELETE', url, options);
61
+ }
62
+ async head(url, options) {
63
+ return this._request('HEAD', url, options);
64
+ }
65
+ async patch(url, options) {
66
+ return this._request('PATCH', url, options);
67
+ }
68
+ async options(url, options) {
69
+ return this._request('OPTIONS', url, options);
70
+ }
71
+ // ─── Cookie Management ────────────────────────────────────────────────
72
+ /** Get all cookies in Netscape format. */
73
+ get cookies() {
74
+ return this._handle.getCookies();
75
+ }
76
+ /** Export cookies as serialized Netscape-format strings (for persistence). */
77
+ exportCookies() {
78
+ return this._handle.getCookies();
79
+ }
80
+ /** Import cookies from Netscape-format strings. */
81
+ importCookies(cookies) {
82
+ for (const line of cookies) {
83
+ this._handle.addCookie(line);
84
+ }
85
+ }
86
+ clearCookies(domain) {
87
+ if (domain) {
88
+ // Filter by domain: get all, clear all, re-add non-matching
89
+ const all = this._handle.getCookies();
90
+ this._handle.clearCookies();
91
+ for (const line of all) {
92
+ // Netscape format: domain \t ... — first field is domain
93
+ const parts = line.split('\t');
94
+ if (parts[0] !== domain && parts[0] !== `.${domain}`) {
95
+ this._handle.addCookie(line);
96
+ }
97
+ }
98
+ }
99
+ else {
100
+ this._handle.clearCookies();
101
+ }
102
+ }
103
+ // ─── Internal ─────────────────────────────────────────────────────────
104
+ /**
105
+ * Core request method — uses the persistent handle directly for cookie
106
+ * persistence and connection reuse. Per-request options are set on the
107
+ * handle before each perform call.
108
+ *
109
+ * **WARNING**: This method calls `curl.perform()` which is a synchronous
110
+ * blocking FFI call. The Node.js event loop is blocked for the duration
111
+ * of the HTTP request. For high-concurrency server environments, consider
112
+ * using the low-level `Curl` API with `performAsync()` instead.
113
+ *
114
+ * A non-blocking Session implementation using `curl_multi` + libuv polling
115
+ * is planned for a future release.
116
+ */
117
+ async _request(method, url, options) {
118
+ const curl = this._handle;
119
+ // Per-request impersonation override
120
+ if (options?.impersonate && options.impersonate !== this._options.impersonate) {
121
+ try {
122
+ curl.impersonateStr(options.impersonate, this._options.defaultHeaders);
123
+ }
124
+ catch (e) {
125
+ throw (0, errors_js_1.parseCurlError)(e instanceof Error ? e.message : String(e));
126
+ }
127
+ }
128
+ // Build URL with params
129
+ let finalUrl = url;
130
+ if (options?.params) {
131
+ const entries = {};
132
+ for (const [k, v] of Object.entries(options.params)) {
133
+ entries[k] = String(v);
134
+ }
135
+ const qs = new URLSearchParams(entries).toString();
136
+ finalUrl += (url.includes('?') ? '&' : '?') + qs;
137
+ }
138
+ curl.setoptStr(NativeCurlOpt.Url, finalUrl);
139
+ // HTTP method — always set explicitly to prevent method leakage
140
+ // between requests on the persistent handle (P2 fix)
141
+ curl.setoptStr(NativeCurlOpt.CustomRequest, method);
142
+ if (method === 'GET' || method === 'HEAD' || method === 'DELETE') {
143
+ curl.setoptLong(NativeCurlOpt.Post, 0);
144
+ }
145
+ else {
146
+ // Re-enable POST mode for POST/PUT/PATCH — required after a
147
+ // previous GET explicitly disabled it (P2 lifecycle fix)
148
+ curl.setoptLong(NativeCurlOpt.Post, 1);
149
+ }
150
+ // Headers: merge session + request
151
+ const headerList = [];
152
+ if (this._options.headers) {
153
+ for (const [k, v] of Object.entries(this._options.headers)) {
154
+ headerList.push(`${k}: ${v}`);
155
+ }
156
+ }
157
+ if (options?.headers) {
158
+ for (const [k, v] of Object.entries(options.headers)) {
159
+ headerList.push(`${k}: ${v}`);
160
+ }
161
+ }
162
+ // Always set headers to prevent stale headers from previous requests (P4 fix)
163
+ curl.setoptList(NativeCurlOpt.HttpHeader, headerList.length > 0 ? headerList : ['_:']);
164
+ // Body
165
+ if (options?.data !== undefined) {
166
+ let body;
167
+ if (Buffer.isBuffer(options.data)) {
168
+ body = options.data.toString('utf8');
169
+ }
170
+ else if (typeof options.data === 'object') {
171
+ body = JSON.stringify(options.data);
172
+ if (!headerList.some((h) => h.toLowerCase().startsWith('content-type'))) {
173
+ curl.setoptList(NativeCurlOpt.HttpHeader, [
174
+ ...headerList,
175
+ 'Content-Type: application/json',
176
+ ]);
177
+ }
178
+ }
179
+ else {
180
+ body = options.data;
181
+ }
182
+ // Reset PostFieldSize to -1 (auto-detect from string length) before
183
+ // setting PostFields. This is required because a previous bodyless
184
+ // request may have set PostFieldSize=0, causing curl to ignore the
185
+ // body even when PostFields is set (P3 lifecycle fix).
186
+ curl.setoptLong(NativeCurlOpt.PostFieldSize, -1);
187
+ curl.setoptStr(NativeCurlOpt.PostFields, body);
188
+ }
189
+ else {
190
+ // Clear any previous POST body to prevent data leakage (P3 fix)
191
+ curl.setoptLong(NativeCurlOpt.PostFieldSize, 0);
192
+ }
193
+ // Timeout
194
+ const timeout = options?.timeout ?? this._options.timeout;
195
+ if (timeout && timeout > 0) {
196
+ curl.setoptLong(NativeCurlOpt.TimeoutMs, Math.floor(timeout * 1000));
197
+ }
198
+ else {
199
+ curl.setoptLong(NativeCurlOpt.TimeoutMs, 0);
200
+ }
201
+ // Redirects
202
+ const followRedirects = options?.followRedirects ?? this._options.followRedirects;
203
+ curl.setoptLong(NativeCurlOpt.FollowLocation, followRedirects ? 1 : 0);
204
+ const maxRedirects = options?.maxRedirects ?? this._options.maxRedirects;
205
+ curl.setoptLong(NativeCurlOpt.MaxRedirs, maxRedirects);
206
+ // Proxy
207
+ const proxy = options?.proxy ?? this._options.proxy;
208
+ if (proxy) {
209
+ curl.setoptStr(NativeCurlOpt.Proxy, proxy);
210
+ }
211
+ // SSL
212
+ const verify = options?.verify ?? this._options.verify;
213
+ if (verify === false) {
214
+ curl.setoptLong(NativeCurlOpt.SslVerifyPeer, 0);
215
+ curl.setoptLong(NativeCurlOpt.SslVerifyHost, 0);
216
+ }
217
+ // Perform synchronously on the persistent handle (keeps cookies!)
218
+ try {
219
+ const result = curl.perform();
220
+ return new response_js_1.Response(result);
221
+ }
222
+ catch (e) {
223
+ throw (0, errors_js_1.parseCurlError)(e instanceof Error ? e.message : String(e));
224
+ }
225
+ }
226
+ }
227
+ exports.Session = Session;
228
+ //# sourceMappingURL=session.js.map