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 { return (await buildFromSource()) ?? (await getInlinedRuntime()) ?? readPrebuiltArtifact(); } export async function loadRuntimeSourceSignature(): Promise { const source = await loadRuntimeSource(); if (!source) return null; return createHash("sha256").update(source).digest("hex"); } export function hashSignatureParts(parts: Array): 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 { 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 { 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; }