firecrawl 4.37.0 → 4.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/{chunk-LFX7WDGT.js → chunk-4DPCCKWG.js} +1 -1
- package/dist/index.cjs +61 -9
- package/dist/index.d.cts +202 -4
- package/dist/index.d.ts +202 -4
- package/dist/index.js +62 -10
- package/dist/{package-SHMK2LJE.js → package-D2QZMRW7.js} +1 -1
- package/package.json +1 -1
- package/pnpm-workspace.yaml +1 -0
- package/src/__tests__/e2e/v2/batch.test.ts +13 -11
- package/src/__tests__/e2e/v2/crawl.test.ts +24 -20
- package/src/__tests__/e2e/v2/extract.test.ts +32 -7
- package/src/__tests__/e2e/v2/map.test.ts +4 -3
- package/src/__tests__/e2e/v2/parse.test.ts +4 -3
- package/src/__tests__/e2e/v2/scrape.test.ts +10 -9
- package/src/__tests__/e2e/v2/search.test.ts +9 -8
- package/src/__tests__/e2e/v2/usage.test.ts +6 -5
- package/src/__tests__/e2e/v2/utils/rateLimit.ts +257 -0
- package/src/__tests__/e2e/v2/watcher.test.ts +4 -3
- package/src/__tests__/unit/v2/agent-effort-trace.unit.test.ts +1 -1
- package/src/__tests__/unit/v2/agent-list.unit.test.ts +68 -0
- package/src/__tests__/unit/v2/agent-thread.unit.test.ts +164 -0
- package/src/__tests__/unit/v2/e2eRateLimitRetry.test.ts +501 -0
- package/src/__tests__/unit/v2/extract.unit.test.ts +1 -1
- package/src/__tests__/unit/v2/scrapeAutoResume.test.ts +1 -1
- package/src/__tests__/unit/v2/search-country.unit.test.ts +32 -0
- package/src/__tests__/unit/v2/search-data-property.test.ts +1 -0
- package/src/__tests__/unit/v2/v1-aliases.test.ts +1 -0
- package/src/v2/client.ts +26 -1
- package/src/v2/methods/agent.ts +35 -1
- package/src/v2/methods/research.ts +14 -8
- package/src/v2/methods/search.ts +1 -0
- package/src/v2/types.ts +195 -34
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import Firecrawl from "../../../index";
|
|
5
5
|
import { config } from "dotenv";
|
|
6
6
|
import { getIdentity, getApiUrl } from "./utils/idmux";
|
|
7
|
+
import { testTimeoutMs, waitForJob, withRateLimitRetry } from "./utils/rateLimit";
|
|
7
8
|
import { describe, test, expect, beforeAll } from "@jest/globals";
|
|
8
9
|
|
|
9
10
|
config();
|
|
@@ -13,7 +14,7 @@ let client: Firecrawl;
|
|
|
13
14
|
|
|
14
15
|
beforeAll(async () => {
|
|
15
16
|
const { apiKey } = await getIdentity({ name: "js-e2e-crawl" });
|
|
16
|
-
client = new Firecrawl({ apiKey, apiUrl: API_URL });
|
|
17
|
+
client = withRateLimitRetry(new Firecrawl({ apiKey, apiUrl: API_URL }));
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
describe("v2.crawl e2e", () => {
|
|
@@ -23,21 +24,21 @@ describe("v2.crawl e2e", () => {
|
|
|
23
24
|
const job = await client.startCrawl("https://docs.firecrawl.dev", { limit: 3 });
|
|
24
25
|
expect(typeof job.id).toBe("string");
|
|
25
26
|
expect(typeof job.url).toBe("string");
|
|
26
|
-
}, 90_000);
|
|
27
|
+
}, testTimeoutMs(90_000));
|
|
27
28
|
|
|
28
29
|
test("start crawl with options", async () => {
|
|
29
30
|
if (!client) throw new Error();
|
|
30
31
|
const job = await client.startCrawl("https://docs.firecrawl.dev", { limit: 5, maxDiscoveryDepth: 2 });
|
|
31
32
|
expect(typeof job.id).toBe("string");
|
|
32
33
|
expect(typeof job.url).toBe("string");
|
|
33
|
-
}, 90_000);
|
|
34
|
+
}, testTimeoutMs(90_000));
|
|
34
35
|
|
|
35
36
|
test("start crawl with prompt", async () => {
|
|
36
37
|
if (!client) throw new Error();
|
|
37
38
|
const job = await client.startCrawl("https://firecrawl.dev", { prompt: "Extract all blog posts", limit: 3 });
|
|
38
39
|
expect(typeof job.id).toBe("string");
|
|
39
40
|
expect(typeof job.url).toBe("string");
|
|
40
|
-
}, 90_000);
|
|
41
|
+
}, testTimeoutMs(90_000));
|
|
41
42
|
|
|
42
43
|
test("get crawl status", async () => {
|
|
43
44
|
if (!client) throw new Error();
|
|
@@ -50,14 +51,14 @@ describe("v2.crawl e2e", () => {
|
|
|
50
51
|
expect(status.id).toBe(start.id);
|
|
51
52
|
// next/expiresAt may be null/undefined depending on state; check shape
|
|
52
53
|
expect(Array.isArray(status.data)).toBe(true);
|
|
53
|
-
},
|
|
54
|
+
}, testTimeoutMs(151_000));
|
|
54
55
|
|
|
55
56
|
test("cancel crawl", async () => {
|
|
56
57
|
if (!client) throw new Error();
|
|
57
58
|
const start = await client.startCrawl("https://docs.firecrawl.dev", { limit: 3 });
|
|
58
59
|
const ok = await client.cancelCrawl(start.id);
|
|
59
60
|
expect(ok).toBe(true);
|
|
60
|
-
},
|
|
61
|
+
}, testTimeoutMs(151_000));
|
|
61
62
|
|
|
62
63
|
test("get crawl errors", async () => {
|
|
63
64
|
if (!client) throw new Error();
|
|
@@ -73,12 +74,12 @@ describe("v2.crawl e2e", () => {
|
|
|
73
74
|
expect(typeof e.url === "string" || e.url == null).toBe(true);
|
|
74
75
|
expect(typeof e.error === "string" || e.error == null).toBe(true);
|
|
75
76
|
}
|
|
76
|
-
},
|
|
77
|
+
}, testTimeoutMs(151_000));
|
|
77
78
|
|
|
78
79
|
test("get crawl errors with invalid id should throw", async () => {
|
|
79
80
|
if (!client) throw new Error();
|
|
80
81
|
await expect(client.getCrawlErrors("invalid-job-id-12345")).rejects.toThrow();
|
|
81
|
-
}, 60_000);
|
|
82
|
+
}, testTimeoutMs(60_000));
|
|
82
83
|
|
|
83
84
|
test("get active crawls", async () => {
|
|
84
85
|
if (!client) throw new Error();
|
|
@@ -93,7 +94,7 @@ describe("v2.crawl e2e", () => {
|
|
|
93
94
|
expect(typeof c.options === "object").toBe(true);
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
|
-
}, 90_000);
|
|
97
|
+
}, testTimeoutMs(90_000));
|
|
97
98
|
|
|
98
99
|
test("get active crawls with running crawl", async () => {
|
|
99
100
|
if (!client) throw new Error();
|
|
@@ -104,20 +105,22 @@ describe("v2.crawl e2e", () => {
|
|
|
104
105
|
const ids = active.crawls.map(c => c.id);
|
|
105
106
|
expect(ids.includes(start.id)).toBe(true);
|
|
106
107
|
await client.cancelCrawl(start.id);
|
|
107
|
-
},
|
|
108
|
+
}, testTimeoutMs(301_000));
|
|
108
109
|
|
|
109
110
|
test("crawl with wait", async () => {
|
|
110
111
|
if (!client) throw new Error();
|
|
111
|
-
const
|
|
112
|
+
const start = await client.startCrawl("https://docs.firecrawl.dev", { limit: 3, maxDiscoveryDepth: 2 });
|
|
113
|
+
const job = await waitForJob(() => client.getCrawlStatus(start.id), { pollInterval: 1, timeout: 120 });
|
|
112
114
|
expect(["completed", "failed"]).toContain(job.status);
|
|
113
115
|
expect(job.completed).toBeGreaterThanOrEqual(0);
|
|
114
116
|
expect(job.total).toBeGreaterThanOrEqual(0);
|
|
115
117
|
expect(Array.isArray(job.data)).toBe(true);
|
|
116
|
-
},
|
|
118
|
+
}, testTimeoutMs(272_000));
|
|
117
119
|
|
|
118
120
|
test("crawl with wait returns job id for error retrieval", async () => {
|
|
119
121
|
if (!client) throw new Error();
|
|
120
|
-
const
|
|
122
|
+
const start = await client.startCrawl("https://docs.firecrawl.dev", { limit: 3, maxDiscoveryDepth: 2 });
|
|
123
|
+
const job = await waitForJob(() => client.getCrawlStatus(start.id), { pollInterval: 1, timeout: 120 });
|
|
121
124
|
// Verify job has id field
|
|
122
125
|
expect(job.id).toBeDefined();
|
|
123
126
|
expect(typeof job.id).toBe("string");
|
|
@@ -127,16 +130,17 @@ describe("v2.crawl e2e", () => {
|
|
|
127
130
|
expect(errors).toHaveProperty("robotsBlocked");
|
|
128
131
|
expect(Array.isArray(errors.errors)).toBe(true);
|
|
129
132
|
expect(Array.isArray(errors.robotsBlocked)).toBe(true);
|
|
130
|
-
},
|
|
133
|
+
}, testTimeoutMs(422_000));
|
|
131
134
|
|
|
132
135
|
test("crawl with prompt and wait", async () => {
|
|
133
136
|
if (!client) throw new Error();
|
|
134
|
-
const
|
|
137
|
+
const start = await client.startCrawl("https://docs.firecrawl.dev", { prompt: "Extract all blog posts", limit: 3 });
|
|
138
|
+
const job = await waitForJob(() => client.getCrawlStatus(start.id), { pollInterval: 1, timeout: 120 });
|
|
135
139
|
expect(["completed", "failed"]).toContain(job.status);
|
|
136
140
|
expect(job.completed).toBeGreaterThanOrEqual(0);
|
|
137
141
|
expect(job.total).toBeGreaterThanOrEqual(0);
|
|
138
142
|
expect(Array.isArray(job.data)).toBe(true);
|
|
139
|
-
},
|
|
143
|
+
}, testTimeoutMs(272_000));
|
|
140
144
|
|
|
141
145
|
test("crawl with scrape options", async () => {
|
|
142
146
|
if (!client) throw new Error();
|
|
@@ -145,7 +149,7 @@ describe("v2.crawl e2e", () => {
|
|
|
145
149
|
scrapeOptions: { formats: ["markdown", "links"], onlyMainContent: false, mobile: true },
|
|
146
150
|
});
|
|
147
151
|
expect(typeof job.id).toBe("string");
|
|
148
|
-
}, 120_000);
|
|
152
|
+
}, testTimeoutMs(120_000));
|
|
149
153
|
|
|
150
154
|
test("crawl with json format object", async () => {
|
|
151
155
|
if (!client) throw new Error();
|
|
@@ -154,7 +158,7 @@ describe("v2.crawl e2e", () => {
|
|
|
154
158
|
scrapeOptions: { formats: [{ type: "json", prompt: "Extract page title", schema: { type: "object", properties: { title: { type: "string" } }, required: ["title"] } }] },
|
|
155
159
|
});
|
|
156
160
|
expect(typeof job.id).toBe("string");
|
|
157
|
-
}, 120_000);
|
|
161
|
+
}, testTimeoutMs(120_000));
|
|
158
162
|
|
|
159
163
|
test("crawl all parameters", async () => {
|
|
160
164
|
if (!client) throw new Error();
|
|
@@ -187,13 +191,13 @@ describe("v2.crawl e2e", () => {
|
|
|
187
191
|
zeroDataRetention: false,
|
|
188
192
|
});
|
|
189
193
|
expect(typeof job.id).toBe("string");
|
|
190
|
-
}, 180_000);
|
|
194
|
+
}, testTimeoutMs(180_000));
|
|
191
195
|
|
|
192
196
|
test("crawl params preview", async () => {
|
|
193
197
|
if (!client) throw new Error();
|
|
194
198
|
const params = await client.crawlParamsPreview("https://docs.firecrawl.dev", "Extract all blog posts and documentation");
|
|
195
199
|
expect(params && typeof params === "object").toBe(true);
|
|
196
200
|
// Optional fields may or may not be present; just assert object shape
|
|
197
|
-
}, 60_000);
|
|
201
|
+
}, testTimeoutMs(60_000));
|
|
198
202
|
});
|
|
199
203
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import Firecrawl from "../../../index";
|
|
5
5
|
import { config } from "dotenv";
|
|
6
6
|
import { getIdentity, getApiUrl } from "./utils/idmux";
|
|
7
|
+
import { testTimeoutMs, waitForJob, withRateLimitRetry } from "./utils/rateLimit";
|
|
7
8
|
import { describe, test, expect, beforeAll } from "@jest/globals";
|
|
8
9
|
import { z } from "zod";
|
|
9
10
|
|
|
@@ -14,14 +15,38 @@ let client: Firecrawl;
|
|
|
14
15
|
|
|
15
16
|
beforeAll(async () => {
|
|
16
17
|
const { apiKey } = await getIdentity({ name: "js-e2e-extract" });
|
|
17
|
-
client = new Firecrawl({ apiKey, apiUrl: API_URL });
|
|
18
|
+
client = withRateLimitRetry(new Firecrawl({ apiKey, apiUrl: API_URL }));
|
|
18
19
|
});
|
|
19
20
|
|
|
20
21
|
describe("v2.extract e2e", () => {
|
|
22
|
+
/**
|
|
23
|
+
* Starts an extract, then polls it. Mirrors client.extract, but keeps every
|
|
24
|
+
* request retryable: the wrapper cannot run client.extract again, because a
|
|
25
|
+
* second run would start a second job.
|
|
26
|
+
*
|
|
27
|
+
* The bound is 180s, the time an extract needs, and the same bound batch
|
|
28
|
+
* uses.
|
|
29
|
+
*
|
|
30
|
+
* The bound is not the whole cost of the test. startExtract goes through
|
|
31
|
+
* the wrapper too, so it can spend the retry budget before the poll starts:
|
|
32
|
+
* 150s for the start, plus 180 + 2 + 150 = 332s for the poll, so 482s. Each
|
|
33
|
+
* test below therefore carries a 333_000 ms base, which gives 483s. The
|
|
34
|
+
* unit test "every wrapped call in a test fits its budget" holds that sum.
|
|
35
|
+
*/
|
|
36
|
+
async function extractAndWait(
|
|
37
|
+
args: Parameters<typeof client.startExtract>[0],
|
|
38
|
+
) {
|
|
39
|
+
const started = await client.startExtract(args);
|
|
40
|
+
if (!started.id) return started;
|
|
41
|
+
return waitForJob(() => client.getExtractStatus(started.id!), {
|
|
42
|
+
timeout: 180,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
21
46
|
test("extract minimal with prompt", async () => {
|
|
22
|
-
const resp = await
|
|
47
|
+
const resp = await extractAndWait({ urls: ["https://docs.firecrawl.dev"], prompt: "Extract the main page title" });
|
|
23
48
|
expect(typeof resp.success === "boolean" || resp.success == null).toBe(true);
|
|
24
|
-
},
|
|
49
|
+
}, testTimeoutMs(333_000));
|
|
25
50
|
|
|
26
51
|
test("extract with schema", async () => {
|
|
27
52
|
const schema = {
|
|
@@ -29,7 +54,7 @@ describe("v2.extract e2e", () => {
|
|
|
29
54
|
properties: { title: { type: "string" } },
|
|
30
55
|
required: ["title"],
|
|
31
56
|
} as const;
|
|
32
|
-
const resp = await
|
|
57
|
+
const resp = await extractAndWait({
|
|
33
58
|
urls: ["https://docs.firecrawl.dev"],
|
|
34
59
|
schema,
|
|
35
60
|
prompt: "Extract the main page title",
|
|
@@ -44,13 +69,13 @@ describe("v2.extract e2e", () => {
|
|
|
44
69
|
expect(typeof resp.data).toBe("object");
|
|
45
70
|
expect((resp.data as any).title).toBeTruthy();
|
|
46
71
|
}
|
|
47
|
-
},
|
|
72
|
+
}, testTimeoutMs(333_000));
|
|
48
73
|
|
|
49
74
|
test("extract with zod schema", async () => {
|
|
50
75
|
const schema = z.object({
|
|
51
76
|
title: z.string(),
|
|
52
77
|
});
|
|
53
|
-
const resp = await
|
|
78
|
+
const resp = await extractAndWait({
|
|
54
79
|
urls: ["https://docs.firecrawl.dev"],
|
|
55
80
|
schema: schema,
|
|
56
81
|
prompt: "Extract the main page title",
|
|
@@ -65,6 +90,6 @@ describe("v2.extract e2e", () => {
|
|
|
65
90
|
expect(typeof resp.data).toBe("object");
|
|
66
91
|
expect(schema.safeParse(resp.data).success).toBe(true);
|
|
67
92
|
}
|
|
68
|
-
},
|
|
93
|
+
}, testTimeoutMs(333_000));
|
|
69
94
|
});
|
|
70
95
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import Firecrawl from "../../../index";
|
|
5
5
|
import { config } from "dotenv";
|
|
6
6
|
import { getIdentity, getApiUrl } from "./utils/idmux";
|
|
7
|
+
import { testTimeoutMs, withRateLimitRetry } from "./utils/rateLimit";
|
|
7
8
|
import { describe, test, expect, beforeAll } from "@jest/globals";
|
|
8
9
|
|
|
9
10
|
config();
|
|
@@ -13,7 +14,7 @@ let client: Firecrawl;
|
|
|
13
14
|
|
|
14
15
|
beforeAll(async () => {
|
|
15
16
|
const { apiKey } = await getIdentity({ name: "js-e2e-map" });
|
|
16
|
-
client = new Firecrawl({ apiKey, apiUrl: API_URL });
|
|
17
|
+
client = withRateLimitRetry(new Firecrawl({ apiKey, apiUrl: API_URL }));
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
describe("v2.map e2e", () => {
|
|
@@ -30,7 +31,7 @@ describe("v2.map e2e", () => {
|
|
|
30
31
|
expect(typeof first.url).toBe("string");
|
|
31
32
|
expect(first.url.startsWith("http")).toBe(true);
|
|
32
33
|
}
|
|
33
|
-
}, 90_000);
|
|
34
|
+
}, testTimeoutMs(90_000));
|
|
34
35
|
|
|
35
36
|
test.each(["only", "skip", "include"]) ("with options sitemap=%s", async (sitemap) => {
|
|
36
37
|
if (!client) throw new Error();
|
|
@@ -50,6 +51,6 @@ describe("v2.map e2e", () => {
|
|
|
50
51
|
expect(typeof link.url).toBe("string");
|
|
51
52
|
expect(link.url.startsWith("http")).toBe(true);
|
|
52
53
|
}
|
|
53
|
-
}, 120_000);
|
|
54
|
+
}, testTimeoutMs(120_000));
|
|
54
55
|
});
|
|
55
56
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Firecrawl from "../../../index";
|
|
2
2
|
import { config } from "dotenv";
|
|
3
3
|
import { getIdentity, getApiUrl } from "./utils/idmux";
|
|
4
|
+
import { testTimeoutMs, withRateLimitRetry } from "./utils/rateLimit";
|
|
4
5
|
import { describe, test, expect, beforeAll } from "@jest/globals";
|
|
5
6
|
|
|
6
7
|
config();
|
|
@@ -10,7 +11,7 @@ let client: Firecrawl;
|
|
|
10
11
|
|
|
11
12
|
beforeAll(async () => {
|
|
12
13
|
const { apiKey } = await getIdentity({ name: "js-e2e-parse" });
|
|
13
|
-
client = new Firecrawl({ apiKey, apiUrl: API_URL });
|
|
14
|
+
client = withRateLimitRetry(new Firecrawl({ apiKey, apiUrl: API_URL }));
|
|
14
15
|
});
|
|
15
16
|
|
|
16
17
|
describe("v2.parse e2e", () => {
|
|
@@ -41,7 +42,7 @@ describe("v2.parse e2e", () => {
|
|
|
41
42
|
expect(doc.markdown).toContain("JS SDK Parse E2E");
|
|
42
43
|
expect(doc.metadata?.creditsUsed).toBe(1);
|
|
43
44
|
},
|
|
44
|
-
60_000,
|
|
45
|
+
testTimeoutMs(60_000),
|
|
45
46
|
);
|
|
46
47
|
|
|
47
48
|
test(
|
|
@@ -62,6 +63,6 @@ describe("v2.parse e2e", () => {
|
|
|
62
63
|
),
|
|
63
64
|
).rejects.toThrow();
|
|
64
65
|
},
|
|
65
|
-
60_000,
|
|
66
|
+
testTimeoutMs(60_000),
|
|
66
67
|
);
|
|
67
68
|
});
|
|
@@ -5,6 +5,7 @@ import Firecrawl from "../../../index";
|
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { config } from "dotenv";
|
|
7
7
|
import { getIdentity, getApiUrl } from "./utils/idmux";
|
|
8
|
+
import { testTimeoutMs, withRateLimitRetry } from "./utils/rateLimit";
|
|
8
9
|
import { describe, test, expect, beforeAll } from "@jest/globals";
|
|
9
10
|
|
|
10
11
|
config();
|
|
@@ -14,7 +15,7 @@ let client: Firecrawl;
|
|
|
14
15
|
|
|
15
16
|
beforeAll(async () => {
|
|
16
17
|
const { apiKey } = await getIdentity({ name: "js-e2e-scrape" });
|
|
17
|
-
client = new Firecrawl({ apiKey, apiUrl: API_URL });
|
|
18
|
+
client = withRateLimitRetry(new Firecrawl({ apiKey, apiUrl: API_URL }));
|
|
18
19
|
});
|
|
19
20
|
|
|
20
21
|
describe("v2.scrape e2e", () => {
|
|
@@ -30,7 +31,7 @@ describe("v2.scrape e2e", () => {
|
|
|
30
31
|
if (!client) throw new Error();
|
|
31
32
|
const doc = await client.scrape("https://docs.firecrawl.dev");
|
|
32
33
|
assertValidDocument(doc);
|
|
33
|
-
}, 60_000);
|
|
34
|
+
}, testTimeoutMs(60_000));
|
|
34
35
|
|
|
35
36
|
test("maximal: scrape with all options", async () => {
|
|
36
37
|
if (!client) throw new Error();
|
|
@@ -71,7 +72,7 @@ describe("v2.scrape e2e", () => {
|
|
|
71
72
|
maxAge: 60_000,
|
|
72
73
|
});
|
|
73
74
|
assertValidDocument(doc);
|
|
74
|
-
}, 90_000);
|
|
75
|
+
}, testTimeoutMs(90_000));
|
|
75
76
|
|
|
76
77
|
test("json format with zod schema (auto-converted internally)", async () => {
|
|
77
78
|
if (!client) throw new Error();
|
|
@@ -90,14 +91,14 @@ describe("v2.scrape e2e", () => {
|
|
|
90
91
|
],
|
|
91
92
|
});
|
|
92
93
|
expect(doc).toBeTruthy();
|
|
93
|
-
}, 90_000);
|
|
94
|
+
}, testTimeoutMs(90_000));
|
|
94
95
|
|
|
95
96
|
test("summary format returns summary string", async () => {
|
|
96
97
|
if (!client) throw new Error();
|
|
97
98
|
const doc = await client.scrape("https://firecrawl.dev", { formats: ["summary"] });
|
|
98
99
|
expect(typeof doc.summary).toBe("string");
|
|
99
100
|
expect((doc.summary || "").length).toBeGreaterThan(10);
|
|
100
|
-
}, 90_000);
|
|
101
|
+
}, testTimeoutMs(90_000));
|
|
101
102
|
|
|
102
103
|
test.each([
|
|
103
104
|
["markdown", "markdown"],
|
|
@@ -119,7 +120,7 @@ describe("v2.scrape e2e", () => {
|
|
|
119
120
|
expect(Array.isArray(doc.links)).toBe(true);
|
|
120
121
|
expect((doc.links || []).length).toBeGreaterThan(0);
|
|
121
122
|
}
|
|
122
|
-
}, 90_000);
|
|
123
|
+
}, testTimeoutMs(90_000));
|
|
123
124
|
|
|
124
125
|
test("images format: extract all images from webpage", async () => {
|
|
125
126
|
if (!client) throw new Error();
|
|
@@ -131,7 +132,7 @@ describe("v2.scrape e2e", () => {
|
|
|
131
132
|
expect(doc.images?.length).toBeGreaterThan(0);
|
|
132
133
|
// Should find firecrawl logo/branding images
|
|
133
134
|
expect(doc.images?.some(img => img.includes("firecrawl") || img.includes("logo"))).toBe(true);
|
|
134
|
-
}, 60_000);
|
|
135
|
+
}, testTimeoutMs(60_000));
|
|
135
136
|
|
|
136
137
|
test("images format: works with multiple formats", async () => {
|
|
137
138
|
if (!client) throw new Error();
|
|
@@ -152,12 +153,12 @@ describe("v2.scrape e2e", () => {
|
|
|
152
153
|
|
|
153
154
|
// Should discover additional images beyond those with obvious extensions
|
|
154
155
|
expect(doc.images?.length).toBeGreaterThanOrEqual(linkImages.length);
|
|
155
|
-
}, 60_000);
|
|
156
|
+
}, testTimeoutMs(60_000));
|
|
156
157
|
|
|
157
158
|
test("invalid url should throw", async () => {
|
|
158
159
|
if (!client) throw new Error();
|
|
159
160
|
await expect(client.scrape("")).rejects.toThrow("URL cannot be empty");
|
|
160
161
|
await expect(client.scrape(" ")).rejects.toThrow("URL cannot be empty");
|
|
161
|
-
});
|
|
162
|
+
}, testTimeoutMs(151_000));
|
|
162
163
|
});
|
|
163
164
|
|
|
@@ -5,6 +5,7 @@ import Firecrawl from "../../../index";
|
|
|
5
5
|
import type { Document, SearchResultWeb, SearchResultNews, SearchResultImages } from "../../../index";
|
|
6
6
|
import { config } from "dotenv";
|
|
7
7
|
import { getIdentity, getApiUrl } from "./utils/idmux";
|
|
8
|
+
import { testTimeoutMs, withRateLimitRetry } from "./utils/rateLimit";
|
|
8
9
|
import { describe, test, expect, beforeAll } from "@jest/globals";
|
|
9
10
|
|
|
10
11
|
config();
|
|
@@ -14,7 +15,7 @@ let client: Firecrawl;
|
|
|
14
15
|
|
|
15
16
|
beforeAll(async () => {
|
|
16
17
|
const { apiKey } = await getIdentity({ name: "js-e2e-search" });
|
|
17
|
-
client = new Firecrawl({ apiKey, apiUrl: API_URL });
|
|
18
|
+
client = withRateLimitRetry(new Firecrawl({ apiKey, apiUrl: API_URL }));
|
|
18
19
|
});
|
|
19
20
|
|
|
20
21
|
function collectTexts(entries: any[] | undefined): string[] {
|
|
@@ -72,7 +73,7 @@ describe("v2.search e2e", () => {
|
|
|
72
73
|
|
|
73
74
|
expect(results.news == null).toBe(true);
|
|
74
75
|
expect(results.images == null).toBe(true);
|
|
75
|
-
}, 90_000);
|
|
76
|
+
}, testTimeoutMs(90_000));
|
|
76
77
|
|
|
77
78
|
test("with sources web+news and limit", async () => {
|
|
78
79
|
if (!client) throw new Error();
|
|
@@ -93,7 +94,7 @@ describe("v2.search e2e", () => {
|
|
|
93
94
|
.map(r => (r.description || "").toString().toLowerCase());
|
|
94
95
|
const allWebText = (webTitles.concat(webDescriptions)).join(" ");
|
|
95
96
|
expect(allWebText.includes("firecrawl")).toBe(true);
|
|
96
|
-
}, 90_000);
|
|
97
|
+
}, testTimeoutMs(90_000));
|
|
97
98
|
|
|
98
99
|
test("result structure", async () => {
|
|
99
100
|
if (!client) throw new Error();
|
|
@@ -108,7 +109,7 @@ describe("v2.search e2e", () => {
|
|
|
108
109
|
expect(typeof result.description === "string" || result.description == null).toBe(true);
|
|
109
110
|
expect(result.url.startsWith("http")).toBe(true);
|
|
110
111
|
}
|
|
111
|
-
}, 90_000);
|
|
112
|
+
}, testTimeoutMs(90_000));
|
|
112
113
|
|
|
113
114
|
test("all parameters (comprehensive)", async () => {
|
|
114
115
|
if (!client) throw new Error();
|
|
@@ -196,7 +197,7 @@ describe("v2.search e2e", () => {
|
|
|
196
197
|
expect(result.url?.startsWith("http")).toBe(true);
|
|
197
198
|
}
|
|
198
199
|
}
|
|
199
|
-
}, 120_000);
|
|
200
|
+
}, testTimeoutMs(120_000));
|
|
200
201
|
|
|
201
202
|
test("formats flexibility: list vs object", async () => {
|
|
202
203
|
if (!client) throw new Error();
|
|
@@ -212,7 +213,7 @@ describe("v2.search e2e", () => {
|
|
|
212
213
|
expect(results2).toBeTruthy();
|
|
213
214
|
expect(results1.web).toBeTruthy();
|
|
214
215
|
expect(results2.web).toBeTruthy();
|
|
215
|
-
},
|
|
216
|
+
}, testTimeoutMs(151_000));
|
|
216
217
|
|
|
217
218
|
test("with json format object", async () => {
|
|
218
219
|
if (!client) throw new Error();
|
|
@@ -229,7 +230,7 @@ describe("v2.search e2e", () => {
|
|
|
229
230
|
});
|
|
230
231
|
expect(results).toBeTruthy();
|
|
231
232
|
expect(Array.isArray(results.web) || results.web == null).toBe(true);
|
|
232
|
-
}, 90_000);
|
|
233
|
+
}, testTimeoutMs(90_000));
|
|
233
234
|
|
|
234
235
|
test("with summary format, documents include summary when present", async () => {
|
|
235
236
|
if (!client) throw new Error();
|
|
@@ -242,6 +243,6 @@ describe("v2.search e2e", () => {
|
|
|
242
243
|
expect(typeof docs[0].summary).toBe("string");
|
|
243
244
|
expect((docs[0].summary || "").length).toBeGreaterThan(5);
|
|
244
245
|
}
|
|
245
|
-
}, 90_000);
|
|
246
|
+
}, testTimeoutMs(90_000));
|
|
246
247
|
});
|
|
247
248
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import Firecrawl from "../../../index";
|
|
5
5
|
import { config } from "dotenv";
|
|
6
6
|
import { getIdentity, getApiUrl } from "./utils/idmux";
|
|
7
|
+
import { testTimeoutMs, withRateLimitRetry } from "./utils/rateLimit";
|
|
7
8
|
import { describe, test, expect, beforeAll } from "@jest/globals";
|
|
8
9
|
|
|
9
10
|
config();
|
|
@@ -13,7 +14,7 @@ let client: Firecrawl;
|
|
|
13
14
|
|
|
14
15
|
beforeAll(async () => {
|
|
15
16
|
const { apiKey } = await getIdentity({ name: "js-e2e-usage" });
|
|
16
|
-
client = new Firecrawl({ apiKey, apiUrl: API_URL });
|
|
17
|
+
client = withRateLimitRetry(new Firecrawl({ apiKey, apiUrl: API_URL }));
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
describe("v2.usage e2e", () => {
|
|
@@ -21,17 +22,17 @@ describe("v2.usage e2e", () => {
|
|
|
21
22
|
const resp = await client.getConcurrency();
|
|
22
23
|
expect(typeof resp.concurrency).toBe("number");
|
|
23
24
|
expect(typeof resp.maxConcurrency).toBe("number");
|
|
24
|
-
}, 60_000);
|
|
25
|
+
}, testTimeoutMs(60_000));
|
|
25
26
|
|
|
26
27
|
test("get_credit_usage", async () => {
|
|
27
28
|
const resp = await client.getCreditUsage();
|
|
28
29
|
expect(typeof resp.remainingCredits).toBe("number");
|
|
29
|
-
}, 60_000);
|
|
30
|
+
}, testTimeoutMs(60_000));
|
|
30
31
|
|
|
31
32
|
test("get_token_usage", async () => {
|
|
32
33
|
const resp = await client.getTokenUsage();
|
|
33
34
|
expect(typeof resp.remainingTokens).toBe("number");
|
|
34
|
-
}, 60_000);
|
|
35
|
+
}, testTimeoutMs(60_000));
|
|
35
36
|
|
|
36
37
|
test("get_queue_status", async () => {
|
|
37
38
|
const resp = await client.getQueueStatus();
|
|
@@ -39,6 +40,6 @@ describe("v2.usage e2e", () => {
|
|
|
39
40
|
expect(typeof resp.activeJobsInQueue).toBe("number");
|
|
40
41
|
expect(typeof resp.waitingJobsInQueue).toBe("number");
|
|
41
42
|
expect(typeof resp.maxConcurrency).toBe("number");
|
|
42
|
-
}, 60_000);
|
|
43
|
+
}, testTimeoutMs(60_000));
|
|
43
44
|
});
|
|
44
45
|
|