@mendable/firecrawl-js 4.19.0 → 4.20.1

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.
@@ -1,4 +1,8 @@
1
- import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from "axios";
1
+ import axios, {
2
+ type AxiosInstance,
3
+ type AxiosRequestConfig,
4
+ type AxiosResponse,
5
+ } from "axios";
2
6
  import { getVersion } from "./getVersion";
3
7
 
4
8
  export interface HttpClientOptions {
@@ -9,6 +13,11 @@ export interface HttpClientOptions {
9
13
  backoffFactor?: number; // seconds factor for 0.5, 1, 2...
10
14
  }
11
15
 
16
+ export interface RequestOptions {
17
+ headers?: Record<string, string>;
18
+ timeoutMs?: number;
19
+ }
20
+
12
21
  export class HttpClient {
13
22
  private instance: AxiosInstance;
14
23
  private readonly apiKey: string;
@@ -39,7 +48,9 @@ export class HttpClient {
39
48
  return this.apiKey;
40
49
  }
41
50
 
42
- private async request<T = any>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {
51
+ private async request<T = any>(
52
+ config: AxiosRequestConfig,
53
+ ): Promise<AxiosResponse<T>> {
43
54
  const version = getVersion();
44
55
  config.headers = {
45
56
  ...(config.headers || {}),
@@ -64,12 +75,13 @@ export class HttpClient {
64
75
  ["post", "put", "patch"].includes(cfg.method.toLowerCase())
65
76
  ) {
66
77
  const data = (cfg.data ?? {}) as Record<string, unknown>;
67
- cfg.data = { ...data, origin: typeof data.origin === "string" && data.origin.includes("mcp") ? data.origin : `js-sdk@${version}` };
68
-
69
- // If timeout is specified in the body, use it to override the request timeout
70
- if (typeof data.timeout === "number") {
71
- cfg.timeout = data.timeout + 5000;
72
- }
78
+ cfg.data = {
79
+ ...data,
80
+ origin:
81
+ typeof data.origin === "string" && data.origin.includes("mcp")
82
+ ? data.origin
83
+ : `js-sdk@${version}`,
84
+ };
73
85
  }
74
86
 
75
87
  if (isFormDataBody) {
@@ -98,25 +110,34 @@ export class HttpClient {
98
110
  }
99
111
 
100
112
  private sleep(seconds: number): Promise<void> {
101
- return new Promise((r) => setTimeout(r, seconds * 1000));
113
+ return new Promise(r => setTimeout(r, seconds * 1000));
102
114
  }
103
115
 
104
- post<T = any>(endpoint: string, body: Record<string, unknown>, headers?: Record<string, string>) {
105
- return this.request<T>({ method: "post", url: endpoint, data: body, headers });
116
+ post<T = any>(
117
+ endpoint: string,
118
+ body: Record<string, unknown>,
119
+ options?: RequestOptions,
120
+ ) {
121
+ return this.request<T>({
122
+ method: "post",
123
+ url: endpoint,
124
+ data: body,
125
+ headers: options?.headers,
126
+ timeout: options?.timeoutMs,
127
+ });
106
128
  }
107
129
 
108
130
  postMultipart<T = any>(
109
131
  endpoint: string,
110
132
  formData: FormData,
111
- headers?: Record<string, string>,
112
- timeoutMs?: number,
133
+ options?: RequestOptions,
113
134
  ) {
114
135
  return this.request<T>({
115
136
  method: "post",
116
137
  url: endpoint,
117
138
  data: formData,
118
- headers,
119
- timeout: timeoutMs,
139
+ headers: options?.headers,
140
+ timeout: options?.timeoutMs,
120
141
  });
121
142
  }
122
143
 
@@ -134,4 +155,3 @@ export class HttpClient {
134
155
  return headers;
135
156
  }
136
157
  }
137
-
package/src/v2/watcher.ts CHANGED
@@ -109,24 +109,37 @@ export class Watcher extends EventEmitter {
109
109
  }
110
110
 
111
111
  async start(): Promise<void> {
112
- try {
113
- const url = this.buildWsUrl();
114
- const wsCtor = await getWebSocketCtor();
115
- if (!wsCtor) {
116
- this.pollLoop();
117
- return;
118
- }
119
- this.ws = new wsCtor(url, this.http.getApiKey()) as any;
120
- if (this.ws && "binaryType" in this.ws) {
121
- (this.ws as any).binaryType = "arraybuffer";
122
- }
123
-
124
- if (this.ws) {
125
- this.attachWsHandlers(this.ws);
126
- }
127
- } catch (err) {
128
- this.pollLoop();
129
- }
112
+ return new Promise<void>((resolve, reject) => {
113
+ const onDone = () => { cleanup(); resolve(); };
114
+ const onError = (err: any) => { cleanup(); resolve(); };
115
+ const cleanup = () => {
116
+ this.removeListener("done", onDone);
117
+ this.removeListener("error", onError);
118
+ };
119
+ this.on("done", onDone);
120
+ this.on("error", onError);
121
+
122
+ (async () => {
123
+ try {
124
+ const url = this.buildWsUrl();
125
+ const wsCtor = await getWebSocketCtor();
126
+ if (!wsCtor) {
127
+ this.pollLoop();
128
+ return;
129
+ }
130
+ this.ws = new wsCtor(url, this.http.getApiKey()) as any;
131
+ if (this.ws && "binaryType" in this.ws) {
132
+ (this.ws as any).binaryType = "arraybuffer";
133
+ }
134
+
135
+ if (this.ws) {
136
+ this.attachWsHandlers(this.ws);
137
+ }
138
+ } catch (err) {
139
+ this.pollLoop();
140
+ }
141
+ })();
142
+ });
130
143
  }
131
144
 
132
145
  private attachWsHandlers(ws: WebSocket) {
@@ -150,14 +163,14 @@ export class Watcher extends EventEmitter {
150
163
  }
151
164
  if (type === "document") {
152
165
  const doc = body.data;
153
- if (doc) this.emit("document", doc as Document & { id: string });
166
+ if (doc) this.emitDocuments([doc]);
154
167
  return;
155
168
  }
156
169
  if (type === "done") {
157
170
  const payload = body.data || body;
158
171
  const data = (payload.data || []) as Document[];
159
172
  if (data.length) this.emitDocuments(data);
160
- this.emit("done", { status: "completed", data, id: this.jobId });
173
+ this.emit("done", { status: "completed", data, id: this.jobId, total: payload.total, completed: payload.completed, creditsUsed: payload.creditsUsed });
161
174
  this.close();
162
175
  return;
163
176
  }
@@ -166,7 +179,10 @@ export class Watcher extends EventEmitter {
166
179
  } catch {
167
180
  // ignore
168
181
  }
169
- if (timeoutMs && Date.now() - startTs > timeoutMs) this.close();
182
+ if (timeoutMs && Date.now() - startTs > timeoutMs) {
183
+ this.emit("error", { status: "failed", data: [], error: "Watcher timeout", id: this.jobId });
184
+ this.close();
185
+ }
170
186
  };
171
187
  ws.onerror = () => {
172
188
  this.emit("error", { status: "failed", data: [], error: "WebSocket error", id: this.jobId });
@@ -227,7 +243,7 @@ export class Watcher extends EventEmitter {
227
243
  };
228
244
  this.emit("snapshot", snap);
229
245
  if (["completed", "failed", "cancelled"].includes(status)) {
230
- this.emit("done", { status, data, id: this.jobId });
246
+ this.emit("done", { status, data, id: this.jobId, total: payload.total ?? 0, completed: payload.completed ?? 0, creditsUsed: payload.creditsUsed });
231
247
  this.close();
232
248
  }
233
249
  }
@@ -243,14 +259,18 @@ export class Watcher extends EventEmitter {
243
259
  this.emitDocuments((snap.data || []) as Document[]);
244
260
  this.emit("snapshot", snap);
245
261
  if (["completed", "failed", "cancelled"].includes(snap.status)) {
246
- this.emit("done", { status: snap.status, data: snap.data, id: this.jobId });
262
+ this.emit("done", { status: snap.status, data: snap.data, id: this.jobId, total: (snap as any).total ?? 0, completed: (snap as any).completed ?? 0, creditsUsed: (snap as any).creditsUsed });
247
263
  this.close();
248
264
  break;
249
265
  }
250
266
  } catch {
251
267
  // ignore polling errors
252
268
  }
253
- if (timeoutMs && Date.now() - startTs > timeoutMs) break;
269
+ if (timeoutMs && Date.now() - startTs > timeoutMs) {
270
+ this.emit("error", { status: "failed", data: [], error: "Watcher timeout", id: this.jobId });
271
+ this.close();
272
+ break;
273
+ }
254
274
  await new Promise((r) => setTimeout(r, Math.max(1000, this.pollInterval * 1000)));
255
275
  }
256
276
  }