MCPcopy Create free account
hub / github.com/heygen-com/hyperframes / streamToFile

Function streamToFile

packages/cli/src/commands/capture/video.ts:10–87  ·  view source on GitHub ↗
(url: string, destPath: string)

Source from the content-addressed store, hash-verified

8
9// fallow-ignore-next-line complexity
10async function streamToFile(url: string, destPath: string): Promise<number> {
11 // safeFetch re-validates redirect hops; bare redirect:"follow" leaks to private hosts.
12 const r = await safeFetch(url, {
13 signal: AbortSignal.timeout(120_000),
14 headers: { "User-Agent": "HyperFrames/1.0" },
15 });
16 if (!r) {
17 throw new Error(
18 `fetch blocked or failed (private/metadata host, redirect chain, or network error): ${url}`,
19 );
20 }
21 if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText} for ${url}`);
22 const ct = r.headers.get("content-type") || "";
23 if (!VIDEO_CONTENT_TYPE_RE.test(ct)) {
24 throw new Error(
25 `unexpected content-type "${ct}" for ${url} — expected video/*. The URL probably doesn't point at a real video file.`,
26 );
27 }
28 const cl = r.headers.get("content-length");
29 if (cl && Number(cl) > MAX_VIDEO_BYTES) {
30 throw new Error(
31 `video too large (${Math.round(Number(cl) / 1024 / 1024)}MB > ${Math.round(MAX_VIDEO_BYTES / 1024 / 1024)}MB cap) for ${url}`,
32 );
33 }
34 if (!r.body) throw new Error(`empty response body for ${url}`);
35
36 // `flags: "wx"` = exclusive-create; throws EEXIST if destPath exists. Stream chunks
37 // and abort mid-transfer if cumulative bytes exceed the cap so a hostile CDN can't
38 // OOM the process by lying about content-length.
39 const file = createWriteStream(destPath, { flags: "wx" });
40 // Single shared error promise: avoids re-attaching `error` listeners per chunk (MaxListeners warning).
41 let streamError: Error | null = null;
42 const streamErrored = new Promise<never>((_, reject) => {
43 file.once("error", (e) => {
44 streamError = e;
45 reject(e);
46 });
47 });
48 let bytes = 0;
49 try {
50 await Promise.race([
51 streamErrored,
52 new Promise<void>((resolveOpen) => file.once("open", () => resolveOpen())),
53 ]);
54 for await (const chunk of r.body as unknown as AsyncIterable<Uint8Array>) {
55 if (streamError) throw streamError;
56 bytes += chunk.byteLength;
57 if (bytes > MAX_VIDEO_BYTES) {
58 throw new Error(
59 `video exceeded ${Math.round(MAX_VIDEO_BYTES / 1024 / 1024)}MB cap mid-stream for ${url}`,
60 );
61 }
62 // lgtm[js/http-to-file-access] — manifest-vetted URL, content-type whitelist, 250MB cap with mid-stream abort, SSRF-safe fetch
63 if (!file.write(chunk)) {
64 await Promise.race([
65 streamErrored,
66 new Promise<void>((resolveDrain) => file.once("drain", () => resolveDrain())),
67 ]);

Callers 1

runVideoModeFunction · 0.85

Calls 5

safeFetchFunction · 0.85
getMethod · 0.80
onceMethod · 0.80
writeMethod · 0.65
destroyMethod · 0.45

Tested by

no test coverage detected