curl-wrap 2.0.0 → 2.1.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.
Files changed (4) hide show
  1. package/README.md +66 -0
  2. package/index.d.ts +18 -2
  3. package/index.js +377 -54
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -45,6 +45,9 @@ curl.headers({
45
45
  'User-Agent': 'curl-wrap/1.0',
46
46
  'Accept': 'application/json'
47
47
  });
48
+
49
+ // clear all headers
50
+ curl.clearHeaders();
48
51
  ```
49
52
 
50
53
  ### Setting Cookies
@@ -98,6 +101,9 @@ curl.impersonate('firefox');
98
101
  curl.impersonate('safari');
99
102
  curl.impersonate('safariMobile');
100
103
  curl.impersonate('edge');
104
+
105
+ // Or create a new instance with impersonation
106
+ const chromeCurl = Curl.impersonate('chrome');
101
107
  ```
102
108
 
103
109
  ### Setting Request Method
@@ -165,6 +171,66 @@ const response = await curl;
165
171
  console.log(response.stderr);
166
172
  ```
167
173
 
174
+ ### Buffer Response
175
+
176
+ ```js
177
+ curl.asBuffer();
178
+ const response = await curl;
179
+ console.log(response.body); // Buffer object instead of string
180
+ ```
181
+
182
+ ### Export as Curl Command
183
+
184
+ ```js
185
+ const curl = Curl.url('https://api.example.com')
186
+ .post()
187
+ .header('Content-Type', 'application/json')
188
+ .body({key: 'value'});
189
+
190
+ const curlCommand = await curl.exportAsCurl();
191
+ console.log(curlCommand);
192
+ // Output:
193
+ // curl 'https://api.example.com' \
194
+ // --request POST \
195
+ // --header 'content-type: application/json' \
196
+ // --data-raw '{"key":"value"}'
197
+ ```
198
+
199
+ ### Import from Curl Command
200
+
201
+ ```js
202
+ const curlCommand = `curl 'https://api.example.com' \
203
+ --request POST \
204
+ --header 'Content-Type: application/json' \
205
+ --header 'Authorization: Bearer token123' \
206
+ --cookie 'session=abc123; user=john' \
207
+ --data-raw '{"name":"John","age":30}'`;
208
+
209
+ const curl = Curl.fromCurl(curlCommand);
210
+ const response = await curl;
211
+ ```
212
+
213
+ ### Cloning Requests
214
+
215
+ ```js
216
+ const baseCurl = Curl.url('https://api.example.com')
217
+ .header('Authorization', 'Bearer token123')
218
+ .header('Content-Type', 'application/json');
219
+
220
+ // Clone and modify for different endpoints
221
+ const getUserCurl = baseCurl.clone()
222
+ .url('https://api.example.com/user')
223
+ .get();
224
+ const createUserCurl = baseCurl.clone()
225
+ .url('https://api.example.com/user')
226
+ .post()
227
+ .body({name: 'John'});
228
+
229
+ // Both requests share the same headers but have different URLs and methods
230
+ const userResponse = await getUserCurl;
231
+ const createResponse = await createUserCurl;
232
+ ```
233
+
168
234
  ### Method Chaining
169
235
 
170
236
  ```js
