chore: import upstream snapshot with attribution
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
/**
|
||||
* Static file server for player perf tests.
|
||||
*
|
||||
* Serves all bundles, vendor scripts, fixtures, and the embed host page from
|
||||
* a single origin so the player iframe stays same-origin. Without same-origin
|
||||
* the runtime probe in `_onIframeLoad` falls into the cross-origin catch path
|
||||
* and the `ready` event fires later (or not at all) — which would be measured
|
||||
* as a player-side regression instead of an environment artifact.
|
||||
*
|
||||
* URL routes:
|
||||
* / → host.html (default fixture: gsap-heavy)
|
||||
* /host.html?fixture=<name> → embed page hosting <hyperframes-player>
|
||||
* /player/hyperframes-player.global.js
|
||||
* /vendor/gsap.min.js
|
||||
* /vendor/hyperframe.runtime.iife.js
|
||||
* /fixtures/<name>/<file> → fixture HTML + assets
|
||||
*/
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const PLAYER_PKG = resolve(HERE, "../..");
|
||||
const REPO_ROOT = resolve(PLAYER_PKG, "../..");
|
||||
|
||||
function firstExisting(candidates: string[]): string {
|
||||
for (const p of candidates) {
|
||||
if (existsSync(p)) return p;
|
||||
}
|
||||
return candidates[0] ?? "";
|
||||
}
|
||||
|
||||
const PATHS = {
|
||||
player: join(PLAYER_PKG, "dist/hyperframes-player.global.js"),
|
||||
runtime: join(REPO_ROOT, "packages/core/dist/hyperframe.runtime.iife.js"),
|
||||
// bun installs gsap into the package's node_modules in workspace mode, but
|
||||
// hoists it to the repo root if multiple packages share the same version.
|
||||
// Probe both locations so the server works regardless of layout.
|
||||
gsap: firstExisting([
|
||||
join(PLAYER_PKG, "node_modules/gsap/dist/gsap.min.js"),
|
||||
join(REPO_ROOT, "node_modules/gsap/dist/gsap.min.js"),
|
||||
]),
|
||||
fixturesDir: join(HERE, "fixtures"),
|
||||
} as const;
|
||||
|
||||
export type ServeOptions = {
|
||||
port?: number;
|
||||
/** Disables HTTP cache so every request is a "cold" fetch. Used for cold-load scenarios. */
|
||||
noCache?: boolean;
|
||||
};
|
||||
|
||||
export type RunningServer = {
|
||||
port: number;
|
||||
origin: string;
|
||||
stop(): Promise<void>;
|
||||
};
|
||||
|
||||
const MIME_TYPES: Record<string, string> = {
|
||||
".html": "text/html; charset=utf-8",
|
||||
".js": "application/javascript; charset=utf-8",
|
||||
".mjs": "application/javascript; charset=utf-8",
|
||||
".css": "text/css; charset=utf-8",
|
||||
".json": "application/json; charset=utf-8",
|
||||
".png": "image/png",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".webp": "image/webp",
|
||||
".mp4": "video/mp4",
|
||||
".webm": "video/webm",
|
||||
".mp3": "audio/mpeg",
|
||||
};
|
||||
|
||||
function mimeFor(path: string): string {
|
||||
const dot = path.lastIndexOf(".");
|
||||
if (dot < 0) return "application/octet-stream";
|
||||
return MIME_TYPES[path.slice(dot).toLowerCase()] ?? "application/octet-stream";
|
||||
}
|
||||
|
||||
function buildHostHtml(fixtureName: string, width: number, height: number): string {
|
||||
const playerSrc = "/player/hyperframes-player.global.js";
|
||||
const fixtureSrc = `/fixtures/${fixtureName}/index.html`;
|
||||
return `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>player perf host: ${fixtureName}</title>
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0; background: #000; }
|
||||
hyperframes-player { display: block; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<hyperframes-player
|
||||
id="player"
|
||||
src="${fixtureSrc}"
|
||||
width="${width}"
|
||||
height="${height}"
|
||||
muted
|
||||
></hyperframes-player>
|
||||
<script>
|
||||
window.__playerReady = false;
|
||||
window.__playerReadyAt = null;
|
||||
window.__playerNavStart = performance.timeOrigin + performance.now();
|
||||
const player = document.getElementById("player");
|
||||
player.addEventListener("ready", function (event) {
|
||||
window.__playerReady = true;
|
||||
window.__playerReadyAt = performance.timeOrigin + performance.now();
|
||||
window.__playerDuration = (event.detail && event.detail.duration) || 0;
|
||||
});
|
||||
player.addEventListener("error", function (event) {
|
||||
window.__playerError = (event.detail && event.detail.message) || "unknown";
|
||||
});
|
||||
</script>
|
||||
<script src="${playerSrc}"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
async function readBunFile(path: string): Promise<Response> {
|
||||
if (!existsSync(path)) {
|
||||
return new Response(`Not found: ${path}`, { status: 404 });
|
||||
}
|
||||
const file = Bun.file(path);
|
||||
return new Response(file, {
|
||||
headers: {
|
||||
"Content-Type": mimeFor(path),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function applyCacheHeaders(res: Response, noCache: boolean): Response {
|
||||
if (noCache) {
|
||||
res.headers.set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
|
||||
res.headers.set("Pragma", "no-cache");
|
||||
res.headers.set("Expires", "0");
|
||||
} else {
|
||||
res.headers.set("Cache-Control", "public, max-age=3600");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export function startServer(options: ServeOptions = {}): RunningServer {
|
||||
const noCache = options.noCache ?? false;
|
||||
|
||||
const server = Bun.serve({
|
||||
port: options.port ?? 0,
|
||||
async fetch(req) {
|
||||
const url = new URL(req.url);
|
||||
const path = url.pathname;
|
||||
|
||||
if (path === "/" || path === "/host.html") {
|
||||
const fixture = url.searchParams.get("fixture") || "gsap-heavy";
|
||||
const width = Number(url.searchParams.get("width") || "1920");
|
||||
const height = Number(url.searchParams.get("height") || "1080");
|
||||
const html = buildHostHtml(fixture, width, height);
|
||||
return applyCacheHeaders(
|
||||
new Response(html, { headers: { "Content-Type": "text/html; charset=utf-8" } }),
|
||||
noCache,
|
||||
);
|
||||
}
|
||||
|
||||
if (path === "/player/hyperframes-player.global.js") {
|
||||
return applyCacheHeaders(await readBunFile(PATHS.player), noCache);
|
||||
}
|
||||
|
||||
if (path === "/vendor/hyperframe.runtime.iife.js") {
|
||||
return applyCacheHeaders(await readBunFile(PATHS.runtime), noCache);
|
||||
}
|
||||
|
||||
if (path === "/vendor/gsap.min.js") {
|
||||
return applyCacheHeaders(await readBunFile(PATHS.gsap), noCache);
|
||||
}
|
||||
|
||||
if (path.startsWith("/fixtures/")) {
|
||||
const rel = path.replace(/^\/fixtures\//, "");
|
||||
const filePath = join(PATHS.fixturesDir, rel);
|
||||
if (!filePath.startsWith(PATHS.fixturesDir)) {
|
||||
return new Response("Forbidden", { status: 403 });
|
||||
}
|
||||
return applyCacheHeaders(await readBunFile(filePath), noCache);
|
||||
}
|
||||
|
||||
return new Response("Not found", { status: 404 });
|
||||
},
|
||||
});
|
||||
|
||||
// server.port is `number | undefined` in Bun's types (undefined only for unix-socket
|
||||
// servers, which we never use). Narrow it once at startup so the rest of the perf
|
||||
// harness can rely on a numeric origin.
|
||||
const port = server.port;
|
||||
if (port === undefined) {
|
||||
throw new Error("[player-perf] Bun.serve did not assign a TCP port");
|
||||
}
|
||||
return {
|
||||
port,
|
||||
origin: `http://127.0.0.1:${port}`,
|
||||
async stop() {
|
||||
server.stop(true);
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user