curl-wrap 2.0.1 → 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 +363 -42
  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
@@ -42,10 +42,59 @@ function makeProxyUrl(proxy, options) {
42
42
  return uri.toString();
43
43
  }
44
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
+
45
94
  class CurlResponse {
46
- constructor() {
95
+ constructor({body = ''} = {}) {
47
96
  this.url = '';
48
- this.body = '';
97
+ this.body = body;
49
98
  this.headers = {};
50
99
  this.statusCode = 0;
51
100
  this.ip = '';
@@ -81,6 +130,31 @@ class CurlResponse {
81
130
  }
82
131
  }
83
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
+
84
158
  class Curl {
85
159
  /**
86
160
  * Creates a new Curl object.
@@ -291,10 +365,14 @@ class Curl {
291
365
  return this;
292
366
  }
293
367
 
368
+ /**
369
+ * @typedef {"chrome"|"chromeMobile"|"edge"|"safari"|"safariMobile"|"firefox"} BrowserType
370
+ */
371
+
294
372
  /**
295
373
  * impersonate a browser
296
374
  *
297
- * @param {string} browser browser to impersonate (chrome / chromeMobile / edge / safari / safariMobile / firefox)
375
+ * @param {BrowserType} browser browser to impersonate
298
376
  * @return {Curl} self
299
377
  */
300
378
  impersonate(browser = 'chrome') {
@@ -566,6 +644,19 @@ class Curl {
566
644
  return this;
567
645
  }
568
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
+
569
660
  /**
570
661
  * Set or unset the followRedirect option for the connection.
571
662
  *
@@ -620,6 +711,16 @@ class Curl {
620
711
  return this;
621
712
  }
622
713
 
714
+ /**
715
+ * Clear all headers
716
+ *
717
+ * @return {Curl} self
718
+ */
719
+ clearHeaders() {
720
+ this.options.headers = {};
721
+ return this;
722
+ }
723
+
623
724
  /**
624
725
  * Set the content type as json
625
726
  * Optionally also set the body of the request.
@@ -739,18 +840,15 @@ class Curl {
739
840
  */
740
841
  cookie(cookieName, cookieValue) {
741
842
  if (cookieValue === undefined) {
742
- this.globalCookies(cookieName);
843
+ this.cookies(cookieName);
743
844
  }
744
845
  else if (typeof cookieName === 'string') {
745
- if (cookieValue === null || cookieValue === false) {
746
- delete this._cookies[cookieName];
747
- }
748
- else {
846
+ if (typeof cookieValue === 'string') {
749
847
  this._cookies[cookieName] = cookieValue;
750
848
  }
751
- }
752
- else if (cookieName && typeof cookieName === 'object') {
753
- Object.assign(this._cookies, cookieName);
849
+ else if (cookieValue === null || cookieValue === false) {
850
+ delete this._cookies[cookieName];
851
+ }
754
852
  }
755
853
 
756
854
  return this;
@@ -760,7 +858,7 @@ class Curl {
760
858
  * Sets multiple cookies.
761
859
  * Can be used to enable global cookies, if cookies is set to true.
762
860
  *
763
- * @param {object|boolean} cookies object representing the cookies
861
+ * @param {object|boolean|string} cookies object representing the cookies
764
862
  * and their values as key:value pairs.
765
863
  * @return {Curl} self
766
864
  */
@@ -771,6 +869,10 @@ class Curl {
771
869
  else if (cookies && typeof cookies === 'object') {
772
870
  Object.assign(this._cookies, cookies);
773
871
  }
872
+ else if (typeof cookies === 'string') {
873
+ const cookies = parseCookies(cookieName);
874
+ Object.assign(this._cookies, cookies);
875
+ }
774
876
 
775
877
  return this;
776
878
  }
@@ -783,8 +885,8 @@ class Curl {
783
885
  */
784
886
  globalCookies(options = true) {
785
887
  if (options === false || options === null) {
786
- delete this.options.cookieJar;
787
- delete this.options.readCookieJar;
888
+ delete this._cookieJar;
889
+ delete this._readCookieJar;
788
890
  return this;
789
891
  }
790
892
 
@@ -804,15 +906,24 @@ class Curl {
804
906
  delete this._cookieFileFnRes;
805
907
 
806
908
  if (options.readOnly) {
807
- this.options.readCookieJar = cookieJar;
909
+ this._readCookieJar = cookieJar;
808
910
  }
809
911
  else {
810
- this.options.cookieJar = cookieJar;
912
+ this._cookieJar = cookieJar;
811
913
  }
812
914
 
813
915
  return this;
814
916
  }
815
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
+
816
927
  /**
817
928
  * Set the value of cookie jar based on a file (cookie store).
818
929
  *
@@ -822,10 +933,10 @@ class Curl {
822
933
  cookieFile(fileName, options = {}) {
823
934
  const setCookieJar = (cookieJar) => {
824
935
  if (options.readOnly) {
825
- this.options.readCookieJar = cookieJar;
936
+ this._readCookieJar = cookieJar;
826
937
  }
827
938
  else {
828
- this.options.cookieJar = cookieJar;
939
+ this._cookieJar = cookieJar;
829
940
  }
830
941
  };
831
942
 
@@ -843,7 +954,7 @@ class Curl {
843
954
 
844
955
  if (!options.readOnly) {
845
956
  this._cookieFileFnRes = async () => {
846
- const cookieJar = this.options.cookieJar;
957
+ const cookieJar = this._cookieJar;
847
958
  if (!cookieJar) return;
848
959
  try {
849
960
  const contents = JSON.stringify(cookieJar.toJSON());
@@ -1088,6 +1199,205 @@ class Curl {
1088
1199
  return this;
1089
1200
  }
1090
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
+
1091
1401
  /**
1092
1402
  * Add the options 'fields' to the options body, form or qs
1093
1403
  * on the basis of the request method.
@@ -1167,7 +1477,7 @@ class Curl {
1167
1477
  await this._cookieFileFn();
1168
1478
  }
1169
1479
 
1170
- const cookieJar = options.readCookieJar || options.cookieJar;
1480
+ const cookieJar = this._readCookieJar || this._cookieJar;
1171
1481
  if (cookieJar) {
1172
1482
  const jarCookies = await cookieJar.getCookies(options.url);
1173
1483
  if (jarCookies) {
@@ -1203,19 +1513,12 @@ class Curl {
1203
1513
  '%{stderr}===<json>==={"json":%{json},"headers":%{header_json}}===</json>===',
1204
1514
  ];
1205
1515
 
1206
- if (body) {
1207
- args.push('--data-raw', body);
1208
- }
1209
-
1210
- const cookieHeader = await this.getCookieHeader();
1211
- if (cookieHeader) {
1212
- this.header('cookie', cookieHeader);
1516
+ const cliOptions = options.cliOptions;
1517
+ if (cliOptions) {
1518
+ args.push(...cliOptions);
1213
1519
  }
1214
1520
 
1215
- for (const [key, value] of Object.entries(options.headers)) {
1216
- args.push('--header', `${key}: ${value}`);
1217
- }
1218
- if (options.compress) {
1521
+ if (options.compress && !cliOptions?.includes('--compressed')) {
1219
1522
  args.push('--compressed');
1220
1523
  }
1221
1524
  if (options.proxy && options.useProxy !== false) {
@@ -1237,9 +1540,17 @@ class Curl {
1237
1540
  args.push('--verbose');
1238
1541
  }
1239
1542
 
1240
- const cliOptions = options.cliOptions;
1241
- if (cliOptions) {
1242
- 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);
1243
1554
  }
1244
1555
 
1245
1556
  return args;
@@ -1247,18 +1558,28 @@ class Curl {
1247
1558
 
1248
1559
  async fetch() {
1249
1560
  const startTime = Date.now();
1250
- const cmd = this.options.cliCommand || 'curl';
1561
+ const options = this.options;
1562
+ const cmd = options.cliCommand || 'curl';
1251
1563
  const args = await this.getCurlArgs();
1252
1564
  const curl = spawn(cmd, args);
1253
- const cookieJar = this.options.cookieJar;
1565
+ const cookieJar = this._cookieJar;
1254
1566
 
1255
- let stdout = '';
1567
+ let stdout = options.asBuffer ? Buffer.from([]) : '';
1256
1568
  let stderr = '';
1257
- const response = new CurlResponse();
1569
+ const response = new CurlResponse({
1570
+ body: options.asBuffer ? Buffer.from([]) : '',
1571
+ });
1258
1572
  return new Promise((resolve, reject) => {
1259
- curl.stdout.on('data', (data) => {
1260
- stdout += data;
1261
- });
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
+ }
1262
1583
 
1263
1584
  curl.stderr.on('data', (data) => {
1264
1585
  stderr += data;
@@ -1272,7 +1593,7 @@ class Curl {
1272
1593
  curl.on('close', async (code) => {
1273
1594
  response.timeTaken = Date.now() - startTime;
1274
1595
  response.exitCode = code;
1275
- response.url = this.options.url;
1596
+ response.url = options.url;
1276
1597
  response.body = stdout;
1277
1598
  stderr = stderr.replace(/===<json>===(.*)===<\/json>===/s, (match, p1) => {
1278
1599
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "curl-wrap",
3
- "version": "2.0.1",
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",