fix(studio-server): keep transparency in proxies of VP8/VP9 alpha webm (#6025)

* fix(studio-server): keep transparency in proxies of VP8/VP9 alpha webm

A webm whose alpha lives in the VP8/VP9 side channel (alpha_mode=1,
pix_fmt yuv420p) was proxied to opaque H.264, and the VP8 proxy path
decoded it with ffmpeg's native decoder, which drops alpha. The alpha
rule and the alpha-reading decoder now live once in
@hyperframes/parsers/media-alpha, used by engine and studio-server.

* build(parsers): raise the packed size budget for the media-alpha subpath

parsers already sat at its packed budget on main; the shared alpha rule
adds 676 B of JS and 803 B of declarations (190958 B packed in CI).
This commit is contained in:
Miguel Ángel
2026-10-21 13:49:30 +00:00
committed by GitHub
parent cde21630e4
commit d67e81c7f1
15 changed files with 218 additions and 99 deletions
@@ -14,6 +14,7 @@ import { createConcurrencyLimit } from "../utils/concurrencyLimit.js";
import { parseHTML } from "linkedom";
import { isPathInside } from "@hyperframes/parsers/asset-paths";
import { resolveProjectRelativeSrc } from "@hyperframes/parsers/asset-resolution";
import { alphaDecoderFor } from "@hyperframes/parsers/media-alpha";
import {
MEDIA_RENDER_ID_ATTR,
isAudibleVideoElement,
@@ -1580,10 +1581,7 @@ export function codecMayHaveAlpha(codec: string | undefined): boolean {
}
export function decoderForCodec(codec: string | undefined): string {
const c = (codec ?? "").toLowerCase();
if (c === "vp9") return "libvpx-vp9";
if (c === "vp8") return "libvpx";
return c;
return alphaDecoderFor(codec) ?? (codec ?? "").toLowerCase();
}
export function resolveFrameFormat(
+1 -27
View File
@@ -5,12 +5,7 @@ import { mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs
import { tmpdir } from "os";
import { basename, resolve } from "path";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
extractMediaMetadata,
extractPngMetadataFromBuffer,
parseFrameRate,
pixelFormatHasAlpha,
} from "./ffprobe.js";
import { extractMediaMetadata, extractPngMetadataFromBuffer, parseFrameRate } from "./ffprobe.js";
function crc32(buf: Buffer): number {
let crc = 0xffffffff;
@@ -1737,27 +1732,6 @@ describe("crc32 works on every runtime the package declares", () => {
});
});
describe("pix_fmt alpha detection", () => {
// The old pattern's (^|[^a-z]) anchor bound to `yuva` alone, and the list
// omitted formats real files actually use.
const ALPHA = [
"yuva420p",
"rgba",
"argb",
"bgra",
"abgr",
"gbrap",
"ya8",
"ya16le",
"ayuv64le",
"yuva444p12le",
];
const OPAQUE = ["yuv420p", "rgb24", "gray", "gbrp", "nv12", "yuv444p10le", "bgr0", "rgb0"];
it.each(ALPHA)("detects alpha in %s", (fmt) => expect(pixelFormatHasAlpha(fmt)).toBe(true));
it.each(OPAQUE)("reports %s as opaque", (fmt) => expect(pixelFormatHasAlpha(fmt)).toBe(false));
});
describe("audio duration decode probe never fails the call and needs no profile allowlist", () => {
afterEach(() => {
vi.resetModules();
+2 -27
View File
@@ -5,6 +5,7 @@ import { chunkCrc32 } from "./crc32.js";
import { StringDecoder } from "node:string_decoder";
import { basename } from "path";
import { redactTelemetryString } from "@hyperframes/core";
import { videoStreamHasAlpha } from "@hyperframes/parsers/media-alpha";
import { urlInputArgs } from "./urlInput.js";
import { FFPROBE_PATH_ENV, getFfprobeBinary } from "./ffmpegBinaries.js";
import { ManagedChildProcess } from "./managedChildProcess.js";
@@ -604,23 +605,6 @@ export function extractPngMetadataFromBuffer(buf: Buffer): StillImageMetadata |
return width > 0 && height > 0 ? { width, height, colorSpace: colorSpaceFromCicp } : null;
}
/**
* Does this pix_fmt carry an alpha channel?
*
* Exported so the test asserts the shipped predicate rather than a copy of
* the pattern. Anchored at the start, matching studio-server's
* mediaMetadata.ts: the previous inline pattern bound its `(^|[^a-z])` anchor
* to the first alternative only — `|` is looser than concatenation — so the
* guard was decorative for every other name. It also missed abgr, ya8/ya16
* and ayuv64, and its `gray[a-z0-9]*a` branch matched only gray8a/gray16a,
* names FFmpeg renamed to ya8/ya16 in 2013. A `ya8` grayscale-plus-alpha PNG
* reported hasAlpha:false, resolveFrameFormat picked jpg, and the overlay
* flattened to an opaque rectangle.
*/
export function pixelFormatHasAlpha(pixelFormat: string): boolean {
return /^(?:yuva|rgba|argb|bgra|abgr|gbrap|ya|ayuv)/i.test(pixelFormat);
}
function extractStillImageMetadata(filePath: string): StillImageMetadata | null {
try {
return extractPngMetadataFromBuffer(readFileSync(filePath));
@@ -791,16 +775,7 @@ export async function extractMediaMetadata(filePath: string): Promise<VideoMetad
const colorSpace =
merged.colorTransfer || merged.colorPrimaries || merged.colorSpace ? merged : null;
const pixelFormat = videoStream.pix_fmt || "";
const alphaMode = readTagCI(videoStream.tags, "alpha_mode");
const hasAlpha = pixelFormatHasAlpha(pixelFormat) || alphaMode === "1";
// Anchored at the start, matching studio-server's mediaMetadata.ts. The
// previous pattern bound its `(^|[^a-z])` anchor to the FIRST alternative
// only — `|` is looser than concatenation — so the guard was decorative
// for every other name. It also missed abgr, ya8/ya16 and ayuv64, and its
// `gray[a-z0-9]*a` branch matched only gray8a/gray16a, names FFmpeg
// renamed to ya8/ya16 in 2013. A `ya8` grayscale-plus-alpha PNG reported
// hasAlpha:false, resolveFrameFormat picked jpg, and the overlay
// flattened to an opaque rectangle.
const hasAlpha = videoStreamHasAlpha(videoStream);
const containerDuration = output?.format.duration ? parseFloat(output.format.duration) : 0;
const streamDuration = videoStream.duration ? parseFloat(videoStream.duration) : 0;
+6
View File
@@ -116,6 +116,12 @@
"types": "./dist/ffBinaries.d.ts",
"environments": ["bun", "node"]
},
"./media-alpha": {
"source": "./src/mediaAlpha.ts",
"runtime": "./dist/mediaAlpha.js",
"types": "./dist/mediaAlpha.d.ts",
"environments": ["browser", "bun", "node"]
},
"./asset-resolution": {
"source": "./src/assetResolution.ts",
"runtime": "./dist/assetResolution.js",
+10
View File
@@ -124,6 +124,12 @@
"import": "./src/ffBinaries.ts",
"types": "./src/ffBinaries.ts"
},
"./media-alpha": {
"bun": "./src/mediaAlpha.ts",
"node": "./dist/mediaAlpha.js",
"import": "./src/mediaAlpha.ts",
"types": "./src/mediaAlpha.ts"
},
"./asset-resolution": {
"bun": "./src/assetResolution.ts",
"node": "./dist/assetResolution.js",
@@ -237,6 +243,10 @@
"import": "./dist/ffBinaries.js",
"types": "./dist/ffBinaries.d.ts"
},
"./media-alpha": {
"import": "./dist/mediaAlpha.js",
"types": "./dist/mediaAlpha.d.ts"
},
"./asset-resolution": {
"import": "./dist/assetResolution.js",
"types": "./dist/assetResolution.d.ts"
+70
View File
@@ -0,0 +1,70 @@
import { describe, expect, it } from "vitest";
import { alphaDecoderFor, pixelFormatHasAlpha, videoStreamHasAlpha } from "./mediaAlpha.js";
describe("pixelFormatHasAlpha", () => {
const ALPHA = [
"yuva420p",
"yuva444p10le",
"yuva444p12le",
"rgba",
"argb",
"bgra",
"abgr",
"gbrap",
"gbrap12le",
"ya8",
"ya16le",
"ayuv64le",
"YUVA420P",
];
const OPAQUE = [
"yuv420p",
"yuv422p10le",
"yuv444p10le",
"rgb24",
"gray",
"gbrp",
"nv12",
"bgr0",
"rgb0",
];
it.each(ALPHA)("detects alpha in %s", (fmt) => expect(pixelFormatHasAlpha(fmt)).toBe(true));
it.each(OPAQUE)("reports %s as opaque", (fmt) => expect(pixelFormatHasAlpha(fmt)).toBe(false));
it("reports a missing pix_fmt as opaque", () =>
expect(pixelFormatHasAlpha(undefined)).toBe(false));
});
describe("videoStreamHasAlpha", () => {
// ffprobe 8.1 on a libvpx-vp9 yuva420p encode: the alpha lives only in the tag.
const vp9Alpha = { codec_name: "vp9", pix_fmt: "yuv420p", tags: { ALPHA_MODE: "1" } };
it("reads the VP8/VP9 alpha_mode tag in any casing", () => {
expect(videoStreamHasAlpha(vp9Alpha)).toBe(true);
expect(videoStreamHasAlpha({ pix_fmt: "yuv420p", tags: { alpha_mode: "1" } })).toBe(true);
});
it("reads an alpha pix_fmt with no tag", () => {
expect(videoStreamHasAlpha({ pix_fmt: "yuva444p10le" })).toBe(true);
});
it("reports opaque streams", () => {
expect(videoStreamHasAlpha({ pix_fmt: "yuv420p" })).toBe(false);
expect(videoStreamHasAlpha({ pix_fmt: "yuv420p", tags: { ALPHA_MODE: "0" } })).toBe(false);
expect(videoStreamHasAlpha({ pix_fmt: "yuv420p", tags: { ENCODER: "Lavc" } })).toBe(false);
});
});
describe("alphaDecoderFor", () => {
it("picks the libvpx decoders that read the alpha side channel", () => {
expect(alphaDecoderFor("vp9")).toBe("libvpx-vp9");
expect(alphaDecoderFor("VP9")).toBe("libvpx-vp9");
expect(alphaDecoderFor("vp8")).toBe("libvpx");
});
it("leaves other codecs on ffmpeg's default decoder", () => {
expect(alphaDecoderFor("prores")).toBeNull();
expect(alphaDecoderFor("h264")).toBeNull();
expect(alphaDecoderFor(undefined)).toBeNull();
});
});
+27
View File
@@ -0,0 +1,27 @@
const ALPHA_PIX_FMT_RE = /^(?:yuva|rgba|argb|bgra|abgr|gbrap|ya|ayuv)/i;
export function pixelFormatHasAlpha(pixFmt: string | undefined): boolean {
return pixFmt !== undefined && ALPHA_PIX_FMT_RE.test(pixFmt);
}
export interface FfprobeAlphaFields {
pix_fmt?: string;
tags?: Record<string, string | undefined>;
}
/** True when an ffprobe video stream carries alpha. VP8/VP9 alpha rides in a side channel:
* ffprobe reports `yuv420p` plus an `alpha_mode=1` tag, whose casing differs across builds. */
export function videoStreamHasAlpha(stream: FfprobeAlphaFields): boolean {
if (pixelFormatHasAlpha(stream.pix_fmt)) return true;
return Object.entries(stream.tags ?? {}).some(
([key, value]) => key.toLowerCase() === "alpha_mode" && value === "1",
);
}
/** The decoder that reads a VP8/VP9 alpha side channel; ffmpeg's native ones drop it. */
export function alphaDecoderFor(codecName: string | undefined): "libvpx-vp9" | "libvpx" | null {
const codec = codecName?.toLowerCase();
if (codec === "vp9") return "libvpx-vp9";
if (codec === "vp8") return "libvpx";
return null;
}
+1
View File
@@ -21,6 +21,7 @@ export default defineConfig({
colorGradingContract: "src/colorGradingContract.ts",
subCompositionValidity: "src/subCompositionValidity.ts",
ffBinaries: "src/ffBinaries.ts",
mediaAlpha: "src/mediaAlpha.ts",
assetResolution: "src/assetResolution.ts",
htmlAttributeSpans: "src/htmlAttributeSpans.ts",
timeSpans: "src/timeSpans.ts",
@@ -22,7 +22,10 @@ import {
const FAKE_FFPROBE_PATH = process.execPath;
function makeRunner(
codecByPath: Record<string, string | { codecName: string; pixFmt?: string }>,
codecByPath: Record<
string,
string | { codecName: string; pixFmt?: string; tags?: Record<string, string> }
>,
): FfprobeRunner {
return (_command, args) => {
const filePath = args[args.length - 1] ?? "";
@@ -34,6 +37,7 @@ function makeRunner(
codec_type: "video",
codec_name: normalized.codecName,
pix_fmt: normalized.pixFmt,
tags: normalized.tags,
},
]
: [];
@@ -164,6 +168,23 @@ describe("probeAssetCodec", () => {
});
});
it("flags a VP9 whose alpha is only in the ALPHA_MODE tag, so it gets the VP8 proxy", async () => {
const project = tmpProject();
const videoPath = join(project, "cutout.webm");
writeFileSync(videoPath, "fake video bytes");
// What ffprobe prints for a libvpx-vp9 yuva420p encode.
const facts = await probeAssetCodec(
videoPath,
makeRunner({
[videoPath]: { codecName: "vp9", pixFmt: "yuv420p", tags: { ALPHA_MODE: "1" } },
}),
);
expect(facts?.hasAlpha).toBe(true);
expect(proxyVariantFor(facts!)).toBe("vp8");
});
it("returns null (never throws) when ffprobe is unresolvable", async () => {
const project = tmpProject();
const videoPath = join(project, "clip.mp4");
@@ -9,7 +9,7 @@ import {
isUnresolvedAssetPlaceholder,
resolveExistingLocalAsset as resolveAsset,
} from "@hyperframes/parsers/asset-resolution";
import { pixelFormatHasAlpha, probeMediaMetadata, type FfprobeRunner } from "./mediaMetadata.js";
import { probeMediaMetadata, type FfprobeRunner } from "./mediaMetadata.js";
/**
* One reusable answer to "what codec is this asset, and is it browser-hostile?",
@@ -24,7 +24,7 @@ export interface AssetCodecFacts {
* when no representative mime exists (ProRes: browsers never decode it, so
* the runtime always proxies rather than probing `canPlayType`). */
representativeMime: string | null;
/** Source carries an alpha channel (ffprobe pix_fmt). Alpha sources use a
/** Source carries an alpha channel (`MediaMetadata.hasAlpha`). Alpha sources use a
* VP8/WebM proxy so their transparency is preserved across Chromium builds. */
hasAlpha: boolean;
}
@@ -205,7 +205,7 @@ async function probeCodecFacts(
if (metadata.kind !== "video") return null;
const codecName = metadata.color.codecName;
if (!codecName) return null;
return codecFactsFor(codecName, pixelFormatHasAlpha(metadata.color.pixelFormat));
return codecFactsFor(codecName, metadata.hasAlpha === true);
}
interface CachedAssetProbe {
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { classifyMediaColor, pixelFormatHasAlpha, probeMediaMetadata } from "./mediaMetadata.js";
import { classifyMediaColor, probeMediaMetadata } from "./mediaMetadata.js";
afterEach(() => vi.unstubAllEnvs());
@@ -175,23 +175,3 @@ describe("probeMediaMetadata", () => {
expect(metadata).toMatchObject({ kind: "video", color: { codecName: "h264" } });
});
});
describe("pixelFormatHasAlpha", () => {
it("detects alpha-bearing pixel formats", () => {
for (const pixFmt of [
"yuva420p",
"yuva444p10le",
"rgba",
"argb",
"bgra",
"abgr",
"gbrap12le",
"ya8",
]) {
expect(pixelFormatHasAlpha(pixFmt)).toBe(true);
}
for (const pixFmt of ["yuv420p", "yuv422p10le", "rgb24", "gbrp", "gray", undefined]) {
expect(pixelFormatHasAlpha(pixFmt)).toBe(false);
}
});
});
@@ -6,6 +6,7 @@ import {
type ToneMapSourceColour,
} from "@hyperframes/core";
import { findFfBinary } from "@hyperframes/parsers/ff-binaries";
import { videoStreamHasAlpha } from "@hyperframes/parsers/media-alpha";
export interface FfprobeRunResult {
status: number | null;
@@ -73,6 +74,8 @@ export interface MediaMetadata {
color: MediaColorMetadata;
/** Video audio stream, when probed. Absent means the drop path stays muted. */
hasAudio?: boolean;
/** Video stream carries alpha (pix_fmt or the VP8/VP9 alpha_mode tag). */
hasAlpha?: boolean;
probeError?: string;
}
@@ -86,6 +89,7 @@ interface FfprobeStream {
color_primaries?: string;
bits_per_raw_sample?: string;
disposition?: { attached_pic?: number };
tags?: Record<string, string | undefined>;
}
const VIDEO_EXT = new Set([
@@ -137,17 +141,6 @@ function colorLabel(input: {
return "SDR/unknown";
}
// Conservative alpha-bearing pix_fmt list: yuva* (yuva420p, yuva444p10le...),
// rgba/argb/bgra/abgr (packed RGB+alpha), gbrap* (planar GBR+alpha, ProRes
// 4444 decodes to these), ya* (gray+alpha). Prefix match keeps bit-depth /
// endianness suffixes covered.
const ALPHA_PIX_FMT_RE = /^(?:yuva|rgba|argb|bgra|abgr|gbrap|ya)/;
/** True when an ffprobe `pix_fmt` carries an alpha component. */
export function pixelFormatHasAlpha(pixFmt: string | undefined): boolean {
return pixFmt !== undefined && ALPHA_PIX_FMT_RE.test(pixFmt.toLowerCase());
}
export function classifyMediaColor(stream: FfprobeStream | null | undefined): MediaColorMetadata {
const colorPrimaries = lower(stream?.color_primaries);
const colorSpace = lower(stream?.color_space);
@@ -222,7 +215,7 @@ export async function probeMediaMetadata(
"-v",
"error",
"-show_entries",
"stream=codec_type,codec_name,profile,pix_fmt,color_space,color_transfer,color_primaries,bits_per_raw_sample:stream_disposition=attached_pic",
"stream=codec_type,codec_name,profile,pix_fmt,color_space,color_transfer,color_primaries,bits_per_raw_sample:stream_disposition=attached_pic:stream_tags=alpha_mode",
"-of",
"json",
"--",
@@ -247,6 +240,7 @@ export async function probeMediaMetadata(
const metadata: MediaMetadata = { kind, color: classifyMediaColor(stream) };
if (kind === "video") {
metadata.hasAudio = (parsed.streams ?? []).some((item) => item.codec_type === "audio");
metadata.hasAlpha = stream !== undefined && videoStreamHasAlpha(stream);
}
return metadata;
} catch {
@@ -23,13 +23,21 @@ const realFfmpegFilters = realFfmpeg
: "";
const canToneMapForReal =
Boolean(findFfBinary("ffprobe")) && /\szscale\s/.test(realFfmpegFilters ?? "");
const realFfmpegEncoders = realFfmpeg
? spawnSync(realFfmpeg, ["-hide_banner", "-encoders"], { encoding: "utf8" }).stdout
: "";
const canEncodeVpxAlphaForReal =
Boolean(findFfBinary("ffprobe")) &&
/\slibvpx\s/.test(realFfmpegEncoders ?? "") &&
/\slibvpx-vp9\s/.test(realFfmpegEncoders ?? "");
function meanLuma(path: string, filters = ""): number {
function meanLuma(path: string, filters = "", inputArgs: string[] = []): number {
const stats = execFileSync(
realFfmpeg!,
[
"-v",
"error",
...inputArgs,
"-i",
path,
"-frames:v",
@@ -134,6 +142,7 @@ async function loadModule(
ffmpegPath: string | undefined,
isHdr = false,
hdrTransfer: string | null = isHdr ? "pq" : null,
codecName?: string,
): Promise<typeof import("./proxyTranscoder.js")> {
vi.resetModules();
vi.doMock("node:child_process", () => {
@@ -146,7 +155,7 @@ async function loadModule(
vi.doMock("./mediaMetadata.js", () => ({
probeMediaMetadata: async () => ({
kind: "video",
color: { isHdr, hdrTransfer },
color: { isHdr, hdrTransfer, codecName },
}),
probeFirstFrameColour: async () => ({}),
}));
@@ -267,6 +276,57 @@ describe("resolveProxy", () => {
await expect(resultPromise).resolves.toBe(vp8Path);
});
it("decodes a VP9 alpha source with libvpx-vp9 so the VP8 proxy keeps its alpha", async () => {
const { spawn, calls } = createSpawnSpy();
const { resolveProxy } = await loadModule(spawn, FFMPEG_PATH, false, null, "vp9");
const projectDir = tmpProject();
const sourcePath = join(projectDir, "cutout.webm");
writeFileSync(sourcePath, "source-bytes");
const vp8 = resolveProxy(projectDir, sourcePath, "vp8");
await flush();
const vp8Args = calls[0]!.args;
expect(vp8Args.slice(vp8Args.indexOf("-i") - 2, vp8Args.indexOf("-i"))).toEqual([
"-c:v",
"libvpx-vp9",
]);
succeed(calls[0]!, "fake-vp8-bytes");
await vp8;
const h264 = resolveProxy(projectDir, sourcePath, "h264");
await flush();
expect(calls[1]!.args.slice(0, calls[1]!.args.indexOf("-i"))).not.toContain("-c:v");
succeed(calls[1]!);
await h264;
});
it.skipIf(!canEncodeVpxAlphaForReal)(
"keeps a real VP9 alpha webm transparent through probe, variant and transcode",
async () => {
vi.resetModules();
vi.doUnmock("./mediaMetadata.js");
const { resolveProxy } = await import("./proxyTranscoder.js");
const { probeAssetCodec, proxyVariantFor } = await import("./mediaCodecMap.js");
const projectDir = tmpProject();
const sourcePath = join(projectDir, "cutout.webm");
// Left half fully transparent, right half opaque: mean alpha 127.5.
execFileSync(realFfmpeg!, [
...["-v", "error", "-f", "lavfi", "-i"],
"testsrc2=s=64x64:d=0.2,format=rgba,geq=r='r(X,Y)':g='g(X,Y)':b='b(X,Y)':a='if(lt(X,32),0,255)'",
...["-c:v", "libvpx-vp9", "-pix_fmt", "yuva420p", "-auto-alt-ref", "0"],
...["-metadata:s:v:0", "alpha_mode=1", sourcePath],
]);
const facts = await probeAssetCodec(sourcePath);
expect(facts).toMatchObject({ codecName: "vp9", hasAlpha: true });
const proxyPath = await resolveProxy(projectDir, sourcePath, proxyVariantFor(facts!));
expect(proxyPath).toMatch(/\.webm$/);
const meanAlpha = meanLuma(proxyPath, "alphaextract,", ["-c:v", "libvpx"]);
expect(Math.abs(meanAlpha - 127.5)).toBeLessThan(8);
},
60_000,
);
it("returns without spawning on a cache hit", async () => {
const { spawn, calls } = createSpawnSpy();
const { resolveProxy, getProxyCachePath } = await loadModule(spawn, FFMPEG_PATH);
@@ -8,6 +8,7 @@ import {
type PreviewProxyBox,
} from "@hyperframes/core";
import { findFfBinary } from "@hyperframes/parsers/ff-binaries";
import { alphaDecoderFor } from "@hyperframes/parsers/media-alpha";
import { probeFirstFrameColour, probeMediaMetadata } from "./mediaMetadata.js";
import { cleanupProxyCache } from "./proxyCache.js";
import { PROXY_VARIANT_CONFIG, type ProxyVariant } from "./mediaCodecMap.js";
@@ -444,7 +445,9 @@ async function runFfmpeg(
: [evenScale, `format=${pixelFormat}`].join(",");
return new Promise((resolvePromise, reject) => {
const commonArgs = ["-y", "-i", sourcePath, "-vf", videoFilter];
const alphaDecoder = keepsAlpha ? alphaDecoderFor(metadata.color.codecName) : null;
const decoderArgs = alphaDecoder ? ["-c:v", alphaDecoder] : [];
const commonArgs = ["-y", ...decoderArgs, "-i", sourcePath, "-vf", videoFilter];
const h264Args = [
"-c:v",
"libx264",
+1 -1
View File
@@ -6,7 +6,7 @@
"@hyperframes/gcp-cloud-run": { "packed": 49412, "unpacked": 192672 },
"@hyperframes/inspector": { "packed": 808905, "unpacked": 3645432 },
"@hyperframes/lint": { "packed": 168745, "unpacked": 654607 },
"@hyperframes/parsers": { "packed": 190591, "unpacked": 831243 },
"@hyperframes/parsers": { "packed": 191000, "unpacked": 831243 },
"@hyperframes/player": { "packed": 157145, "unpacked": 541601 },
"@hyperframes/producer": { "packed": 768210, "unpacked": 2807938 },
"@hyperframes/sdk": { "packed": 90662, "unpacked": 325069 },