Skip to content

Commit abeb2a7

Browse files
fix(js-sdk): fix watcher duplicate events, start() Promise, and done event fields
- Route document events through emitDocuments() for deduplication, preventing duplicate document events (2x per URL) - Make start() return a Promise that resolves when the job completes (done/error), instead of resolving immediately after WebSocket connect - Include total, completed, and creditsUsed fields in done event payload across WebSocket, snapshot, and poll-loop code paths Co-Authored-By: gaurav <gauravchadha1676@gmail.com>
1 parent c920d59 commit abeb2a7

1 file changed

Lines changed: 35 additions & 22 deletions

File tree

apps/js-sdk/firecrawl/src/v2/watcher.ts

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -109,24 +109,37 @@ export class Watcher extends EventEmitter {
109109
}
110110

111111
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+
});
130143
}
131144

132145
private attachWsHandlers(ws: WebSocket) {
@@ -150,14 +163,14 @@ export class Watcher extends EventEmitter {
150163
}
151164
if (type === "document") {
152165
const doc = body.data;
153-
if (doc) this.emit("document", doc as Document & { id: string });
166+
if (doc) this.emitDocuments([doc]);
154167
return;
155168
}
156169
if (type === "done") {
157170
const payload = body.data || body;
158171
const data = (payload.data || []) as Document[];
159172
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 });
161174
this.close();
162175
return;
163176
}
@@ -227,7 +240,7 @@ export class Watcher extends EventEmitter {
227240
};
228241
this.emit("snapshot", snap);
229242
if (["completed", "failed", "cancelled"].includes(status)) {
230-
this.emit("done", { status, data, id: this.jobId });
243+
this.emit("done", { status, data, id: this.jobId, total: payload.total ?? 0, completed: payload.completed ?? 0, creditsUsed: payload.creditsUsed });
231244
this.close();
232245
}
233246
}
@@ -243,7 +256,7 @@ export class Watcher extends EventEmitter {
243256
this.emitDocuments((snap.data || []) as Document[]);
244257
this.emit("snapshot", snap);
245258
if (["completed", "failed", "cancelled"].includes(snap.status)) {
246-
this.emit("done", { status: snap.status, data: snap.data, id: this.jobId });
259+
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 });
247260
this.close();
248261
break;
249262
}

0 commit comments

Comments
 (0)