@hyperframes/studio-server 0.8.193 → 0.8.195
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/{chunk-JPKAFJIJ.js → chunk-4HP26XV4.js} +74 -21
- package/dist/helpers/mediaProxyPreview.d.ts +1 -1
- package/dist/history/index.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +142 -57
- package/dist/{mediaProxyPreview-C0DZX2Mo.d.ts → mediaProxyPreview-CdpmyC5i.d.ts} +1 -0
- package/package.json +3 -3
|
@@ -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
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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(
|
|
563
|
+
await mkdir(dirname(blobPath(dir, hash)), { recursive: true });
|
|
537
564
|
const size = (await stat(temp)).size;
|
|
538
|
-
await rename(temp,
|
|
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:
|
|
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
|
|
560
|
-
|
|
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 });
|
|
@@ -1,5 +1,5 @@
|
|
|
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-
|
|
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-CdpmyC5i.js';
|
|
3
3
|
import './mediaCodecMap.js';
|
|
4
4
|
import 'sharp';
|
|
5
5
|
import '@hyperframes/parsers';
|
package/dist/history/index.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
import { S as StudioApiAdapter, P as PreviewDocumentStore, M as MediaProcessingJobState } from './mediaProxyPreview-
|
|
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-
|
|
2
|
+
import { S as StudioApiAdapter, P as PreviewDocumentStore, M as MediaProcessingJobState } from './mediaProxyPreview-CdpmyC5i.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-CdpmyC5i.js';
|
|
4
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';
|
|
@@ -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
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
resolveProjectSignature,
|
|
63
63
|
retainSnapshotOutput,
|
|
64
64
|
settledFileTag
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-4HP26XV4.js";
|
|
66
66
|
import {
|
|
67
67
|
clearElementScreenshotIsolation,
|
|
68
68
|
getElementScreenshotClip
|
|
@@ -267,6 +267,53 @@ import {
|
|
|
267
267
|
} from "@hyperframes/core/compiler";
|
|
268
268
|
import { isFullHtmlDocument } from "@hyperframes/core/compiler/html-document";
|
|
269
269
|
import { gsapCdnDist } from "@hyperframes/core/gsap-cdn";
|
|
270
|
+
|
|
271
|
+
// src/helpers/gsapCoreScript.ts
|
|
272
|
+
import { findStartTags, insertBeforeCloseTag } from "@hyperframes/core/compiler/html-document";
|
|
273
|
+
var URL_BASE = "http://localhost/";
|
|
274
|
+
function urlPath(src) {
|
|
275
|
+
return URL.canParse(src, URL_BASE) ? new URL(src, URL_BASE).pathname : null;
|
|
276
|
+
}
|
|
277
|
+
function isGsapCoreUrl(src) {
|
|
278
|
+
return /\/gsap(-\d+(\.\d+)*)?(\.min)?\.js$/i.test(urlPath(src) ?? "");
|
|
279
|
+
}
|
|
280
|
+
var GSAP_PLUGIN_FILE = /\/(\w+Plugin|CustomBounce|CustomEase|CustomWiggle|Draggable|EasePack|Flip|GSDevTools|MotionPathHelper|Observer|ScrollSmoother|ScrollTrigger|SplitText)(\.min)?\.js$/i;
|
|
281
|
+
function isGsapPluginUrl(src) {
|
|
282
|
+
const path = urlPath(src);
|
|
283
|
+
if (!path || isGsapCoreUrl(src)) return false;
|
|
284
|
+
return GSAP_PLUGIN_FILE.test(path) || /\/gsap(@[^/]*)?\/(.+\/)?[^/]+\.js$/i.test(path);
|
|
285
|
+
}
|
|
286
|
+
var TEMPLATE_ELEMENT = /<template\b[^>]*>[\s\S]*?<\/template>/gi;
|
|
287
|
+
var SCRIPT_SRC = /<script\b[^>]*?\ssrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
|
|
288
|
+
var INLINED_MARKER = /\/\*\s*inlined:\s*(\S+)\s*\*\//gi;
|
|
289
|
+
var CORE_LICENSE_BANNER = /\/\*!\s*\*\s*GSAP\s+\d/;
|
|
290
|
+
var PLUGIN_LICENSE_BANNER = /\/\*!\s*\*\s*(?!GSAP\s)\w+\s+\d[^*]*\*\s*https:\/\/gsap\.com/;
|
|
291
|
+
var CORE_BODY = /Missing plugin\? gsap\.registerPlugin\(\)/;
|
|
292
|
+
function scriptUrls(html) {
|
|
293
|
+
const srcs = [...html.matchAll(SCRIPT_SRC)].map(
|
|
294
|
+
(m) => (m[1] ?? m[2] ?? m[3] ?? "").replace(/&/gi, "&")
|
|
295
|
+
);
|
|
296
|
+
return [...srcs, ...[...html.matchAll(INLINED_MARKER)].map((m) => m[1] ?? "")];
|
|
297
|
+
}
|
|
298
|
+
function htmlHasGsapCore(html) {
|
|
299
|
+
const live = html.replace(TEMPLATE_ELEMENT, "");
|
|
300
|
+
return scriptUrls(live).some(isGsapCoreUrl) || CORE_LICENSE_BANNER.test(live) || CORE_BODY.test(live);
|
|
301
|
+
}
|
|
302
|
+
function isGsapPluginScript(script) {
|
|
303
|
+
return scriptUrls(script).some(isGsapPluginUrl) || PLUGIN_LICENSE_BANNER.test(script);
|
|
304
|
+
}
|
|
305
|
+
function insertGsapCoreBeforeHeadPlugin(html, coreTag) {
|
|
306
|
+
const lowered = html.toLowerCase();
|
|
307
|
+
const at = findStartTags(html, "script").find((start) => {
|
|
308
|
+
const close = lowered.indexOf("</script", start);
|
|
309
|
+
return isGsapPluginScript(html.slice(start, close === -1 ? void 0 : close));
|
|
310
|
+
});
|
|
311
|
+
if (at === void 0 || insertBeforeCloseTag(html.slice(0, at), "head", "") !== null) return null;
|
|
312
|
+
return `${html.slice(0, at)}${coreTag}
|
|
313
|
+
${html.slice(at)}`;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/helpers/subComposition.ts
|
|
270
317
|
function rewriteRelativePaths(root, compPath, projectDir) {
|
|
271
318
|
const assetExists = (path) => existsSync(join2(projectDir, path));
|
|
272
319
|
rewriteAssetPaths(
|
|
@@ -367,7 +414,7 @@ function tagRootCompositionFile(bodyHtml, compPath) {
|
|
|
367
414
|
function rootHeadContent(rootHtml) {
|
|
368
415
|
return rootHtml.match(/<head[^>]*>([\s\S]*?)<\/head>/i)?.[1] ?? "";
|
|
369
416
|
}
|
|
370
|
-
function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, rawOverride) {
|
|
417
|
+
function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, rawOverride, gsapBaseUrl) {
|
|
371
418
|
const compFile = join2(projectDir, compPath);
|
|
372
419
|
if (!existsSync(compFile)) return null;
|
|
373
420
|
const rawComp = rawOverride ?? readFileSync(compFile, "utf-8");
|
|
@@ -417,9 +464,10 @@ ${compHeadContent}`;
|
|
|
417
464
|
headContent = stripEmbeddedRuntimeScripts(headContent);
|
|
418
465
|
headContent += `
|
|
419
466
|
<script ${RUNTIME_BOOTSTRAP_ATTR}="1" src="${runtimeUrl}"></script>`;
|
|
420
|
-
if (!headContent
|
|
421
|
-
|
|
422
|
-
|
|
467
|
+
if (!htmlHasGsapCore(headContent)) {
|
|
468
|
+
const core = `<script src="${gsapCdnDist(void 0, gsapBaseUrl).replace(/&/g, "&")}gsap.min.js"></script>`;
|
|
469
|
+
headContent = insertGsapCoreBeforeHeadPlugin(headContent, core) ?? `${headContent}
|
|
470
|
+
${core}`;
|
|
423
471
|
}
|
|
424
472
|
const htmlOpen = htmlAttrs ? `<html ${htmlAttrs}>` : "<html>";
|
|
425
473
|
const bodyOpen = bodyAttrs ? `<body ${bodyAttrs}>` : "<body>";
|
|
@@ -1225,7 +1273,7 @@ function resolveGsapWriter(env) {
|
|
|
1225
1273
|
// src/routes/files.ts
|
|
1226
1274
|
import {
|
|
1227
1275
|
hasCompositionOutsideTemplates,
|
|
1228
|
-
insertBeforeCloseTag
|
|
1276
|
+
insertBeforeCloseTag as insertBeforeCloseTag2
|
|
1229
1277
|
} from "@hyperframes/core/compiler/html-document";
|
|
1230
1278
|
async function loadGsapParser() {
|
|
1231
1279
|
return import("@hyperframes/parsers/gsap-parser-recast");
|
|
@@ -2097,8 +2145,8 @@ async function prepareGsapMutationScript(c, res, firstMutation) {
|
|
|
2097
2145
|
"</script>"
|
|
2098
2146
|
].join("\n");
|
|
2099
2147
|
const [first, second] = hasCompositionOutsideTemplates(html) ? ["body", "template"] : ["template", "body"];
|
|
2100
|
-
html =
|
|
2101
|
-
`) ??
|
|
2148
|
+
html = insertBeforeCloseTag2(html, first, `${bootstrap}
|
|
2149
|
+
`) ?? insertBeforeCloseTag2(html, second, `${bootstrap}
|
|
2102
2150
|
`) ?? `${html}
|
|
2103
2151
|
${bootstrap}`;
|
|
2104
2152
|
block = extractGsapScriptBlock(html);
|
|
@@ -3828,7 +3876,7 @@ import {
|
|
|
3828
3876
|
AFTER_FONTS_SCRIPT_TYPE,
|
|
3829
3877
|
addScenePartsManifest,
|
|
3830
3878
|
RUNTIME_BOOTSTRAP_ATTR as RUNTIME_BOOTSTRAP_ATTR2,
|
|
3831
|
-
insertBeforeCloseTag as
|
|
3879
|
+
insertBeforeCloseTag as insertBeforeCloseTag3,
|
|
3832
3880
|
insertRuntimeTag,
|
|
3833
3881
|
stripEmbeddedRuntimeScripts as stripEmbeddedRuntimeScripts2
|
|
3834
3882
|
} from "@hyperframes/core/compiler";
|
|
@@ -3838,8 +3886,14 @@ import {
|
|
|
3838
3886
|
STUDIO_PREVIEW_ERRORS
|
|
3839
3887
|
} from "@hyperframes/core/studio-preview-mark";
|
|
3840
3888
|
import { PREVIEW_PROXY_BOX_PARAM, parsePreviewProxyBox } from "@hyperframes/core";
|
|
3841
|
-
import {
|
|
3842
|
-
|
|
3889
|
+
import {
|
|
3890
|
+
GSAP_BASE_URL_META,
|
|
3891
|
+
gsapCdnDist as gsapCdnDist2,
|
|
3892
|
+
motionPathPluginUrl,
|
|
3893
|
+
normalizeGsapBaseUrl,
|
|
3894
|
+
normalizeGsapLocalRoot
|
|
3895
|
+
} from "@hyperframes/core/gsap-cdn";
|
|
3896
|
+
import { findStartTags as findStartTags3, injectTagsAtHeadStart } from "@hyperframes/core/compiler/html-document";
|
|
3843
3897
|
import { isWithinProjectRoot } from "@hyperframes/parsers/asset-resolution";
|
|
3844
3898
|
import { ensureHfIds as ensureHfIds2 } from "@hyperframes/parsers/hf-ids";
|
|
3845
3899
|
|
|
@@ -3974,7 +4028,7 @@ function injectPreviewVariables(html, values) {
|
|
|
3974
4028
|
}
|
|
3975
4029
|
|
|
3976
4030
|
// src/helpers/lazyPreviewImages.ts
|
|
3977
|
-
import { findStartTags } from "@hyperframes/core/compiler/html-document";
|
|
4031
|
+
import { findStartTags as findStartTags2 } from "@hyperframes/core/compiler/html-document";
|
|
3978
4032
|
import { parseHTML as parseHTML6 } from "linkedom";
|
|
3979
4033
|
import { STUDIO_PREVIEW_LAZY_ATTR } from "@hyperframes/core/studio-preview-mark";
|
|
3980
4034
|
function startsAfterZero(el) {
|
|
@@ -3987,7 +4041,7 @@ var hasLoading = (img) => Array.from(img.attributes).some((attr) => attr.name.to
|
|
|
3987
4041
|
function lazyPreviewImages(html) {
|
|
3988
4042
|
if (!/<!doctype|<html[\s>]/i.test(html)) return html;
|
|
3989
4043
|
const images = [...parseHTML6(html).document.querySelectorAll("img")];
|
|
3990
|
-
const tags =
|
|
4044
|
+
const tags = findStartTags2(html, "img");
|
|
3991
4045
|
if (tags.length !== images.length) return html;
|
|
3992
4046
|
const parts = [];
|
|
3993
4047
|
let from = 0;
|
|
@@ -4003,9 +4057,12 @@ function lazyPreviewImages(html) {
|
|
|
4003
4057
|
|
|
4004
4058
|
// src/routes/preview.ts
|
|
4005
4059
|
var PROJECT_SIGNATURE_META = "hyperframes-project-signature";
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4060
|
+
function escapeGsapUrl(url) {
|
|
4061
|
+
return url.replace(/&/g, "&").replace(/"/g, """);
|
|
4062
|
+
}
|
|
4063
|
+
function gsapScriptTag(file, baseUrl) {
|
|
4064
|
+
return `<script src="${escapeGsapUrl(gsapCdnDist2(void 0, baseUrl) + file)}"></script>`;
|
|
4065
|
+
}
|
|
4009
4066
|
function injectProjectSignature(html, signature) {
|
|
4010
4067
|
const tag = `<meta name="${PROJECT_SIGNATURE_META}" content="${signature}">`;
|
|
4011
4068
|
if (html.includes(`name="${PROJECT_SIGNATURE_META}"`)) {
|
|
@@ -4014,7 +4071,7 @@ function injectProjectSignature(html, signature) {
|
|
|
4014
4071
|
tag
|
|
4015
4072
|
);
|
|
4016
4073
|
}
|
|
4017
|
-
return
|
|
4074
|
+
return insertBeforeCloseTag3(html, "head", `${tag}
|
|
4018
4075
|
`) ?? `${tag}
|
|
4019
4076
|
${html}`;
|
|
4020
4077
|
}
|
|
@@ -4044,14 +4101,10 @@ function withPreviewRuntime(html, runtimeUrl) {
|
|
|
4044
4101
|
return insertRuntimeTag(stripEmbeddedRuntimeScripts2(html), tag);
|
|
4045
4102
|
}
|
|
4046
4103
|
function injectScriptTagIntoHead(html, scriptTag) {
|
|
4047
|
-
return
|
|
4104
|
+
return insertBeforeCloseTag3(html, "head", `${scriptTag}
|
|
4048
4105
|
`) ?? `${scriptTag}
|
|
4049
4106
|
${html}`;
|
|
4050
4107
|
}
|
|
4051
|
-
function htmlHasGsap(html) {
|
|
4052
|
-
const outsideTemplates = html.replace(/<template\b[^>]*>[\s\S]*?<\/template>/gi, "");
|
|
4053
|
-
return /<script\b[^>]*src=["'][^"']*gsap/i.test(outsideTemplates) || /\/\*\s*inlined:.*gsap/i.test(outsideTemplates) || /\b(GreenSock|_gsScope)\b/.test(outsideTemplates) || /\bgsap\.(config|defaults|registerPlugin|version)\b/.test(outsideTemplates);
|
|
4054
|
-
}
|
|
4055
4108
|
function htmlHasCustomEase(html) {
|
|
4056
4109
|
return /<script\b[^>]*src=["'][^"']*CustomEase/i.test(html) || /\bwindow\.CustomEase\b/.test(html) || /\bCustomEase\s*=\s*/.test(html);
|
|
4057
4110
|
}
|
|
@@ -4061,24 +4114,20 @@ function htmlUsesMotionPath(html) {
|
|
|
4061
4114
|
function htmlHasMotionPathPlugin(html) {
|
|
4062
4115
|
return /<script\b[^>]*src=["'][^"']*MotionPathPlugin/i.test(html) || /\bwindow\.MotionPathPlugin\b/.test(html) || /\bMotionPathPlugin\s*=\s*/.test(html);
|
|
4063
4116
|
}
|
|
4064
|
-
function injectMotionPathPluginIfNeeded(html) {
|
|
4117
|
+
function injectMotionPathPluginIfNeeded(html, baseUrl) {
|
|
4065
4118
|
if (!htmlUsesMotionPath(html) || htmlHasMotionPathPlugin(html)) return html;
|
|
4066
4119
|
const gsap = findGsapCoreScript(html);
|
|
4067
4120
|
if (gsap) {
|
|
4068
4121
|
const version = gsap.src.match(/gsap@([\d.]+)/)?.[1];
|
|
4069
4122
|
const ordering = `${gsap.type ? ` type="${gsap.type}"` : ""}${gsap.defer ? " defer" : ""}`;
|
|
4070
|
-
const pluginTag = `<script${ordering} src="${motionPathPluginUrl(version)}"></script>`;
|
|
4123
|
+
const pluginTag = `<script${ordering} src="${escapeGsapUrl(motionPathPluginUrl(version, baseUrl))}"></script>`;
|
|
4071
4124
|
return html.slice(0, gsap.end) + "\n" + pluginTag + html.slice(gsap.end);
|
|
4072
4125
|
}
|
|
4073
|
-
return injectScriptTagIntoHead(html,
|
|
4074
|
-
}
|
|
4075
|
-
function isGsapCoreUrl(src) {
|
|
4076
|
-
const base = "http://localhost/";
|
|
4077
|
-
return URL.canParse(src, base) && /\/gsap(\.min)?\.js$/i.test(new URL(src, base).pathname);
|
|
4126
|
+
return injectScriptTagIntoHead(html, gsapScriptTag("MotionPathPlugin.min.js", baseUrl));
|
|
4078
4127
|
}
|
|
4079
4128
|
function findGsapCoreScript(html) {
|
|
4080
4129
|
const scripts = [...parseHTML7(html).document.querySelectorAll("script")];
|
|
4081
|
-
const starts =
|
|
4130
|
+
const starts = findStartTags3(html, "script");
|
|
4082
4131
|
if (starts.length !== scripts.length) return null;
|
|
4083
4132
|
const at = scripts.findIndex(
|
|
4084
4133
|
(script) => isGsapCoreUrl(lowerCaseAttributes(script).get("src") ?? "")
|
|
@@ -4103,30 +4152,35 @@ function scriptCloseEnd(html, start) {
|
|
|
4103
4152
|
const closeAt = close.exec(html)?.index;
|
|
4104
4153
|
return closeAt === void 0 ? 0 : html.indexOf(">", closeAt) + 1;
|
|
4105
4154
|
}
|
|
4106
|
-
function injectStudioMotionDependencies(html, manifestContent) {
|
|
4155
|
+
function injectStudioMotionDependencies(html, manifestContent, baseUrl) {
|
|
4107
4156
|
const manifest = parseStudioMotionManifestContent(manifestContent);
|
|
4108
4157
|
if (!manifest.hasMotion) return html;
|
|
4109
4158
|
let next = html;
|
|
4110
|
-
if (!
|
|
4159
|
+
if (!htmlHasGsapCore(next)) {
|
|
4160
|
+
const core = gsapScriptTag("gsap.min.js", baseUrl);
|
|
4161
|
+
next = insertGsapCoreBeforeHeadPlugin(next, core) ?? injectScriptTagIntoHead(next, core);
|
|
4162
|
+
}
|
|
4111
4163
|
if (manifest.hasCustomEase && !htmlHasCustomEase(next)) {
|
|
4112
|
-
next = injectScriptTagIntoHead(next,
|
|
4164
|
+
next = injectScriptTagIntoHead(next, gsapScriptTag("CustomEase.min.js", baseUrl));
|
|
4113
4165
|
}
|
|
4114
4166
|
return next;
|
|
4115
4167
|
}
|
|
4116
|
-
function
|
|
4168
|
+
function readStudioMotion(projectDir, activeCompositionPath) {
|
|
4117
4169
|
const manifestContent = readStudioMotionManifestContent(projectDir);
|
|
4118
4170
|
const script = createStudioMotionRenderBodyScript(manifestContent, {
|
|
4119
4171
|
activeCompositionPath
|
|
4120
4172
|
});
|
|
4121
|
-
|
|
4122
|
-
|
|
4173
|
+
return script ? { manifestContent, script } : null;
|
|
4174
|
+
}
|
|
4175
|
+
function injectStudioMotionScript(html, script) {
|
|
4123
4176
|
const tag = `<script type="${AFTER_FONTS_SCRIPT_TYPE}">${script.replace(/<\/script/gi, "<\\/script")}</script>`;
|
|
4124
|
-
return
|
|
4177
|
+
return insertBeforeCloseTag3(html, "body", tag) ?? `${html}${tag}`;
|
|
4125
4178
|
}
|
|
4126
4179
|
var GSAP_FALLBACK_ATTR = "data-hf-gsap-fallback";
|
|
4127
|
-
|
|
4180
|
+
function gsapCdnFallbackScript(baseUrl) {
|
|
4181
|
+
return `<script ${GSAP_FALLBACK_ATTR}>
|
|
4128
4182
|
(function(){
|
|
4129
|
-
var cdnBase
|
|
4183
|
+
var cdnBase=${JSON.stringify(gsapCdnDist2(void 0, baseUrl)).replace(/</g, "\\u003c")};
|
|
4130
4184
|
var loaded={};
|
|
4131
4185
|
function loadFallback(file){
|
|
4132
4186
|
if(loaded[file])return loaded[file];
|
|
@@ -4146,10 +4200,12 @@ var GSAP_CDN_FALLBACK_SCRIPT = `<script ${GSAP_FALLBACK_ATTR}>
|
|
|
4146
4200
|
},true);
|
|
4147
4201
|
})();
|
|
4148
4202
|
</script>`;
|
|
4149
|
-
|
|
4203
|
+
}
|
|
4204
|
+
function injectGsapCdnFallback(html, baseUrl) {
|
|
4150
4205
|
if (html.includes(GSAP_FALLBACK_ATTR)) return html;
|
|
4151
|
-
if (html.includes("<head>"))
|
|
4152
|
-
|
|
4206
|
+
if (html.includes("<head>"))
|
|
4207
|
+
return html.replace("<head>", "<head>" + gsapCdnFallbackScript(baseUrl));
|
|
4208
|
+
return gsapCdnFallbackScript(baseUrl) + html;
|
|
4153
4209
|
}
|
|
4154
4210
|
function parsePreviewVariablesParam(raw) {
|
|
4155
4211
|
if (raw === void 0 || raw === "") return { ok: true, values: null };
|
|
@@ -4176,23 +4232,32 @@ function previewVariablesFromRequest(rawVariables) {
|
|
|
4176
4232
|
var PREVIEW_CAPTURE_PARAM = "hf-capture";
|
|
4177
4233
|
var EARLY_ERRORS_SCRIPT = `<script>(function(){var seen=window.${STUDIO_PREVIEW_ERRORS}=[];
|
|
4178
4234
|
addEventListener("error",function(e){seen.push(e.message||String(e))});})();</script>`;
|
|
4179
|
-
function injectStudioPreviewAugmentations(html, adapter, projectDir, activeCompositionPath,
|
|
4180
|
-
const marked = capture ? injectTagsAtHeadStart(
|
|
4235
|
+
function injectStudioPreviewAugmentations(html, adapter, projectDir, activeCompositionPath, options) {
|
|
4236
|
+
const marked = options.capture ? injectTagsAtHeadStart(
|
|
4181
4237
|
html,
|
|
4182
4238
|
`<meta name="${STUDIO_PREVIEW_MARK_META}" content="${STUDIO_PREVIEW_CAPTURE_MODE}">`
|
|
4183
4239
|
) : injectTagsAtHeadStart(
|
|
4184
4240
|
lazyPreviewImages(html),
|
|
4185
4241
|
`<meta name="${STUDIO_PREVIEW_MARK_META}">${EARLY_ERRORS_SCRIPT}`
|
|
4186
4242
|
);
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4243
|
+
const withFallback = injectGsapCdnFallback(
|
|
4244
|
+
injectProjectSignature(
|
|
4245
|
+
injectTagsAtHeadStart(
|
|
4246
|
+
marked,
|
|
4247
|
+
`<meta name="${GSAP_BASE_URL_META}" content="${escapeGsapUrl(options.gsapBaseUrl)}">`
|
|
4248
|
+
),
|
|
4249
|
+
resolveProjectSignature(adapter, projectDir)
|
|
4192
4250
|
),
|
|
4193
|
-
|
|
4194
|
-
activeCompositionPath
|
|
4251
|
+
options.gsapBaseUrl
|
|
4195
4252
|
);
|
|
4253
|
+
const studioMotion = readStudioMotion(projectDir, activeCompositionPath);
|
|
4254
|
+
const withMotionDependencies = studioMotion ? injectStudioMotionDependencies(
|
|
4255
|
+
withFallback,
|
|
4256
|
+
studioMotion.manifestContent,
|
|
4257
|
+
options.gsapBaseUrl
|
|
4258
|
+
) : withFallback;
|
|
4259
|
+
const withPlugin = injectMotionPathPluginIfNeeded(withMotionDependencies, options.gsapBaseUrl);
|
|
4260
|
+
return studioMotion ? injectStudioMotionScript(withPlugin, studioMotion.script) : withPlugin;
|
|
4196
4261
|
}
|
|
4197
4262
|
async function transformPreviewHtml(html, adapter, project, activeCompositionPath) {
|
|
4198
4263
|
if (!adapter.transformPreviewHtml) return html;
|
|
@@ -4234,7 +4299,23 @@ var PREVIEW_BUNDLE_OPTIONS = {
|
|
|
4234
4299
|
};
|
|
4235
4300
|
var previewBaseHref = (projectId) => `/api/projects/${encodeURIComponent(projectId)}/preview/`;
|
|
4236
4301
|
var withPreviewBase = (html, projectId) => hasBaseElement(html) ? html : injectTagsAtHeadStart(html, `<base href="${previewBaseHref(projectId)}">`);
|
|
4302
|
+
function resolvePreviewGsapBaseUrl(adapter) {
|
|
4303
|
+
const configured = process.env.HYPERFRAMES_GSAP_BASE_URL;
|
|
4304
|
+
if (configured?.trim() || !adapter.gsapLocalRoot) {
|
|
4305
|
+
try {
|
|
4306
|
+
return normalizeGsapBaseUrl(configured);
|
|
4307
|
+
} catch (cause) {
|
|
4308
|
+
throw new Error(
|
|
4309
|
+
"Invalid HYPERFRAMES_GSAP_BASE_URL: use an absolute HTTPS npm mirror root (plain HTTP only for localhost) without credentials, query, or fragment",
|
|
4310
|
+
{ cause }
|
|
4311
|
+
);
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
return normalizeGsapLocalRoot(adapter.gsapLocalRoot);
|
|
4315
|
+
}
|
|
4237
4316
|
function registerPreviewRoutes(api, adapter) {
|
|
4317
|
+
const gsapBaseUrl = resolvePreviewGsapBaseUrl(adapter);
|
|
4318
|
+
const gsapEtagSalt = `:gsap:${createHash2("sha1").update(gsapBaseUrl).digest("hex").slice(0, 12)}`;
|
|
4238
4319
|
const previewCacheHeaders = (etag) => ({
|
|
4239
4320
|
"Cache-Control": "private, no-cache",
|
|
4240
4321
|
ETag: etag
|
|
@@ -4269,7 +4350,7 @@ function registerPreviewRoutes(api, adapter) {
|
|
|
4269
4350
|
adapter,
|
|
4270
4351
|
project.dir,
|
|
4271
4352
|
mainCompositionPath,
|
|
4272
|
-
capture
|
|
4353
|
+
{ capture, gsapBaseUrl }
|
|
4273
4354
|
);
|
|
4274
4355
|
if (previewVariables) bundled = injectPreviewVariables(bundled, previewVariables);
|
|
4275
4356
|
bundled = await injectMediaCodecMap(
|
|
@@ -4297,7 +4378,7 @@ function registerPreviewRoutes(api, adapter) {
|
|
|
4297
4378
|
adapter,
|
|
4298
4379
|
project.dir,
|
|
4299
4380
|
fallback.compositionPath,
|
|
4300
|
-
capture
|
|
4381
|
+
{ capture, gsapBaseUrl }
|
|
4301
4382
|
);
|
|
4302
4383
|
if (previewVariables) {
|
|
4303
4384
|
fallbackAugmented = injectPreviewVariables(fallbackAugmented, previewVariables);
|
|
@@ -4322,7 +4403,7 @@ function registerPreviewRoutes(api, adapter) {
|
|
|
4322
4403
|
if (vars.error !== void 0) return c.json({ error: vars.error }, 400);
|
|
4323
4404
|
const previewVariables = vars.values;
|
|
4324
4405
|
const capture = c.req.query(PREVIEW_CAPTURE_PARAM) !== void 0;
|
|
4325
|
-
const etag = `"preview:${signature}${variablesEtagSalt(vars.raw)}${capture ? ":capture" : ""}"`;
|
|
4406
|
+
const etag = `"preview:${signature}${gsapEtagSalt}${variablesEtagSalt(vars.raw)}${capture ? ":capture" : ""}"`;
|
|
4326
4407
|
const ifNoneMatch = c.req.header("If-None-Match");
|
|
4327
4408
|
if (ifNoneMatch === etag) {
|
|
4328
4409
|
return new Response(null, {
|
|
@@ -4372,7 +4453,7 @@ ${etag}`;
|
|
|
4372
4453
|
}
|
|
4373
4454
|
const compPathHash = createHash2("sha1").update(compPath).digest("hex");
|
|
4374
4455
|
const capture = c.req.query(PREVIEW_CAPTURE_PARAM) !== void 0;
|
|
4375
|
-
const etag = `"comp:v2:${compPathHash}:${signature}${variablesEtagSalt(vars.raw)}${capture ? ":capture" : ""}"`;
|
|
4456
|
+
const etag = `"comp:v2:${compPathHash}:${signature}${gsapEtagSalt}${variablesEtagSalt(vars.raw)}${capture ? ":capture" : ""}"`;
|
|
4376
4457
|
const ifNoneMatch = c.req.header("If-None-Match");
|
|
4377
4458
|
if (ifNoneMatch === etag) {
|
|
4378
4459
|
return new Response(null, {
|
|
@@ -4388,12 +4469,16 @@ ${etag}`;
|
|
|
4388
4469
|
compPath,
|
|
4389
4470
|
adapter.runtimeUrl,
|
|
4390
4471
|
baseHref,
|
|
4391
|
-
stamped
|
|
4472
|
+
stamped,
|
|
4473
|
+
gsapBaseUrl
|
|
4392
4474
|
);
|
|
4393
4475
|
if (!html) return c.text("not found", 404);
|
|
4394
4476
|
recordPreviewReferences(project.dir, html);
|
|
4395
4477
|
html = ensureHfIds2(await transformPreviewHtml(html, adapter, project, compPath));
|
|
4396
|
-
html = injectStudioPreviewAugmentations(html, adapter, project.dir, compPath,
|
|
4478
|
+
html = injectStudioPreviewAugmentations(html, adapter, project.dir, compPath, {
|
|
4479
|
+
capture,
|
|
4480
|
+
gsapBaseUrl
|
|
4481
|
+
});
|
|
4397
4482
|
if (previewVariables) html = injectPreviewVariables(html, previewVariables);
|
|
4398
4483
|
html = await injectMediaCodecMap(html, adapter, project.dir, compPath, mediaCodecProbeCache);
|
|
4399
4484
|
return c.html(html, 200, previewCacheHeaders(etag));
|
|
@@ -135,6 +135,7 @@ interface StudioApiAdapter {
|
|
|
135
135
|
lintProject?: (projectDir: string) => Promise<ProjectLintResult> | ProjectLintResult;
|
|
136
136
|
/** URL to the hyperframe runtime JS (injected into preview HTML). */
|
|
137
137
|
runtimeUrl: string;
|
|
138
|
+
gsapLocalRoot?: `/${string}`;
|
|
138
139
|
/**
|
|
139
140
|
* Optional: post-process preview HTML before Studio augments it.
|
|
140
141
|
* Useful when preview must mirror render-time compilation steps.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio-server",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.195",
|
|
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/
|
|
88
|
-
"@hyperframes/
|
|
87
|
+
"@hyperframes/parsers": "0.8.195",
|
|
88
|
+
"@hyperframes/core": "0.8.195"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
91
|
"@types/node": "^25.0.10",
|