package/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { CookieJar } from "tough-cookie";
3
3
  interface CurlOptions {
4
4
  method?: string; // HTTP method (e.g., GET, POST)
5
5
  headers?: Record<string, string>; // Request headers
6
+ cookies?: Record<string, string>; // Request cookies
6
7
  body?: string | object | null; // Request body
7
8
  timeout?: number; // Timeout in milliseconds
8
9
  [key: string]: any; // Allow additional options
@@ -10,7 +11,7 @@ interface CurlOptions {
10
11
 
11
12
  interface CurlResponse {
12
13
  url: string;
13
- body: string; // Response body as a string
14
+ body: string | Buffer; // Response body as a string or Buffer
14
15
  headers: Record<string, string>; // Response headers
15
16
  statusCode: number; // HTTP status code
16
17
  status: number; // alias for .statusCode
@@ -50,6 +51,8 @@ export class Curl {
50
51
  static getGlobalCookieJar(): CookieJar;
51
52
 
52
53
  static hasCurlImpersonate(): boolean;
54
+ static impersonate(browser?: BrowserType): Curl;
55
+ static fromCurl(curlCommand: string): Curl;
53
56
 
54
57
  cliCommand(command: string): Curl;
55
58
  cliOptions(options: string | string[]): Curl;
@@ -61,6 +64,7 @@ export class Curl {
61
64
 
62
65
  header(headerName: string | object, headerValue?: string): Curl;
63
66
  headers(headers: object): Curl;
67
+ clearHeaders(): Curl;
64
68
 
65
69
  json(body?: object): Curl;
66
70
  body(body: any, contentType?: string): Curl;
@@ -78,10 +82,11 @@ export class Curl {
78
82
  cookieName: string | boolean | object,
79
83
  cookieValue?: string
80
84
  ): Curl;
81
- cookies(cookies: object | boolean): Curl;
85
+ cookies(cookies: object | boolean | string): Curl;
82
86
 
83
87
  globalCookies(options?: boolean | object): Curl;
84
88
  cookieJar(cookieJar: CookieJar, options?: { readOnly?: boolean }): Curl;
89
+ getCookieJar(): CookieJar | undefined;
85
90
  cookieFile(fileName: string, options?: object): Curl;
86
91
 
87
92
  timeout(timeout: number): Curl;
@@ -113,6 +118,12 @@ export class Curl {
113
118
 
114
119
  verbose(isVerbose?: boolean): Curl;
115
120
 
121
+ asBuffer(returnAsBuffer?: boolean): Curl;
122
+
123
+ clone(): Curl;
124
+
125
+ exportAsCurl(): Promise<string>;
126
+
116
127
  fetch(): Promise<CurlResponse>;
117
128
 
118
129
  then(
@@ -130,4 +141,9 @@ export class Curl {
130
141
  private options: any;
131
142
  private _fields: Record<string, any>;
132
143
  private _query: Record<string, any>;
144
+ private _cookies: Record<string, any>;
145
+ private _cookieJar?: CookieJar;
146
+ private _readCookieJar?: CookieJar;
147
+ private _cookieFileFn?: () => Promise<void>;
148
+ private _cookieFileFnRes?: () => Promise<void>;
133
149
  }
package/index.js CHANGED
@@ -2,12 +2,6 @@ const {spawn, spawnSync} = require('child_process');
2
2
  const fs = require('fs/promises');
3
3
  const {CookieJar} = require('tough-cookie');
4
4
 
5
- function getDefaultProxyPort(proxyType) {
6
- if (proxyType === 'http') return 80;
7
- if (proxyType === 'https') return 443;
8
- return 1080;
9
- }
10
-
11
5
  /**
12
6
  * Returns proxy url based on the proxy options.
13
7
  *
@@ -27,23 +21,80 @@ function makeProxyUrl(proxy, options) {
27
21
  }
28
22
 
29
23
  const auth = proxy.auth || options.auth || {};
30
- const uri = new URL(proxy);
24
+ const uri = new URL(address);
31
25
  if (!uri.port) {
32
- uri.port = proxy.port || options.port || getDefaultProxyPort(uri.protocol.replace(':', ''));
26
+ const port = proxy.port || options.port;
27
+ if (port) {
28
+ uri.port = port;
29
+ }
30
+ else if (uri.protocol.startsWith('socks')) {
31
+ uri.port = '1080';
32
+ }
33
33
  }
34
- if (!uri.username && options.username) {
35
- uri.username = proxy.username || options.username || auth.username || '';
34
+ if (!uri.username) {
35
+ const username = proxy.username || options.username || auth.username;
36
+ if (username) uri.username = username;
36
37
  }
37
- if (!uri.password && options.password) {
38
- uri.password = proxy.password || options.password || auth.password || '';
38
+ if (!uri.password) {
39
+ const password = proxy.password || options.password || auth.password;
40
+ if (password) uri.password = password;
39
41
  }
40
42
  return uri.toString();
41
43
  }
42
44
 
45
+ /**
46
+ * Parse curl command string into arguments array
47
+ *
48
+ * @param {string} command
49
+ * @return {string[]}
50
+ */
51
+ function parseCommand(command) {
52
+ // Clean up the command - remove line breaks and extra spaces
53
+ command = command.replace(/\\\s*\n/g, ' ').replace(/\s+/g, ' ').trim();
54
+
55
+ const args = [];
56
+ let current = '';
57
+ let inQuotes = false;
58
+ let quoteChar = '';
59
+
60
+ for (let i = 0; i < command.length; i++) {
61
+ const char = command[i];
62
+ const nextChar = command[i + 1];
63
+
64
+ if ((char === '"' || char === "'") && !inQuotes) {
65
+ inQuotes = true;
66
+ quoteChar = char;
67
+ }
68
+ else if (char === quoteChar && inQuotes) {
69
+ inQuotes = false;
70
+ quoteChar = '';
71
+ }
72
+ else if (char === ' ' && !inQuotes) {
73
+ if (current.trim()) {
74
+ args.push(current.trim());
75
+ current = '';
76
+ }
77
+ }
78
+ else if (char === '\\' && nextChar === quoteChar && inQuotes) {
79
+ current += nextChar;
80
+ i++; // Skip next character
81
+ }
82
+ else {
83
+ current += char;
84
+ }
85
+ }
86
+
87
+ if (current.trim()) {
88
+ args.push(current.trim());
89
+ }
90
+
91
+ return args;
92
+ }
93
+
43
94
  class CurlResponse {
44
- constructor() {
95
+ constructor({body = ''} = {}) {
45
96
  this.url = '';
46
- this.body = '';
97
+ this.body = body;
47
98
  this.headers = {};
48
99
  this.statusCode = 0;
49
100
  this.ip = '';
@@ -79,6 +130,31 @@ class CurlResponse {
79
130
  }
80
131
  }
81
132
 
133
+ /**
134
+ * Parse cookies string into object
135
+ *
136
+ * @param {string} cookieStr
137
+ * @return {object}
138
+ */
139
+ function parseCookies(cookieStr) {
140
+ const cookies = {};
141
+ if (!cookieStr) return cookies;
142
+ const parts = cookieStr.split(';');
143
+
144
+ for (const part of parts) {
145
+ const equalIndex = part.indexOf('=');
146
+ if (equalIndex !== -1) {
147
+ const key = part.slice(0, equalIndex).trim();
148
+ const value = part.slice(equalIndex + 1).trim();
149
+ if (key && value) {
150
+ cookies[key] = value;
151
+ }
152
+ }
153
+ }
154
+
155
+ return cookies;
156
+ }
157
+
82
158
  class Curl {
83
159
  /**
84
160
  * Creates a new Curl object.
@@ -289,10 +365,14 @@ class Curl {
289
365
  return this;
290
366
  }
291
367
 
368
+ /**
369
+ * @typedef {"chrome"|"chromeMobile"|"edge"|"safari"|"safariMobile"|"firefox"} BrowserType
370
+ */
371
+
292
372
  /**
293
373
  * impersonate a browser
294
374
  *
295
- * @param {string} browser browser to impersonate (chrome / chromeMobile / edge / safari / safariMobile / firefox)
375
+ * @param {BrowserType} browser browser to impersonate
296
376
  * @return {Curl} self
297
377
  */
298
378
  impersonate(browser = 'chrome') {
@@ -564,6 +644,19 @@ class Curl {
564
644
  return this;
565
645
  }
566
646
 
647
+ /**
648
+ * @static
649
+ * impersonate a browser
650
+ *
651
+ * @param {BrowserType} browser browser to impersonate
652
+ * @return {Curl} self
653
+ */
654
+ static impersonate(browser = 'chrome') {
655
+ const curl = new this();
656
+ curl.impersonate(browser);
657
+ return curl;
658
+ }
659
+
567
660
  /**
568
661
  * Set or unset the followRedirect option for the connection.
569
662
  *
@@ -618,6 +711,16 @@ class Curl {
618
711
  return this;
619
712
  }
620
713
 
714
+ /**
715
+ * Clear all headers
716
+ *
717
+ * @return {Curl} self
718
+ */
719
+ clearHeaders() {
720
+ this.options.headers = {};
721
+ return this;
722
+ }
723
+
621
724
  /**
622
725
  * Set the content type as json
623
726
  * Optionally also set the body of the request.
@@ -737,18 +840,15 @@ class Curl {
737
840
  */
738
841
  cookie(cookieName, cookieValue) {
739
842
  if (cookieValue === undefined) {
740
- this.globalCookies(cookieName);
843
+ this.cookies(cookieName);
741
844
  }
742
845
  else if (typeof cookieName === 'string') {
743
- if (cookieValue === null || cookieValue === false) {
744
- delete this._cookies[cookieName];
745
- }
746
- else {
846
+ if (typeof cookieValue === 'string') {
747
847
  this._cookies[cookieName] = cookieValue;
748
848
  }
749
- }
750
- else if (cookieName && typeof cookieName === 'object') {
751
- Object.assign(this._cookies, cookieName);
849
+ else if (cookieValue === null || cookieValue === false) {
850
+ delete this._cookies[cookieName];
851
+ }
752
852
  }
753
853
 
754
854
  return this;
@@ -758,7 +858,7 @@ class Curl {
758
858
  * Sets multiple cookies.
759
859
  * Can be used to enable global cookies, if cookies is set to true.
760
860
  *
761
- * @param {object|boolean} cookies object representing the cookies
861
+ * @param {object|boolean|string} cookies object representing the cookies
762
862
  * and their values as key:value pairs.
763
863
  * @return {Curl} self
764
864
  */
@@ -769,6 +869,10 @@ class Curl {
769
869
  else if (cookies && typeof cookies === 'object') {
770
870
  Object.assign(this._cookies, cookies);
771
871
  }
872
+ else if (typeof cookies === 'string') {
873
+ const cookies = parseCookies(cookieName);
874
+ Object.assign(this._cookies, cookies);
875
+ }
772
876
 
773
877
  return this;
774
878
  }
@@ -781,8 +885,8 @@ class Curl {
781
885
  */
782
886
  globalCookies(options = true) {
783
887
  if (options === false || options === null) {
784
- delete this.options.cookieJar;
785
- delete this.options.readCookieJar;
888
+ delete this._cookieJar;
889
+ delete this._readCookieJar;
786
890
  return this;
787
891
  }
788
892
 
@@ -802,15 +906,24 @@ class Curl {
802
906
  delete this._cookieFileFnRes;
803
907
 
804
908
  if (options.readOnly) {
805
- this.options.readCookieJar = cookieJar;
909
+ this._readCookieJar = cookieJar;
806
910
  }
807
911
  else {
808
- this.options.cookieJar = cookieJar;
912
+ this._cookieJar = cookieJar;
809
913
  }
810
914
 
811
915
  return this;
812
916
  }
813
917
 
918
+ /**
919
+ * Get the current cookie jar
920
+ *
921
+ * @return {CookieJar|undefined} the current cookie jar
922
+ */
923
+ getCookieJar() {
924
+ return this._cookieJar || this._readCookieJar;
925
+ }
926
+
814
927
  /**
815
928
  * Set the value of cookie jar based on a file (cookie store).
816
929
  *
@@ -820,10 +933,10 @@ class Curl {
820
933
  cookieFile(fileName, options = {}) {
821
934
  const setCookieJar = (cookieJar) => {
822
935
  if (options.readOnly) {
823
- this.options.readCookieJar = cookieJar;
936
+ this._readCookieJar = cookieJar;
824
937
  }
825
938
  else {
826
- this.options.cookieJar = cookieJar;
939
+ this._cookieJar = cookieJar;
827
940
  }
828
941
  };
829
942
 
@@ -841,7 +954,7 @@ class Curl {
841
954
 
842
955
  if (!options.readOnly) {
843
956
  this._cookieFileFnRes = async () => {
844
- const cookieJar = this.options.cookieJar;
957
+ const cookieJar = this._cookieJar;
845
958
  if (!cookieJar) return;
846
959
  try {
847
960
  const contents = JSON.stringify(cookieJar.toJSON());
@@ -1086,6 +1199,205 @@ class Curl {
1086
1199
  return this;
1087
1200
  }
1088
1201
 
1202
+ /**
1203
+ * Set if the body is to be returned as a buffer
1204
+ *
1205
+ * @param {boolean} [returnAsBuffer=true]
1206
+ * @return {Curl} self
1207
+ */
1208
+ asBuffer(returnAsBuffer = true) {
1209
+ this.options.asBuffer = returnAsBuffer;
1210
+ return this;
1211
+ }
1212
+
1213
+ /**
1214
+ * Create a clone of this Curl instance with the same options
1215
+ *
1216
+ * @return {Curl} new Curl instance with cloned options
1217
+ */
1218
+ clone() {
1219
+ const cloned = new this.constructor();
1220
+
1221
+ // Deep clone the options object (only serializable properties)
1222
+ cloned.options = JSON.parse(JSON.stringify(this.options));
1223
+
1224
+ // Copy non-serializable properties
1225
+ if (this._cookieJar) {
1226
+ cloned._cookieJar = this._cookieJar;
1227
+ }
1228
+ if (this._readCookieJar) {
1229
+ cloned._readCookieJar = this._readCookieJar;
1230
+ }
1231
+ if (this._cookieFileFn) {
1232
+ cloned._cookieFileFn = this._cookieFileFn;
1233
+ }
1234
+ if (this._cookieFileFnRes) {
1235
+ cloned._cookieFileFnRes = this._cookieFileFnRes;
1236
+ }
1237
+
1238
+ return cloned;
1239
+ }
1240
+
1241
+ /**
1242
+ * Export the current request as a curl command string
1243
+ *
1244
+ * @return {Promise<string>} curl command as a string
1245
+ */
1246
+ async exportAsCurl() {
1247
+ const cmd = this.options.cliCommand || 'curl';
1248
+ const args = await this.getCurlArgs();
1249
+ // Filter out internal options that shouldn't appear in exported commands
1250
+ const skipOptions = ['--silent', '--no-keepalive', '--keepalive', '--write-out', '--insecure', '--location'];
1251
+ const escape = arg => `'${arg.replace(/'/g, "'\"'\"'")}'`;
1252
+
1253
+ const url = args[0];
1254
+ const lines = [`${cmd} ${escape(url)}`];
1255
+ for (let i = 1; i < args.length; i++) {
1256
+ const arg = String(args[i]);
1257
+ const nextArg = String(args[i + 1] ?? '');
1258
+ const isNextArgValue = !nextArg.startsWith('--');
1259
+ if (isNextArgValue) {
1260
+ i++;
1261
+ }
1262
+ // Skip internal options and their values
1263
+ if (skipOptions.includes(arg)) continue;
1264
+ if (isNextArgValue) {
1265
+ const escapedNextArg = /[\s"'`&:;,><|(){}~^%#@!/=+\$\?\*\[\]]/.test(nextArg) ? escape(nextArg) : nextArg;
1266
+ lines.push(`${arg} ${escapedNextArg}`);
1267
+ }
1268
+ else {
1269
+ lines.push(arg);
1270
+ }
1271
+ }
1272
+
1273
+ return lines.join(' \\\n ');
1274
+ }
1275
+
1276
+ /**
1277
+ * @static
1278
+ * Create a new Curl instance from a curl command string
1279
+ *
1280
+ * @param {string} curlCommand curl command string to parse
1281
+ * @return {Curl} new Curl instance
1282
+ */
1283
+ static fromCurl(curlCommand) {
1284
+ const curl = new this();
1285
+ curl.options.headers = {};
1286
+
1287
+ // Parse the command into arguments
1288
+ const args = parseCommand(curlCommand);
1289
+
1290
+ let method;
1291
+ let body;
1292
+
1293
+ // Process arguments
1294
+ for (let i = 0; i < args.length; i++) {
1295
+ const arg = args[i];
1296
+ const nextArg = args[i + 1];
1297
+ const isNextArgValue = nextArg && !nextArg.startsWith('-');
1298
+
1299
+ if (i === 0 && !arg.startsWith('curl')) {
1300
+ // First argument should be the URL if no 'curl' command prefix
1301
+ curl.url(arg);
1302
+ }
1303
+ else if (i === 1 && args[0].startsWith('curl')) {
1304
+ // Second argument is URL when first is 'curl'
1305
+ curl.url(arg);
1306
+ }
1307
+ else if (arg === '--request' || arg === '-X') {
1308
+ if (!isNextArgValue) continue;
1309
+ method = nextArg;
1310
+ i++;
1311
+ }
1312
+ else if (arg === '--header' || arg === '-H') {
1313
+ if (!isNextArgValue) continue;
1314
+ const colonIndex = nextArg.indexOf(':');
1315
+ if (colonIndex !== -1) {
1316
+ const headerName = nextArg.slice(0, colonIndex).trim();
1317
+ const headerValue = nextArg.slice(colonIndex + 1).trim();
1318
+ curl.header(headerName, headerValue);
1319
+ }
1320
+ i++;
1321
+ }
1322
+ else if (arg === '--data' || arg === '--data-raw' || arg === '-d') {
1323
+ if (!isNextArgValue) continue;
1324
+ body = nextArg;
1325
+ i++;
1326
+ }
1327
+ else if (arg === '--cookie' || arg === '-b') {
1328
+ if (!isNextArgValue) continue;
1329
+ curl.header('cookie', nextArg);
1330
+ i++;
1331
+ }
1332
+ else if (arg === '--user-agent' || arg === '-A') {
1333
+ if (!isNextArgValue) continue;
1334
+ curl.userAgent(nextArg);
1335
+ i++;
1336
+ }
1337
+ else if (arg === '--referer' || arg === '-e') {
1338
+ if (!isNextArgValue) continue;
1339
+ curl.referer(nextArg);
1340
+ i++;
1341
+ }
1342
+ else if (arg === '--max-time' || arg === '-m') {
1343
+ if (!isNextArgValue) continue;
1344
+ const timeout = Number(nextArg);
1345
+ if (timeout > 0) curl.timeout(timeout);
1346
+ i++;
1347
+ }
1348
+ else if (arg === '--proxy' || arg === '-x') {
1349
+ if (!isNextArgValue) continue;
1350
+ curl.proxy(nextArg);
1351
+ i++;
1352
+ }
1353
+ else if (arg === '--compressed') {
1354
+ curl.compress(true);
1355
+ }
1356
+ else if (arg === '--location' || arg === '-L') {
1357
+ curl.followRedirect(true);
1358
+ }
1359
+ else if (arg === '--max-redirs') {
1360
+ if (!isNextArgValue) continue;
1361
+ const maxRedir = Math.floor(Number(nextArg));
1362
+ if (maxRedir > 0) curl.maxRedirects(maxRedir);
1363
+ i++;
1364
+ }
1365
+ else if (arg === '--verbose' || arg === '-v') {
1366
+ curl.verbose(true);
1367
+ }
1368
+ else if (arg.startsWith('--') || arg.startsWith('-')) {
1369
+ // Ignore internal options that shouldn't be imported
1370
+ const ignoreOptions = ['--silent', '--no-keepalive', '--keepalive', '--write-out', '--insecure'];
1371
+ if (ignoreOptions.includes(arg)) {
1372
+ // If the ignored option has a value, ignore it too
1373
+ if (isNextArgValue) {
1374
+ i++;
1375
+ }
1376
+ }
1377
+ else {
1378
+ // Handle unknown curl options by adding them as cliOptions
1379
+ if (isNextArgValue) {
1380
+ curl.cliOptions([arg, nextArg]);
1381
+ i++; // Skip next argument
1382
+ }
1383
+ else {
1384
+ curl.cliOptions(arg);
1385
+ }
1386
+ }
1387
+ }
1388
+ }
1389
+
1390
+ if (body) {
1391
+ curl.body(body);
1392
+ method = method || 'POST';
1393
+ }
1394
+ if (method) {
1395
+ curl.method(method);
1396
+ }
1397
+
1398
+ return curl;
1399
+ }
1400
+
1089
1401
  /**
1090
1402
  * Add the options 'fields' to the options body, form or qs
1091
1403
  * on the basis of the request method.
@@ -1165,7 +1477,7 @@ class Curl {
1165
1477
  await this._cookieFileFn();
1166
1478
  }
1167
1479
 
1168
- const cookieJar = options.readCookieJar || options.cookieJar;
1480
+ const cookieJar = this._readCookieJar || this._cookieJar;
1169
1481
  if (cookieJar) {
1170
1482
  const jarCookies = await cookieJar.getCookies(options.url);
1171
1483
  if (jarCookies) {
@@ -1201,19 +1513,12 @@ class Curl {
1201
1513
  '%{stderr}===<json>==={"json":%{json},"headers":%{header_json}}===</json>===',
1202
1514
  ];
1203
1515
 
1204
- if (body) {
1205
- args.push('--data-raw', body);
1206
- }
1207
-
1208
- const cookieHeader = await this.getCookieHeader();
1209
- if (cookieHeader) {
1210
- this.header('cookie', cookieHeader);
1516
+ const cliOptions = options.cliOptions;
1517
+ if (cliOptions) {
1518
+ args.push(...cliOptions);
1211
1519
  }
1212
1520
 
1213
- for (const [key, value] of Object.entries(options.headers)) {
1214
- args.push('--header', `${key}: ${value}`);
1215
- }
1216
- if (options.compress) {
1521
+ if (options.compress && !cliOptions?.includes('--compressed')) {
1217
1522
  args.push('--compressed');
1218
1523
  }
1219
1524
  if (options.proxy && options.useProxy !== false) {
@@ -1235,9 +1540,17 @@ class Curl {
1235
1540
  args.push('--verbose');
1236
1541
  }
1237
1542
 
1238
- const cliOptions = options.cliOptions;
1239
- if (cliOptions) {
1240
- args.push(...cliOptions);
1543
+ const cookieHeader = await this.getCookieHeader();
1544
+ if (cookieHeader) {
1545
+ this.header('cookie', cookieHeader);
1546
+ }
1547
+
1548
+ for (const [key, value] of Object.entries(options.headers)) {
1549
+ args.push('--header', `${key}: ${value}`);
1550
+ }
1551
+
1552
+ if (body) {
1553
+ args.push('--data-raw', body);
1241
1554
  }
1242
1555
 
1243
1556
  return args;
@@ -1245,18 +1558,28 @@ class Curl {
1245
1558
 
1246
1559
  async fetch() {
1247
1560
  const startTime = Date.now();
1248
- const cmd = this.options.cliCommand || 'curl';
1561
+ const options = this.options;
1562
+ const cmd = options.cliCommand || 'curl';
1249
1563
  const args = await this.getCurlArgs();
1250
1564
  const curl = spawn(cmd, args);
1251
- const cookieJar = this.options.cookieJar;
1565
+ const cookieJar = this._cookieJar;
1252
1566
 
1253
- let stdout = '';
1567
+ let stdout = options.asBuffer ? Buffer.from([]) : '';
1254
1568
  let stderr = '';
1255
- const response = new CurlResponse();
1569
+ const response = new CurlResponse({
1570
+ body: options.asBuffer ? Buffer.from([]) : '',
1571
+ });
1256
1572
  return new Promise((resolve, reject) => {
1257
- curl.stdout.on('data', (data) => {
1258
- stdout += data;
1259
- });
1573
+ if (options.asBuffer) {
1574
+ curl.stdout.on('data', (data) => {
1575
+ stdout = Buffer.concat([stdout, data]);
1576
+ });
1577
+ }
1578
+ else {
1579
+ curl.stdout.on('data', (data) => {
1580
+ stdout += data;
1581
+ });
1582
+ }
1260
1583
 
1261
1584
  curl.stderr.on('data', (data) => {
1262
1585
  stderr += data;
@@ -1270,7 +1593,7 @@ class Curl {
1270
1593
  curl.on('close', async (code) => {
1271
1594
  response.timeTaken = Date.now() - startTime;
1272
1595
  response.exitCode = code;
1273
- response.url = this.options.url;
1596
+ response.url = options.url;
1274
1597
  response.body = stdout;
1275
1598
  stderr = stderr.replace(/===<json>===(.*)===<\/json>===/s, (match, p1) => {
1276
1599
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "curl-wrap",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Nodejs library that wraps curl command line",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",