@mendable/firecrawl-js 4.19.0 → 4.20.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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  require_package
3
- } from "./chunk-JJY4NJXL.js";
3
+ } from "./chunk-UXVHRV2G.js";
4
4
 
5
5
  // src/v2/utils/httpClient.ts
6
6
  import axios from "axios";
@@ -58,10 +58,10 @@ var HttpClient = class {
58
58
  const isPlainObjectBody = !isFormDataBody && cfg.data != null && typeof cfg.data === "object" && !Array.isArray(cfg.data);
59
59
  if (isPlainObjectBody && cfg.method && ["post", "put", "patch"].includes(cfg.method.toLowerCase())) {
60
60
  const data = cfg.data ?? {};
61
- cfg.data = { ...data, origin: typeof data.origin === "string" && data.origin.includes("mcp") ? data.origin : `js-sdk@${version}` };
62
- if (typeof data.timeout === "number") {
63
- cfg.timeout = data.timeout + 5e3;
64
- }
61
+ cfg.data = {
62
+ ...data,
63
+ origin: typeof data.origin === "string" && data.origin.includes("mcp") ? data.origin : `js-sdk@${version}`
64
+ };
65
65
  }
66
66
  if (isFormDataBody) {
67
67
  cfg.headers = { ...cfg.headers || {} };
@@ -89,16 +89,22 @@ var HttpClient = class {
89
89
  sleep(seconds) {
90
90
  return new Promise((r) => setTimeout(r, seconds * 1e3));
91
91
  }
92
- post(endpoint, body, headers) {
93
- return this.request({ method: "post", url: endpoint, data: body, headers });
92
+ post(endpoint, body, options) {
93
+ return this.request({
94
+ method: "post",
95
+ url: endpoint,
96
+ data: body,
97
+ headers: options?.headers,
98
+ timeout: options?.timeoutMs
99
+ });
94
100
  }
95
- postMultipart(endpoint, formData, headers, timeoutMs) {
101
+ postMultipart(endpoint, formData, options) {
96
102
  return this.request({
97
103
  method: "post",
98
104
  url: endpoint,
99
105
  data: formData,
100
- headers,
101
- timeout: timeoutMs
106
+ headers: options?.headers,
107
+ timeout: options?.timeoutMs
102
108
  });
103
109
  }
104
110
  get(endpoint, headers) {
@@ -364,7 +370,11 @@ async function scrape(http, url, options) {
364
370
  const payload = { url: url.trim() };
365
371
  if (options) Object.assign(payload, options);
366
372
  try {
367
- const res = await http.post("/v2/scrape", payload);
373
+ const res = await http.post(
374
+ "/v2/scrape",
375
+ payload,
376
+ typeof options?.timeout === "number" ? { timeoutMs: options.timeout + 5e3 } : {}
377
+ );
368
378
  if (res.status !== 200 || !res.data?.success) {
369
379
  throwForBadResponse(res, "scrape");
370
380
  }
@@ -392,12 +402,15 @@ async function interact(http, jobId, args) {
392
402
  try {
393
403
  const res = await http.post(
394
404
  `/v2/scrape/${jobId}/interact`,
395
- body
405
+ body,
406
+ args.timeout != null ? { timeoutMs: args.timeout * 1e3 + 5e3 } : {}
396
407
  );
397
- if (res.status !== 200) throwForBadResponse(res, "interact with scrape browser");
408
+ if (res.status !== 200)
409
+ throwForBadResponse(res, "interact with scrape browser");
398
410
  return res.data;
399
411
  } catch (err) {
400
- if (err?.isAxiosError) return normalizeAxiosError(err, "interact with scrape browser");
412
+ if (err?.isAxiosError)
413
+ return normalizeAxiosError(err, "interact with scrape browser");
401
414
  throw err;
402
415
  }
403
416
  }
@@ -435,7 +448,9 @@ function toUploadBlob(input, contentType) {
435
448
  return new Blob([input], { type: contentType });
436
449
  }
437
450
  if (typeof input === "string") {
438
- return new Blob([input], { type: contentType ?? "text/plain; charset=utf-8" });
451
+ return new Blob([input], {
452
+ type: contentType ?? "text/plain; charset=utf-8"
453
+ });
439
454
  }
440
455
  throw new Error("Unsupported parse file data type");
441
456
  }
@@ -463,9 +478,12 @@ async function parse(http, file, options) {
463
478
  toUploadBlob(file.data, file.contentType),
464
479
  file.filename.trim()
465
480
  );
466
- const requestTimeoutMs = typeof normalizedOptions.timeout === "number" ? normalizedOptions.timeout + 5e3 : void 0;
467
481
  try {
468
- const res = await http.postMultipart("/v2/parse", formData, void 0, requestTimeoutMs);
482
+ const res = await http.postMultipart(
483
+ "/v2/parse",
484
+ formData,
485
+ typeof normalizedOptions.timeout === "number" ? { timeoutMs: normalizedOptions.timeout + 5e3 } : {}
486
+ );
469
487
  if (res.status !== 200 || !res.data?.success) {
470
488
  throwForBadResponse(res, "parse");
471
489
  }
@@ -479,8 +497,10 @@ async function parse(http, file, options) {
479
497
  // src/v2/methods/search.ts
480
498
  function prepareSearchPayload(req) {
481
499
  if (!req.query || !req.query.trim()) throw new Error("Query cannot be empty");
482
- if (req.limit != null && req.limit <= 0) throw new Error("limit must be positive");
483
- if (req.timeout != null && req.timeout <= 0) throw new Error("timeout must be positive");
500
+ if (req.limit != null && req.limit <= 0)
501
+ throw new Error("limit must be positive");
502
+ if (req.timeout != null && req.timeout <= 0)
503
+ throw new Error("timeout must be positive");
484
504
  const payload = {
485
505
  query: req.query
486
506
  };
@@ -489,9 +509,11 @@ function prepareSearchPayload(req) {
489
509
  if (req.limit != null) payload.limit = req.limit;
490
510
  if (req.tbs != null) payload.tbs = req.tbs;
491
511
  if (req.location != null) payload.location = req.location;
492
- if (req.ignoreInvalidURLs != null) payload.ignoreInvalidURLs = req.ignoreInvalidURLs;
512
+ if (req.ignoreInvalidURLs != null)
513
+ payload.ignoreInvalidURLs = req.ignoreInvalidURLs;
493
514
  if (req.timeout != null) payload.timeout = req.timeout;
494
- if (req.integration && req.integration.trim()) payload.integration = req.integration.trim();
515
+ if (req.integration && req.integration.trim())
516
+ payload.integration = req.integration.trim();
495
517
  if (req.origin) payload.origin = req.origin;
496
518
  if (req.scrapeOptions) {
497
519
  ensureValidScrapeOptions(req.scrapeOptions);
@@ -517,7 +539,11 @@ function transformArray(arr) {
517
539
  async function search(http, request) {
518
540
  const payload = prepareSearchPayload(request);
519
541
  try {
520
- const res = await http.post("/v2/search", payload);
542
+ const res = await http.post(
543
+ "/v2/search",
544
+ payload,
545
+ typeof request.timeout === "number" ? { timeoutMs: request.timeout + 5e3 } : {}
546
+ );
521
547
  if (res.status !== 200 || !res.data?.success) {
522
548
  throwForBadResponse(res, "search");
523
549
  }
@@ -525,7 +551,8 @@ async function search(http, request) {
525
551
  const out = {};
526
552
  if (data.web) out.web = transformArray(data.web);
527
553
  if (data.news) out.news = transformArray(data.news);
528
- if (data.images) out.images = transformArray(data.images);
554
+ if (data.images)
555
+ out.images = transformArray(data.images);
529
556
  return out;
530
557
  } catch (err) {
531
558
  if (err?.isAxiosError) return normalizeAxiosError(err, "search");
@@ -540,11 +567,14 @@ function prepareMapPayload(url, options) {
540
567
  if (options) {
541
568
  if (options.sitemap != null) payload.sitemap = options.sitemap;
542
569
  if (options.search != null) payload.search = options.search;
543
- if (options.includeSubdomains != null) payload.includeSubdomains = options.includeSubdomains;
544
- if (options.ignoreQueryParameters != null) payload.ignoreQueryParameters = options.ignoreQueryParameters;
570
+ if (options.includeSubdomains != null)
571
+ payload.includeSubdomains = options.includeSubdomains;
572
+ if (options.ignoreQueryParameters != null)
573
+ payload.ignoreQueryParameters = options.ignoreQueryParameters;
545
574
  if (options.limit != null) payload.limit = options.limit;
546
575
  if (options.timeout != null) payload.timeout = options.timeout;
547
- if (options.integration != null && options.integration.trim()) payload.integration = options.integration.trim();
576
+ if (options.integration != null && options.integration.trim())
577
+ payload.integration = options.integration.trim();
548
578
  if (options.origin) payload.origin = options.origin;
549
579
  if (options.location != null) payload.location = options.location;
550
580
  }
@@ -553,7 +583,11 @@ function prepareMapPayload(url, options) {
553
583
  async function map(http, url, options) {
554
584
  const payload = prepareMapPayload(url, options);
555
585
  try {
556
- const res = await http.post("/v2/map", payload);
586
+ const res = await http.post(
587
+ "/v2/map",
588
+ payload,
589
+ typeof options?.timeout === "number" ? { timeoutMs: options.timeout + 5e3 } : {}
590
+ );
557
591
  if (res.status !== 200 || !res.data?.success) {
558
592
  throwForBadResponse(res, "map");
559
593
  }
@@ -561,7 +595,12 @@ async function map(http, url, options) {
561
595
  const links = [];
562
596
  for (const item of linksIn) {
563
597
  if (typeof item === "string") links.push({ url: item });
564
- else if (item && typeof item === "object") links.push({ url: item.url, title: item.title, description: item.description });
598
+ else if (item && typeof item === "object")
599
+ links.push({
600
+ url: item.url,
601
+ title: item.title,
602
+ description: item.description
603
+ });
565
604
  }
566
605
  return { links };
567
606
  } catch (err) {
@@ -773,7 +812,8 @@ async function startBatchScrape(http, urls, {
773
812
  integration,
774
813
  origin
775
814
  } = {}) {
776
- if (!Array.isArray(urls) || urls.length === 0) throw new Error("URLs list cannot be empty");
815
+ if (!Array.isArray(urls) || urls.length === 0)
816
+ throw new Error("URLs list cannot be empty");
777
817
  const payload = { urls };
778
818
  if (options) {
779
819
  ensureValidScrapeOptions(options);
@@ -784,22 +824,30 @@ async function startBatchScrape(http, urls, {
784
824
  if (ignoreInvalidURLs != null) payload.ignoreInvalidURLs = ignoreInvalidURLs;
785
825
  if (maxConcurrency != null) payload.maxConcurrency = maxConcurrency;
786
826
  if (zeroDataRetention != null) payload.zeroDataRetention = zeroDataRetention;
787
- if (integration != null && integration.trim()) payload.integration = integration.trim();
827
+ if (integration != null && integration.trim())
828
+ payload.integration = integration.trim();
788
829
  if (origin) payload.origin = origin;
789
830
  try {
790
831
  const headers = http.prepareHeaders(idempotencyKey);
791
- const res = await http.post("/v2/batch/scrape", payload, headers);
792
- if (res.status !== 200 || !res.data?.success) throwForBadResponse(res, "start batch scrape");
793
- return { id: res.data.id, url: res.data.url, invalidURLs: res.data.invalidURLs || void 0 };
832
+ const res = await http.post("/v2/batch/scrape", payload, { headers });
833
+ if (res.status !== 200 || !res.data?.success)
834
+ throwForBadResponse(res, "start batch scrape");
835
+ return {
836
+ id: res.data.id,
837
+ url: res.data.url,
838
+ invalidURLs: res.data.invalidURLs || void 0
839
+ };
794
840
  } catch (err) {
795
- if (err?.isAxiosError) return normalizeAxiosError(err, "start batch scrape");
841
+ if (err?.isAxiosError)
842
+ return normalizeAxiosError(err, "start batch scrape");
796
843
  throw err;
797
844
  }
798
845
  }
799
846
  async function getBatchScrapeStatus(http, jobId, pagination) {
800
847
  try {
801
848
  const res = await http.get(`/v2/batch/scrape/${jobId}`);
802
- if (res.status !== 200 || !res.data?.success) throwForBadResponse(res, "get batch scrape status");
849
+ if (res.status !== 200 || !res.data?.success)
850
+ throwForBadResponse(res, "get batch scrape status");
803
851
  const body = res.data;
804
852
  const initialDocs = body.data || [];
805
853
  const auto = pagination?.autoPaginate ?? true;
@@ -815,7 +863,12 @@ async function getBatchScrapeStatus(http, jobId, pagination) {
815
863
  data: initialDocs
816
864
  };
817
865
  }
818
- const aggregated = await fetchAllPages(http, body.next, initialDocs, pagination);
866
+ const aggregated = await fetchAllPages(
867
+ http,
868
+ body.next,
869
+ initialDocs,
870
+ pagination
871
+ );
819
872
  return {
820
873
  id: jobId,
821
874
  status: body.status,
@@ -827,17 +880,21 @@ async function getBatchScrapeStatus(http, jobId, pagination) {
827
880
  data: aggregated
828
881
  };
829
882
  } catch (err) {
830
- if (err?.isAxiosError) return normalizeAxiosError(err, "get batch scrape status");
883
+ if (err?.isAxiosError)
884
+ return normalizeAxiosError(err, "get batch scrape status");
831
885
  throw err;
832
886
  }
833
887
  }
834
888
  async function cancelBatchScrape(http, jobId) {
835
889
  try {
836
- const res = await http.delete(`/v2/batch/scrape/${jobId}`);
890
+ const res = await http.delete(
891
+ `/v2/batch/scrape/${jobId}`
892
+ );
837
893
  if (res.status !== 200) throwForBadResponse(res, "cancel batch scrape");
838
894
  return res.data?.status === "cancelled";
839
895
  } catch (err) {
840
- if (err?.isAxiosError) return normalizeAxiosError(err, "cancel batch scrape");
896
+ if (err?.isAxiosError)
897
+ return normalizeAxiosError(err, "cancel batch scrape");
841
898
  throw err;
842
899
  }
843
900
  }
@@ -846,9 +903,13 @@ async function getBatchScrapeErrors(http, jobId) {
846
903
  const res = await http.get(`/v2/batch/scrape/${jobId}/errors`);
847
904
  if (res.status !== 200) throwForBadResponse(res, "get batch scrape errors");
848
905
  const payload = res.data?.data ?? res.data;
849
- return { errors: payload.errors || [], robotsBlocked: payload.robotsBlocked || [] };
906
+ return {
907
+ errors: payload.errors || [],
908
+ robotsBlocked: payload.robotsBlocked || []
909
+ };
850
910
  } catch (err) {
851
- if (err?.isAxiosError) return normalizeAxiosError(err, "get batch scrape errors");
911
+ if (err?.isAxiosError)
912
+ return normalizeAxiosError(err, "get batch scrape errors");
852
913
  throw err;
853
914
  }
854
915
  }
@@ -883,7 +944,12 @@ async function waitForBatchCompletion(http, jobId, pollInterval = 2, timeout) {
883
944
  }
884
945
  async function batchScrape(http, urls, opts = {}) {
885
946
  const start = await startBatchScrape(http, urls, opts);
886
- return waitForBatchCompletion(http, start.id, opts.pollInterval ?? 2, opts.timeout);
947
+ return waitForBatchCompletion(
948
+ http,
949
+ start.id,
950
+ opts.pollInterval ?? 2,
951
+ opts.timeout
952
+ );
887
953
  }
888
954
 
889
955
  // src/v2/methods/extract.ts
@@ -1022,7 +1088,8 @@ async function browser(http, args = {}) {
1022
1088
  if (res.status !== 200) throwForBadResponse(res, "create browser session");
1023
1089
  return res.data;
1024
1090
  } catch (err) {
1025
- if (err?.isAxiosError) return normalizeAxiosError(err, "create browser session");
1091
+ if (err?.isAxiosError)
1092
+ return normalizeAxiosError(err, "create browser session");
1026
1093
  throw err;
1027
1094
  }
1028
1095
  }
@@ -1035,12 +1102,14 @@ async function browserExecute(http, sessionId, args) {
1035
1102
  try {
1036
1103
  const res = await http.post(
1037
1104
  `/v2/browser/${sessionId}/execute`,
1038
- body
1105
+ body,
1106
+ args.timeout != null ? { timeoutMs: args.timeout * 1e3 + 5e3 } : {}
1039
1107
  );
1040
1108
  if (res.status !== 200) throwForBadResponse(res, "execute browser code");
1041
1109
  return res.data;
1042
1110
  } catch (err) {
1043
- if (err?.isAxiosError) return normalizeAxiosError(err, "execute browser code");
1111
+ if (err?.isAxiosError)
1112
+ return normalizeAxiosError(err, "execute browser code");
1044
1113
  throw err;
1045
1114
  }
1046
1115
  }
@@ -1052,7 +1121,8 @@ async function deleteBrowser(http, sessionId) {
1052
1121
  if (res.status !== 200) throwForBadResponse(res, "delete browser session");
1053
1122
  return res.data;
1054
1123
  } catch (err) {
1055
- if (err?.isAxiosError) return normalizeAxiosError(err, "delete browser session");
1124
+ if (err?.isAxiosError)
1125
+ return normalizeAxiosError(err, "delete browser session");
1056
1126
  throw err;
1057
1127
  }
1058
1128
  }
@@ -1064,7 +1134,8 @@ async function listBrowsers(http, args = {}) {
1064
1134
  if (res.status !== 200) throwForBadResponse(res, "list browser sessions");
1065
1135
  return res.data;
1066
1136
  } catch (err) {
1067
- if (err?.isAxiosError) return normalizeAxiosError(err, "list browser sessions");
1137
+ if (err?.isAxiosError)
1138
+ return normalizeAxiosError(err, "list browser sessions");
1068
1139
  throw err;
1069
1140
  }
1070
1141
  }
@@ -1707,7 +1778,7 @@ var FirecrawlApp = class {
1707
1778
  if (typeof process !== "undefined" && process.env && process.env.npm_package_version) {
1708
1779
  return process.env.npm_package_version;
1709
1780
  }
1710
- const packageJson = await import("./package-HMEPZJ3J.js");
1781
+ const packageJson = await import("./package-ELM7Z5VP.js");
1711
1782
  return packageJson.default.version;
1712
1783
  } catch (error) {
1713
1784
  const isTest = typeof process !== "undefined" && (process.env.JEST_WORKER_ID != null || false);
@@ -1,4 +1,4 @@
1
1
  import {
2
2
  require_package
3
- } from "./chunk-JJY4NJXL.js";
3
+ } from "./chunk-UXVHRV2G.js";
4
4
  export default require_package();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mendable/firecrawl-js",
3
- "version": "4.19.0",
3
+ "version": "4.20.0",
4
4
  "description": "JavaScript SDK for Firecrawl API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,12 +14,15 @@ describe("JS SDK v2 scrape-browser methods", () => {
14
14
  }));
15
15
 
16
16
  const http = { post } as any;
17
- const response = await interact(http, "job-123", { code: "console.log('ok')" });
18
-
19
- expect(post).toHaveBeenCalledWith("/v2/scrape/job-123/interact", {
17
+ const response = await interact(http, "job-123", {
20
18
  code: "console.log('ok')",
21
- language: "node",
22
19
  });
20
+
21
+ expect(post).toHaveBeenCalledWith(
22
+ "/v2/scrape/job-123/interact",
23
+ { code: "console.log('ok')", language: "node" },
24
+ {},
25
+ );
23
26
  expect(response.success).toBe(true);
24
27
  expect(response.exitCode).toBe(0);
25
28
  });
@@ -38,22 +41,27 @@ describe("JS SDK v2 scrape-browser methods", () => {
38
41
  }));
39
42
 
40
43
  const http = { post } as any;
41
- const response = await interact(http, "job-456", { prompt: "Click the login button" });
42
-
43
- expect(post).toHaveBeenCalledWith("/v2/scrape/job-456/interact", {
44
+ const response = await interact(http, "job-456", {
44
45
  prompt: "Click the login button",
45
- language: "node",
46
46
  });
47
+
48
+ expect(post).toHaveBeenCalledWith(
49
+ "/v2/scrape/job-456/interact",
50
+ { prompt: "Click the login button", language: "node" },
51
+ {},
52
+ );
47
53
  expect(response.success).toBe(true);
48
54
  expect(response.output).toBe("Clicked the button");
49
55
  expect(response.liveViewUrl).toBe("https://live.example.com/view");
50
- expect(response.interactiveLiveViewUrl).toBe("https://live.example.com/interactive");
56
+ expect(response.interactiveLiveViewUrl).toBe(
57
+ "https://live.example.com/interactive",
58
+ );
51
59
  });
52
60
 
53
61
  test("interact throws when neither code nor prompt provided", async () => {
54
62
  const http = { post: jest.fn() } as any;
55
63
  await expect(interact(http, "job-123", {})).rejects.toThrow(
56
- "Either 'code' or 'prompt' must be provided"
64
+ "Either 'code' or 'prompt' must be provided",
57
65
  );
58
66
  });
59
67
 
@@ -68,7 +76,7 @@ describe("JS SDK v2 scrape-browser methods", () => {
68
76
 
69
77
  const http = { post } as any;
70
78
  await expect(
71
- interact(http, "bad-id", { code: "console.log('ok')" })
79
+ interact(http, "bad-id", { code: "console.log('ok')" }),
72
80
  ).rejects.toBeInstanceOf(SdkError);
73
81
  });
74
82
 
@@ -98,7 +106,26 @@ describe("JS SDK v2 scrape-browser methods", () => {
98
106
 
99
107
  const http = { delete: del } as any;
100
108
  await expect(stopInteraction(http, "job-123")).rejects.toBeInstanceOf(
101
- SdkError
109
+ SdkError,
110
+ );
111
+ });
112
+
113
+ test("interact converts seconds-based body timeout to ms axios timeout", async () => {
114
+ const post = jest.fn(async () => ({
115
+ status: 200,
116
+ data: { success: true, stdout: "ok", exitCode: 0 },
117
+ }));
118
+
119
+ const http = { post } as any;
120
+ await interact(http, "job-123", {
121
+ code: "console.log('ok')",
122
+ timeout: 150,
123
+ });
124
+
125
+ expect(post).toHaveBeenCalledWith(
126
+ "/v2/scrape/job-123/interact",
127
+ { code: "console.log('ok')", language: "node", timeout: 150 },
128
+ { timeoutMs: 155000 },
102
129
  );
103
130
  });
104
131
  });