@hyperframes/engine 0.6.119 → 0.6.120

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 (73) hide show
  1. package/package.json +24 -7
  2. package/scripts/generate-lut-reference.py +0 -168
  3. package/scripts/test-fitTextFontSize-browser.ts +0 -135
  4. package/src/cdp-headless-experimental.d.ts +0 -54
  5. package/src/config.test.ts +0 -213
  6. package/src/config.ts +0 -417
  7. package/src/index.ts +0 -273
  8. package/src/services/audioMixer.test.ts +0 -326
  9. package/src/services/audioMixer.ts +0 -604
  10. package/src/services/audioMixer.types.ts +0 -35
  11. package/src/services/audioVolumeEnvelope.test.ts +0 -176
  12. package/src/services/audioVolumeEnvelope.ts +0 -138
  13. package/src/services/browserManager.test.ts +0 -330
  14. package/src/services/browserManager.ts +0 -670
  15. package/src/services/chunkEncoder.test.ts +0 -1415
  16. package/src/services/chunkEncoder.ts +0 -831
  17. package/src/services/chunkEncoder.types.ts +0 -60
  18. package/src/services/extractionCache.test.ts +0 -199
  19. package/src/services/extractionCache.ts +0 -216
  20. package/src/services/fileServer.ts +0 -110
  21. package/src/services/frameCapture-discardWarmup.test.ts +0 -183
  22. package/src/services/frameCapture-namePolyfill.test.ts +0 -78
  23. package/src/services/frameCapture-pollImagesReady.test.ts +0 -153
  24. package/src/services/frameCapture-staticDedupIndex.test.ts +0 -76
  25. package/src/services/frameCapture-warmupTicks.test.ts +0 -174
  26. package/src/services/frameCapture.test.ts +0 -192
  27. package/src/services/frameCapture.ts +0 -1934
  28. package/src/services/hdrCapture.test.ts +0 -159
  29. package/src/services/hdrCapture.ts +0 -315
  30. package/src/services/parallelCoordinator.test.ts +0 -139
  31. package/src/services/parallelCoordinator.ts +0 -437
  32. package/src/services/screenshotService.test.ts +0 -510
  33. package/src/services/screenshotService.ts +0 -615
  34. package/src/services/streamingEncoder.test.ts +0 -832
  35. package/src/services/streamingEncoder.ts +0 -594
  36. package/src/services/systemMemory.test.ts +0 -324
  37. package/src/services/systemMemory.ts +0 -180
  38. package/src/services/videoFrameExtractor.test.ts +0 -1062
  39. package/src/services/videoFrameExtractor.ts +0 -1139
  40. package/src/services/videoFrameInjector.test.ts +0 -300
  41. package/src/services/videoFrameInjector.ts +0 -687
  42. package/src/services/vp9Options.ts +0 -13
  43. package/src/types.ts +0 -191
  44. package/src/utils/alphaBlit.test.ts +0 -1349
  45. package/src/utils/alphaBlit.ts +0 -1015
  46. package/src/utils/assertSwiftShader.test.ts +0 -130
  47. package/src/utils/assertSwiftShader.ts +0 -126
  48. package/src/utils/ffmpegBinaries.test.ts +0 -43
  49. package/src/utils/ffmpegBinaries.ts +0 -63
  50. package/src/utils/ffprobe.test.ts +0 -342
  51. package/src/utils/ffprobe.ts +0 -457
  52. package/src/utils/gpuEncoder.test.ts +0 -140
  53. package/src/utils/gpuEncoder.ts +0 -268
  54. package/src/utils/hdr.test.ts +0 -191
  55. package/src/utils/hdr.ts +0 -137
  56. package/src/utils/hdrCompositing.test.ts +0 -130
  57. package/src/utils/htmlTemplate.test.ts +0 -42
  58. package/src/utils/htmlTemplate.ts +0 -42
  59. package/src/utils/layerCompositor.test.ts +0 -150
  60. package/src/utils/layerCompositor.ts +0 -58
  61. package/src/utils/parityContract.ts +0 -1
  62. package/src/utils/processTracker.test.ts +0 -74
  63. package/src/utils/processTracker.ts +0 -41
  64. package/src/utils/readWebGlVendorInfoFromCanvas.ts +0 -52
  65. package/src/utils/runFfmpeg.test.ts +0 -102
  66. package/src/utils/runFfmpeg.ts +0 -136
  67. package/src/utils/shaderTransitions.test.ts +0 -738
  68. package/src/utils/shaderTransitions.ts +0 -1130
  69. package/src/utils/uint16-alignment-audit.test.ts +0 -125
  70. package/src/utils/urlDownloader.test.ts +0 -65
  71. package/src/utils/urlDownloader.ts +0 -143
  72. package/tsconfig.json +0 -19
  73. package/vitest.config.ts +0 -7
