firecrawl 4.44.1 → 4.46.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/audit-ci.jsonc +1 -15
- package/dist/{chunk-JU6NENYM.js → chunk-R4XT7PME.js} +5 -9
- package/dist/index.cjs +77 -21
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +74 -14
- package/dist/{package-FEZU4ANT.js → package-EI4Q33HD.js} +1 -1
- package/package.json +5 -9
- package/pnpm-workspace.yaml +0 -6
- package/src/__tests__/e2e/v1/index.test.ts +1 -1
- package/src/__tests__/e2e/v2/batch.test.ts +1 -1
- package/src/__tests__/e2e/v2/crawl.test.ts +1 -1
- package/src/__tests__/e2e/v2/extract.test.ts +1 -1
- package/src/__tests__/e2e/v2/map.test.ts +1 -1
- package/src/__tests__/e2e/v2/parse.test.ts +1 -1
- package/src/__tests__/e2e/v2/scrape.test.ts +1 -1
- package/src/__tests__/e2e/v2/search.test.ts +1 -1
- package/src/__tests__/e2e/v2/usage.test.ts +1 -1
- package/src/__tests__/e2e/v2/utils/rateLimit.ts +2 -2
- package/src/__tests__/e2e/v2/watcher.test.ts +1 -1
- package/src/__tests__/unit/v1/batch-scrape-poll.test.ts +7 -7
- package/src/__tests__/unit/v1/monitor-job-status-retry.test.ts +6 -6
- package/src/__tests__/unit/v1/next-url-pinning.test.ts +3 -3
- package/src/__tests__/unit/v1/optional-params.test.ts +4 -4
- package/src/__tests__/unit/v2/agent-effort-trace.unit.test.ts +6 -6
- package/src/__tests__/unit/v2/agent-hints.test.ts +3 -3
- package/src/__tests__/unit/v2/agent-list.unit.test.ts +4 -4
- package/src/__tests__/unit/v2/agent-thread.unit.test.ts +7 -7
- package/src/__tests__/unit/v2/agent.test.ts +1 -1
- package/src/__tests__/unit/v2/audit-metadata.unit.test.ts +3 -3
- package/src/__tests__/unit/v2/branding.test.ts +2 -2
- package/src/__tests__/unit/v2/clientOptions.test.ts +1 -0
- package/src/__tests__/unit/v2/crawl-cancel-abort.unit.test.ts +189 -0
- package/src/__tests__/unit/v2/developer.test.ts +4 -4
- package/src/__tests__/unit/v2/e2eRateLimitRetry.test.ts +16 -16
- package/src/__tests__/unit/v2/errorHandler.test.ts +1 -1
- package/src/__tests__/unit/v2/extract.unit.test.ts +4 -4
- package/src/__tests__/unit/v2/gov.test.ts +4 -4
- package/src/__tests__/unit/v2/menu.test.ts +2 -2
- package/src/__tests__/unit/v2/monitor-search-target.test.ts +1 -0
- package/src/__tests__/unit/v2/next-url-pinning.test.ts +1 -1
- package/src/__tests__/unit/v2/pagination.test.ts +6 -6
- package/src/__tests__/unit/v2/parse-formats.unit.test.ts +1 -1
- package/src/__tests__/unit/v2/parse.unit.test.ts +1 -1
- package/src/__tests__/unit/v2/product.test.ts +2 -2
- package/src/__tests__/unit/v2/prototype-discoverability.test.ts +1 -0
- package/src/__tests__/unit/v2/research.test.ts +1 -1
- package/src/__tests__/unit/v2/scrape-browser.unit.test.ts +8 -8
- package/src/__tests__/unit/v2/scrape.unit.test.ts +1 -1
- package/src/__tests__/unit/v2/scrapeAutoResume.test.ts +13 -13
- package/src/__tests__/unit/v2/search-country.unit.test.ts +2 -2
- package/src/__tests__/unit/v2/search-data-property.test.ts +3 -3
- package/src/__tests__/unit/v2/search-developer.unit.test.ts +2 -2
- package/src/__tests__/unit/v2/search-highlights.unit.test.ts +2 -2
- package/src/__tests__/unit/v2/threat-protection.unit.test.ts +3 -3
- package/src/__tests__/unit/v2/tools.unit.test.ts +3 -3
- package/src/__tests__/unit/v2/v1-aliases.test.ts +2 -2
- package/src/__tests__/unit/v2/validation.test.ts +1 -1
- package/src/__tests__/unit/v2/zodSchemaToJson.test.ts +1 -1
- package/src/v1/index.ts +1 -3
- package/src/v2/client.ts +8 -5
- package/src/v2/methods/crawl.ts +90 -6
- package/src/v2/types.ts +8 -0
- package/vitest.config.ts +7 -0
- package/jest.config.js +0 -16
package/src/v2/client.ts
CHANGED
|
@@ -394,7 +394,7 @@ export class FirecrawlClient {
|
|
|
394
394
|
/**
|
|
395
395
|
* Cancel a crawl job.
|
|
396
396
|
* @param jobId Crawl job id.
|
|
397
|
-
* @returns True if cancelled.
|
|
397
|
+
* @returns True if cancelled. False if the crawl was already completed (the API answers 409).
|
|
398
398
|
*/
|
|
399
399
|
async cancelCrawl(jobId: string): Promise<boolean> {
|
|
400
400
|
return cancelCrawl(this.http, jobId);
|
|
@@ -402,11 +402,14 @@ export class FirecrawlClient {
|
|
|
402
402
|
/**
|
|
403
403
|
* Convenience waiter: start a crawl and poll until it finishes.
|
|
404
404
|
* @param url Root URL to crawl.
|
|
405
|
-
* @param req Crawl configuration plus waiter controls (pollInterval, timeout seconds).
|
|
405
|
+
* @param req Crawl configuration plus waiter controls (pollInterval, timeout seconds, signal).
|
|
406
|
+
* When `signal` aborts, polling stops, the job gets a best-effort cancel request,
|
|
407
|
+
* and the promise rejects with `signal.reason`.
|
|
406
408
|
* @returns Final job snapshot.
|
|
407
409
|
*/
|
|
408
|
-
async crawl(url: string, req: CrawlOptions & { pollInterval?: number; timeout?: number } = {}): Promise<CrawlJob> {
|
|
409
|
-
|
|
410
|
+
async crawl(url: string, req: CrawlOptions & { pollInterval?: number; timeout?: number; signal?: AbortSignal } = {}): Promise<CrawlJob> {
|
|
411
|
+
const { pollInterval, timeout, signal, ...options } = req;
|
|
412
|
+
return crawlWaiter(this.http, { url, ...options }, pollInterval, timeout, signal);
|
|
410
413
|
}
|
|
411
414
|
/**
|
|
412
415
|
* Retrieve crawl errors and robots.txt blocks.
|
|
@@ -724,7 +727,7 @@ export class FirecrawlClient {
|
|
|
724
727
|
}
|
|
725
728
|
|
|
726
729
|
/** @deprecated V1 compatibility alias for agent recovery. Prefer crawl(). */
|
|
727
|
-
async crawlUrl(url: string, req: CrawlOptions & { pollInterval?: number; timeout?: number } = {}): Promise<CrawlJob> {
|
|
730
|
+
async crawlUrl(url: string, req: CrawlOptions & { pollInterval?: number; timeout?: number; signal?: AbortSignal } = {}): Promise<CrawlJob> {
|
|
728
731
|
return this.crawl(url, req);
|
|
729
732
|
}
|
|
730
733
|
|
package/src/v2/methods/crawl.ts
CHANGED
|
@@ -107,28 +107,94 @@ export async function getCrawlStatus(
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Cancel a crawl job. Resolves `false` when the API answers 409 because the
|
|
112
|
+
* crawl already finished, so there is nothing left to cancel.
|
|
113
|
+
*/
|
|
110
114
|
export async function cancelCrawl(http: HttpClient, jobId: string): Promise<boolean> {
|
|
111
115
|
try {
|
|
112
116
|
const res = await http.delete<{ status: string }>(`/v2/crawl/${jobId}`);
|
|
117
|
+
if (res.status === 409) return false;
|
|
113
118
|
if (res.status !== 200) throwForBadResponse(res, "cancel crawl");
|
|
114
119
|
return res.data?.status === "cancelled";
|
|
115
120
|
} catch (err: any) {
|
|
116
|
-
if (err?.isAxiosError)
|
|
121
|
+
if (err?.isAxiosError) {
|
|
122
|
+
if (err.response?.status === 409) return false;
|
|
123
|
+
return normalizeAxiosError(err, "cancel crawl");
|
|
124
|
+
}
|
|
117
125
|
throw err;
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
128
|
|
|
121
|
-
|
|
129
|
+
function abortReason(signal: AbortSignal): unknown {
|
|
130
|
+
if (signal.reason !== undefined) return signal.reason;
|
|
131
|
+
return new DOMException("This operation was aborted", "AbortError");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Reject with the abort reason as soon as `signal` aborts. */
|
|
135
|
+
function raceAbort<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {
|
|
136
|
+
if (!signal) return promise;
|
|
137
|
+
if (signal.aborted) {
|
|
138
|
+
promise.catch(() => {});
|
|
139
|
+
return Promise.reject(abortReason(signal));
|
|
140
|
+
}
|
|
141
|
+
return new Promise<T>((resolve, reject) => {
|
|
142
|
+
const onAbort = () => {
|
|
143
|
+
promise.catch(() => {});
|
|
144
|
+
reject(abortReason(signal));
|
|
145
|
+
};
|
|
146
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
147
|
+
promise.then(
|
|
148
|
+
(value) => {
|
|
149
|
+
signal.removeEventListener("abort", onAbort);
|
|
150
|
+
resolve(value);
|
|
151
|
+
},
|
|
152
|
+
(err) => {
|
|
153
|
+
signal.removeEventListener("abort", onAbort);
|
|
154
|
+
reject(err);
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
161
|
+
return new Promise<void>((resolve, reject) => {
|
|
162
|
+
if (signal?.aborted) return reject(abortReason(signal));
|
|
163
|
+
const onAbort = () => {
|
|
164
|
+
clearTimeout(timer);
|
|
165
|
+
reject(abortReason(signal!));
|
|
166
|
+
};
|
|
167
|
+
const timer = setTimeout(() => {
|
|
168
|
+
signal?.removeEventListener("abort", onAbort);
|
|
169
|
+
resolve();
|
|
170
|
+
}, ms);
|
|
171
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Poll a crawl until it reaches a terminal status. When `signal` aborts, polling
|
|
177
|
+
* stops and the promise rejects with the abort reason. The job is not cancelled.
|
|
178
|
+
*/
|
|
179
|
+
export async function waitForCrawlCompletion(
|
|
180
|
+
http: HttpClient,
|
|
181
|
+
jobId: string,
|
|
182
|
+
pollInterval = 2,
|
|
183
|
+
timeout?: number,
|
|
184
|
+
signal?: AbortSignal,
|
|
185
|
+
): Promise<CrawlJob> {
|
|
122
186
|
const start = Date.now();
|
|
123
187
|
|
|
124
188
|
while (true) {
|
|
189
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
125
190
|
try {
|
|
126
|
-
const status = await getCrawlStatus(http, jobId);
|
|
191
|
+
const status = await raceAbort(getCrawlStatus(http, jobId), signal);
|
|
127
192
|
|
|
128
193
|
if (["completed", "failed", "cancelled"].includes(status.status)) {
|
|
129
194
|
return status;
|
|
130
195
|
}
|
|
131
196
|
} catch (err: any) {
|
|
197
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
132
198
|
// Don't retry on permanent errors (4xx) - re-throw immediately with jobId context
|
|
133
199
|
if (!isRetryableError(err)) {
|
|
134
200
|
// Create new error with jobId for better debugging (non-retryable errors like 404)
|
|
@@ -151,13 +217,31 @@ export async function waitForCrawlCompletion(http: HttpClient, jobId: string, po
|
|
|
151
217
|
throw new JobTimeoutError(jobId, timeout, 'crawl');
|
|
152
218
|
}
|
|
153
219
|
|
|
154
|
-
await
|
|
220
|
+
await sleep(Math.max(1000, pollInterval * 1000), signal);
|
|
155
221
|
}
|
|
156
222
|
}
|
|
157
223
|
|
|
158
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Start a crawl and wait for it to finish. When `signal` aborts, polling stops,
|
|
226
|
+
* the job gets a best-effort cancel request, and the promise rejects with the
|
|
227
|
+
* abort reason at once, without waiting for the cancel. A signal that is already
|
|
228
|
+
* aborted rejects before any request.
|
|
229
|
+
*/
|
|
230
|
+
export async function crawl(
|
|
231
|
+
http: HttpClient,
|
|
232
|
+
request: CrawlRequest,
|
|
233
|
+
pollInterval = 2,
|
|
234
|
+
timeout?: number,
|
|
235
|
+
signal?: AbortSignal,
|
|
236
|
+
): Promise<CrawlJob> {
|
|
237
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
159
238
|
const started = await startCrawl(http, request);
|
|
160
|
-
|
|
239
|
+
try {
|
|
240
|
+
return await waitForCrawlCompletion(http, started.id, pollInterval, timeout, signal);
|
|
241
|
+
} catch (err) {
|
|
242
|
+
if (signal?.aborted) cancelCrawl(http, started.id).catch(() => {});
|
|
243
|
+
throw err;
|
|
244
|
+
}
|
|
161
245
|
}
|
|
162
246
|
|
|
163
247
|
export async function getCrawlErrors(http: HttpClient, crawlId: string): Promise<CrawlErrorsResponse> {
|
package/src/v2/types.ts
CHANGED
|
@@ -31,6 +31,7 @@ export interface JsonFormat extends Format {
|
|
|
31
31
|
type: "json";
|
|
32
32
|
prompt?: string;
|
|
33
33
|
schema?: Record<string, unknown> | ZodTypeAny;
|
|
34
|
+
/** @deprecated Use the top-level `checkPromptInjection` scrape option. */
|
|
34
35
|
checkPromptInjection?: boolean;
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -276,6 +277,13 @@ export interface ScrapeOptions {
|
|
|
276
277
|
minAge?: number;
|
|
277
278
|
storeInCache?: boolean;
|
|
278
279
|
lockdown?: boolean;
|
|
280
|
+
/**
|
|
281
|
+
* Scans the page content for prompt injection with any format except
|
|
282
|
+
* rawBase64, before LLM-backed formats run. A detection fails the scrape
|
|
283
|
+
* with SCRAPE_PROMPT_INJECTION_DETECTED.
|
|
284
|
+
* Adds 4 credits when the check scans the whole page.
|
|
285
|
+
*/
|
|
286
|
+
checkPromptInjection?: boolean;
|
|
279
287
|
redactPII?: boolean | RedactPIIOptions;
|
|
280
288
|
threatProtection?: ThreatProtectionOptions;
|
|
281
289
|
auditMetadata?: AuditMetadata;
|
package/vitest.config.ts
ADDED
package/jest.config.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/** @type {import('ts-jest').JestConfigWithTsJest} **/
|
|
2
|
-
export default {
|
|
3
|
-
testEnvironment: "node",
|
|
4
|
-
"moduleNameMapper": {
|
|
5
|
-
"^(\\.{1,2}/.*)\\.js$": "$1",
|
|
6
|
-
},
|
|
7
|
-
"extensionsToTreatAsEsm": [".ts"],
|
|
8
|
-
"transform": {
|
|
9
|
-
"^.+\\.(mt|t|cj|j)s$": [
|
|
10
|
-
"ts-jest",
|
|
11
|
-
{
|
|
12
|
-
"useESM": true
|
|
13
|
-
}
|
|
14
|
-
]
|
|
15
|
-
},
|
|
16
|
-
};
|