hyperframes 0.4.4 → 0.4.5

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 (2) hide show
  1. package/dist/cli.js +102 -75
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -54,7 +54,7 @@ var VERSION;
54
54
  var init_version = __esm({
55
55
  "src/version.ts"() {
56
56
  "use strict";
57
- VERSION = true ? "0.4.4" : "0.0.0-dev";
57
+ VERSION = true ? "0.4.5" : "0.0.0-dev";
58
58
  }
59
59
  });
60
60
 
@@ -6245,7 +6245,15 @@ function runSkillsAdd(repo) {
6245
6245
  return new Promise((resolve35, reject) => {
6246
6246
  const child = spawn("npx", ["skills", "add", repo, "--all"], {
6247
6247
  stdio: "inherit",
6248
- timeout: 12e4
6248
+ timeout: 12e4,
6249
+ // GH #316 — the upstream `skills` CLI shells out to `git clone`.
6250
+ // When Git's clone-hook protection is active (shipped on by
6251
+ // default in 2.45.1, reverted in 2.45.2, still present on many
6252
+ // corporate and CI setups), any globally-registered
6253
+ // `git lfs install` post-checkout hook aborts the clone. The
6254
+ // `repo` reaching this function is hardcoded in SOURCES below
6255
+ // — no user input reaches the spawn — so opting out here is safe.
6256
+ env: { ...process.env, GIT_CLONE_PROTECTION_ACTIVE: "0" }
6249
6257
  });
6250
6258
  child.on("close", (code, signal) => {
6251
6259
  if (code === 0) resolve35();
@@ -10507,10 +10515,10 @@ function compareDocumentPosition(nodeA, nodeB) {
10507
10515
  function uniqueSort(nodes) {
10508
10516
  nodes = nodes.filter((node, i2, arr) => !arr.includes(node, i2 + 1));
10509
10517
  nodes.sort((a, b) => {
10510
- const relative4 = compareDocumentPosition(a, b);
10511
- if (relative4 & DocumentPosition.PRECEDING) {
10518
+ const relative5 = compareDocumentPosition(a, b);
10519
+ if (relative5 & DocumentPosition.PRECEDING) {
10512
10520
  return -1;
10513
- } else if (relative4 & DocumentPosition.FOLLOWING) {
10521
+ } else if (relative5 & DocumentPosition.FOLLOWING) {
10514
10522
  return 1;
10515
10523
  }
10516
10524
  return 0;
@@ -24302,6 +24310,40 @@ var init_ffprobe2 = __esm({
24302
24310
  }
24303
24311
  });
24304
24312
 
24313
+ // ../producer/src/utils/paths.ts
24314
+ import { resolve as resolve13, basename, join as join27, relative as relative2, isAbsolute as isAbsolute3 } from "path";
24315
+ function isPathInside(childPath, parentPath) {
24316
+ const absChild = resolve13(childPath);
24317
+ const absParent = resolve13(parentPath);
24318
+ if (absChild === absParent) return true;
24319
+ const rel = relative2(absParent, absChild);
24320
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute3(rel);
24321
+ }
24322
+ function toExternalAssetKey(absPath) {
24323
+ if (absPath.startsWith("hf-ext/")) return absPath;
24324
+ let normalised = absPath.replace(/\\/g, "/");
24325
+ normalised = normalised.replace(/^\/\/\?\/UNC\//i, "//");
24326
+ normalised = normalised.replace(/^\/\/\?\//, "");
24327
+ normalised = normalised.replace(/^\/\/([^/]+)\//, "unc/$1/");
24328
+ normalised = normalised.replace(/^\/+/, "");
24329
+ normalised = normalised.replace(/^([A-Za-z]):\/?/, "$1/");
24330
+ return "hf-ext/" + normalised;
24331
+ }
24332
+ function resolveRenderPaths(projectDir, outputPath, rendersDir = DEFAULT_RENDERS_DIR) {
24333
+ const absoluteProjectDir = resolve13(projectDir);
24334
+ const projectName = basename(absoluteProjectDir);
24335
+ const resolvedOutputPath = outputPath ?? join27(rendersDir, `${projectName}.mp4`);
24336
+ const absoluteOutputPath = resolve13(resolvedOutputPath);
24337
+ return { absoluteProjectDir, absoluteOutputPath };
24338
+ }
24339
+ var DEFAULT_RENDERS_DIR;
24340
+ var init_paths = __esm({
24341
+ "../producer/src/utils/paths.ts"() {
24342
+ "use strict";
24343
+ DEFAULT_RENDERS_DIR = process.env.PRODUCER_RENDERS_DIR ?? resolve13(new URL(import.meta.url).pathname, "../../..", "renders");
24344
+ }
24345
+ });
24346
+
24305
24347
  // ../producer/src/utils/urlDownloader.ts
24306
24348
  var init_urlDownloader2 = __esm({
24307
24349
  "../producer/src/utils/urlDownloader.ts"() {
@@ -24365,7 +24407,7 @@ var init_fontData_generated = __esm({
24365
24407
  // ../producer/src/services/deterministicFonts.ts
24366
24408
  import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync18, writeFileSync as writeFileSync9 } from "fs";
24367
24409
  import { homedir as homedir6 } from "os";
24368
- import { join as join27 } from "path";
24410
+ import { join as join28 } from "path";
24369
24411
  function normalizeFamilyName(family) {
24370
24412
  return family.trim().replace(/^['"]|['"]$/g, "").trim().toLowerCase();
24371
24413
  }
@@ -24476,14 +24518,14 @@ function fontSlug(familyName) {
24476
24518
  return familyName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
24477
24519
  }
24478
24520
  function fontCacheDir(slug) {
24479
- const dir = join27(GOOGLE_FONTS_CACHE_DIR, slug);
24521
+ const dir = join28(GOOGLE_FONTS_CACHE_DIR, slug);
24480
24522
  if (!existsSync25(dir)) {
24481
24523
  mkdirSync15(dir, { recursive: true });
24482
24524
  }
24483
24525
  return dir;
24484
24526
  }
24485
24527
  function cachedWoff2Path(slug, weight, style) {
24486
- return join27(fontCacheDir(slug), `${weight}-${style}.woff2`);
24528
+ return join28(fontCacheDir(slug), `${weight}-${style}.woff2`);
24487
24529
  }
24488
24530
  async function fetchGoogleFont(familyName) {
24489
24531
  const slug = fontSlug(familyName);
@@ -24695,14 +24737,14 @@ var init_deterministicFonts = __esm({
24695
24737
  poppins: "poppins",
24696
24738
  "segoe ui": "roboto"
24697
24739
  };
24698
- GOOGLE_FONTS_CACHE_DIR = join27(homedir6(), ".cache", "hyperframes", "fonts");
24740
+ GOOGLE_FONTS_CACHE_DIR = join28(homedir6(), ".cache", "hyperframes", "fonts");
24699
24741
  WOFF2_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
24700
24742
  }
24701
24743
  });
24702
24744
 
24703
24745
  // ../producer/src/services/htmlCompiler.ts
24704
24746
  import { readFileSync as readFileSync19, existsSync as existsSync26, mkdirSync as mkdirSync16 } from "fs";
24705
- import { join as join28, dirname as dirname9, resolve as resolve13 } from "path";
24747
+ import { join as join29, dirname as dirname9, resolve as resolve14 } from "path";
24706
24748
  import postcss from "postcss";
24707
24749
  function dedupeElementsById(elements) {
24708
24750
  const deduped = /* @__PURE__ */ new Map();
@@ -24721,7 +24763,7 @@ async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagNa
24721
24763
  return { duration: 0, resolvedPath: src };
24722
24764
  }
24723
24765
  } else if (!filePath.startsWith("/")) {
24724
- filePath = join28(baseDir, filePath);
24766
+ filePath = join29(baseDir, filePath);
24725
24767
  }
24726
24768
  if (!existsSync26(filePath)) {
24727
24769
  return { duration: 0, resolvedPath: filePath };
@@ -24787,7 +24829,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
24787
24829
  const elEnd = elEndRaw ? parseFloat(elEndRaw) : Infinity;
24788
24830
  const absoluteStart = parentOffset + elStart;
24789
24831
  const absoluteEnd = Math.min(parentEnd, isFinite(elEnd) ? parentOffset + elEnd : Infinity);
24790
- const filePath = resolve13(projectDir, srcPath);
24832
+ const filePath = resolve14(projectDir, srcPath);
24791
24833
  if (visited.has(filePath)) {
24792
24834
  continue;
24793
24835
  }
@@ -24987,7 +25029,7 @@ function inlineSubCompositions(html, subCompositions, projectDir) {
24987
25029
  if (!srcPath) continue;
24988
25030
  let compHtml = subCompositions.get(srcPath) || null;
24989
25031
  if (!compHtml) {
24990
- const filePath = resolve13(projectDir, srcPath);
25032
+ const filePath = resolve14(projectDir, srcPath);
24991
25033
  if (existsSync26(filePath)) {
24992
25034
  compHtml = readFileSync19(filePath, "utf-8");
24993
25035
  }
@@ -25200,7 +25242,7 @@ ${safeText}
25200
25242
  return result;
25201
25243
  }
25202
25244
  function collectExternalAssets(html, projectDir) {
25203
- const absProjectDir = resolve13(projectDir);
25245
+ const absProjectDir = resolve14(projectDir);
25204
25246
  const externalAssets = /* @__PURE__ */ new Map();
25205
25247
  const CSS_URL_RE2 = /\burl\(\s*(["']?)([^)"']+)\1\s*\)/g;
25206
25248
  function processPath(rawPath) {
@@ -25208,12 +25250,12 @@ function collectExternalAssets(html, projectDir) {
25208
25250
  if (!trimmed || trimmed.startsWith("/") || trimmed.startsWith("http://") || trimmed.startsWith("https://") || trimmed.startsWith("//") || trimmed.startsWith("data:") || trimmed.startsWith("#")) {
25209
25251
  return null;
25210
25252
  }
25211
- const absPath = resolve13(absProjectDir, trimmed);
25212
- if (absPath.startsWith(absProjectDir + "/") || absPath === absProjectDir) {
25253
+ const absPath = resolve14(absProjectDir, trimmed);
25254
+ if (isPathInside(absPath, absProjectDir)) {
25213
25255
  return null;
25214
25256
  }
25215
25257
  if (!existsSync26(absPath)) return null;
25216
- const safeKey = "hf-ext/" + absPath.replace(/^\//, "");
25258
+ const safeKey = toExternalAssetKey(absPath);
25217
25259
  externalAssets.set(safeKey, absPath);
25218
25260
  return safeKey;
25219
25261
  }
@@ -25285,7 +25327,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
25285
25327
  const audios = dedupeElementsById([...mainAudios, ...subAudios]);
25286
25328
  for (const video of videos) {
25287
25329
  if (isHttpUrl(video.src)) continue;
25288
- const videoPath = resolve13(projectDir, video.src);
25330
+ const videoPath = resolve14(projectDir, video.src);
25289
25331
  const reencode = `ffmpeg -i "${video.src}" -c:v libx264 -r 30 -g 30 -keyint_min 30 -movflags +faststart -c:a copy output.mp4`;
25290
25332
  Promise.all([analyzeKeyframeIntervals(videoPath), extractVideoMetadata(videoPath)]).then(([analysis, metadata]) => {
25291
25333
  if (analysis.isProblematic) {
@@ -25420,6 +25462,7 @@ var init_htmlCompiler2 = __esm({
25420
25462
  init_esm10();
25421
25463
  init_src();
25422
25464
  init_ffprobe2();
25465
+ init_paths();
25423
25466
  init_src2();
25424
25467
  init_urlDownloader2();
25425
25468
  init_deterministicFonts();
@@ -25478,7 +25521,7 @@ import {
25478
25521
  copyFileSync as copyFileSync2,
25479
25522
  appendFileSync
25480
25523
  } from "fs";
25481
- import { join as join29, dirname as dirname10, resolve as resolve14 } from "path";
25524
+ import { join as join30, dirname as dirname10, resolve as resolve15 } from "path";
25482
25525
  import { randomUUID as randomUUID2 } from "crypto";
25483
25526
  import { freemem as freemem2 } from "os";
25484
25527
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -25534,17 +25577,17 @@ function installDebugLogger(logPath, log = defaultLogger) {
25534
25577
  };
25535
25578
  }
25536
25579
  function writeCompiledArtifacts(compiled, workDir, includeSummary) {
25537
- const compileDir = join29(workDir, "compiled");
25580
+ const compileDir = join30(workDir, "compiled");
25538
25581
  mkdirSync17(compileDir, { recursive: true });
25539
- writeFileSync10(join29(compileDir, "index.html"), compiled.html, "utf-8");
25582
+ writeFileSync10(join30(compileDir, "index.html"), compiled.html, "utf-8");
25540
25583
  for (const [srcPath, html] of compiled.subCompositions) {
25541
- const outPath = join29(compileDir, srcPath);
25584
+ const outPath = join30(compileDir, srcPath);
25542
25585
  mkdirSync17(dirname10(outPath), { recursive: true });
25543
25586
  writeFileSync10(outPath, html, "utf-8");
25544
25587
  }
25545
25588
  for (const [relativePath, absolutePath] of compiled.externalAssets) {
25546
- const outPath = resolve14(join29(compileDir, relativePath));
25547
- if (!outPath.startsWith(compileDir + "/")) {
25589
+ const outPath = resolve15(join30(compileDir, relativePath));
25590
+ if (!isPathInside(outPath, compileDir)) {
25548
25591
  console.warn(`[Render] Skipping external asset with unsafe path: ${relativePath}`);
25549
25592
  continue;
25550
25593
  }
@@ -25572,7 +25615,7 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
25572
25615
  })),
25573
25616
  subCompositions: Array.from(compiled.subCompositions.keys())
25574
25617
  };
25575
- writeFileSync10(join29(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
25618
+ writeFileSync10(join30(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
25576
25619
  }
25577
25620
  }
25578
25621
  function createRenderJob(config) {
@@ -25616,9 +25659,9 @@ function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
25616
25659
  }
25617
25660
  async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSignal) {
25618
25661
  const moduleDir = dirname10(fileURLToPath3(import.meta.url));
25619
- const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve14(process.env.PRODUCER_RENDERS_DIR, "..") : resolve14(moduleDir, "../..");
25620
- const debugDir = join29(producerRoot, ".debug");
25621
- const workDir = job.config.debug ? join29(debugDir, job.id) : join29(dirname10(outputPath), `work-${job.id}`);
25662
+ const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve15(process.env.PRODUCER_RENDERS_DIR, "..") : resolve15(moduleDir, "../..");
25663
+ const debugDir = join30(producerRoot, ".debug");
25664
+ const workDir = job.config.debug ? join30(debugDir, job.id) : join30(dirname10(outputPath), `work-${job.id}`);
25622
25665
  const pipelineStart = Date.now();
25623
25666
  const log = job.config.logger ?? defaultLogger;
25624
25667
  let fileServer = null;
@@ -25626,7 +25669,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
25626
25669
  let lastBrowserConsole = [];
25627
25670
  let restoreLogger = null;
25628
25671
  const perfStages = {};
25629
- const perfOutputPath = join29(workDir, "perf-summary.json");
25672
+ const perfOutputPath = join30(workDir, "perf-summary.json");
25630
25673
  const cfg = { ...job.config.producerConfig ?? resolveConfig() };
25631
25674
  const outputFormat = job.config.format ?? "mp4";
25632
25675
  const isWebm = outputFormat === "webm";
@@ -25648,19 +25691,19 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
25648
25691
  assertNotAborted();
25649
25692
  if (!existsSync27(workDir)) mkdirSync17(workDir, { recursive: true });
25650
25693
  if (job.config.debug) {
25651
- const logPath = join29(workDir, "render.log");
25694
+ const logPath = join30(workDir, "render.log");
25652
25695
  restoreLogger = installDebugLogger(logPath, log);
25653
25696
  }
25654
25697
  const entryFile = job.config.entryFile || "index.html";
25655
- let htmlPath = join29(projectDir, entryFile);
25698
+ let htmlPath = join30(projectDir, entryFile);
25656
25699
  if (!existsSync27(htmlPath)) {
25657
25700
  throw new Error(`Entry file not found: ${htmlPath}`);
25658
25701
  }
25659
25702
  assertNotAborted();
25660
25703
  const rawEntry = readFileSync20(htmlPath, "utf-8");
25661
25704
  if (entryFile !== "index.html" && rawEntry.trimStart().startsWith("<template")) {
25662
- const wrapperPath = join29(workDir, "standalone-entry.html");
25663
- const projectIndexPath = join29(projectDir, "index.html");
25705
+ const wrapperPath = join30(workDir, "standalone-entry.html");
25706
+ const projectIndexPath = join30(projectDir, "index.html");
25664
25707
  if (!existsSync27(projectIndexPath)) {
25665
25708
  throw new Error(
25666
25709
  `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
@@ -25684,7 +25727,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
25684
25727
  const stage1Start = Date.now();
25685
25728
  updateJobStatus(job, "preprocessing", "Compiling composition", 5, onProgress);
25686
25729
  const compileStart = Date.now();
25687
- let compiled = await compileForRender(projectDir, htmlPath, join29(workDir, "downloads"));
25730
+ let compiled = await compileForRender(projectDir, htmlPath, join30(workDir, "downloads"));
25688
25731
  assertNotAborted();
25689
25732
  perfStages.compileOnlyMs = Date.now() - compileStart;
25690
25733
  writeCompiledArtifacts(compiled, workDir, Boolean(job.config.debug));
@@ -25713,7 +25756,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
25713
25756
  reasons.push(`${compiled.unresolvedCompositions.length} unresolved composition(s)`);
25714
25757
  fileServer = await createFileServer2({
25715
25758
  projectDir,
25716
- compiledDir: join29(workDir, "compiled"),
25759
+ compiledDir: join30(workDir, "compiled"),
25717
25760
  port: 0
25718
25761
  });
25719
25762
  assertNotAborted();
@@ -25726,7 +25769,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
25726
25769
  };
25727
25770
  probeSession = await createCaptureSession(
25728
25771
  fileServer.url,
25729
- join29(workDir, "probe"),
25772
+ join30(workDir, "probe"),
25730
25773
  captureOpts,
25731
25774
  null,
25732
25775
  cfg
@@ -25758,7 +25801,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
25758
25801
  compiled,
25759
25802
  resolutions,
25760
25803
  projectDir,
25761
- join29(workDir, "downloads")
25804
+ join30(workDir, "downloads")
25762
25805
  );
25763
25806
  assertNotAborted();
25764
25807
  composition.videos = compiled.videos;
@@ -25893,12 +25936,12 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
25893
25936
  const stage2Start = Date.now();
25894
25937
  updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress);
25895
25938
  let frameLookup = null;
25896
- const compiledDir = join29(workDir, "compiled");
25939
+ const compiledDir = join30(workDir, "compiled");
25897
25940
  if (composition.videos.length > 0) {
25898
25941
  const extractionResult = await extractAllVideoFrames(
25899
25942
  composition.videos,
25900
25943
  projectDir,
25901
- { fps: job.config.fps, outputDir: join29(workDir, "video-frames") },
25944
+ { fps: job.config.fps, outputDir: join30(workDir, "video-frames") },
25902
25945
  abortSignal,
25903
25946
  void 0,
25904
25947
  compiledDir
@@ -25932,13 +25975,13 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
25932
25975
  }
25933
25976
  const stage3Start = Date.now();
25934
25977
  updateJobStatus(job, "preprocessing", "Processing audio tracks", 20, onProgress);
25935
- const audioOutputPath = join29(workDir, "audio.aac");
25978
+ const audioOutputPath = join30(workDir, "audio.aac");
25936
25979
  let hasAudio = false;
25937
25980
  if (composition.audios.length > 0) {
25938
25981
  const audioResult = await processCompositionAudio(
25939
25982
  composition.audios,
25940
25983
  projectDir,
25941
- join29(workDir, "audio-work"),
25984
+ join30(workDir, "audio-work"),
25942
25985
  audioOutputPath,
25943
25986
  job.duration,
25944
25987
  abortSignal,
@@ -25956,12 +25999,12 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
25956
25999
  if (!fileServer) {
25957
26000
  fileServer = await createFileServer2({
25958
26001
  projectDir,
25959
- compiledDir: join29(workDir, "compiled"),
26002
+ compiledDir: join30(workDir, "compiled"),
25960
26003
  port: 0
25961
26004
  });
25962
26005
  assertNotAborted();
25963
26006
  }
25964
- const framesDir = join29(workDir, "captured-frames");
26007
+ const framesDir = join30(workDir, "captured-frames");
25965
26008
  if (!existsSync27(framesDir)) mkdirSync17(framesDir, { recursive: true });
25966
26009
  const captureOptions = {
25967
26010
  width,
@@ -25973,7 +26016,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
25973
26016
  const workerCount = calculateOptimalWorkers(job.totalFrames, job.config.workers, cfg);
25974
26017
  const FORMAT_EXT2 = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
25975
26018
  const videoExt = FORMAT_EXT2[outputFormat] ?? ".mp4";
25976
- const videoOnlyPath = join29(workDir, `video-only${videoExt}`);
26019
+ const videoOnlyPath = join30(workDir, `video-only${videoExt}`);
25977
26020
  const preset = getEncoderPreset(job.config.quality, outputFormat);
25978
26021
  const effectiveQuality = job.config.crf ?? preset.quality;
25979
26022
  const effectiveBitrate = job.config.videoBitrate;
@@ -26249,7 +26292,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
26249
26292
  }
26250
26293
  if (job.config.debug) {
26251
26294
  if (existsSync27(outputPath)) {
26252
- const debugOutput = join29(workDir, `output${videoExt}`);
26295
+ const debugOutput = join30(workDir, `output${videoExt}`);
26253
26296
  copyFileSync2(outputPath, debugOutput);
26254
26297
  }
26255
26298
  } else {
@@ -26350,6 +26393,7 @@ var init_renderOrchestrator = __esm({
26350
26393
  init_fileServer2();
26351
26394
  init_htmlCompiler2();
26352
26395
  init_logger();
26396
+ init_paths();
26353
26397
  RenderCancelledError = class extends Error {
26354
26398
  reason;
26355
26399
  constructor(message = "render_cancelled", reason = "aborted") {
@@ -26387,7 +26431,7 @@ var init_config3 = __esm({
26387
26431
 
26388
26432
  // ../producer/src/services/hyperframeLint.ts
26389
26433
  import { existsSync as existsSync28, readFileSync as readFileSync21, statSync as statSync8 } from "fs";
26390
- import { resolve as resolve15, join as join30 } from "path";
26434
+ import { resolve as resolve16, join as join31 } from "path";
26391
26435
  function isStringRecord(value) {
26392
26436
  if (!value || typeof value !== "object" || Array.isArray(value)) {
26393
26437
  return false;
@@ -26414,7 +26458,7 @@ function pickEntryFile(files, preferredEntryFile) {
26414
26458
  return null;
26415
26459
  }
26416
26460
  function readProjectEntryFile(projectDir, preferredEntryFile) {
26417
- const absProjectDir = resolve15(projectDir);
26461
+ const absProjectDir = resolve16(projectDir);
26418
26462
  if (!existsSync28(absProjectDir) || !statSync8(absProjectDir).isDirectory()) {
26419
26463
  return { error: `Project directory not found: ${absProjectDir}` };
26420
26464
  }
@@ -26422,7 +26466,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
26422
26466
  (value) => typeof value === "string" && value.trim().length > 0
26423
26467
  );
26424
26468
  for (const entryFile of entryCandidates) {
26425
- const absoluteEntryPath = resolve15(absProjectDir, entryFile);
26469
+ const absoluteEntryPath = resolve16(absProjectDir, entryFile);
26426
26470
  if (!absoluteEntryPath.startsWith(absProjectDir)) {
26427
26471
  return { error: `Entry file must stay inside project directory: ${entryFile}` };
26428
26472
  }
@@ -26435,7 +26479,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
26435
26479
  }
26436
26480
  }
26437
26481
  return {
26438
- error: `No HTML entry file found in project directory: ${join30(absProjectDir, preferredEntryFile || "index.html")}`
26482
+ error: `No HTML entry file found in project directory: ${join31(absProjectDir, preferredEntryFile || "index.html")}`
26439
26483
  };
26440
26484
  }
26441
26485
  function prepareHyperframeLintBody(body) {
@@ -26481,23 +26525,6 @@ var init_hyperframeLint = __esm({
26481
26525
  }
26482
26526
  });
26483
26527
 
26484
- // ../producer/src/utils/paths.ts
26485
- import { resolve as resolve16, basename, join as join31 } from "path";
26486
- function resolveRenderPaths(projectDir, outputPath, rendersDir = DEFAULT_RENDERS_DIR) {
26487
- const absoluteProjectDir = resolve16(projectDir);
26488
- const projectName = basename(absoluteProjectDir);
26489
- const resolvedOutputPath = outputPath ?? join31(rendersDir, `${projectName}.mp4`);
26490
- const absoluteOutputPath = resolve16(resolvedOutputPath);
26491
- return { absoluteProjectDir, absoluteOutputPath };
26492
- }
26493
- var DEFAULT_RENDERS_DIR;
26494
- var init_paths = __esm({
26495
- "../producer/src/utils/paths.ts"() {
26496
- "use strict";
26497
- DEFAULT_RENDERS_DIR = process.env.PRODUCER_RENDERS_DIR ?? resolve16(new URL(import.meta.url).pathname, "../../..", "renders");
26498
- }
26499
- });
26500
-
26501
26528
  // ../producer/src/utils/semaphore.ts
26502
26529
  var Semaphore;
26503
26530
  var init_semaphore = __esm({
@@ -28300,7 +28327,7 @@ __export(add_exports, {
28300
28327
  runAdd: () => runAdd
28301
28328
  });
28302
28329
  import { existsSync as existsSync33 } from "fs";
28303
- import { resolve as resolve21, relative as relative2 } from "path";
28330
+ import { resolve as resolve21, relative as relative3 } from "path";
28304
28331
  function remapTarget(item, originalTarget, paths) {
28305
28332
  if (item.type === "hyperframes:block") {
28306
28333
  const blocksDir = paths.blocks.replace(/\/+$/, "");
@@ -28439,7 +28466,7 @@ var init_add = __esm({
28439
28466
  console.log("");
28440
28467
  console.log(`${c.success("\u2713")} Added ${c.accent(result.name)} (${result.type})`);
28441
28468
  for (const file of result.written) {
28442
- console.log(` ${c.dim(relative2(projectDir, file))}`);
28469
+ console.log(` ${c.dim(relative3(projectDir, file))}`);
28443
28470
  }
28444
28471
  if (result.snippet) {
28445
28472
  console.log("");
@@ -30868,18 +30895,18 @@ function checkFFmpeg() {
30868
30895
  return {
30869
30896
  ok: false,
30870
30897
  detail: "Not found",
30871
- hint: process.platform === "darwin" ? "brew install ffmpeg" : "sudo apt install ffmpeg"
30898
+ hint: getFFmpegInstallHint()
30872
30899
  };
30873
30900
  }
30874
30901
  function checkFFprobe() {
30875
30902
  try {
30876
- const result = execSync3("which ffprobe", { encoding: "utf-8", timeout: 5e3 }).trim();
30877
- return { ok: true, detail: result };
30903
+ const version = execSync3("ffprobe -version", { encoding: "utf-8", timeout: 5e3 }).split("\n")[0] ?? "";
30904
+ return { ok: true, detail: version.trim() };
30878
30905
  } catch {
30879
30906
  return {
30880
30907
  ok: false,
30881
30908
  detail: "Not found",
30882
- hint: "Installed with ffmpeg"
30909
+ hint: `Installed with ffmpeg \u2014 ${getFFmpegInstallHint()}`
30883
30910
  };
30884
30911
  }
30885
30912
  }
@@ -31531,7 +31558,7 @@ __export(snapshot_exports, {
31531
31558
  examples: () => examples18
31532
31559
  });
31533
31560
  import { existsSync as existsSync44, readFileSync as readFileSync31, mkdirSync as mkdirSync24 } from "fs";
31534
- import { resolve as resolve32, join as join44, dirname as dirname20, relative as relative3, isAbsolute as isAbsolute3 } from "path";
31561
+ import { resolve as resolve32, join as join44, dirname as dirname20, relative as relative4, isAbsolute as isAbsolute4 } from "path";
31535
31562
  import { fileURLToPath as fileURLToPath8 } from "url";
31536
31563
  async function captureSnapshots(projectDir, opts) {
31537
31564
  const { bundleToSingleHtml: bundleToSingleHtml2 } = await Promise.resolve().then(() => (init_compiler(), compiler_exports));
@@ -31564,8 +31591,8 @@ async function captureSnapshots(projectDir, opts) {
31564
31591
  return;
31565
31592
  }
31566
31593
  const filePath = resolve32(projectDir, decodeURIComponent(url).replace(/^\//, ""));
31567
- const rel = relative3(projectDir, filePath);
31568
- if (rel.startsWith("..") || isAbsolute3(rel)) {
31594
+ const rel = relative4(projectDir, filePath);
31595
+ if (rel.startsWith("..") || isAbsolute4(rel)) {
31569
31596
  res.writeHead(403);
31570
31597
  res.end();
31571
31598
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperframes",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "HyperFrames CLI — create, preview, and render HTML video compositions",
5
5
  "repository": {
6
6
  "type": "git",