hyperframes 0.8.163 → 0.8.164

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.
Files changed (92) hide show
  1. package/dist/{add-Y7BGO2MK.js → add-5UR2S4ZU.js} +5 -5
  2. package/dist/{asset-OONPCZS5.js → asset-TB7K7OFL.js} +2 -2
  3. package/dist/{assetCataloger-YWJRJK23.js → assetCataloger-FE4K64GW.js} +25 -5
  4. package/dist/{audio-KPTXNHDC.js → audio-WH3XWV6N.js} +5 -5
  5. package/dist/{auth-UKXSZ4WA.js → auth-4BBOL3DI.js} +1 -1
  6. package/dist/{autoUpdate-4MRUONUQ.js → autoUpdate-7LARILUN.js} +2 -2
  7. package/dist/{backgroundChecks-X5M7Z6L7.js → backgroundChecks-5A5VAWOQ.js} +3 -3
  8. package/dist/backgroundChecksWorker.js +3 -3
  9. package/dist/{beats-EQNT7OZE.js → beats-EVQOYYYQ.js} +4 -4
  10. package/dist/{benchmark-WXWALVZJ.js → benchmark-5KE22VUG.js} +5 -5
  11. package/dist/{browser-ZSNFVRC3.js → browser-BLGJSG23.js} +3 -3
  12. package/dist/{capture-TKWJBFGC.js → capture-KSXWVLT7.js} +306 -92
  13. package/dist/{capture-JFFYE7XM.js → capture-PR52XOEQ.js} +2 -2
  14. package/dist/{catalog-LK5MNUQY.js → catalog-W3AG6ISP.js} +5 -5
  15. package/dist/{catch-up-ISTWIF6K.js → catch-up-JLZUALKW.js} +4 -4
  16. package/dist/{check-YVEBXKNY.js → check-PGH3VCIR.js} +13 -13
  17. package/dist/{checkBrowser-OJAXYTRD.js → checkBrowser-MNJBUCZD.js} +9 -9
  18. package/dist/{chunk-HQMWZNO2.js → chunk-36CWAWB7.js} +1 -1
  19. package/dist/{chunk-OGGPNDGO.js → chunk-3XJVN7MR.js} +2 -2
  20. package/dist/{chunk-Z6P5OVCF.js → chunk-56RENSU4.js} +1 -1
  21. package/dist/{chunk-2ALJMIFW.js → chunk-57SASPYR.js} +12 -1
  22. package/dist/{chunk-NGKM64BP.js → chunk-5CTORVUM.js} +17 -2
  23. package/dist/{chunk-SQN7N27A.js → chunk-6DJLEE74.js} +2 -2
  24. package/dist/{chunk-OZUYK6IS.js → chunk-FTFWZHTZ.js} +1 -1
  25. package/dist/{chunk-VG3ZMY2H.js → chunk-G3QMPIFG.js} +2 -2
  26. package/dist/{chunk-WJVDUUH2.js → chunk-GJOW6ZFW.js} +2 -2
  27. package/dist/{chunk-M4237QMQ.js → chunk-HYRDV7AL.js} +1 -1
  28. package/dist/{chunk-JYR3YAAK.js → chunk-KR3M4GKH.js} +1 -1
  29. package/dist/{chunk-23IS5QVP.js → chunk-NOVAH7N4.js} +1 -1
  30. package/dist/{chunk-NU4PDQ6G.js → chunk-PRZDWAH3.js} +1 -1
  31. package/dist/{chunk-TQMJKZS5.js → chunk-Q4XFXAQ3.js} +1 -1
  32. package/dist/{chunk-OFD2XWIQ.js → chunk-SYOB5Y35.js} +178 -1
  33. package/dist/{chunk-QXAXCRK5.js → chunk-UF45OJ3N.js} +1 -1
  34. package/dist/{chunk-WMXOKTGO.js → chunk-UH4IL4K4.js} +2 -2
  35. package/dist/{chunk-2XIPB4MP.js → chunk-VEAKQW4X.js} +1 -1
  36. package/dist/{chunk-ZVOCFUPA.js → chunk-WE5X5AKF.js} +4 -4
  37. package/dist/{chunk-43H7XDO6.js → chunk-WLQII3K3.js} +2 -2
  38. package/dist/{chunk-LHETZ63N.js → chunk-WZSWLCEH.js} +1 -1
  39. package/dist/{chunk-54E2ELUA.js → chunk-XYDPJ3CQ.js} +4 -4
  40. package/dist/{clean-PGEA3UPT.js → clean-R7BXTAFB.js} +3 -3
  41. package/dist/cli.js +63 -55
  42. package/dist/{cloud-SAODZ2PK.js → cloud-IPXFPX7M.js} +4 -4
  43. package/dist/{compare-R7X7LXCX.js → compare-S3RMZBB4.js} +6 -6
  44. package/dist/{component-DTVCMTLX.js → component-VSOWEAK4.js} +3 -3
  45. package/dist/{compositions-UXS2ISGS.js → compositions-7LPCAY7M.js} +5 -5
  46. package/dist/{delete-LIGHRH4L.js → delete-L32WDJGO.js} +2 -2
  47. package/dist/{doctor-6VR5JUWZ.js → doctor-7TSVAIF2.js} +3 -3
  48. package/dist/{dotEnv-AECLXQSO.js → dotEnv-64S3VKGH.js} +3 -1
  49. package/dist/{events-ZQFIOH5Z.js → events-FDHKA6NV.js} +3 -3
  50. package/dist/{events-YP2MRCPP.js → events-RFABFHWU.js} +2 -2
  51. package/dist/{feedback-47UEIUOO.js → feedback-SDAJ2HAM.js} +4 -4
  52. package/dist/{figma-ZTHHEDY5.js → figma-KJBYCHXQ.js} +3 -3
  53. package/dist/fontLocalizeCli.js +1 -1
  54. package/dist/{get-EZGQNT7G.js → get-6Z3JCPJ6.js} +2 -2
  55. package/dist/{grade-compare-SEFOAA4E.js → grade-compare-HAQXCQSE.js} +6 -6
  56. package/dist/{help-WIZONGVE.js → help-WMMVG2PR.js} +1 -1
  57. package/dist/{history-PE45CSGA.js → history-5AQNASLD.js} +6 -6
  58. package/dist/hyperframes-player.global.js +1 -1
  59. package/dist/{info-WIRSMQKB.js → info-OUJ3HXTN.js} +5 -5
  60. package/dist/{init-N26RW4YN.js → init-NEEYHH7E.js} +6 -6
  61. package/dist/{inspect-XHQUASDV.js → inspect-ZWBDYWTY.js} +8 -8
  62. package/dist/{keyframes-RNTYYHMK.js → keyframes-BA5R5Q7G.js} +6 -6
  63. package/dist/{layout-YRCKPNAO.js → layout-GJVLRWIJ.js} +8 -8
  64. package/dist/{lint-HLOZHJVM.js → lint-FBH2BA2A.js} +6 -6
  65. package/dist/{list-X7NTQB32.js → list-XDZIX2PU.js} +2 -2
  66. package/dist/{login-S7PVNUEB.js → login-CUHIF5GP.js} +4 -4
  67. package/dist/{media-treatment-465Y6CWE.js → media-treatment-HZDD67IL.js} +5 -5
  68. package/dist/{motionShot-K6OBEZ2N.js → motionShot-D2C44I6P.js} +2 -2
  69. package/dist/{normalize-audio-VODIXLAC.js → normalize-audio-B6PKQGHV.js} +4 -4
  70. package/dist/{open-K5OZGYJ7.js → open-EIN2FH4D.js} +4 -4
  71. package/dist/{play-QOXKOZ6L.js → play-KLOHE5XH.js} +4 -4
  72. package/dist/{present-7CVCJIS3.js → present-4K53KZ3P.js} +4 -4
  73. package/dist/{preview-HX5F6RGT.js → preview-PSALQBNR.js} +5 -5
  74. package/dist/{render-TRU75TVB.js → render-2QY3DTYM.js} +8 -8
  75. package/dist/{render-XS22VQHC.js → render-NAJFHWUG.js} +5 -5
  76. package/dist/{skills-RKDR4AXV.js → skills-ETYVJOLG.js} +5 -5
  77. package/dist/{skillsUpdateCheck-QWIERB7M.js → skillsUpdateCheck-6P4VCFZZ.js} +3 -3
  78. package/dist/{snapshot-SSSF334Y.js → snapshot-P3RZFXUC.js} +8 -8
  79. package/dist/studio/assets/{hyperframes-player-oCk9rjq9.js → hyperframes-player-CQX9TDla.js} +1 -1
  80. package/dist/studio/assets/{index-BOQPXBQ0.js → index-BnU7S7oM.js} +1 -1
  81. package/dist/studio/assets/{index-DK2wgdLx.js → index-DHqUZqg6.js} +4 -4
  82. package/dist/studio/index.html +1 -1
  83. package/dist/{studioServer-KWNULEM4.js → studioServer-HF6ZCYQO.js} +7 -7
  84. package/dist/{telemetry-4G6CD3X7.js → telemetry-ASFTF5YV.js} +3 -3
  85. package/dist/{timeline-TKPBJ4EP.js → timeline-H7Q2K6ZQ.js} +5 -5
  86. package/dist/{tokens-TX7THSB7.js → tokens-LWCXLQCR.js} +2 -2
  87. package/dist/{transcribe-7DBONX4C.js → transcribe-X2GOG3HZ.js} +3 -3
  88. package/dist/{updateCheck-64D7B73Z.js → updateCheck-EA7X36NI.js} +2 -2
  89. package/dist/{upgrade-MXQDGGYO.js → upgrade-Y6H3ZEEJ.js} +2 -2
  90. package/dist/{validate-5ZNGP5QO.js → validate-TZAB2PVX.js} +8 -8
  91. package/dist/{video-62JJK3XL.js → video-LNLD2PKZ.js} +1 -1
  92. package/package.json +1 -1
