@hyperframes/studio-server 0.8.192 → 0.8.194

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.
@@ -512,14 +512,41 @@ async function cloneOrCopy(from, to, beforeReplace) {
512
512
  throw error;
513
513
  }
514
514
  }
515
+ var listDir = (path) => readdir(path, { withFileTypes: true }).catch((error) => {
516
+ if (error.code === "ENOENT") return [];
517
+ throw error;
518
+ });
515
519
  async function openBlobStore(dir) {
516
520
  const sizes = /* @__PURE__ */ new Map();
521
+ const asideDir = join2(dirname(dir), "aside");
522
+ const asideSizes = /* @__PURE__ */ new Map();
523
+ const holds = /* @__PURE__ */ new Map();
517
524
  await mkdir(dir, { recursive: true });
518
- for (const shard of await readdir(dir))
519
- for (const hash of await readdir(join2(dir, shard)).catch(() => []))
520
- sizes.set(hash, (await stat(join2(dir, shard, hash))).size);
521
- const pathOf = (hash) => blobPath(dir, hash);
525
+ for (const [root, found] of [
526
+ [dir, sizes],
527
+ [asideDir, asideSizes]
528
+ ])
529
+ for (const shard of await listDir(root)) {
530
+ if (!shard.isDirectory()) {
531
+ if (shard.name.startsWith("incoming-")) await rm(join2(root, shard.name), { force: true });
532
+ continue;
533
+ }
534
+ for (const hash of await listDir(join2(root, shard.name)))
535
+ found.set(hash.name, (await stat(join2(root, shard.name, hash.name))).size);
536
+ }
537
+ for (const hash of asideSizes.keys()) holds.set(hash, /* @__PURE__ */ new Set(["*"]));
538
+ const pathOf = (hash) => blobPath(sizes.has(hash) ? dir : asideDir, hash);
522
539
  let total = [...sizes.values()].reduce((sum, size) => sum + size, 0);
540
+ const forget = async (hash) => {
541
+ await rm(blobPath(dir, hash), { force: true });
542
+ total -= sizes.get(hash) ?? 0;
543
+ sizes.delete(hash);
544
+ };
545
+ const forgetAside = async (hash) => {
546
+ await rm(blobPath(asideDir, hash), { force: true });
547
+ asideSizes.delete(hash);
548
+ holds.delete(hash);
549
+ };
523
550
  return {
524
551
  async put(absPath) {
525
552
  const temp = join2(dir, `incoming-${randomUUID3()}`);
@@ -533,35 +560,55 @@ async function openBlobStore(dir) {
533
560
  await rm(temp, { force: true });
534
561
  return hash;
535
562
  }
536
- await mkdir(dirname(pathOf(hash)), { recursive: true });
563
+ await mkdir(dirname(blobPath(dir, hash)), { recursive: true });
537
564
  const size = (await stat(temp)).size;
538
- await rename(temp, pathOf(hash));
565
+ await rename(temp, blobPath(dir, hash));
539
566
  sizes.set(hash, size);
540
567
  total += size;
541
568
  return hash;
542
569
  },
543
570
  reference: (absPath) => hashFile(absPath),
544
- has: (hash) => sizes.has(hash),
545
- read: async (hash) => readBlob(dir, hash),
571
+ has: (hash) => sizes.has(hash) || asideSizes.has(hash),
572
+ read: (hash) => readFile(pathOf(hash)),
546
573
  writeTo: async (hash, absPath, beforeReplace) => cloneOrCopy(pathOf(hash), absPath, beforeReplace),
574
+ async release(held) {
575
+ for (const [hash, path] of held) {
576
+ holds.get(hash)?.delete(path);
577
+ if (holds.get(hash)?.size === 0) await forgetAside(hash);
578
+ }
579
+ },
580
+ async keep(path, absPath, remove) {
581
+ const hash = await hashFile(absPath);
582
+ if (sizes.has(hash)) return;
583
+ if (asideSizes.has(hash)) {
584
+ holds.get(hash).add(path);
585
+ return;
586
+ }
587
+ const to = blobPath(asideDir, hash);
588
+ await mkdir(dirname(to), { recursive: true });
589
+ const moved = remove && await rename(absPath, to).then(
590
+ () => true,
591
+ () => false
592
+ );
593
+ if (!moved) {
594
+ const temp = join2(asideDir, `incoming-${randomUUID3()}`);
595
+ await cloneOrStream(absPath, temp).catch(async (error) => {
596
+ await rm(temp, { force: true });
597
+ throw error;
598
+ });
599
+ await rename(temp, to);
600
+ }
601
+ asideSizes.set(hash, (await stat(to)).size);
602
+ holds.set(hash, /* @__PURE__ */ new Set([path]));
603
+ },
547
604
  bytes: () => total,
548
605
  size: (hash) => sizes.get(hash) ?? 0,
549
606
  async drop(hashes) {
550
- for (const hash of hashes) {
551
- const size = sizes.get(hash);
552
- if (size === void 0) continue;
553
- await rm(pathOf(hash), { force: true });
554
- sizes.delete(hash);
555
- total -= size;
556
- }
607
+ for (const hash of hashes) if (sizes.has(hash)) await forget(hash);
557
608
  },
558
609
  async prune(keep) {
559
- for (const [hash, size] of sizes) {
560
- if (keep.has(hash)) continue;
561
- await rm(pathOf(hash), { force: true });
562
- sizes.delete(hash);
563
- total -= size;
564
- }
610
+ for (const hash of [...sizes.keys()]) if (!keep.has(hash)) await forget(hash);
611
+ for (const hash of [...asideSizes.keys()]) if (!keep.has(hash)) await forgetAside(hash);
565
612
  }
566
613
  };
567
614
  }