@@ -1,130 +0,0 @@
1
- /**
2
- * Tests for assertSwiftShader and its companion readWebGlVendorInfo helper.
3
- *
4
- * We don't spin up a real Chrome here — the assertion's contract is "given a
5
- * WebGL info pair, accept SwiftShader and reject anything else." Tests inject
6
- * the info pair through the optional `readInfo` override that the
7
- * production code path leaves as a default.
8
- */
9
-
10
- import { describe, expect, it } from "vitest";
11
- import type { Page } from "puppeteer-core";
12
- import {
13
- BROWSER_GPU_NOT_SOFTWARE,
14
- SwiftShaderAssertionError,
15
- assertSwiftShader,
16
- } from "./assertSwiftShader.js";
17
-
18
- // Minimal Page stub. Only assertSwiftShader's default `readInfo` ever touches
19
- // `page.goto` / `page.evaluate`; when we inject a custom `readInfo` the page
20
- // object is never used, so an empty cast is safe.
21
- const stubPage = {} as unknown as Page;
22
-
23
- describe("assertSwiftShader", () => {
24
- it("accepts the canonical SwiftShader vendor + renderer pair", async () => {
25
- await assertSwiftShader(stubPage, async () => ({
26
- vendor: "Google Inc. (Google)",
27
- renderer:
28
- "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)), SwiftShader driver)",
29
- }));
30
- });
31
-
32
- it("accepts SwiftShader regardless of trailing whitespace on vendor", async () => {
33
- await assertSwiftShader(stubPage, async () => ({
34
- vendor: " Google Inc. (Google) ",
35
- renderer: "SwiftShader",
36
- }));
37
- });
38
-
39
- it("accepts case-insensitive renderer token", async () => {
40
- await assertSwiftShader(stubPage, async () => ({
41
- vendor: "Google Inc. (Google)",
42
- renderer: "ANGLE (Google, swiftshader Device, swiftshader driver)",
43
- }));
44
- });
45
-
46
- it("throws SwiftShaderAssertionError when vendor is hardware-accelerated", async () => {
47
- let caught: unknown;
48
- try {
49
- await assertSwiftShader(stubPage, async () => ({
50
- vendor: "Google Inc. (NVIDIA Corporation)",
51
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 4090, OpenGL 4.6)",
52
- }));
53
- } catch (err) {
54
- caught = err;
55
- }
56
- expect(caught).toBeInstanceOf(SwiftShaderAssertionError);
57
- expect((caught as SwiftShaderAssertionError).code).toBe(BROWSER_GPU_NOT_SOFTWARE);
58
- expect((caught as Error).message).toContain("non-SwiftShader");
59
- expect((caught as Error).message).toContain("--use-gl=swiftshader");
60
- expect((caught as SwiftShaderAssertionError).vendor).toBe("Google Inc. (NVIDIA Corporation)");
61
- });
62
-
63
- it("throws when the renderer string lacks SwiftShader even if vendor matches", async () => {
64
- // Google Inc. is the umbrella vendor for many ANGLE backends — vendor
65
- // alone is not enough; the renderer must actually mention SwiftShader.
66
- let caught: unknown;
67
- try {
68
- await assertSwiftShader(stubPage, async () => ({
69
- vendor: "Google Inc. (Google)",
70
- renderer: "ANGLE (Google, Vulkan 1.3.0 (Intel(R) UHD Graphics 630), OpenGL ES 3.0)",
71
- }));
72
- } catch (err) {
73
- caught = err;
74
- }
75
- expect(caught).toBeInstanceOf(SwiftShaderAssertionError);
76
- expect((caught as SwiftShaderAssertionError).code).toBe(BROWSER_GPU_NOT_SOFTWARE);
77
- });
78
-
79
- it("throws when both vendor and renderer are empty", async () => {
80
- // Some chrome:// pages return empty strings before the GPU info table
81
- // populates. We treat that as failure rather than silently passing.
82
- let caught: unknown;
83
- try {
84
- await assertSwiftShader(stubPage, async () => ({ vendor: "", renderer: "" }));
85
- } catch (err) {
86
- caught = err;
87
- }
88
- expect(caught).toBeInstanceOf(SwiftShaderAssertionError);
89
- expect((caught as SwiftShaderAssertionError).code).toBe(BROWSER_GPU_NOT_SOFTWARE);
90
- });
91
-
92
- it("propagates errors from the info reader without wrapping", async () => {
93
- const upstream = new Error("simulated CDP failure");
94
- let caught: unknown;
95
- try {
96
- await assertSwiftShader(stubPage, async () => {
97
- throw upstream;
98
- });
99
- } catch (err) {
100
- caught = err;
101
- }
102
- // Reader errors should not be masked by SwiftShaderAssertionError —
103
- // they are a separate failure class (probably retryable).
104
- expect(caught).toBe(upstream);
105
- });
106
-
107
- it("rejects an unrelated vendor that happens to contain the SwiftShader token in the renderer", async () => {
108
- // Defensive: if some future ANGLE build uses a non-Google vendor string
109
- // but still mentions SwiftShader in the renderer for some reason, we
110
- // still want to require the exact Google vendor signature.
111
- let caught: unknown;
112
- try {
113
- await assertSwiftShader(stubPage, async () => ({
114
- vendor: "Mesa/X.org",
115
- renderer: "llvmpipe (SwiftShader compatible)",
116
- }));
117
- } catch (err) {
118
- caught = err;
119
- }
120
- expect(caught).toBeInstanceOf(SwiftShaderAssertionError);
121
- });
122
- });
123
-
124
- describe("SwiftShaderAssertionError", () => {
125
- it("exposes the BROWSER_GPU_NOT_SOFTWARE typed-failure code", () => {
126
- const err = new SwiftShaderAssertionError("test", "v", "r");
127
- expect(err.code).toBe(BROWSER_GPU_NOT_SOFTWARE);
128
- expect(err.code).toBe("BROWSER_GPU_NOT_SOFTWARE");
129
- });
130
- });
@@ -1,126 +0,0 @@
1
- /**
2
- * assertSwiftShader — verify Chrome's WebGL is rendered by SwiftShader.
3
- *
4
- * Distributed renders pixel-lock on the GPU backend: hardware GL is bitwise
5
- * unstable across worker machines (different drivers, driver versions, GL
6
- * extension sets, even differing fp32 rounding on the same vendor). Chunk
7
- * workers launch Chrome with `--use-gl=swiftshader --use-angle=swiftshader`
8
- * so every worker uses the same pure-software GL implementation.
9
- *
10
- * Those Chrome flags are advisory: a misconfigured base image, a missing
11
- * SwiftShader library, or a `chrome://gpu` blocklist override can silently
12
- * downgrade to system GL. The distributed pipeline cannot detect the
13
- * downgrade by sampling pixels (one machine = one render), so we read
14
- * `chrome://gpu` directly after launch and refuse to render if the active
15
- * GL renderer is anything other than SwiftShader.
16
- */
17
-
18
- import type { Page } from "puppeteer-core";
19
-
20
- /**
21
- * Error code classifying this failure as non-retryable for distributed
22
- * workflow adapters — a downgraded GPU on a worker will not heal on retry.
23
- */
24
- export const BROWSER_GPU_NOT_SOFTWARE = "BROWSER_GPU_NOT_SOFTWARE";
25
-
26
- /**
27
- * Error thrown when chrome://gpu reports a non-SwiftShader WebGL backend.
28
- *
29
- * Carries a `code` property so the adapter can match on it without parsing
30
- * the message string — Temporal/Step Functions retry policies key off the
31
- * code, not the message.
32
- */
33
- export class SwiftShaderAssertionError extends Error {
34
- readonly code: typeof BROWSER_GPU_NOT_SOFTWARE = BROWSER_GPU_NOT_SOFTWARE;
35
- readonly vendor: string;
36
- readonly renderer: string;
37
-
38
- constructor(message: string, vendor: string, renderer: string) {
39
- super(message);
40
- this.name = "SwiftShaderAssertionError";
41
- this.vendor = vendor;
42
- this.renderer = renderer;
43
- }
44
- }
45
-
46
- /**
47
- * SwiftShader identifies itself on `chrome://gpu` and in
48
- * `WEBGL_debug_renderer_info` with this exact vendor string. Locking to
49
- * Google's own GL string (rather than a substring match on "swiftshader")
50
- * avoids false-positives from third-party ANGLE backends that incidentally
51
- * mention SwiftShader in unrelated diagnostic text.
52
- */
53
- const SWIFTSHADER_VENDOR_SIGNATURE = "Google Inc. (Google)";
54
- /**
55
- * Renderer string contains the literal "SwiftShader" token. We match
56
- * case-insensitively and only require the substring; Chrome occasionally
57
- * appends a build suffix (e.g. " Vulkan 1.3").
58
- */
59
- const SWIFTSHADER_RENDERER_TOKEN = "swiftshader";
60
-
61
- interface WebGlInfo {
62
- vendor: string;
63
- renderer: string;
64
- }
65
-
66
- /**
67
- * Read the WebGL vendor/renderer strings from a live `chrome://gpu` page.
68
- *
69
- * Extracted from `assertSwiftShader` so tests can stub the navigation +
70
- * extraction step. Returns the raw values; callers decide how to interpret
71
- * them. Both fields are best-effort — Chrome returns empty strings if the
72
- * GPU info table hasn't populated yet, which the caller treats as failure.
73
- */
74
- export async function readWebGlVendorInfo(page: Page): Promise<WebGlInfo> {
75
- await page.goto("chrome://gpu", { waitUntil: "domcontentloaded", timeout: 30_000 });
76
- // The "GL_VENDOR" / "GL_RENDERER" rows live inside <info-view> shadow DOM
77
- // in modern Chrome. We pull the structured `info_log_` payload off the
78
- // page-level globals instead of querying the DOM, since the DOM layout has
79
- // drifted across versions.
80
- const info = await page.evaluate((): WebGlInfo => {
81
- type Row = { description?: string; value?: string };
82
- type InfoLog = { graphics_info?: { basic_info?: Row[] } };
83
- const w = window as unknown as { browserBridge?: { gpuInfo_?: InfoLog } };
84
- const rows: Row[] = w.browserBridge?.gpuInfo_?.graphics_info?.basic_info ?? [];
85
- let vendor = "";
86
- let renderer = "";
87
- for (const row of rows) {
88
- if (typeof row.description !== "string" || typeof row.value !== "string") continue;
89
- if (row.description === "GL_VENDOR") vendor = row.value;
90
- else if (row.description === "GL_RENDERER") renderer = row.value;
91
- }
92
- return { vendor, renderer };
93
- });
94
- return info;
95
- }
96
-
97
- /**
98
- * Validate that the active WebGL renderer is SwiftShader. Throws
99
- * `SwiftShaderAssertionError` otherwise.
100
- *
101
- * Pass an optional `readInfo` override for tests that don't have a real
102
- * Puppeteer `Page`. The default implementation navigates to `chrome://gpu`
103
- * and parses the GL_VENDOR / GL_RENDERER rows.
104
- */
105
- export async function assertSwiftShader(
106
- page: Page,
107
- readInfo: (page: Page) => Promise<WebGlInfo> = readWebGlVendorInfo,
108
- ): Promise<void> {
109
- const { vendor, renderer } = await readInfo(page);
110
-
111
- const vendorMatches = vendor.trim() === SWIFTSHADER_VENDOR_SIGNATURE;
112
- const rendererMatches = renderer.toLowerCase().includes(SWIFTSHADER_RENDERER_TOKEN);
113
-
114
- if (vendorMatches && rendererMatches) return;
115
-
116
- throw new SwiftShaderAssertionError(
117
- `[assertSwiftShader] Chrome reported a non-SwiftShader WebGL backend. ` +
118
- `Distributed renders require pure-software GL for pixel-identical retries. ` +
119
- `Got vendor=${JSON.stringify(vendor)} renderer=${JSON.stringify(renderer)}; ` +
120
- `expected vendor=${JSON.stringify(SWIFTSHADER_VENDOR_SIGNATURE)} renderer to contain "SwiftShader". ` +
121
- `Ensure Chrome was launched with --use-gl=swiftshader --use-angle=swiftshader and that the ` +
122
- `SwiftShader libraries are present in the runtime image.`,
123
- vendor,
124
- renderer,
125
- );
126
- }
@@ -1,43 +0,0 @@
1
- // fallow-ignore-file code-duplication
2
- import { resolve } from "node:path";
3
- import { afterEach, describe, expect, it } from "vitest";
4
- import {
5
- assertConfiguredFfmpegBinariesExist,
6
- getFfmpegBinary,
7
- getFfprobeBinary,
8
- } from "./ffmpegBinaries.js";
9
-
10
- describe("ffmpeg binary env resolution", () => {
11
- const originalFfmpegPath = process.env.HYPERFRAMES_FFMPEG_PATH;
12
- const originalFfprobePath = process.env.HYPERFRAMES_FFPROBE_PATH;
13
-
14
- afterEach(() => {
15
- if (originalFfmpegPath === undefined) delete process.env.HYPERFRAMES_FFMPEG_PATH;
16
- else process.env.HYPERFRAMES_FFMPEG_PATH = originalFfmpegPath;
17
- if (originalFfprobePath === undefined) delete process.env.HYPERFRAMES_FFPROBE_PATH;
18
- else process.env.HYPERFRAMES_FFPROBE_PATH = originalFfprobePath;
19
- });
20
-
21
- it("uses configured absolute paths when env vars are set", () => {
22
- process.env.HYPERFRAMES_FFMPEG_PATH = "/tools/ffmpeg.exe";
23
- process.env.HYPERFRAMES_FFPROBE_PATH = "/tools/ffprobe.exe";
24
-
25
- expect(getFfmpegBinary()).toBe(resolve("/tools/ffmpeg.exe"));
26
- expect(getFfprobeBinary()).toBe(resolve("/tools/ffprobe.exe"));
27
- });
28
-
29
- it("throws a clear error when a configured FFmpeg path is missing", () => {
30
- process.env.HYPERFRAMES_FFMPEG_PATH = "/missing/ffmpeg.exe";
31
-
32
- expect(() => assertConfiguredFfmpegBinariesExist()).toThrow(
33
- /FFmpeg binary not found at HYPERFRAMES_FFMPEG_PATH/,
34
- );
35
- });
36
-
37
- it("accepts existing configured paths", () => {
38
- process.env.HYPERFRAMES_FFMPEG_PATH = process.execPath;
39
- process.env.HYPERFRAMES_FFPROBE_PATH = process.execPath;
40
-
41
- expect(() => assertConfiguredFfmpegBinariesExist()).not.toThrow();
42
- });
43
- });
@@ -1,63 +0,0 @@
1
- // fallow-ignore-file code-duplication
2
- import { execFileSync } from "child_process";
3
- import { existsSync } from "fs";
4
- import { resolve } from "path";
5
-
6
- export const FFMPEG_PATH_ENV = "HYPERFRAMES_FFMPEG_PATH";
7
- export const FFPROBE_PATH_ENV = "HYPERFRAMES_FFPROBE_PATH";
8
-
9
- const pathCache = new Map<string, string | undefined>();
10
-
11
- function findOnPath(name: "ffmpeg" | "ffprobe"): string | undefined {
12
- if (pathCache.has(name)) return pathCache.get(name);
13
- try {
14
- const command = process.platform === "win32" ? "where" : "which";
15
- const output = execFileSync(command, [name], {
16
- encoding: "utf-8",
17
- stdio: ["pipe", "pipe", "pipe"],
18
- timeout: 5000,
19
- });
20
- const first = output
21
- .split(/\r?\n/)
22
- .map((s) => s.trim())
23
- .find(Boolean);
24
- const resolved = first ? resolve(first) : undefined;
25
- pathCache.set(name, resolved);
26
- return resolved;
27
- } catch {
28
- pathCache.set(name, undefined);
29
- return undefined;
30
- }
31
- }
32
-
33
- function getConfiguredBinary(envName: string, binaryName: "ffmpeg" | "ffprobe"): string {
34
- const configured = process.env[envName]?.trim();
35
- if (configured) return resolve(configured);
36
- return findOnPath(binaryName) ?? binaryName;
37
- }
38
-
39
- export function getFfmpegBinary(): string {
40
- return getConfiguredBinary(FFMPEG_PATH_ENV, "ffmpeg");
41
- }
42
-
43
- export function getFfprobeBinary(): string {
44
- return getConfiguredBinary(FFPROBE_PATH_ENV, "ffprobe");
45
- }
46
-
47
- export function assertConfiguredFfmpegBinariesExist(): void {
48
- const ffmpegPath = process.env[FFMPEG_PATH_ENV]?.trim();
49
- if (ffmpegPath && !existsSync(ffmpegPath)) {
50
- throw new Error(
51
- `[FFmpeg] FFmpeg binary not found at ${FFMPEG_PATH_ENV}="${ffmpegPath}". ` +
52
- "Install FFmpeg or unset the override.",
53
- );
54
- }
55
-
56
- const ffprobePath = process.env[FFPROBE_PATH_ENV]?.trim();
57
- if (ffprobePath && !existsSync(ffprobePath)) {
58
- throw new Error(
59
- `[FFmpeg] FFprobe binary not found at ${FFPROBE_PATH_ENV}="${ffprobePath}". ` +
60
- "Install FFmpeg or unset the override.",
61
- );
62
- }
63
- }