@@ -19,9 +19,10 @@ import {
19
19
  isPrivateUrl,
20
20
  mergeDrops,
21
21
  noDrops,
22
+ resolvePublicAddress,
22
23
  safeFetch,
23
24
  totalDrops
24
- } from "./chunk-OFD2XWIQ.js";
25
+ } from "./chunk-SYOB5Y35.js";
25
26
  import {
26
27
  CaptureDirRefusedError,
27
28
  NavigationDeadlineError,
@@ -35,8 +36,10 @@ import {
35
36
  withRemainingBudget
36
37
  } from "./chunk-AWNBM3MZ.js";
37
38
  import {
38
- launchManagedBrowser
39
- } from "./chunk-2ALJMIFW.js";
39
+ closeManagedBrowser,
40
+ launchManagedBrowser,
41
+ registerBrowserSessionCleanup
42
+ } from "./chunk-57SASPYR.js";
40
43
  import "./chunk-Z6VBXCYN.js";
41
44
  import {
42
45
  writeNewFileSync
@@ -49,8 +52,9 @@ import {
49
52
  diag
50
53
  } from "./chunk-VWG7OQKU.js";
51
54
  import {
55
+ CAPTURE_ENV_KEYS,
52
56
  applyDotEnv
53
- } from "./chunk-NGKM64BP.js";
57
+ } from "./chunk-5CTORVUM.js";
54
58
  import "./chunk-IGLEPPTN.js";
55
59
  import "./chunk-PBN6GBJE.js";
56
60
  import {
@@ -177,6 +181,220 @@ function writePartialCaptureBundle(opts, state, lastPhase) {
177
181
  };
178
182
  }
179
183
 
