mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 12:54:29 +00:00
99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { existsSync, readFileSync } from "node:fs";
|
|
import { resolve, dirname } from "node:path";
|
|
|
|
const ARTIFACT_NAMES = ["hyperframe-runtime.js", "hyperframe.runtime.iife.js"];
|
|
|
|
/**
|
|
* Resolve the runtime JS source for the studio preview server.
|
|
*
|
|
* Three resolution strategies, in priority order:
|
|
*
|
|
* 1. esbuild from source (dev only — gated on entry.ts existence)
|
|
* 2. Inlined constant (production — baked into @hyperframes/core at build time)
|
|
* 3. Pre-built artifact (fallback — reads IIFE file from dist/)
|
|
*/
|
|
export async function loadRuntimeSource(): Promise<string | null> {
|
|
return (await buildFromSource()) ?? (await getInlinedRuntime()) ?? readPrebuiltArtifact();
|
|
}
|
|
|
|
export async function loadRuntimeSourceSignature(): Promise<string | null> {
|
|
const source = await loadRuntimeSource();
|
|
if (!source) return null;
|
|
return createHash("sha256").update(source).digest("hex");
|
|
}
|
|
|
|
export function hashSignatureParts(parts: Array<string | null | undefined>): string {
|
|
const hash = createHash("sha256");
|
|
for (const part of parts) {
|
|
hash.update(part ?? "");
|
|
hash.update("\n--hf-signature-part--\n");
|
|
}
|
|
return hash.digest("hex");
|
|
}
|
|
|
|
// ── Strategy 1: live build from source (dev only) ──────────────────────────
|
|
|
|
const ENTRY_TS = resolve(__dirname, "..", "..", "..", "core", "src", "runtime", "entry.ts");
|
|
|
|
async function buildFromSource(): Promise<string | null> {
|
|
if (!existsSync(ENTRY_TS)) return null;
|
|
try {
|
|
const mod = await import("@hyperframes/core");
|
|
if (typeof mod.loadHyperframeRuntimeSource === "function") {
|
|
const source = mod.loadHyperframeRuntimeSource();
|
|
if (source) return source;
|
|
}
|
|
} catch {
|
|
// esbuild failed — fall through to inlined / artifact
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// ── Strategy 2: inlined constant from core build ──────────────────────────
|
|
|
|
async function getInlinedRuntime(): Promise<string | null> {
|
|
try {
|
|
const mod = await import("@hyperframes/core");
|
|
if (typeof mod.getHyperframeRuntimeScript === "function") {
|
|
return mod.getHyperframeRuntimeScript() ?? null;
|
|
}
|
|
} catch {
|
|
// Not available — fall through to artifact
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// ── Strategy 3: pre-built IIFE artifact ───────────────────────────────────
|
|
|
|
function readPrebuiltArtifact(): string | null {
|
|
return readFromDir(__dirname) ?? readFromCoreDistDir() ?? readFromNodeModules();
|
|
}
|
|
|
|
function readFromDir(dir: string): string | null {
|
|
for (const name of ARTIFACT_NAMES) {
|
|
const path = resolve(dir, name);
|
|
if (existsSync(path)) return readFileSync(path, "utf-8");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function readFromCoreDistDir(): string | null {
|
|
return readFromDir(resolve(__dirname, "..", "..", "..", "core", "dist"));
|
|
}
|
|
|
|
function readFromNodeModules(): string | null {
|
|
const subPaths = ["node_modules/hyperframes/dist", "node_modules/@hyperframes/core/dist"];
|
|
let dir = __dirname;
|
|
for (;;) {
|
|
for (const sub of subPaths) {
|
|
const result = readFromDir(resolve(dir, sub));
|
|
if (result) return result;
|
|
}
|
|
const parent = dirname(dir);
|
|
if (parent === dir) break;
|
|
dir = parent;
|
|
}
|
|
return null;
|
|
}
|