@hyperframes/studio-server 0.8.117 → 0.8.119

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.
package/dist/index.js CHANGED
@@ -3258,6 +3258,7 @@ import {
3258
3258
  stripEmbeddedRuntimeScripts as stripEmbeddedRuntimeScripts2
3259
3259
  } from "@hyperframes/core/compiler";
3260
3260
  import { STUDIO_PREVIEW_MARK_META } from "@hyperframes/core/studio-preview-mark";
3261
+ import { gsapCdnDist as gsapCdnDist2, motionPathPluginUrl } from "@hyperframes/core/gsap-cdn";
3261
3262
  import { injectTagsAtHeadStart } from "@hyperframes/core/compiler/html-document";
3262
3263
  import { isWithinProjectRoot } from "@hyperframes/parsers/asset-resolution";
3263
3264
 
@@ -3272,6 +3273,7 @@ import {
3272
3273
  } from "@hyperframes/core";
3273
3274
  import { stripEmbeddedRuntimeScripts } from "@hyperframes/core/compiler";
3274
3275
  import { isFullHtmlDocument } from "@hyperframes/core/compiler/html-document";
3276
+ import { gsapCdnDist } from "@hyperframes/core/gsap-cdn";
3275
3277
  function rewriteRelativePaths(root, compPath, projectDir) {
3276
3278
  const assetExists = (path) => existsSync4(join6(projectDir, path));
3277
3279
  rewriteAssetPaths(
@@ -3421,7 +3423,7 @@ ${compHeadContent}`;
3421
3423
  }
3422
3424
  if (!headContent.includes("gsap")) {
3423
3425
  headContent += `
3424
- <script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>`;
3426
+ <script src="${gsapCdnDist()}gsap.min.js"></script>`;
3425
3427
  }
3426
3428
  const htmlOpen = htmlAttrs ? `<html ${htmlAttrs}>` : "<html>";
3427
3429
  const bodyOpen = bodyAttrs ? `<body ${bodyAttrs}>` : "<body>";
@@ -3572,10 +3574,9 @@ function lazyPreviewImages(html) {
3572
3574
 
3573
3575
  // src/routes/preview.ts
3574
3576
  var PROJECT_SIGNATURE_META = "hyperframes-project-signature";
3575
- var GSAP_CDN_VERSION = "3.15.0";
3576
- var GSAP_CDN_SCRIPT = `<script src="https://cdn.jsdelivr.net/npm/gsap@${GSAP_CDN_VERSION}/dist/gsap.min.js"></script>`;
3577
- var GSAP_CUSTOM_EASE_CDN_SCRIPT = `<script src="https://cdn.jsdelivr.net/npm/gsap@${GSAP_CDN_VERSION}/dist/CustomEase.min.js"></script>`;
3578
- var GSAP_MOTION_PATH_CDN_SCRIPT = `<script src="https://cdn.jsdelivr.net/npm/gsap@${GSAP_CDN_VERSION}/dist/MotionPathPlugin.min.js"></script>`;
3577
+ var GSAP_CDN_SCRIPT = `<script src="${gsapCdnDist2()}gsap.min.js"></script>`;
3578
+ var GSAP_CUSTOM_EASE_CDN_SCRIPT = `<script src="${gsapCdnDist2()}CustomEase.min.js"></script>`;
3579
+ var GSAP_MOTION_PATH_CDN_SCRIPT = `<script src="${motionPathPluginUrl()}"></script>`;
3579
3580
  function injectProjectSignature(html, signature) {
3580
3581
  const tag = `<meta name="${PROJECT_SIGNATURE_META}" content="${signature}">`;
3581
3582
  if (html.includes(`name="${PROJECT_SIGNATURE_META}"`)) {
@@ -3632,8 +3633,8 @@ function injectMotionPathPluginIfNeeded(html) {
3632
3633
  const gsapScript = /<script\b[^>]*\bsrc=["'][^"']*\/gsap(\.min)?\.js["'][^>]*>\s*<\/script>/i;
3633
3634
  const match = html.match(gsapScript);
3634
3635
  if (match) {
3635
- const version = match[0].match(/gsap@([\d.]+)/)?.[1] ?? GSAP_CDN_VERSION;
3636
- const pluginTag = `<script src="https://cdn.jsdelivr.net/npm/gsap@${version}/dist/MotionPathPlugin.min.js"></script>`;
3636
+ const version = match[0].match(/gsap@([\d.]+)/)?.[1];
3637
+ const pluginTag = `<script src="${motionPathPluginUrl(version)}"></script>`;
3637
3638
  const end = html.indexOf(match[0]) + match[0].length;
3638
3639
  return html.slice(0, end) + "\n" + pluginTag + html.slice(end);
3639
3640
  }
@@ -3665,7 +3666,7 @@ function injectStudioMotionScript(html, projectDir, activeCompositionPath) {
3665
3666
  var GSAP_FALLBACK_ATTR = "data-hf-gsap-fallback";
3666
3667
  var GSAP_CDN_FALLBACK_SCRIPT = `<script ${GSAP_FALLBACK_ATTR}>
3667
3668
  (function(){
3668
- var cdnBase="https://cdn.jsdelivr.net/npm/gsap@${GSAP_CDN_VERSION}/dist/";
3669
+ var cdnBase="${gsapCdnDist2()}";
3669
3670
  var loaded={};
3670
3671
  function loadFallback(file){
3671
3672
  if(loaded[file])return loaded[file];
@@ -4890,10 +4891,14 @@ function registerWaveformRoutes(api, adapter) {
4890
4891
 
4891
4892
  // src/routes/freezeFrame.ts
4892
4893
  import { execFile } from "child_process";
4893
- import { closeSync as closeSync3, openSync as openSync3, readFileSync as readFileSync11, rmSync as rmSync5, statSync as statSync8 } from "fs";
4894
- import { dirname as dirname5, join as join13, relative as relative4, sep as sep2 } from "path";
4894
+ import { readFileSync as readFileSync11, rmSync as rmSync5, statSync as statSync8 } from "fs";
4895
+ import { basename as basename2, dirname as dirname5, join as join13, relative as relative4, sep as sep2 } from "path";
4895
4896
  import { findFfBinary as findFfBinary3 } from "@hyperframes/parsers/ff-binaries";
4896
- import { replaceFileAtomically as replaceFileAtomically3 } from "@hyperframes/core/atomic-file";
4897
+ import {
4898
+ atomicTempPath,
4899
+ createFileAtomically as createFileAtomically2,
4900
+ replaceFileAtomically as replaceFileAtomically3
4901
+ } from "@hyperframes/core/atomic-file";
4897
4902
 
4898
4903
  // src/helpers/freezeFrame.ts
4899
4904
  import { createHash as createHash5, randomBytes } from "crypto";
@@ -4918,7 +4923,22 @@ function freezeFrameMediaTime(input) {
4918
4923
  return input.mediaStart + sourceTimeAt(rate, Math.max(0, input.playhead - input.clipStart));
4919
4924
  }
4920
4925
  function freezeExtractArgs(src, mediaTime, output) {
4921
- return ["-y", "-ss", String(round3(mediaTime)), "-i", src, "-frames:v", "1", output];
4926
+ return [
4927
+ "-y",
4928
+ "-ss",
4929
+ String(round3(mediaTime)),
4930
+ "-i",
4931
+ src,
4932
+ "-frames:v",
4933
+ "1",
4934
+ "-c:v",
4935
+ "png",
4936
+ "-f",
4937
+ "image2",
4938
+ "-update",
4939
+ "1",
4940
+ output
4941
+ ];
4922
4942
  }
4923
4943
  var randomStillToken = () => randomBytes(4).toString("hex");
4924
4944
  function freezeStillFileName(clipId, playhead, token = randomStillToken()) {
@@ -5091,15 +5111,38 @@ function readExpected(absPath, expectedVersion) {
5091
5111
  }
5092
5112
  return fileContentVersion(content) === expectedVersion ? { content } : { error: "file conflict", status: 409 };
5093
5113
  }
5094
- function claimStillNameBeforeExtracting(path) {
5114
+ function readFrame(path) {
5115
+ try {
5116
+ return readFileSync11(path);
5117
+ } catch (error) {
5118
+ if (error.code === "ENOENT") return null;
5119
+ throw error;
5120
+ }
5121
+ }
5122
+ function publishStill(path, frame) {
5095
5123
  try {
5096
- closeSync3(openSync3(path, "wx"));
5124
+ createFileAtomically2(path, frame);
5097
5125
  return true;
5098
5126
  } catch (error) {
5099
5127
  if (error.code === "EEXIST") return false;
5100
5128
  throw error;
5101
5129
  }
5102
5130
  }
5131
+ async function extractThenPublishWhole(extract, mediaPath, mediaTime, imagePath) {
5132
+ const unseenByHistory = atomicTempPath(imagePath);
5133
+ try {
5134
+ const extracted = await extract(freezeExtractArgs(mediaPath, mediaTime, unseenByHistory));
5135
+ const frame = extracted.ok ? readFrame(unseenByHistory) : null;
5136
+ if (!frame?.length) {
5137
+ const reason = extracted.ok ? "no frame at this time" : extracted.error ?? "ffmpeg failed";
5138
+ return { error: `Could not extract the frame: ${reason}`, status: 500 };
5139
+ }
5140
+ if (publishStill(imagePath, frame)) return null;
5141
+ return { error: `freeze still already exists: ${basename2(imagePath)}`, status: 409 };
5142
+ } finally {
5143
+ rmSync5(unseenByHistory, { force: true });
5144
+ }
5145
+ }
5103
5146
  async function extractStill(projectDir, absPath, source, playhead, tools) {
5104
5147
  const fileDir = dirname5(absPath);
5105
5148
  const mediaPath = pinWithinProject(projectDir, relative4(projectDir, join13(fileDir, source.src)));
@@ -5111,15 +5154,13 @@ async function extractStill(projectDir, absPath, source, playhead, tools) {
5111
5154
  if (!imagePath || dirname5(imagePath) !== freezeDir) {
5112
5155
  return { error: `forbidden freeze path: ${fileName}`, status: 403 };
5113
5156
  }
5114
- if (!claimStillNameBeforeExtracting(imagePath))
5115
- return { error: `freeze still already exists: ${fileName}`, status: 409 };
5116
- const extracted = await tools.extract(freezeExtractArgs(mediaPath, source.mediaTime, imagePath));
5117
- const partialOrNoFrame = !extracted.ok || statSync8(imagePath).size === 0;
5118
- if (partialOrNoFrame) {
5119
- rmSync5(imagePath, { force: true });
5120
- const reason = extracted.ok ? "no frame at this time" : extracted.error ?? "ffmpeg failed";
5121
- return { error: `Could not extract the frame: ${reason}`, status: 500 };
5122
- }
5157
+ const failed = await extractThenPublishWhole(
5158
+ tools.extract,
5159
+ mediaPath,
5160
+ source.mediaTime,
5161
+ imagePath
5162
+ );
5163
+ if (failed) return failed;
5123
5164
  const depth = relative4(projectDir, fileDir).split(sep2).filter(Boolean).length;
5124
5165
  const stillPath = `${FREEZE_DIR.join("/")}/${fileName}`;
5125
5166
  return { imageSrc: `${"../".repeat(depth)}${stillPath}`, stillPath, imagePath };
@@ -5408,7 +5449,7 @@ function registerPeakRoutes(api, adapter, decode = decodePeakMap) {
5408
5449
  }
5409
5450
 
5410
5451
  // src/routes/fonts.ts
5411
- import { closeSync as closeSync4, constants, fstatSync as fstatSync3, openSync as openSync4, readSync } from "fs";
5452
+ import { closeSync as closeSync3, constants, fstatSync as fstatSync3, openSync as openSync3, readSync } from "fs";
5412
5453
  import {
5413
5454
  collectFontFileEntries,
5414
5455
  fontDirectories,
@@ -5515,7 +5556,7 @@ function registerFontRoutes(api) {
5515
5556
  if (!located) return c.json({ error: "font not found" }, 404);
5516
5557
  let fd;
5517
5558
  try {
5518
- fd = openSync4(located.path, constants.O_RDONLY | constants.O_NOFOLLOW);
5559
+ fd = openSync3(located.path, constants.O_RDONLY | constants.O_NOFOLLOW);
5519
5560
  } catch {
5520
5561
  return c.json({ error: "font file not accessible" }, 404);
5521
5562
  }
@@ -5537,7 +5578,7 @@ function registerFontRoutes(api) {
5537
5578
  } catch {
5538
5579
  return c.json({ error: "failed to read font file" }, 500);
5539
5580
  } finally {
5540
- closeSync4(fd);
5581
+ closeSync3(fd);
5541
5582
  }
5542
5583
  });
5543
5584
  }
@@ -5674,7 +5715,7 @@ function registerSelectionRoutes(api, adapter) {
5674
5715
  // src/routes/media.ts
5675
5716
  import { streamSSE as streamSSE2 } from "hono/streaming";
5676
5717
  import { existsSync as existsSync10 } from "fs";
5677
- import { basename as basename2, dirname as dirname6, extname, join as join15, posix } from "path";
5718
+ import { basename as basename3, dirname as dirname6, extname, join as join15, posix } from "path";
5678
5719
  var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
5679
5720
  ".mp4",
5680
5721
  ".mov",
@@ -5704,7 +5745,7 @@ function containsNullByte(path) {
5704
5745
  return path.includes("\0");
5705
5746
  }
5706
5747
  function slugFileBase(path) {
5707
- const name = basename2(path, extname(path)).replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
5748
+ const name = basename3(path, extname(path)).replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
5708
5749
  return name || "media";
5709
5750
  }
5710
5751
  function uniqueAssetPath(projectDir, assetPath, taken) {