184
+ // src/capture/launchCaptureBrowser.ts
185
+ import { mkdtemp, mkdir, writeFile, rm } from "fs/promises";
186
+ import { tmpdir } from "os";
187
+ import { join as join2 } from "path";
188
+
189
+ // src/capture/browserLaunchArgs.ts
190
+ function captureBrowserArgs(disableWebgl, viewportWidth, viewportHeight, platform = process.platform) {
191
+ const needsLinuxSandboxException = platform === "linux";
192
+ return [
193
+ ...needsLinuxSandboxException ? ["--no-sandbox"] : [],
194
+ "--disable-dev-shm-usage",
195
+ ...disableWebgl ? ["--disable-gpu"] : ["--enable-webgl", "--ignore-gpu-blocklist", "--use-gl=angle", "--use-angle=swiftshader"],
196
+ "--disable-blink-features=AutomationControlled",
197
+ "--disable-background-timer-throttling",
198
+ "--disable-renderer-backgrounding",
199
+ `--window-size=${viewportWidth},${viewportHeight}`
200
+ ];
201
+ }
202
+
203
+ // src/capture/captureProxy.ts
204
+ import { createServer, request } from "http";
205
+ import { connect } from "net";
206
+ var CONNECTION_TIMEOUT_MS = 15e3;
207
+ var HOP_HEADERS = /* @__PURE__ */ new Set([
208
+ "connection",
209
+ "proxy-connection",
210
+ "proxy-authorization",
211
+ "proxy-authenticate",
212
+ "keep-alive",
213
+ "te",
214
+ "trailer",
215
+ "transfer-encoding",
216
+ "upgrade"
217
+ ]);
218
+ function forwardHeaders(message) {
219
+ const headers = { ...message.headers };
220
+ const connection = String(headers.connection ?? "").split(",");
221
+ for (const name of [...HOP_HEADERS, ...connection]) delete headers[name.trim().toLowerCase()];
222
+ return headers;
223
+ }
224
+ function destination(raw, tunnel) {
225
+ const url = new URL(tunnel ? `https://${raw}` : raw);
226
+ if (url.username || url.password || url.hash || (tunnel ? url.protocol !== "https:" || url.pathname !== "/" || !!url.search : url.protocol !== "http:")) {
227
+ throw new Error("Unsupported capture destination");
228
+ }
229
+ return url;
230
+ }
231
+ function refuse(response) {
232
+ if (!response.headersSent) response.writeHead(403, { Connection: "close" });
233
+ response.end();
234
+ }
235
+ async function forwardHttp(incoming, response, sockets) {
236
+ try {
237
+ const url = destination(incoming.url ?? "", false);
238
+ const address = await resolvePublicAddress(url.hostname);
239
+ if (incoming.destroyed || response.destroyed) return;
240
+ const headers = forwardHeaders(incoming);
241
+ headers.host = url.host;
242
+ const upstream = request(
243
+ {
244
+ hostname: address,
245
+ family: 4,
246
+ port: url.port || 80,
247
+ method: incoming.method,
248
+ path: url.pathname + url.search,
249
+ headers,
250
+ agent: false,
251
+ signal: AbortSignal.timeout(3e4)
252
+ },
253
+ (received) => {
254
+ response.writeHead(received.statusCode ?? 502, forwardHeaders(received));
255
+ received.on("error", () => response.destroy());
256
+ received.pipe(response);
257
+ }
258
+ );
259
+ upstream.on("socket", (socket) => trackSocket(socket, sockets));
260
+ upstream.setTimeout(
261
+ CONNECTION_TIMEOUT_MS,
262
+ () => upstream.destroy(new Error("Capture request timed out"))
263
+ );
264
+ upstream.on("error", () => {
265
+ if (!response.headersSent) response.writeHead(502, { Connection: "close" });
266
+ response.end();
267
+ });
268
+ response.on("close", () => upstream.destroy());
269
+ incoming.on("error", () => upstream.destroy());
270
+ incoming.pipe(upstream);
271
+ } catch {
272
+ refuse(response);
273
+ }
274
+ }
275
+ function trackSocket(socket, sockets) {
276
+ sockets.add(socket);
277
+ socket.once("close", () => sockets.delete(socket));
278
+ socket.on("error", () => socket.destroy());
279
+ }
280
+ async function forwardTunnel(incoming, client, head, sockets) {
281
+ try {
282
+ const url = destination(incoming.url ?? "", true);
283
+ const address = await resolvePublicAddress(url.hostname);
284
+ if (client.destroyed) return;
285
+ const upstream = connect({ host: address, family: 4, port: Number(url.port || 443) });
286
+ trackSocket(upstream, sockets);
287
+ const connectDeadline = setTimeout(() => upstream.destroy(), CONNECTION_TIMEOUT_MS);
288
+ connectDeadline.unref();
289
+ upstream.once("close", () => clearTimeout(connectDeadline));
290
+ upstream.setTimeout(CONNECTION_TIMEOUT_MS, () => upstream.destroy());
291
+ upstream.once("connect", () => {
292
+ clearTimeout(connectDeadline);
293
+ if (client.destroyed) return upstream.destroy();
294
+ client.write("HTTP/1.1 200 Connection Established\r\n\r\n");
295
+ if (head.length) upstream.write(head);
296
+ upstream.pipe(client);
297
+ client.pipe(upstream);
298
+ });
299
+ upstream.once("close", () => client.destroy());
300
+ client.once("close", () => upstream.destroy());
301
+ } catch {
302
+ client.end("HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
303
+ }
304
+ }
305
+ async function startCaptureProxy() {
306
+ const sockets = /* @__PURE__ */ new Set();
307
+ const server = createServer(
308
+ (incoming, response) => void forwardHttp(incoming, response, sockets)
309
+ );
310
+ server.on("connection", (socket) => trackSocket(socket, sockets));
311
+ server.on(
312
+ "connect",
313
+ (incoming, socket, head) => void forwardTunnel(incoming, socket, head, sockets)
314
+ );
315
+ server.on("upgrade", (_incoming, socket) => socket.destroy());
316
+ server.maxConnections = 128;
317
+ server.headersTimeout = CONNECTION_TIMEOUT_MS;
318
+ server.requestTimeout = 3e4;
319
+ server.keepAliveTimeout = 1e3;
320
+ await new Promise((resolve2, reject) => {
321
+ server.once("error", reject);
322
+ server.listen(0, "127.0.0.1", resolve2);
323
+ });
324
+ const address = server.address();
325
+ if (!address || typeof address === "string") throw new Error("Capture proxy did not bind");
326
+ let closing;
327
+ return {
328
+ url: `http://127.0.0.1:${address.port}`,
329
+ close() {
330
+ closing ??= new Promise((resolve2) => {
331
+ server.close(() => resolve2());
332
+ for (const socket of sockets) socket.destroy();
333
+ });
334
+ return closing;
335
+ }
336
+ };
337
+ }
338
+
339
+ // src/capture/launchCaptureBrowser.ts
340
+ async function launchCaptureBrowser(options) {
341
+ const proxy = await startCaptureProxy();
342
+ let profile;
343
+ let browser;
344
+ let closing;
345
+ let unregister;
346
+ const close = () => {
347
+ closing ??= (async () => {
348
+ try {
349
+ await proxy.close();
350
+ if (browser) await closeManagedBrowser(browser);
351
+ } finally {
352
+ try {
353
+ if (profile) await rm(profile, { recursive: true, force: true });
354
+ } finally {
355
+ unregister?.();
356
+ }
357
+ }
358
+ })();
359
+ return closing;
360
+ };
361
+ try {
362
+ const profilePath = await mkdtemp(join2(tmpdir(), "hf-capture-profile-"));
363
+ profile = profilePath;
364
+ await mkdir(join2(profile, "Default"));
365
+ await writeFile(
366
+ join2(profile, "Default", "Preferences"),
367
+ JSON.stringify({
368
+ webrtc: { ip_handling_policy: "disable_non_proxied_udp" }
369
+ })
370
+ );
371
+ unregister = registerBrowserSessionCleanup(close);
372
+ const puppeteer = await import("puppeteer-core");
373
+ browser = await launchManagedBrowser(puppeteer.default, {
374
+ headless: true,
375
+ userDataDir: profile,
376
+ executablePath: options.executablePath,
377
+ protocolTimeout: options.protocolTimeout,
378
+ // Linux containers may lack the user namespaces Chromium's sandbox requires.
379
+ args: [
380
+ ...captureBrowserArgs(options.disableWebgl, options.width, options.height),
381
+ `--proxy-server=${proxy.url}`,
382
+ "--proxy-bypass-list=<-loopback>",
383
+ "--disable-quic",
384
+ "--force-webrtc-ip-handling-policy=disable_non_proxied_udp"
385
+ ]
386
+ });
387
+ browser.once("disconnected", () => void proxy.close());
388
+ return {
389
+ browser,
390
+ close
391
+ };
392
+ } catch (error) {
393
+ await close();
394
+ throw error;
395
+ }
396
+ }
397
+
180
398
  // src/capture/lottieValidation.ts
181
399
  import AdmZip from "adm-zip";
182
400
 
@@ -398,7 +616,7 @@ function candidatePriority(url, contentType) {
398
616
 
399
617
  // src/capture/captureAttempt.ts
400
618
  import { existsSync as existsSync5 } from "fs";
401
- import { join as join10 } from "path";
619
+ import { join as join11 } from "path";
402
620
 
403
621
  // src/capture/lazyScrollForCapture.ts
404
622
  var LAZY_SCROLL_STEP_DELAY_MS = 400;
@@ -650,14 +868,15 @@ async function collectAnimationCatalog(page, cdpAnimations, cdp, opts = {}) {
650
868
 
651
869
  // src/capture/scaffolding.ts
652
870
  import { existsSync, readFileSync } from "fs";
653
- import { join as join2, resolve } from "path";
871
+ import { join as join3, resolve } from "path";
654
872
  function loadEnvFile(startDir) {
873
+ if (process.env.HYPERFRAMES_NO_CAPTURE_DOTENV === "1") return;
655
874
  try {
656
875
  let dir = resolve(startDir);
657
876
  for (let i = 0; i < 5; i++) {
658
877
  const envPath = resolve(dir, ".env");
659
878
  try {
660
- applyDotEnv(readFileSync(envPath, "utf-8"), process.env);
879
+ applyDotEnv(readFileSync(envPath, "utf-8"), process.env, CAPTURE_ENV_KEYS);
661
880
  break;
662
881
  } catch {
663
882
  dir = resolve(dir, "..");
@@ -667,7 +886,7 @@ function loadEnvFile(startDir) {
667
886
  }
668
887
  }
669
888
  async function generateProjectScaffold(outputDir, url, tokens, animationCatalog, hasScreenshots, hasLotties, hasShaders, catalogedAssets, progress, warnings, detectedLibraries) {
670
- const metaPath = join2(outputDir, "meta.json");
889
+ const metaPath = join3(outputDir, "meta.json");
671
890
  if (!existsSync(metaPath)) {
672
891
  const hostname = new URL(url).hostname.replace(/^www\./, "");
673
892
  writeNewFileSync(
@@ -694,19 +913,6 @@ async function generateProjectScaffold(outputDir, url, tokens, animationCatalog,
694
913
  }
695
914
  }
696
915
 
697
- // src/capture/browserLaunchArgs.ts
698
- function captureBrowserArgs(disableWebgl, viewportWidth, viewportHeight) {
699
- return [
700
- "--no-sandbox",
701
- "--disable-dev-shm-usage",
702
- ...disableWebgl ? ["--disable-gpu"] : ["--enable-webgl", "--ignore-gpu-blocklist", "--use-gl=angle", "--use-angle=swiftshader"],
703
- "--disable-blink-features=AutomationControlled",
704
- "--disable-background-timer-throttling",
705
- "--disable-renderer-backgrounding",
706
- `--window-size=${viewportWidth},${viewportHeight}`
707
- ];
708
- }
709
-
710
916
  // src/capture/pageBlockDetection.ts
711
917
  function detectBlockedPage(evidence) {
712
918
  const isMinimalDom = evidence.bodyChildCount <= 5 && evidence.textLength < 500;
@@ -758,18 +964,18 @@ async function navigateForCapture(page, url, totalTimeoutMs) {
758
964
  }
759
965
 
760
966
  // src/capture/responseRecord.ts
761
- import { join as join3 } from "path";
967
+ import { join as join4 } from "path";
762
968
  var RESPONSE_RECORD_FILENAME = "response.json";
763
969
  function writeResponseRecord(extractedDir, record2) {
764
970
  writeCaptureFileSync(
765
- join3(extractedDir, RESPONSE_RECORD_FILENAME),
971
+ join4(extractedDir, RESPONSE_RECORD_FILENAME),
766
972
  JSON.stringify(record2, null, 2),
767
973
  "utf-8"
768
974
  );
769
975
  }
770
976
 
771
977
  // src/capture/navigationPhase.ts
772
- import { join as join4 } from "path";
978
+ import { join as join5 } from "path";
773
979
  async function navigatePage(context) {
774
980
  let navigation;
775
981
  try {
@@ -823,7 +1029,7 @@ async function checkPageContent(context) {
823
1029
  function persistNavigationResult(context, navigationResponse, pageContentCheck) {
824
1030
  context.httpStatus = navigationResponse ? navigationResponse.status() : null;
825
1031
  if (context.canWrite()) {
826
- writeResponseRecord(join4(context.outputDir, "extracted"), { status: context.httpStatus });
1032
+ writeResponseRecord(join5(context.outputDir, "extracted"), { status: context.httpStatus });
827
1033
  }
828
1034
  const blockedReason = detectBlockedPage({
829
1035
  httpStatus: context.httpStatus,
@@ -858,7 +1064,7 @@ import {
858
1064
  fstatSync,
859
1065
  closeSync
860
1066
  } from "fs";
861
- import { basename, join as join5 } from "path";
1067
+ import { basename, join as join6 } from "path";
862
1068
  var DEFAULT_VISION_REQUEST_TIMEOUT_MS = 3e4;
863
1069
  function resolveVisionPhaseCompletion(outcome, remainingMs) {
864
1070
  if (outcome.budgetExhausted || remainingMs <= 0) {
@@ -885,7 +1091,7 @@ function resolveVisionRequestTimeoutMs() {
885
1091
  const configured = Number(process.env.HYPERFRAMES_VISION_TIMEOUT_MS);
886
1092
  return Number.isFinite(configured) && configured > 0 ? configured : DEFAULT_VISION_REQUEST_TIMEOUT_MS;
887
1093
  }
888
- async function runBoundedVisionRequest(request, timeoutMs) {
1094
+ async function runBoundedVisionRequest(request2, timeoutMs) {
889
1095
  const controller = new AbortController();
890
1096
  let timer;
891
1097
  const timeout = new Promise((_resolve, reject) => {
@@ -895,7 +1101,7 @@ async function runBoundedVisionRequest(request, timeoutMs) {
895
1101
  }, timeoutMs);
896
1102
  });
897
1103
  try {
898
- return await Promise.race([request(controller.signal), timeout]);
1104
+ return await Promise.race([request2(controller.signal), timeout]);
899
1105
  } finally {
900
1106
  if (timer) clearTimeout(timer);
901
1107
  }
@@ -1146,7 +1352,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
1146
1352
  return response.text?.trim() || "";
1147
1353
  };
1148
1354
  }
1149
- const imageFiles = readdirSync(join5(outputDir, "assets")).filter(
1355
+ const imageFiles = readdirSync(join6(outputDir, "assets")).filter(
1150
1356
  (f) => /\.(png|jpg|jpeg|webp|gif)$/i.test(f)
1151
1357
  );
1152
1358
  const BATCH_SIZE = 20;
@@ -1174,7 +1380,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
1174
1380
  const batch = imageFiles.slice(i, i + BATCH_SIZE);
1175
1381
  const results = await Promise.allSettled(
1176
1382
  batch.map(async (file) => {
1177
- const filePath = join5(outputDir, "assets", file);
1383
+ const filePath = join6(outputDir, "assets", file);
1178
1384
  const fd = openSync(filePath, "r", 384);
1179
1385
  let buffer;
1180
1386
  try {
@@ -1213,11 +1419,11 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
1213
1419
  `${Object.keys(geminiCaptions).length} images captioned with ${providerName}`
1214
1420
  );
1215
1421
  const svgFiles = [];
1216
- const assetsDir = join5(outputDir, "assets");
1422
+ const assetsDir = join6(outputDir, "assets");
1217
1423
  for (const f of readdirSync(assetsDir)) {
1218
1424
  if (/\.svg$/i.test(f)) svgFiles.push({ file: f, relPath: f });
1219
1425
  }
1220
- const svgsSubdir = join5(assetsDir, "svgs");
1426
+ const svgsSubdir = join6(assetsDir, "svgs");
1221
1427
  if (existsSync2(svgsSubdir)) {
1222
1428
  for (const f of readdirSync(svgsSubdir)) {
1223
1429
  if (/\.svg$/i.test(f)) svgFiles.push({ file: f, relPath: `svgs/${f}` });
@@ -1251,7 +1457,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
1251
1457
  sharp.concurrency(1);
1252
1458
  try {
1253
1459
  for (const { relPath } of batch) {
1254
- const filePath = join5(assetsDir, relPath);
1460
+ const filePath = join6(assetsDir, relPath);
1255
1461
  try {
1256
1462
  const svgSource = readFileSync2(filePath, "utf-8");
1257
1463
  const lightFillHits = (svgSource.match(/fill\s*=\s*["'](#fff(fff)?|white|#[ef][ef][ef]|#[ef]{6})["']/gi) || []).length;
@@ -1326,11 +1532,11 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
1326
1532
  const uncaptionedLines = [];
1327
1533
  const svgLines = [];
1328
1534
  const fontLines = [];
1329
- const assetsPath = join5(outputDir, "assets");
1535
+ const assetsPath = join6(outputDir, "assets");
1330
1536
  try {
1331
1537
  for (const file of readdirSync(assetsPath)) {
1332
1538
  if (file === "svgs" || file === "fonts" || file === "lottie" || file === "videos") continue;
1333
- const filePath = join5(assetsPath, file);
1539
+ const filePath = join6(assetsPath, file);
1334
1540
  const stat = statSync(filePath);
1335
1541
  if (!stat.isFile()) continue;
1336
1542
  const sizeKb = Math.round(stat.size / 1024);
@@ -1359,7 +1565,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
1359
1565
  } catch {
1360
1566
  }
1361
1567
  try {
1362
- const svgsPath = join5(assetsPath, "svgs");
1568
+ const svgsPath = join6(assetsPath, "svgs");
1363
1569
  for (const file of readdirSync(svgsPath)) {
1364
1570
  if (!file.endsWith(".svg")) continue;
1365
1571
  const svgMatch = tokens.svgs.find(
@@ -1378,7 +1584,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
1378
1584
  } catch {
1379
1585
  }
1380
1586
  try {
1381
- const fontsPath = join5(assetsPath, "fonts");
1587
+ const fontsPath = join6(assetsPath, "fonts");
1382
1588
  for (const file of readdirSync(fontsPath)) {
1383
1589
  fontLines.push(`fonts/${file} \u2014 font file`);
1384
1590
  }
@@ -1387,7 +1593,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
1387
1593
  const videoLines = [];
1388
1594
  try {
1389
1595
  const manifest = JSON.parse(
1390
- readFileSync2(join5(outputDir, "extracted", "video-manifest.json"), "utf-8")
1596
+ readFileSync2(join6(outputDir, "extracted", "video-manifest.json"), "utf-8")
1391
1597
  );
1392
1598
  for (const v of manifest) {
1393
1599
  if (!v.localPath) continue;
@@ -1405,7 +1611,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
1405
1611
  }
1406
1612
 
1407
1613
  // src/capture/coreExtractionPhase.ts
1408
- import { join as join7 } from "path";
1614
+ import { join as join8 } from "path";
1409
1615
 
1410
1616
  // src/capture/htmlExtractor.ts
1411
1617
  var DEFAULT_SETTLE_TIME = 3e3;
@@ -1417,12 +1623,15 @@ async function extractHtml(page, opts = {}) {
1417
1623
  })()`);
1418
1624
  for (const href of stylesheetUrls) {
1419
1625
  try {
1420
- if (isPrivateUrl(href)) continue;
1421
- const res = await fetch(href, {
1626
+ const res = await safeFetch(href, {
1422
1627
  signal: AbortSignal.timeout(1e4),
1423
1628
  headers: { "User-Agent": CAPTURE_USER_AGENT }
1424
1629
  });
1425
- if (!res.ok) continue;
1630
+ if (!res?.ok) {
1631
+ await res?.body?.cancel().catch(() => {
1632
+ });
1633
+ continue;
1634
+ }
1426
1635
  let css = await res.text();
1427
1636
  css = css.replace(/url\(\s*['"]?([^'")\s]+)['"]?\s*\)/g, (match, url) => {
1428
1637
  if (url.startsWith("data:") || url.startsWith("http") || url.startsWith("//")) return match;
@@ -2616,7 +2825,7 @@ async function extractDesignStyles(page) {
2616
2825
 
2617
2826
  // src/capture/mediaCapture.ts
2618
2827
  import { readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
2619
- import { join as join6, extname } from "path";
2828
+ import { join as join7, extname } from "path";
2620
2829
 
2621
2830
  // src/capture/lottiePreviewRequests.ts
2622
2831
  var LOTTIE_RUNTIME_URL = "https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js";
@@ -2628,9 +2837,9 @@ function allowedLottieRequest(url, resourceType) {
2628
2837
  }
2629
2838
  async function guardLottiePreviewRequests(page) {
2630
2839
  await page.setRequestInterception(true);
2631
- page.on("request", (request) => {
2632
- if (request.isInterceptResolutionHandled()) return;
2633
- const action = allowedLottieRequest(request.url(), request.resourceType()) ? request.continue() : request.abort("blockedbyclient");
2840
+ page.on("request", (request2) => {
2841
+ if (request2.isInterceptResolutionHandled()) return;
2842
+ const action = allowedLottieRequest(request2.url(), request2.resourceType()) ? request2.continue() : request2.abort("blockedbyclient");
2634
2843
  void action.catch(() => {
2635
2844
  });
2636
2845
  });
@@ -2675,7 +2884,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir, outputDir, bud
2675
2884
  if (savedHashes.has(hash)) continue;
2676
2885
  savedHashes.add(hash);
2677
2886
  if (!validLottieJson(jsonData)) continue;
2678
- writeCaptureFileSync(join6(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
2887
+ writeCaptureFileSync(join7(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
2679
2888
  savedCount++;
2680
2889
  }
2681
2890
  } catch {
@@ -2685,18 +2894,18 @@ async function saveLottieAnimations(discoveredLotties, lottieDir, outputDir, bud
2685
2894
  }
2686
2895
  async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget = {}) {
2687
2896
  const manifest = [];
2688
- const previewDir = join6(lottieDir, "previews");
2897
+ const previewDir = join7(lottieDir, "previews");
2689
2898
  ensureCaptureDirSync(outputDir, previewDir);
2690
2899
  for (const file of readdirSync2(lottieDir)) {
2691
2900
  if (!file.endsWith(".json")) continue;
2692
2901
  if (liveRemainingMs(budget, 1) <= 0) break;
2693
2902
  try {
2694
- const raw = JSON.parse(readFileSync3(join6(lottieDir, file), "utf-8"));
2903
+ const raw = JSON.parse(readFileSync3(join7(lottieDir, file), "utf-8"));
2695
2904
  const fr = raw.fr || 30;
2696
2905
  const dur = ((raw.op || 0) - (raw.ip || 0)) / fr;
2697
2906
  const previewName = file.replace(".json", "-preview.png");
2698
2907
  let preview;
2699
- const fileSize = statSync2(join6(lottieDir, file)).size;
2908
+ const fileSize = statSync2(join7(lottieDir, file)).size;
2700
2909
  if (fileSize > 2e6) continue;
2701
2910
  let previewPage;
2702
2911
  try {
@@ -2705,7 +2914,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
2705
2914
  if (liveRemainingMs(budget, 1) <= 0) break;
2706
2915
  await guardLottiePreviewRequests(previewPage);
2707
2916
  await previewPage.setViewport({ width: 400, height: 400 });
2708
- const animData = JSON.parse(readFileSync3(join6(lottieDir, file), "utf-8"));
2917
+ const animData = JSON.parse(readFileSync3(join7(lottieDir, file), "utf-8"));
2709
2918
  const midFrame = Math.floor(((raw.op || 0) - (raw.ip || 0)) * 0.3);
2710
2919
  await previewPage.setContent(
2711
2920
  `<!DOCTYPE html>
@@ -2736,7 +2945,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
2736
2945
  });
2737
2946
  if (liveRemainingMs(budget, 1) > 0) {
2738
2947
  const shot = await previewPage.screenshot({ type: "png", omitBackground: true });
2739
- writeCaptureFileSync(join6(previewDir, previewName), shot);
2948
+ writeCaptureFileSync(join7(previewDir, previewName), shot);
2740
2949
  preview = `assets/lottie/previews/${previewName}`;
2741
2950
  }
2742
2951
  } catch {
@@ -2759,7 +2968,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
2759
2968
  }
2760
2969
  if (manifest.length > 0) {
2761
2970
  writeCaptureFileSync(
2762
- join6(outputDir, "extracted", "lottie-manifest.json"),
2971
+ join7(outputDir, "extracted", "lottie-manifest.json"),
2763
2972
  JSON.stringify(manifest, null, 2),
2764
2973
  "utf-8"
2765
2974
  );
@@ -2800,7 +3009,7 @@ async function downloadVideoBody(srcUrl, filename, videosDir, timeoutMs) {
2800
3009
  }
2801
3010
  if (total < 1024) return null;
2802
3011
  const safe = /\.[a-z0-9]+$/i.test(filename) ? filename.replace(/[^\w.-]/g, "_") : `video${ext}`;
2803
- writeCaptureFileSync(join6(videosDir, safe), Buffer.concat(chunks));
3012
+ writeCaptureFileSync(join7(videosDir, safe), Buffer.concat(chunks));
2804
3013
  return `assets/videos/${safe}`;
2805
3014
  } catch {
2806
3015
  return null;
@@ -2912,9 +3121,9 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
2912
3121
  }
2913
3122
  const merged = [...byKey.values()];
2914
3123
  if (merged.length === 0) return;
2915
- const videoManifestDir = join6(outputDir, "assets", "videos");
3124
+ const videoManifestDir = join7(outputDir, "assets", "videos");
2916
3125
  ensureCaptureDirSync(outputDir, videoManifestDir);
2917
- const previewDir = join6(videoManifestDir, "previews");
3126
+ const previewDir = join7(videoManifestDir, "previews");
2918
3127
  ensureCaptureDirSync(outputDir, previewDir);
2919
3128
  const videoManifest = [];
2920
3129
  const dlStart = Date.now();
@@ -2951,7 +3160,7 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
2951
3160
  height: Math.min(rect.height, 1080)
2952
3161
  }
2953
3162
  });
2954
- writeCaptureFileSync(join6(previewDir, previewName), shot);
3163
+ writeCaptureFileSync(join7(previewDir, previewName), shot);
2955
3164
  preview = `assets/videos/previews/${previewName}`;
2956
3165
  }
2957
3166
  }
@@ -2981,7 +3190,7 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
2981
3190
  }
2982
3191
  if (videoManifest.length > 0) {
2983
3192
  writeCaptureFileSync(
2984
- join6(outputDir, "extracted", "video-manifest.json"),
3193
+ join7(outputDir, "extracted", "video-manifest.json"),
2985
3194
  JSON.stringify(videoManifest, null, 2),
2986
3195
  "utf-8"
2987
3196
  );
@@ -3126,7 +3335,7 @@ async function runCoreExtraction(input) {
3126
3335
  await collectNetworkLotties();
3127
3336
  const saveDiscoveredLotties = async () => {
3128
3337
  if (discoveredLotties.length > 0 && remainingMs() > 0) {
3129
- const lottieDir = join7(outputDir, "assets", "lottie");
3338
+ const lottieDir = join8(outputDir, "assets", "lottie");
3130
3339
  if (!canWrite()) return;
3131
3340
  const lottieBudget = { remainingMs, byteBudget: downloadByteBudget };
3132
3341
  const savedCount = await saveLottieAnimations(
@@ -3158,7 +3367,7 @@ async function runCoreExtraction(input) {
3158
3367
  capturedShaders = unique;
3159
3368
  if (canWrite()) {
3160
3369
  writeCaptureFileSync(
3161
- join7(outputDir, "extracted", "shaders.json"),
3370
+ join8(outputDir, "extracted", "shaders.json"),
3162
3371
  JSON.stringify(unique, null, 2),
3163
3372
  "utf-8"
3164
3373
  );
@@ -3176,7 +3385,7 @@ async function runCoreExtraction(input) {
3176
3385
  state.tokens = tokens;
3177
3386
  if (canWrite()) {
3178
3387
  writeCaptureFileSync(
3179
- join7(outputDir, "extracted", "tokens.json"),
3388
+ join8(outputDir, "extracted", "tokens.json"),
3180
3389
  serializeTokensForCapture(tokens),
3181
3390
  "utf-8"
3182
3391
  );
@@ -3187,7 +3396,7 @@ async function runCoreExtraction(input) {
3187
3396
  state.designStyles = designStyles;
3188
3397
  if (canWrite()) {
3189
3398
  writeCaptureFileSync(
3190
- join7(outputDir, "extracted", "design-styles.json"),
3399
+ join8(outputDir, "extracted", "design-styles.json"),
3191
3400
  JSON.stringify(designStyles, null, 2),
3192
3401
  "utf-8"
3193
3402
  );
@@ -3271,7 +3480,7 @@ ${err.stack}` : normalizeErrorMessage(err);
3271
3480
  const runHtmlExtraction = async () => {
3272
3481
  progress("design", "Cataloging assets...");
3273
3482
  try {
3274
- const { catalogAssets } = await import("./assetCataloger-YWJRJK23.js");
3483
+ const { catalogAssets } = await import("./assetCataloger-FE4K64GW.js");
3275
3484
  catalogedAssets = await catalogAssets(page1);
3276
3485
  progress("design", `${catalogedAssets.length} assets cataloged`);
3277
3486
  if (catalogedAssets.length === 0) {
@@ -3337,11 +3546,11 @@ ${err.stack}` : normalizeErrorMessage(err);
3337
3546
 
3338
3547
  // src/capture/postExtractionPhase.ts
3339
3548
  import { existsSync as existsSync4 } from "fs";
3340
- import { join as join9 } from "path";
3549
+ import { join as join10 } from "path";
3341
3550
 
3342
3551
  // src/capture/fontMetadataExtractor.ts
3343
3552
  import { readdirSync as readdirSync3, readFileSync as readFileSync4, existsSync as existsSync3 } from "fs";
3344
- import { join as join8 } from "path";
3553
+ import { join as join9 } from "path";
3345
3554
  import * as fontkit from "fontkit";
3346
3555
  function isFontCollection(value) {
3347
3556
  return value.type === "TTC" || value.type === "DFont";
@@ -3352,7 +3561,7 @@ function extractFontMetadata(fontsDir, outputPath) {
3352
3561
  if (existsSync3(fontsDir)) {
3353
3562
  const fontFiles = readdirSync3(fontsDir).filter((f) => /\.(woff2?|ttf|otf)$/i.test(f));
3354
3563
  for (const filename of fontFiles) {
3355
- const fullPath = join8(fontsDir, filename);
3564
+ const fullPath = join9(fontsDir, filename);
3356
3565
  const meta = readSingleFont(fullPath, filename);
3357
3566
  if (meta.identified) {
3358
3567
  files.push(meta);
@@ -3526,7 +3735,7 @@ function hasVisionCredentials(skipVision) {
3526
3735
  }
3527
3736
  function writeAssetDescriptionsFile(outputDir, lines, header) {
3528
3737
  writeCaptureFileSync(
3529
- join9(outputDir, "extracted", "asset-descriptions.md"),
3738
+ join10(outputDir, "extracted", "asset-descriptions.md"),
3530
3739
  header + lines.map((line) => `- ${line}`).join("\n") + "\n",
3531
3740
  "utf-8"
3532
3741
  );
@@ -3569,8 +3778,8 @@ async function runPostExtraction(input) {
3569
3778
  );
3570
3779
  try {
3571
3780
  const fontsManifest = extractFontMetadata(
3572
- join9(outputDir, "assets", "fonts"),
3573
- join9(outputDir, "extracted", "fonts-manifest.json")
3781
+ join10(outputDir, "assets", "fonts"),
3782
+ join10(outputDir, "extracted", "fonts-manifest.json")
3574
3783
  );
3575
3784
  if (fontsManifest.families.length > 0) {
3576
3785
  const summary = fontsManifest.families.map((f) => `${f.family}${f.variable ? " (variable)" : ""} \xD7 ${f.fileCount}`).join(", ");
@@ -3601,7 +3810,7 @@ async function runPostExtraction(input) {
3601
3810
  };
3602
3811
  if (canWrite()) {
3603
3812
  writeCaptureFileSync(
3604
- join9(outputDir, "extracted", "animations.json"),
3813
+ join10(outputDir, "extracted", "animations.json"),
3605
3814
  JSON.stringify(leanCatalog, null, 2),
3606
3815
  "utf-8"
3607
3816
  );
@@ -3624,7 +3833,7 @@ async function runPostExtraction(input) {
3624
3833
  state.assets = assets;
3625
3834
  if (canWrite()) {
3626
3835
  writeCaptureFileSync(
3627
- join9(outputDir, "extracted", "icons-manifest.json"),
3836
+ join10(outputDir, "extracted", "icons-manifest.json"),
3628
3837
  JSON.stringify(assetPass.icons, null, 2),
3629
3838
  "utf-8"
3630
3839
  );
@@ -3676,7 +3885,7 @@ async function runPostExtraction(input) {
3676
3885
  if (assets.length && Array.isArray(tokens.sections)) {
3677
3886
  if (canWrite()) {
3678
3887
  writeCaptureFileSync(
3679
- join9(outputDir, "extracted", "tokens.json"),
3888
+ join10(outputDir, "extracted", "tokens.json"),
3680
3889
  serializeTokensForCapture(tokens),
3681
3890
  "utf-8"
3682
3891
  );
@@ -3699,14 +3908,14 @@ ${extracted.bodyHtml}
3699
3908
  `;
3700
3909
  state.pageHtml = pageHtml;
3701
3910
  if (canWrite())
3702
- writeCaptureFileSync(join9(outputDir, "extracted", "page.html"), pageHtml, "utf-8");
3911
+ writeCaptureFileSync(join10(outputDir, "extracted", "page.html"), pageHtml, "utf-8");
3703
3912
  } catch (err) {
3704
3913
  warnings.push(`page.html write failed: ${err}`);
3705
3914
  }
3706
3915
  if (visibleTextContent) {
3707
3916
  if (canWrite()) {
3708
3917
  writeCaptureFileSync(
3709
- join9(outputDir, "extracted", "visible-text.txt"),
3918
+ join10(outputDir, "extracted", "visible-text.txt"),
3710
3919
  visibleTextContent,
3711
3920
  "utf-8"
3712
3921
  );
@@ -3768,8 +3977,8 @@ ${extracted.bodyHtml}
3768
3977
  const contactSheetBudget = { remainingMs };
3769
3978
  const runScrollContactSheet = async () => {
3770
3979
  const scrollSheets = await createScrollContactSheet(
3771
- join9(outputDir, "screenshots"),
3772
- join9(outputDir, "screenshots", "contact-sheet.jpg"),
3980
+ join10(outputDir, "screenshots"),
3981
+ join10(outputDir, "screenshots", "contact-sheet.jpg"),
3773
3982
  contactSheetBudget
3774
3983
  );
3775
3984
  if (scrollSheets.length > 0)
@@ -3780,11 +3989,11 @@ ${extracted.bodyHtml}
3780
3989
  };
3781
3990
  await runScrollContactSheet();
3782
3991
  const runAssetContactSheet = async () => {
3783
- const assetsImgDir = join9(outputDir, "assets");
3992
+ const assetsImgDir = join10(outputDir, "assets");
3784
3993
  if (existsSync4(assetsImgDir)) {
3785
3994
  const assetSheets = await createAssetContactSheet(
3786
3995
  assetsImgDir,
3787
- join9(outputDir, "assets", "contact-sheet.jpg"),
3996
+ join10(outputDir, "assets", "contact-sheet.jpg"),
3788
3997
  contactSheetBudget
3789
3998
  );
3790
3999
  if (assetSheets.length > 0)
@@ -3796,11 +4005,11 @@ ${extracted.bodyHtml}
3796
4005
  };
3797
4006
  await runAssetContactSheet();
3798
4007
  const runSvgContactSheet = async () => {
3799
- const svgsDir = join9(outputDir, "assets", "svgs");
3800
- const assetsRootDir = join9(outputDir, "assets");
4008
+ const svgsDir = join10(outputDir, "assets", "svgs");
4009
+ const assetsRootDir = join10(outputDir, "assets");
3801
4010
  const hasSvgsDir = existsSync4(svgsDir);
3802
4011
  if (hasSvgsDir) ensureCaptureDirSync(outputDir, svgsDir);
3803
- const svgOutputPath = hasSvgsDir ? join9(outputDir, "assets", "svgs", "contact-sheet.jpg") : join9(outputDir, "assets", "contact-sheet-svgs.jpg");
4012
+ const svgOutputPath = hasSvgsDir ? join10(outputDir, "assets", "svgs", "contact-sheet.jpg") : join10(outputDir, "assets", "contact-sheet-svgs.jpg");
3804
4013
  const svgSheets = await createSvgContactSheet(
3805
4014
  svgsDir,
3806
4015
  svgOutputPath,
@@ -3850,6 +4059,10 @@ async function captureWebsiteAttempt(opts, onProgress, disableWebgl, watchdog, s
3850
4059
  postNavigationBudgetMs = DEFAULT_POST_NAVIGATION_BUDGET_MS,
3851
4060
  onPhase
3852
4061
  } = opts;
4062
+ const initialUrl = new URL(url);
4063
+ if (!["http:", "https:"].includes(initialUrl.protocol) || initialUrl.username || initialUrl.password) {
4064
+ throw new Error("Capture requires a public HTTP or HTTPS URL without credentials");
4065
+ }
3853
4066
  const downloadByteBudget = createCaptureDownloadBudget();
3854
4067
  const warnings = [...state.warnings];
3855
4068
  state.warnings = warnings;
@@ -3880,20 +4093,21 @@ async function captureWebsiteAttempt(opts, onProgress, disableWebgl, watchdog, s
3880
4093
  };
3881
4094
  phase("browser", "started");
3882
4095
  loadEnvFile(outputDir);
3883
- ensureCaptureDirSync(outputDir, join10(outputDir, "extracted"));
3884
- ensureCaptureDirSync(outputDir, join10(outputDir, "screenshots"));
3885
- ensureCaptureDirSync(outputDir, join10(outputDir, "assets"));
4096
+ ensureCaptureDirSync(outputDir, join11(outputDir, "extracted"));
4097
+ ensureCaptureDirSync(outputDir, join11(outputDir, "screenshots"));
4098
+ ensureCaptureDirSync(outputDir, join11(outputDir, "assets"));
3886
4099
  progress("browser", "Launching headless Chrome...");
3887
4100
  const { ensureBrowser } = await import("./manager-HEUPD2YB.js");
3888
4101
  const browser = await ensureBrowser();
3889
- const puppeteer = await import("puppeteer-core");
3890
- const chromeBrowser = await launchManagedBrowser(puppeteer.default, {
3891
- headless: true,
4102
+ const captureBrowser = await launchCaptureBrowser({
3892
4103
  executablePath: browser.executablePath,
3893
4104
  protocolTimeout: captureProtocolTimeoutMs(timeout, budgetMs),
3894
- args: captureBrowserArgs(disableWebgl, viewportWidth, viewportHeight)
4105
+ disableWebgl,
4106
+ width: viewportWidth,
4107
+ height: viewportHeight
3895
4108
  });
3896
- watchdog.registerBrowser(chromeBrowser);
4109
+ const chromeBrowser = captureBrowser.browser;
4110
+ watchdog.registerBrowser(captureBrowser);
3897
4111
  let animationCatalog;
3898
4112
  let tokens = state.tokens;
3899
4113
  let extracted = state.extracted;
@@ -4052,7 +4266,7 @@ async function captureWebsiteAttempt(opts, onProgress, disableWebgl, watchdog, s
4052
4266
  animationCatalog,
4053
4267
  screenshots.length > 0,
4054
4268
  discoveredLotties.length > 0,
4055
- existsSync5(join10(outputDir, "extracted", "shaders.json")),
4269
+ existsSync5(join11(outputDir, "extracted", "shaders.json")),
4056
4270
  catalogedAssets,
4057
4271
  progress,
4058
4272
  warnings,
@@ -4083,8 +4297,8 @@ async function captureWebsiteAttempt(opts, onProgress, disableWebgl, watchdog, s
4083
4297
  lastPhase
4084
4298
  };
4085
4299
  } finally {
4086
- watchdog.unregisterBrowser(chromeBrowser);
4087
- await chromeBrowser.close();
4300
+ watchdog.unregisterBrowser(captureBrowser);
4301
+ await captureBrowser.close();
4088
4302
  }
4089
4303
  }
4090
4304