hyperframes 0.8.162 → 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.
- package/dist/{add-UW7GCPQF.js → add-5UR2S4ZU.js} +11 -11
- package/dist/{animatedGifPrep-YAJTDXB7.js → animatedGifPrep-NUU6VMFL.js} +5 -5
- package/dist/api.js +11 -11
- package/dist/{asset-TWBPYQOL.js → asset-TB7K7OFL.js} +2 -2
- package/dist/{assetCataloger-BIDQA34M.js → assetCataloger-FE4K64GW.js} +27 -7
- package/dist/{audio-DJIE4YLH.js → audio-WH3XWV6N.js} +10 -10
- package/dist/{auth-VTCEZYBT.js → auth-4BBOL3DI.js} +1 -1
- package/dist/{autoUpdate-JA3SV472.js → autoUpdate-7LARILUN.js} +2 -2
- package/dist/{backgroundChecks-J3WJDPQJ.js → backgroundChecks-5A5VAWOQ.js} +3 -3
- package/dist/backgroundChecksWorker.js +3 -3
- package/dist/{batchRender-C2JCG5EK.js → batchRender-GJ65EZ26.js} +3 -3
- package/dist/{beats-QVJVXZV5.js → beats-EVQOYYYQ.js} +4 -4
- package/dist/{benchmark-WNBAKOXS.js → benchmark-5KE22VUG.js} +8 -8
- package/dist/{browser-VJOTRIXL.js → browser-BLGJSG23.js} +3 -3
- package/dist/{bundleWithLocalizedFonts-SLBVWQ7O.js → bundleWithLocalizedFonts-O3BXZLCD.js} +2 -2
- package/dist/{capture-M5ZAD3BI.js → capture-KSXWVLT7.js} +312 -98
- package/dist/{capture-GUO23PVN.js → capture-PR52XOEQ.js} +2 -2
- package/dist/{catalog-SAC4GFIN.js → catalog-W3AG6ISP.js} +11 -11
- package/dist/{catch-up-T5RIRFJF.js → catch-up-JLZUALKW.js} +4 -4
- package/dist/{check-IRETF27J.js → check-PGH3VCIR.js} +30 -30
- package/dist/{checkBrowser-5IXR4QCK.js → checkBrowser-MNJBUCZD.js} +28 -27
- package/dist/{chunk-YLBV2PXM.js → chunk-36CWAWB7.js} +12 -1
- package/dist/{chunk-RTS7BGEC.js → chunk-3E3VMAAM.js} +1 -1
- package/dist/{chunk-MMV6IY6U.js → chunk-3KY7FUSS.js} +2 -2
- package/dist/{chunk-XTLV6UVP.js → chunk-3TKNBY6K.js} +2 -2
- package/dist/{chunk-KAKBRDU4.js → chunk-3XJVN7MR.js} +2 -2
- package/dist/{chunk-LYIG2GZ5.js → chunk-42AER3PM.js} +1 -1
- package/dist/{chunk-B7RUGHBT.js → chunk-4LXWLCIX.js} +1 -1
- package/dist/{chunk-T5BG7PYZ.js → chunk-56RENSU4.js} +1 -1
- package/dist/{chunk-B5FZ2CQM.js → chunk-57SASPYR.js} +13 -2
- package/dist/{chunk-NGKM64BP.js → chunk-5CTORVUM.js} +17 -2
- package/dist/{chunk-4ID635FL.js → chunk-5K5YH6M7.js} +1 -1
- package/dist/{chunk-T65MIV36.js → chunk-6DJLEE74.js} +3 -3
- package/dist/{chunk-GJQHHAAX.js → chunk-74H5SHQL.js} +3 -3
- package/dist/{chunk-PLEMSN3E.js → chunk-A356JY2J.js} +29 -19
- package/dist/{chunk-OMJA7JP6.js → chunk-AFPGUKMN.js} +4 -4
- package/dist/{chunk-RGYEL7QD.js → chunk-AXATVUCD.js} +3 -3
- package/dist/{chunk-GG37I7B3.js → chunk-BHGIBGUZ.js} +1 -1
- package/dist/{chunk-5YSHR4XC.js → chunk-CXVBPW5F.js} +6 -6
- package/dist/{chunk-GIPNECWL.js → chunk-D5PUMG2P.js} +1 -1
- package/dist/{chunk-D33YJZOH.js → chunk-DSXDYQQV.js} +1 -1
- package/dist/{chunk-3LQEZ4EI.js → chunk-FTFWZHTZ.js} +1 -1
- package/dist/{chunk-3NSVHCOH.js → chunk-G3QMPIFG.js} +2 -2
- package/dist/{chunk-PPF65XAF.js → chunk-GD7VU7CY.js} +1 -1
- package/dist/{chunk-GNFWKGSQ.js → chunk-GJOW6ZFW.js} +2 -2
- package/dist/{chunk-ERTJPFCH.js → chunk-H3JGPRWA.js} +1 -1
- package/dist/{chunk-EVRUR7DW.js → chunk-H45RC6HN.js} +1 -1
- package/dist/{chunk-DJZEIVAP.js → chunk-H46R2BND.js} +1 -1
- package/dist/{chunk-NQR5SBXX.js → chunk-HYRDV7AL.js} +1 -1
- package/dist/{chunk-XWWYGXJ5.js → chunk-JEW5CCRU.js} +2 -2
- package/dist/{chunk-FF5TAEPT.js → chunk-KR3M4GKH.js} +1 -1
- package/dist/{chunk-G642IIVK.js → chunk-LMHXUDSR.js} +1 -1
- package/dist/{chunk-VXVRPIJA.js → chunk-NOVAH7N4.js} +69 -1
- package/dist/{chunk-Q3TFX4OZ.js → chunk-NZ7K76Y2.js} +1 -1
- package/dist/{chunk-MP676E4B.js → chunk-O2CXBATN.js} +4 -3
- package/dist/{chunk-YJKUE3LV.js → chunk-PRZDWAH3.js} +1 -1
- package/dist/{chunk-KIJO75L4.js → chunk-Q4XFXAQ3.js} +18 -4
- package/dist/{chunk-N2NDDTYW.js → chunk-QGYAHDOR.js} +2 -2
- package/dist/{chunk-2ZLYSXZC.js → chunk-R64V3KQN.js} +10 -9
- package/dist/{chunk-PBNV5V5G.js → chunk-SYOB5Y35.js} +191 -3
- package/dist/{chunk-ON2HYNGB.js → chunk-UF45OJ3N.js} +6 -6
- package/dist/{chunk-S6OLD4SL.js → chunk-UH4IL4K4.js} +7 -7
- package/dist/{chunk-P4QE7SSN.js → chunk-ULAB77ZO.js} +1 -1
- package/dist/{chunk-YU2QRNWN.js → chunk-UZOZ2W2E.js} +3 -2
- package/dist/{chunk-ZEWX3GHP.js → chunk-VEAKQW4X.js} +1 -1
- package/dist/{chunk-AAUK5GDI.js → chunk-WE5X5AKF.js} +9 -9
- package/dist/{chunk-5K34RPE5.js → chunk-WLQII3K3.js} +2 -2
- package/dist/{chunk-JJYFRAI4.js → chunk-WZSWLCEH.js} +1 -1
- package/dist/{chunk-GS5EXZBV.js → chunk-XYDPJ3CQ.js} +9 -9
- package/dist/{chunk-SB5DX6AD.js → chunk-Z6VBXCYN.js} +2 -2
- package/dist/{chunk-P47XYVEQ.js → chunk-ZJO4SLHC.js} +1 -1
- package/dist/{clean-WB62QULT.js → clean-R7BXTAFB.js} +11 -11
- package/dist/cli.js +68 -60
- package/dist/{cloud-E5WB4DWZ.js → cloud-IPXFPX7M.js} +4 -4
- package/dist/{cloudrun-BIQXBUDO.js → cloudrun-RMBT2JQK.js} +3 -3
- package/dist/commands/layout-audit.browser.js +347 -20
- package/dist/{compare-GHFVHZ5A.js → compare-S3RMZBB4.js} +22 -22
- package/dist/{compiler-57M6W5WY.js → compiler-RKH75FZH.js} +2 -2
- package/dist/{component-ZGCC3MHX.js → component-VSOWEAK4.js} +3 -3
- package/dist/{compositions-THPEDDIX.js → compositions-7LPCAY7M.js} +9 -9
- package/dist/{delete-6FWT7DZX.js → delete-L32WDJGO.js} +2 -2
- package/dist/{dist-4JFB3WAR.js → dist-P5OHBFEV.js} +2 -2
- package/dist/{doctor-RDGDOUC4.js → doctor-7TSVAIF2.js} +7 -7
- package/dist/{dotEnv-AECLXQSO.js → dotEnv-64S3VKGH.js} +3 -1
- package/dist/{events-CU6FDXHC.js → events-FDHKA6NV.js} +3 -3
- package/dist/{events-VHXICDA2.js → events-RFABFHWU.js} +2 -2
- package/dist/{feedback-PUPN6EKP.js → feedback-SDAJ2HAM.js} +7 -7
- package/dist/{figma-ZKWBF7QH.js → figma-KJBYCHXQ.js} +3 -3
- package/dist/fontLocalizeCli.js +7 -7
- package/dist/{get-JHIBQRPM.js → get-6Z3JCPJ6.js} +2 -2
- package/dist/{gpuPolicy-VGE3I5J4.js → gpuPolicy-SYV7N237.js} +5 -5
- package/dist/{grade-compare-GPLUXDPW.js → grade-compare-HAQXCQSE.js} +22 -22
- package/dist/{help-GO6RGM37.js → help-WMMVG2PR.js} +1 -1
- package/dist/{history-RUTC4SH5.js → history-5AQNASLD.js} +6 -6
- package/dist/hyperframe-runtime.js +23 -23
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +23 -23
- package/dist/hyperframes-player.global.js +1 -1
- package/dist/{info-773VXW37.js → info-OUJ3HXTN.js} +7 -7
- package/dist/{init-YXSIRYZI.js → init-NEEYHH7E.js} +20 -20
- package/dist/{inspect-L4RRJEAA.js → inspect-ZWBDYWTY.js} +23 -23
- package/dist/{installer-T3STEB4U.js → installer-QWK7EA5M.js} +4 -4
- package/dist/{keyframes-P5IOQ6AM.js → keyframes-BA5R5Q7G.js} +6 -6
- package/dist/{lambda-RAJA6ZNA.js → lambda-LQHGF3LK.js} +4 -4
- package/dist/{layout-OH2VTN5E.js → layout-GJVLRWIJ.js} +23 -23
- package/dist/{lint-YEEPVE6Q.js → lint-FBH2BA2A.js} +6 -6
- package/dist/{list-QURRZTVN.js → list-XDZIX2PU.js} +2 -2
- package/dist/{login-DFT4L5YY.js → login-CUHIF5GP.js} +4 -4
- package/dist/{media-treatment-ERQ2PBIN.js → media-treatment-HZDD67IL.js} +8 -8
- package/dist/{models-OA4MXEYG.js → models-J2FZXHMD.js} +8 -8
- package/dist/{motionShot-C2YDYRNB.js → motionShot-D2C44I6P.js} +11 -11
- package/dist/{normalize-audio-R23VODOA.js → normalize-audio-B6PKQGHV.js} +4 -4
- package/dist/{open-GEOB4L3S.js → open-EIN2FH4D.js} +4 -4
- package/dist/{parakeetRunner-DBLHMD5T.js → parakeetRunner-NDIU5PKO.js} +8 -8
- package/dist/{pipeline-FSY65OQ4.js → pipeline-UKGI44NN.js} +5 -5
- package/dist/{play-BYUS37GA.js → play-KLOHE5XH.js} +16 -16
- package/dist/{present-BZLRNHDY.js → present-4K53KZ3P.js} +12 -12
- package/dist/{preview-TXML6PQ7.js → preview-PSALQBNR.js} +15 -15
- package/dist/{producer-C5V2A3YL.js → producer-66MQWVVQ.js} +1 -1
- package/dist/{projectConfig-X2IEHCSK.js → projectConfig-EVBNJKPS.js} +6 -6
- package/dist/{publication-JEANCWBW.js → publication-F6BOBZMI.js} +2 -2
- package/dist/{publish-VX5OH4ST.js → publish-6KTQDKL6.js} +8 -8
- package/dist/{remove-background-IXCISD26.js → remove-background-TJTKY3YM.js} +6 -6
- package/dist/{render-57SXH6NS.js → render-2QY3DTYM.js} +42 -29
- package/dist/{render-JTY7XQFQ.js → render-NAJFHWUG.js} +9 -9
- package/dist/{render-ISUT7Q3X.js → render-X4MWSR5L.js} +3 -3
- package/dist/{render-batch-2C6RUP3F.js → render-batch-VKFOIMV5.js} +3 -3
- package/dist/{resolver-KTNKZVIB.js → resolver-VGAM26OR.js} +4 -4
- package/dist/{sherpa-GFRG3TMM.js → sherpa-3OI5VMCY.js} +9 -7
- package/dist/skills/hyperframes-cli/references/lint-validate-inspect.md +1 -1
- package/dist/{skills-2LBVMKVY.js → skills-ETYVJOLG.js} +5 -5
- package/dist/{skillsUpdateCheck-5DRXL565.js → skillsUpdateCheck-6P4VCFZZ.js} +3 -3
- package/dist/{snapshot-V2L77FCF.js → snapshot-P3RZFXUC.js} +25 -25
- package/dist/{src-GPUDUKY6.js → src-LZXECBBQ.js} +4 -4
- package/dist/{src-IMCQRH4P.js → src-RQPKPFRO.js} +6 -6
- package/dist/{staticProjectServer-4CQ3KDQK.js → staticProjectServer-DMG2CER4.js} +12 -12
- package/dist/studio/assets/{hyperframes-player-CAclPB6q.js → hyperframes-player-CQX9TDla.js} +1 -1
- package/dist/studio/assets/{index-DCwGKghK.js → index-BnU7S7oM.js} +1 -1
- package/dist/studio/assets/{index-B82aOvgB.js → index-DHqUZqg6.js} +4 -4
- package/dist/studio/index.html +1 -1
- package/dist/{studio-api-RYCDLEOR.js → studio-api-KRPBPUYQ.js} +6 -6
- package/dist/{studioServer-B7H7EQ4U.js → studioServer-HF6ZCYQO.js} +54 -41
- package/dist/{telemetry-WVHWY3DU.js → telemetry-ASFTF5YV.js} +3 -3
- package/dist/{timeline-EDXJ5L45.js → timeline-H7Q2K6ZQ.js} +13 -13
- package/dist/{tokens-UNJONAXT.js → tokens-LWCXLQCR.js} +2 -2
- package/dist/{transcribe-FL6NGWHF.js → transcribe-HOLKYWY5.js} +6 -6
- package/dist/{transcribe-VX7ZE2IZ.js → transcribe-X2GOG3HZ.js} +12 -12
- package/dist/{updateCheck-NLH5E44Q.js → updateCheck-EA7X36NI.js} +2 -2
- package/dist/{upgrade-RGQJA2B4.js → upgrade-Y6H3ZEEJ.js} +2 -2
- package/dist/{urlDownloader-KDJGLR2Y.js → urlDownloader-Z435W35E.js} +4 -4
- package/dist/{validate-4AN4PDV7.js → validate-TZAB2PVX.js} +17 -17
- package/dist/{video-AHO4CDCK.js → video-LNLD2PKZ.js} +5 -5
- 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-
|
|
25
|
+
} from "./chunk-SYOB5Y35.js";
|
|
25
26
|
import {
|
|
26
27
|
CaptureDirRefusedError,
|
|
27
28
|
NavigationDeadlineError,
|
|
@@ -35,9 +36,11 @@ import {
|
|
|
35
36
|
withRemainingBudget
|
|
36
37
|
} from "./chunk-AWNBM3MZ.js";
|
|
37
38
|
import {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
closeManagedBrowser,
|
|
40
|
+
launchManagedBrowser,
|
|
41
|
+
registerBrowserSessionCleanup
|
|
42
|
+
} from "./chunk-57SASPYR.js";
|
|
43
|
+
import "./chunk-Z6VBXCYN.js";
|
|
41
44
|
import {
|
|
42
45
|
writeNewFileSync
|
|
43
46
|
} from "./chunk-4YYJOFKS.js";
|
|
@@ -49,21 +52,22 @@ import {
|
|
|
49
52
|
diag
|
|
50
53
|
} from "./chunk-VWG7OQKU.js";
|
|
51
54
|
import {
|
|
55
|
+
CAPTURE_ENV_KEYS,
|
|
52
56
|
applyDotEnv
|
|
53
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-5CTORVUM.js";
|
|
54
58
|
import "./chunk-IGLEPPTN.js";
|
|
55
59
|
import "./chunk-PBN6GBJE.js";
|
|
56
60
|
import {
|
|
57
61
|
loadOptionalPackage
|
|
58
62
|
} from "./chunk-3DKN5WKL.js";
|
|
59
63
|
import "./chunk-6BEFEWFB.js";
|
|
60
|
-
import "./chunk-
|
|
61
|
-
import "./chunk-
|
|
64
|
+
import "./chunk-A356JY2J.js";
|
|
65
|
+
import "./chunk-42AER3PM.js";
|
|
62
66
|
import "./chunk-KCF6GHJP.js";
|
|
63
67
|
import "./chunk-JYVGYKXG.js";
|
|
64
68
|
import "./chunk-7KDQAD2W.js";
|
|
65
|
-
import "./chunk-
|
|
66
|
-
import "./chunk-
|
|
69
|
+
import "./chunk-NZ7K76Y2.js";
|
|
70
|
+
import "./chunk-ZJO4SLHC.js";
|
|
67
71
|
import "./chunk-VC76R6W2.js";
|
|
68
72
|
import "./chunk-UJKQWGFF.js";
|
|
69
73
|
import "./chunk-ALLKNYC3.js";
|
|
@@ -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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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([
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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;
|
|
@@ -1978,7 +2187,7 @@ var EXTRACT_SCRIPT = `(() => {
|
|
|
1978
2187
|
viewBox: svg.getAttribute("viewBox") || undefined,
|
|
1979
2188
|
width: Math.round(rect.width),
|
|
1980
2189
|
height: Math.round(rect.height),
|
|
1981
|
-
outerHTML: svg.outerHTML
|
|
2190
|
+
outerHTML: svg.outerHTML,
|
|
1982
2191
|
isLogo: isLogo
|
|
1983
2192
|
};
|
|
1984
2193
|
}).filter(Boolean).slice(0, 50);
|
|
@@ -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
|
|
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", (
|
|
2632
|
-
if (
|
|
2633
|
-
const action = allowedLottieRequest(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
3124
|
+
const videoManifestDir = join7(outputDir, "assets", "videos");
|
|
2916
3125
|
ensureCaptureDirSync(outputDir, videoManifestDir);
|
|
2917
|
-
const previewDir =
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
3573
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
3772
|
-
|
|
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 =
|
|
3992
|
+
const assetsImgDir = join10(outputDir, "assets");
|
|
3784
3993
|
if (existsSync4(assetsImgDir)) {
|
|
3785
3994
|
const assetSheets = await createAssetContactSheet(
|
|
3786
3995
|
assetsImgDir,
|
|
3787
|
-
|
|
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 =
|
|
3800
|
-
const assetsRootDir =
|
|
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 ?
|
|
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,
|
|
3884
|
-
ensureCaptureDirSync(outputDir,
|
|
3885
|
-
ensureCaptureDirSync(outputDir,
|
|
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
|
|
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
|
-
|
|
4105
|
+
disableWebgl,
|
|
4106
|
+
width: viewportWidth,
|
|
4107
|
+
height: viewportHeight
|
|
3895
4108
|
});
|
|
3896
|
-
|
|
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(
|
|
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(
|
|
4087
|
-
await
|
|
4300
|
+
watchdog.unregisterBrowser(captureBrowser);
|
|
4301
|
+
await captureBrowser.close();
|
|
4088
4302
|
}
|
|
4089
4303
|
}
|
|
4090
4304
|
|