@@ -2309,6 +2356,9 @@ var Engine = class {
2309
2356
  this.windows.push(group);
2310
2357
  try {
2311
2358
  for (const [path, hash] of changes) await this.writeProjectFile(path, hash);
2359
+ await this.blobs.release(
2360
+ changes.flatMap(([path, hash]) => hash ? [[hash, path]] : [])
2361
+ );
2312
2362
  await this.sweep();
2313
2363
  } finally {
2314
2364
  this.windows = this.windows.filter((open) => open !== group);
@@ -2324,6 +2374,9 @@ var Engine = class {
2324
2374
  const version = hash === null ? DELETED_VERSION : hashVersion(hash);
2325
2375
  recordFileWriteReceipt(absPath, { path, version, writeToken });
2326
2376
  }
2377
+ const standing = lstatSync4(absPath, { throwIfNoEntry: false });
2378
+ if (standing?.isFile() && isUncopied(path, standing.size))
2379
+ await this.blobs.keep(path, absPath, hash === null);
2327
2380
  if (hash === null) {
2328
2381
  this.assertWritable();
2329
2382
  rmSync3(absPath, { force: true });
@@ -2409,6 +2462,18 @@ var Engine = class {
2409
2462
  [...this.tracked.keys()].map((path) => [path, null])
2410
2463
  );
2411
2464
  for (const [path, hash] of files) target.set(path, hash);
2465
+ if (point === START && this.adopting.size > 0) {
2466
+ throw new HistoryRefusedError(
2467
+ "The project's starting copy is still being saved; try again in a moment. Nothing was changed."
2468
+ );
2469
+ }
2470
+ const isProjectFile = (path) => !KEPT_HIDDEN_PATHS.has(path);
2471
+ const keepsAny = [...target].some(([path, hash]) => hash !== null && isProjectFile(path));
2472
+ if ([...this.tracked.keys()].some(isProjectFile) && !keepsAny) {
2473
+ throw new HistoryRefusedError(
2474
+ "Restoring this point would remove every file in this project, so nothing was changed."
2475
+ );
2476
+ }
2412
2477
  return this.writeAs(who, label, target, { restoredTo: point });
2413
2478
  }
2414
2479
  /** Settles, then runs `task` with its writes labelled `writeToken`. */
@@ -2528,6 +2593,12 @@ var HistoryClosedError = class extends Error {
2528
2593
  this.name = "HistoryClosedError";
2529
2594
  }
2530
2595
  };
2596
+ var HistoryRefusedError = class extends Error {
2597
+ constructor(message) {
2598
+ super(message);
2599
+ this.name = "HistoryRefusedError";
2600
+ }
2601
+ };
2531
2602
  async function openProjectHistory(options) {
2532
2603
  if (!existsSync3(options.projectDir))
2533
2604
  throw new HistoryClosedError(`${options.projectDir} is gone or now another project.`);
@@ -2692,6 +2763,7 @@ export {
2692
2763
  MAX_WINDOW_IDLE_MS,
2693
2764
  UNDO_MODES,
2694
2765
  HistoryClosedError,
2766
+ HistoryRefusedError,
2695
2767
  openProjectHistory,
2696
2768
  readProjectHistory,
2697
2769
  reclaimUncopiedBlobs,
@@ -1,7 +1,7 @@
1
1
  import '@hyperframes/core';
2
- export { a as PreviewApiAdapter, P as PreviewDocumentStore, i as injectMediaCodecMap, f as injectMediaCodecMapIntoHtml, g as isAutoProxyEnabled, p as proxyEtagSalt, r as resolvePreviewMediaCodecProbeCache } from '../mediaProxyPreview-CAiGJucG.js';
2
+ export { a as PreviewApiAdapter, P as PreviewDocumentStore, i as injectMediaCodecMap, f as injectMediaCodecMapIntoHtml, g as isAutoProxyEnabled, p as proxyEtagSalt, r as resolvePreviewMediaCodecProbeCache } from '../mediaProxyPreview-C0DZX2Mo.js';
3
3
  import './mediaCodecMap.js';
4
4
  import 'sharp';
5
5
  import '@hyperframes/parsers';
6
6
  import '@hyperframes/core/compiler';
7
- import '../projectHistory-ByC9sa3X.js';
7
+ import '../projectHistory-C18HDvac.js';
@@ -1,5 +1,5 @@
1
- import { P as ProjectHistory, f as HistoryTarget, g as HistoryWho } from '../projectHistory-ByC9sa3X.js';
2
- export { C as ClosedWindow, D as DEFAULT_HISTORY_ROOT, S as HISTORY_START, H as HistoryClosedError, a as HistoryEntry, b as HistoryEntrySide, c as HistoryFileChange, d as HistoryListItem, e as HistoryResult, h as HistoryWindow, M as MAX_WINDOW_IDLE_MS, i as ProjectHistoryOptions, j as ProjectHistoryView, U as UNDO_MODES, k as UndoMode, o as openProjectHistory, r as readProjectHistory } from '../projectHistory-ByC9sa3X.js';
1
+ import { P as ProjectHistory, g as HistoryTarget, h as HistoryWho } from '../projectHistory-C18HDvac.js';
2
+ export { C as ClosedWindow, D as DEFAULT_HISTORY_ROOT, S as HISTORY_START, H as HistoryClosedError, a as HistoryEntry, b as HistoryEntrySide, c as HistoryFileChange, d as HistoryListItem, e as HistoryRefusedError, f as HistoryResult, i as HistoryWindow, M as MAX_WINDOW_IDLE_MS, j as ProjectHistoryOptions, k as ProjectHistoryView, U as UNDO_MODES, l as UndoMode, o as openProjectHistory, r as readProjectHistory } from '../projectHistory-C18HDvac.js';
3
3
 
4
4
  interface ReclaimedHistory {
5
5
  id: string;
@@ -3,6 +3,7 @@ import {
3
3
  HistoryBusyError,
4
4
  HistoryClosedError,
5
5
  HistoryIdError,
6
+ HistoryRefusedError,
6
7
  MAX_WINDOW_IDLE_MS,
7
8
  START,
8
9
  UNDO_MODES,
@@ -21,7 +22,7 @@ import {
21
22
  recordRenderSuccess,
22
23
  registerRenderOutput,
23
24
  retainSnapshotOutput
24
- } from "../chunk-B4NDV4NM.js";
25
+ } from "../chunk-4HP26XV4.js";
25
26
  import "../chunk-QF7MFJJU.js";
26
27
  import "../chunk-SVQFE44C.js";
27
28
  import "../chunk-L3HJVTQE.js";
@@ -31,6 +32,7 @@ export {
31
32
  HistoryBusyError,
32
33
  HistoryClosedError,
33
34
  HistoryIdError,
35
+ HistoryRefusedError,
34
36
  MAX_WINDOW_IDLE_MS,
35
37
  UNDO_MODES,
36
38
  artifactDirectory,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Hono } from 'hono';
2
- import { S as StudioApiAdapter, P as PreviewDocumentStore, M as MediaProcessingJobState } from './mediaProxyPreview-CAiGJucG.js';
3
- export { L as LintResult, a as PreviewApiAdapter, R as RenderJobState, b as ResolvedProject, c as StudioSelectionResponse, d as StudioSelectionSnapshot, e as StudioSelectionTextField } from './mediaProxyPreview-CAiGJucG.js';
4
- export { C as ClosedWindow, D as DEFAULT_HISTORY_ROOT, S as HISTORY_START, H as HistoryClosedError, a as HistoryEntry, b as HistoryEntrySide, c as HistoryFileChange, d as HistoryListItem, e as HistoryResult, f as HistoryTarget, g as HistoryWho, h as HistoryWindow, M as MAX_WINDOW_IDLE_MS, P as ProjectHistory, i as ProjectHistoryOptions, j as ProjectHistoryView, U as UNDO_MODES, k as UndoMode, o as openProjectHistory, r as readProjectHistory } from './projectHistory-ByC9sa3X.js';
2
+ import { S as StudioApiAdapter, P as PreviewDocumentStore, M as MediaProcessingJobState } from './mediaProxyPreview-C0DZX2Mo.js';
3
+ export { L as LintResult, a as PreviewApiAdapter, R as RenderJobState, b as ResolvedProject, c as StudioSelectionResponse, d as StudioSelectionSnapshot, e as StudioSelectionTextField } from './mediaProxyPreview-C0DZX2Mo.js';
4
+ export { C as ClosedWindow, D as DEFAULT_HISTORY_ROOT, S as HISTORY_START, H as HistoryClosedError, a as HistoryEntry, b as HistoryEntrySide, c as HistoryFileChange, d as HistoryListItem, e as HistoryRefusedError, f as HistoryResult, g as HistoryTarget, h as HistoryWho, i as HistoryWindow, M as MAX_WINDOW_IDLE_MS, P as ProjectHistory, j as ProjectHistoryOptions, k as ProjectHistoryView, U as UNDO_MODES, l as UndoMode, o as openProjectHistory, r as readProjectHistory } from './projectHistory-C18HDvac.js';
5
5
  export { ArtifactCapture, ArtifactInput, ArtifactRecord, HistoryBusyError, HistoryIdError, ProjectHistoryRecord, PrunedHistory, ReclaimedHistory, RenderRecord, VideoState, artifactDirectory, beginArtifactCapture, finishSnapshotRun, forgetRecordedArtifacts, historyCache, listProjectHistories, pruneGoneProjectHistories, reclaimUncopiedBlobs, recordArtifact, recordArtifacts, recordRenderSuccess, registerRenderOutput, retainSnapshotOutput } from './history/index.js';
6
6
  export { CACHE_DIR_NAME as PROXY_CACHE_DIR_NAME } from './helpers/proxyTranscoder.js';
7
7
  export { walkDir } from './helpers/safePath.js';
@@ -188,7 +188,7 @@ declare const consumeFileWriteReceipt: typeof identifyFileWrite;
188
188
  * by last-write-wins script execution order — this is intentional so the
189
189
  * composition can pin a specific version.
190
190
  */
191
- declare function buildSubCompositionHtml(projectDir: string, compPath: string, runtimeUrl: string, baseHref?: string, rawOverride?: string): string | null;
191
+ declare function buildSubCompositionHtml(projectDir: string, compPath: string, runtimeUrl: string, baseHref?: string, rawOverride?: string, gsapBaseUrl?: string): string | null;
192
192
 
193
193
  interface ThumbnailOutputDimensions {
194
194
  width: number;
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ import {
31
31
  HistoryBusyError,
32
32
  HistoryClosedError,
33
33
  HistoryIdError,
34
+ HistoryRefusedError,
34
35
  MAX_WINDOW_IDLE_MS,
35
36
  START,
36
37
  STUDIO_SIGNATURE_MANIFEST_PATHS,
@@ -61,7 +62,7 @@ import {
61
62
  resolveProjectSignature,
62
63
  retainSnapshotOutput,
63
64
  settledFileTag
64
- } from "./chunk-B4NDV4NM.js";
65
+ } from "./chunk-4HP26XV4.js";
65
66
  import {
66
67
  clearElementScreenshotIsolation,
67
68
  getElementScreenshotClip
@@ -366,7 +367,7 @@ function tagRootCompositionFile(bodyHtml, compPath) {
366
367
  function rootHeadContent(rootHtml) {
367
368
  return rootHtml.match(/<head[^>]*>([\s\S]*?)<\/head>/i)?.[1] ?? "";
368
369
  }
369
- function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, rawOverride) {
370
+ function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, rawOverride, gsapBaseUrl) {
370
371
  const compFile = join2(projectDir, compPath);
371
372
  if (!existsSync(compFile)) return null;
372
373
  const rawComp = rawOverride ?? readFileSync(compFile, "utf-8");
@@ -418,7 +419,7 @@ ${compHeadContent}`;
418
419
  <script ${RUNTIME_BOOTSTRAP_ATTR}="1" src="${runtimeUrl}"></script>`;
419
420
  if (!headContent.includes("gsap")) {
420
421
  headContent += `
421
- <script src="${gsapCdnDist()}gsap.min.js"></script>`;
422
+ <script src="${gsapCdnDist(void 0, gsapBaseUrl).replace(/&/g, "&amp;")}gsap.min.js"></script>`;
422
423
  }
423
424
  const htmlOpen = htmlAttrs ? `<html ${htmlAttrs}>` : "<html>";
424
425
  const bodyOpen = bodyAttrs ? `<body ${bodyAttrs}>` : "<body>";
@@ -3837,7 +3838,12 @@ import {
3837
3838
  STUDIO_PREVIEW_ERRORS
3838
3839
  } from "@hyperframes/core/studio-preview-mark";
3839
3840
  import { PREVIEW_PROXY_BOX_PARAM, parsePreviewProxyBox } from "@hyperframes/core";
3840
- import { gsapCdnDist as gsapCdnDist2, motionPathPluginUrl } from "@hyperframes/core/gsap-cdn";
3841
+ import {
3842
+ GSAP_BASE_URL_META,
3843
+ gsapCdnDist as gsapCdnDist2,
3844
+ motionPathPluginUrl,
3845
+ normalizeGsapBaseUrl
3846
+ } from "@hyperframes/core/gsap-cdn";
3841
3847
  import { findStartTags as findStartTags2, injectTagsAtHeadStart } from "@hyperframes/core/compiler/html-document";
3842
3848
  import { isWithinProjectRoot } from "@hyperframes/parsers/asset-resolution";
3843
3849
  import { ensureHfIds as ensureHfIds2 } from "@hyperframes/parsers/hf-ids";
@@ -4002,9 +4008,12 @@ function lazyPreviewImages(html) {
4002
4008
 
4003
4009
  // src/routes/preview.ts
4004
4010
  var PROJECT_SIGNATURE_META = "hyperframes-project-signature";
4005
- var GSAP_CDN_SCRIPT = `<script src="${gsapCdnDist2()}gsap.min.js"></script>`;
4006
- var GSAP_CUSTOM_EASE_CDN_SCRIPT = `<script src="${gsapCdnDist2()}CustomEase.min.js"></script>`;
4007
- var GSAP_MOTION_PATH_CDN_SCRIPT = `<script src="${motionPathPluginUrl()}"></script>`;
4011
+ function escapeGsapUrl(url) {
4012
+ return url.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
4013
+ }
4014
+ function gsapScriptTag(file, baseUrl) {
4015
+ return `<script src="${escapeGsapUrl(gsapCdnDist2(void 0, baseUrl) + file)}"></script>`;
4016
+ }
4008
4017
  function injectProjectSignature(html, signature) {
4009
4018
  const tag = `<meta name="${PROJECT_SIGNATURE_META}" content="${signature}">`;
4010
4019
  if (html.includes(`name="${PROJECT_SIGNATURE_META}"`)) {
@@ -4060,16 +4069,16 @@ function htmlUsesMotionPath(html) {
4060
4069
  function htmlHasMotionPathPlugin(html) {
4061
4070
  return /<script\b[^>]*src=["'][^"']*MotionPathPlugin/i.test(html) || /\bwindow\.MotionPathPlugin\b/.test(html) || /\bMotionPathPlugin\s*=\s*/.test(html);
4062
4071
  }
4063
- function injectMotionPathPluginIfNeeded(html) {
4072
+ function injectMotionPathPluginIfNeeded(html, baseUrl) {
4064
4073
  if (!htmlUsesMotionPath(html) || htmlHasMotionPathPlugin(html)) return html;
4065
4074
  const gsap = findGsapCoreScript(html);
4066
4075
  if (gsap) {
4067
4076
  const version = gsap.src.match(/gsap@([\d.]+)/)?.[1];
4068
4077
  const ordering = `${gsap.type ? ` type="${gsap.type}"` : ""}${gsap.defer ? " defer" : ""}`;
4069
- const pluginTag = `<script${ordering} src="${motionPathPluginUrl(version)}"></script>`;
4078
+ const pluginTag = `<script${ordering} src="${escapeGsapUrl(motionPathPluginUrl(version, baseUrl))}"></script>`;
4070
4079
  return html.slice(0, gsap.end) + "\n" + pluginTag + html.slice(gsap.end);
4071
4080
  }
4072
- return injectScriptTagIntoHead(html, GSAP_MOTION_PATH_CDN_SCRIPT);
4081
+ return injectScriptTagIntoHead(html, gsapScriptTag("MotionPathPlugin.min.js", baseUrl));
4073
4082
  }
4074
4083
  function isGsapCoreUrl(src) {
4075
4084
  const base = "http://localhost/";
@@ -4102,30 +4111,32 @@ function scriptCloseEnd(html, start) {
4102
4111
  const closeAt = close.exec(html)?.index;
4103
4112
  return closeAt === void 0 ? 0 : html.indexOf(">", closeAt) + 1;
4104
4113
  }
4105
- function injectStudioMotionDependencies(html, manifestContent) {
4114
+ function injectStudioMotionDependencies(html, manifestContent, baseUrl) {
4106
4115
  const manifest = parseStudioMotionManifestContent(manifestContent);
4107
4116
  if (!manifest.hasMotion) return html;
4108
4117
  let next = html;
4109
- if (!htmlHasGsap(next)) next = injectScriptTagIntoHead(next, GSAP_CDN_SCRIPT);
4118
+ if (!htmlHasGsap(next))
4119
+ next = injectScriptTagIntoHead(next, gsapScriptTag("gsap.min.js", baseUrl));
4110
4120
  if (manifest.hasCustomEase && !htmlHasCustomEase(next)) {
4111
- next = injectScriptTagIntoHead(next, GSAP_CUSTOM_EASE_CDN_SCRIPT);
4121
+ next = injectScriptTagIntoHead(next, gsapScriptTag("CustomEase.min.js", baseUrl));
4112
4122
  }
4113
4123
  return next;
4114
4124
  }
4115
- function injectStudioMotionScript(html, projectDir, activeCompositionPath) {
4125
+ function injectStudioMotionScript(html, projectDir, activeCompositionPath, baseUrl) {
4116
4126
  const manifestContent = readStudioMotionManifestContent(projectDir);
4117
4127
  const script = createStudioMotionRenderBodyScript(manifestContent, {
4118
4128
  activeCompositionPath
4119
4129
  });
4120
4130
  if (!script) return html;
4121
- const withDependencies = injectStudioMotionDependencies(html, manifestContent);
4131
+ const withDependencies = injectStudioMotionDependencies(html, manifestContent, baseUrl);
4122
4132
  const tag = `<script type="${AFTER_FONTS_SCRIPT_TYPE}">${script.replace(/<\/script/gi, "<\\/script")}</script>`;
4123
4133
  return insertBeforeCloseTag2(withDependencies, "body", tag) ?? `${withDependencies}${tag}`;
4124
4134
  }
4125
4135
  var GSAP_FALLBACK_ATTR = "data-hf-gsap-fallback";
4126
- var GSAP_CDN_FALLBACK_SCRIPT = `<script ${GSAP_FALLBACK_ATTR}>
4136
+ function gsapCdnFallbackScript(baseUrl) {
4137
+ return `<script ${GSAP_FALLBACK_ATTR}>
4127
4138
  (function(){
4128
- var cdnBase="${gsapCdnDist2()}";
4139
+ var cdnBase=${JSON.stringify(gsapCdnDist2(void 0, baseUrl)).replace(/</g, "\\u003c")};
4129
4140
  var loaded={};
4130
4141
  function loadFallback(file){
4131
4142
  if(loaded[file])return loaded[file];
@@ -4145,10 +4156,12 @@ var GSAP_CDN_FALLBACK_SCRIPT = `<script ${GSAP_FALLBACK_ATTR}>
4145
4156
  },true);
4146
4157
  })();
4147
4158
  </script>`;
4148
- function injectGsapCdnFallback(html) {
4159
+ }
4160
+ function injectGsapCdnFallback(html, baseUrl) {
4149
4161
  if (html.includes(GSAP_FALLBACK_ATTR)) return html;
4150
- if (html.includes("<head>")) return html.replace("<head>", "<head>" + GSAP_CDN_FALLBACK_SCRIPT);
4151
- return GSAP_CDN_FALLBACK_SCRIPT + html;
4162
+ if (html.includes("<head>"))
4163
+ return html.replace("<head>", "<head>" + gsapCdnFallbackScript(baseUrl));
4164
+ return gsapCdnFallbackScript(baseUrl) + html;
4152
4165
  }
4153
4166
  function parsePreviewVariablesParam(raw) {
4154
4167
  if (raw === void 0 || raw === "") return { ok: true, values: null };
@@ -4175,8 +4188,8 @@ function previewVariablesFromRequest(rawVariables) {
4175
4188
  var PREVIEW_CAPTURE_PARAM = "hf-capture";
4176
4189
  var EARLY_ERRORS_SCRIPT = `<script>(function(){var seen=window.${STUDIO_PREVIEW_ERRORS}=[];
4177
4190
  addEventListener("error",function(e){seen.push(e.message||String(e))});})();</script>`;
4178
- function injectStudioPreviewAugmentations(html, adapter, projectDir, activeCompositionPath, capture) {
4179
- const marked = capture ? injectTagsAtHeadStart(
4191
+ function injectStudioPreviewAugmentations(html, adapter, projectDir, activeCompositionPath, options) {
4192
+ const marked = options.capture ? injectTagsAtHeadStart(
4180
4193
  html,
4181
4194
  `<meta name="${STUDIO_PREVIEW_MARK_META}" content="${STUDIO_PREVIEW_CAPTURE_MODE}">`
4182
4195
  ) : injectTagsAtHeadStart(
@@ -4186,11 +4199,20 @@ function injectStudioPreviewAugmentations(html, adapter, projectDir, activeCompo
4186
4199
  return injectStudioMotionScript(
4187
4200
  injectMotionPathPluginIfNeeded(
4188
4201
  injectGsapCdnFallback(
4189
- injectProjectSignature(marked, resolveProjectSignature(adapter, projectDir))
4190
- )
4202
+ injectProjectSignature(
4203
+ injectTagsAtHeadStart(
4204
+ marked,
4205
+ `<meta name="${GSAP_BASE_URL_META}" content="${escapeGsapUrl(options.gsapBaseUrl)}">`
4206
+ ),
4207
+ resolveProjectSignature(adapter, projectDir)
4208
+ ),
4209
+ options.gsapBaseUrl
4210
+ ),
4211
+ options.gsapBaseUrl
4191
4212
  ),
4192
4213
  projectDir,
4193
- activeCompositionPath
4214
+ activeCompositionPath,
4215
+ options.gsapBaseUrl
4194
4216
  );
4195
4217
  }
4196
4218
  async function transformPreviewHtml(html, adapter, project, activeCompositionPath) {
@@ -4234,6 +4256,16 @@ var PREVIEW_BUNDLE_OPTIONS = {
4234
4256
  var previewBaseHref = (projectId) => `/api/projects/${encodeURIComponent(projectId)}/preview/`;
4235
4257
  var withPreviewBase = (html, projectId) => hasBaseElement(html) ? html : injectTagsAtHeadStart(html, `<base href="${previewBaseHref(projectId)}">`);
4236
4258
  function registerPreviewRoutes(api, adapter) {
4259
+ let gsapBaseUrl;
4260
+ try {
4261
+ gsapBaseUrl = normalizeGsapBaseUrl(process.env.HYPERFRAMES_GSAP_BASE_URL);
4262
+ } catch (cause) {
4263
+ throw new Error(
4264
+ "Invalid HYPERFRAMES_GSAP_BASE_URL: use an absolute HTTP(S) npm mirror root without credentials, query, or fragment",
4265
+ { cause }
4266
+ );
4267
+ }
4268
+ const gsapEtagSalt = `:gsap:${createHash2("sha1").update(gsapBaseUrl).digest("hex").slice(0, 12)}`;
4237
4269
  const previewCacheHeaders = (etag) => ({
4238
4270
  "Cache-Control": "private, no-cache",
4239
4271
  ETag: etag
@@ -4268,7 +4300,7 @@ function registerPreviewRoutes(api, adapter) {
4268
4300
  adapter,
4269
4301
  project.dir,
4270
4302
  mainCompositionPath,
4271
- capture
4303
+ { capture, gsapBaseUrl }
4272
4304
  );
4273
4305
  if (previewVariables) bundled = injectPreviewVariables(bundled, previewVariables);
4274
4306
  bundled = await injectMediaCodecMap(
@@ -4296,7 +4328,7 @@ function registerPreviewRoutes(api, adapter) {
4296
4328
  adapter,
4297
4329
  project.dir,
4298
4330
  fallback.compositionPath,
4299
- capture
4331
+ { capture, gsapBaseUrl }
4300
4332
  );
4301
4333
  if (previewVariables) {
4302
4334
  fallbackAugmented = injectPreviewVariables(fallbackAugmented, previewVariables);
@@ -4321,7 +4353,7 @@ function registerPreviewRoutes(api, adapter) {
4321
4353
  if (vars.error !== void 0) return c.json({ error: vars.error }, 400);
4322
4354
  const previewVariables = vars.values;
4323
4355
  const capture = c.req.query(PREVIEW_CAPTURE_PARAM) !== void 0;
4324
- const etag = `"preview:${signature}${variablesEtagSalt(vars.raw)}${capture ? ":capture" : ""}"`;
4356
+ const etag = `"preview:${signature}${gsapEtagSalt}${variablesEtagSalt(vars.raw)}${capture ? ":capture" : ""}"`;
4325
4357
  const ifNoneMatch = c.req.header("If-None-Match");
4326
4358
  if (ifNoneMatch === etag) {
4327
4359
  return new Response(null, {
@@ -4371,7 +4403,7 @@ ${etag}`;
4371
4403
  }
4372
4404
  const compPathHash = createHash2("sha1").update(compPath).digest("hex");
4373
4405
  const capture = c.req.query(PREVIEW_CAPTURE_PARAM) !== void 0;
4374
- const etag = `"comp:v2:${compPathHash}:${signature}${variablesEtagSalt(vars.raw)}${capture ? ":capture" : ""}"`;
4406
+ const etag = `"comp:v2:${compPathHash}:${signature}${gsapEtagSalt}${variablesEtagSalt(vars.raw)}${capture ? ":capture" : ""}"`;
4375
4407
  const ifNoneMatch = c.req.header("If-None-Match");
4376
4408
  if (ifNoneMatch === etag) {
4377
4409
  return new Response(null, {
@@ -4387,12 +4419,16 @@ ${etag}`;
4387
4419
  compPath,
4388
4420
  adapter.runtimeUrl,
4389
4421
  baseHref,
4390
- stamped
4422
+ stamped,
4423
+ gsapBaseUrl
4391
4424
  );
4392
4425
  if (!html) return c.text("not found", 404);
4393
4426
  recordPreviewReferences(project.dir, html);
4394
4427
  html = ensureHfIds2(await transformPreviewHtml(html, adapter, project, compPath));
4395
- html = injectStudioPreviewAugmentations(html, adapter, project.dir, compPath, capture);
4428
+ html = injectStudioPreviewAugmentations(html, adapter, project.dir, compPath, {
4429
+ capture,
4430
+ gsapBaseUrl
4431
+ });
4396
4432
  if (previewVariables) html = injectPreviewVariables(html, previewVariables);
4397
4433
  html = await injectMediaCodecMap(html, adapter, project.dir, compPath, mediaCodecProbeCache);
4398
4434
  return c.html(html, 200, previewCacheHeaders(etag));
@@ -6764,6 +6800,7 @@ export {
6764
6800
  HistoryBusyError,
6765
6801
  HistoryClosedError,
6766
6802
  HistoryIdError,
6803
+ HistoryRefusedError,
6767
6804
  MAX_UPLOAD_BYTES,
6768
6805
  MAX_WINDOW_IDLE_MS,
6769
6806
  MIME_TYPES,
@@ -3,7 +3,7 @@ import { RegistryItem, PreviewProxyBox } from '@hyperframes/core';
3
3
  import * as sharp from 'sharp';
4
4
  import { CanvasResolution } from '@hyperframes/parsers';
5
5
  import { BundleOptions } from '@hyperframes/core/compiler';
6
- import { P as ProjectHistory } from './projectHistory-ByC9sa3X.js';
6
+ import { P as ProjectHistory } from './projectHistory-C18HDvac.js';
7
7
  import { MediaCodecProbeCache, HtmlSourceLike } from './helpers/mediaCodecMap.js';
8
8
 
9
9
  /** Resolved info about a single project. */
@@ -146,6 +146,9 @@ interface ProjectHistory {
146
146
  declare class HistoryClosedError extends Error {
147
147
  constructor(message: string);
148
148
  }
149
+ declare class HistoryRefusedError extends Error {
150
+ constructor(message: string);
151
+ }
149
152
  /** Opens a project's history: every write to its files becomes an entry that can be undone or restored. */
150
153
  declare function openProjectHistory(options: ProjectHistoryOptions): Promise<ProjectHistory>;
151
154
  /** A project's history as its log stands now, read without the owner lock: it reads while another process owns it. */
@@ -157,4 +160,4 @@ interface ProjectHistoryView {
157
160
  /** Reads only: takes no lock and files, prunes or writes nothing; an owner's half-appended last line is skipped. */
158
161
  declare function readProjectHistory(options: Pick<ProjectHistoryOptions, "projectDir" | "historyRoot">): ProjectHistoryView;
159
162
 
160
- export { type ClosedWindow as C, DEFAULT_HISTORY_ROOT as D, HistoryClosedError as H, MAX_WINDOW_IDLE_MS as M, type ProjectHistory as P, START as S, UNDO_MODES as U, type HistoryEntry as a, type HistoryEntrySide as b, type HistoryFileChange as c, type HistoryListItem as d, type HistoryResult as e, type HistoryTarget as f, type HistoryWho as g, type HistoryWindow as h, type ProjectHistoryOptions as i, type ProjectHistoryView as j, type UndoMode as k, openProjectHistory as o, readProjectHistory as r };
163
+ export { type ClosedWindow as C, DEFAULT_HISTORY_ROOT as D, HistoryClosedError as H, MAX_WINDOW_IDLE_MS as M, type ProjectHistory as P, START as S, UNDO_MODES as U, type HistoryEntry as a, type HistoryEntrySide as b, type HistoryFileChange as c, type HistoryListItem as d, HistoryRefusedError as e, type HistoryResult as f, type HistoryTarget as g, type HistoryWho as h, type HistoryWindow as i, type ProjectHistoryOptions as j, type ProjectHistoryView as k, type UndoMode as l, openProjectHistory as o, readProjectHistory as r };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio-server",
3
- "version": "0.8.192",
3
+ "version": "0.8.194",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/heygen-com/hyperframes",
@@ -84,8 +84,8 @@
84
84
  "linkedom": "^0.18.12",
85
85
  "postcss": "^8.5.8",
86
86
  "postcss-selector-parser": "^7.1.2",
87
- "@hyperframes/core": "0.8.192",
88
- "@hyperframes/parsers": "0.8.192"
87
+ "@hyperframes/core": "0.8.194",
88
+ "@hyperframes/parsers": "0.8.194"
89
89
  },
90
90
  "devDependencies": {
91
91
  "@types/node": "^25.0.10",