@hyperframes/studio-server 0.8.142 → 0.8.144
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-DSVQ2U7J.js → chunk-32IH7WBY.js} +36 -14
- package/dist/chunk-32IH7WBY.js.map +1 -0
- package/dist/{chunk-QP7LKFU2.js → chunk-AZBGTB3N.js} +98 -30
- package/dist/chunk-AZBGTB3N.js.map +1 -0
- package/dist/{chunk-74JVVCWL.js → chunk-RN33UALN.js} +16 -18
- package/dist/chunk-RN33UALN.js.map +1 -0
- package/dist/{chunk-5VCIZYUE.js → chunk-VE2AOX4K.js} +3 -3
- package/dist/helpers/loudness.js +4 -1
- package/dist/helpers/mediaCodecMap.js +1 -1
- package/dist/helpers/mediaProxyPreview.d.ts +1 -1
- package/dist/helpers/mediaProxyPreview.js +3 -3
- package/dist/helpers/proxyTranscoder.d.ts +12 -12
- package/dist/helpers/proxyTranscoder.js +2 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +247 -50
- package/dist/index.js.map +1 -1
- package/dist/{mediaProxyPreview-CJ4lbPcF.d.ts → mediaProxyPreview-ozy7FkMP.d.ts} +1 -0
- package/package.json +3 -3
- package/dist/chunk-74JVVCWL.js.map +0 -1
- package/dist/chunk-DSVQ2U7J.js.map +0 -1
- package/dist/chunk-QP7LKFU2.js.map +0 -1
- /package/dist/{chunk-5VCIZYUE.js.map → chunk-VE2AOX4K.js.map} +0 -0
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
resolveProxyVariantRequest,
|
|
14
14
|
scanProjectMediaCodecMap,
|
|
15
15
|
shouldPrewarmProxy
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-RN33UALN.js";
|
|
17
17
|
import "../chunk-DTKK7TKL.js";
|
|
18
18
|
import "../chunk-6XMC64FJ.js";
|
|
19
19
|
import "../chunk-4ETS2LXI.js";
|
|
@@ -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-ozy7FkMP.js';
|
|
3
3
|
import './mediaCodecMap.js';
|
|
4
4
|
import '@hyperframes/parsers';
|
|
5
5
|
import '@hyperframes/core/compiler';
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
isAutoProxyEnabled,
|
|
5
5
|
proxyEtagSalt,
|
|
6
6
|
resolvePreviewMediaCodecProbeCache
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
7
|
+
} from "../chunk-VE2AOX4K.js";
|
|
8
|
+
import "../chunk-AZBGTB3N.js";
|
|
9
|
+
import "../chunk-RN33UALN.js";
|
|
10
10
|
import "../chunk-DTKK7TKL.js";
|
|
11
11
|
import "../chunk-6XMC64FJ.js";
|
|
12
12
|
import "../chunk-4ETS2LXI.js";
|
|
@@ -7,15 +7,9 @@ import { ProxyVariant } from './mediaCodecMap.js';
|
|
|
7
7
|
* project routes (U3/U4) to serve a `?hf-proxy=` request; never used on
|
|
8
8
|
* the render path (render always sees the original file).
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* caller
|
|
13
|
-
* killed the ffmpeg child on client abort (page reload, HMR), every other
|
|
14
|
-
* caller waiting on that same promise would fail too, and the next request
|
|
15
|
-
* would restart a transcode that may have been minutes into a long asset.
|
|
16
|
-
* Callers MUST let the child run to completion regardless of request
|
|
17
|
-
* cancellation and simply let the held response also abort — the cache
|
|
18
|
-
* entry still lands for the next request.
|
|
10
|
+
* A started ffmpeg child is never killed for one caller: every caller of a cache
|
|
11
|
+
* key shares it (in-flight dedupe below). A caller's `signal` only detaches that
|
|
12
|
+
* caller, and drops the copy while it is still queued with no caller left.
|
|
19
13
|
*/
|
|
20
14
|
declare const PROXY_PARAMS_VERSION = "v5";
|
|
21
15
|
declare const CACHE_DIR_NAME = ".transcode-cache";
|
|
@@ -51,7 +45,7 @@ declare function getProxyCachePath(projectDir: string, absoluteSourcePath: strin
|
|
|
51
45
|
/** Test hook: forget remembered transcode failures (module state persists
|
|
52
46
|
* across tests that don't reload the module). */
|
|
53
47
|
declare function clearFailedTranscodesForTest(): void;
|
|
54
|
-
/** Null while
|
|
48
|
+
/** Null while this project has a copy in progress; otherwise a mark that moves when any copy finishes or is refused. */
|
|
55
49
|
declare function proxyActivityMark(projectDir: string): string | null;
|
|
56
50
|
/**
|
|
57
51
|
* Resolves the cached proxy variant for `absoluteSourcePath`, transcoding it at
|
|
@@ -62,7 +56,13 @@ declare function proxyActivityMark(projectDir: string): string | null;
|
|
|
62
56
|
* nonzero exit) — callers (route handlers) decide how to surface that (502).
|
|
63
57
|
* A `box` makes a smaller preview-only copy with its own cache entry; without
|
|
64
58
|
* one the copy keeps the source size (CLI play, static servers, publish).
|
|
59
|
+
* `priority` moves the copy ahead of every normal queued copy; a `signal`
|
|
60
|
+
* lets this caller leave (see the detachment note at the top of this file).
|
|
65
61
|
*/
|
|
66
|
-
declare function resolveProxy(projectDir: string, absoluteSourcePath: string, variant?: ProxyVariant, box?: PreviewProxyBox): Promise<string>;
|
|
62
|
+
declare function resolveProxy(projectDir: string, absoluteSourcePath: string, variant?: ProxyVariant, box?: PreviewProxyBox, options?: ResolveProxyOptions): Promise<string>;
|
|
63
|
+
interface ResolveProxyOptions {
|
|
64
|
+
priority?: boolean;
|
|
65
|
+
signal?: AbortSignal;
|
|
66
|
+
}
|
|
67
67
|
|
|
68
|
-
export { CACHE_DIR_NAME, DEFAULT_PROXY_WAIT_TIMEOUT_MS, FfmpegMissingFilterError, PROXY_PARAMS_VERSION, PROXY_PENDING_RETRY_AFTER_SECONDS, ProxyCapacityError, ProxySourceOutsideProjectError, ProxyTranscodeError, ProxyWaitTimeoutError, TRANSCODE_TIMEOUT_MS, clearFailedTranscodesForTest, getProxyCachePath, proxyActivityMark, resolveProxy, waitForProxy };
|
|
68
|
+
export { CACHE_DIR_NAME, DEFAULT_PROXY_WAIT_TIMEOUT_MS, FfmpegMissingFilterError, PROXY_PARAMS_VERSION, PROXY_PENDING_RETRY_AFTER_SECONDS, ProxyCapacityError, ProxySourceOutsideProjectError, ProxyTranscodeError, ProxyWaitTimeoutError, type ResolveProxyOptions, TRANSCODE_TIMEOUT_MS, clearFailedTranscodesForTest, getProxyCachePath, proxyActivityMark, resolveProxy, waitForProxy };
|
|
@@ -14,8 +14,8 @@ import {
|
|
|
14
14
|
proxyActivityMark,
|
|
15
15
|
resolveProxy,
|
|
16
16
|
waitForProxy
|
|
17
|
-
} from "../chunk-
|
|
18
|
-
import "../chunk-
|
|
17
|
+
} from "../chunk-AZBGTB3N.js";
|
|
18
|
+
import "../chunk-RN33UALN.js";
|
|
19
19
|
import "../chunk-DTKK7TKL.js";
|
|
20
20
|
import "../chunk-6XMC64FJ.js";
|
|
21
21
|
import "../chunk-4ETS2LXI.js";
|
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-ozy7FkMP.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-ozy7FkMP.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 HistoryResult, f as HistoryWho, g as HistoryWindow, M as MAX_WINDOW_IDLE_MS, P as ProjectHistory, h as ProjectHistoryOptions, U as UNDO_MODES, i as UndoMode, o as openProjectHistory } from './projectHistory-Cm8Mdmmv.js';
|
|
5
5
|
export { HistoryBusyError, HistoryIdError, ProjectHistoryRecord, PrunedHistory, historyCache, listProjectHistories, pruneGoneProjectHistories } from './history/index.js';
|
|
6
6
|
export { CACHE_DIR_NAME as PROXY_CACHE_DIR_NAME } from './helpers/proxyTranscoder.js';
|
|
@@ -11,6 +11,7 @@ export { PatchOperation, dedupeClonedCompositionId, findTargetElement, parseSour
|
|
|
11
11
|
export { ScreenshotClip, clearElementScreenshotIsolation, getElementScreenshotClip } from './helpers/screenshotClip.js';
|
|
12
12
|
export { STUDIO_MANUAL_EDITS_PATH, StudioManualEditsRenderScriptOptions, createStudioManualEditsRenderBodyScript, createStudioPositionSeekReapplyScript } from './helpers/manualEditsRenderScript.js';
|
|
13
13
|
export { STUDIO_MOTION_PATH, StudioMotionRenderScriptOptions, createStudioMotionRenderBodyScript } from './helpers/studioMotionRenderScript.js';
|
|
14
|
+
export { decodeWellFormedEscapes } from '@hyperframes/parsers/asset-paths';
|
|
14
15
|
export { isSafePath } from '@hyperframes/core/safe-path';
|
|
15
16
|
import '@hyperframes/core';
|
|
16
17
|
import '@hyperframes/parsers';
|
package/dist/index.js
CHANGED
|
@@ -55,16 +55,19 @@ import {
|
|
|
55
55
|
import {
|
|
56
56
|
DEFAULT_TARGET_LUFS,
|
|
57
57
|
absoluteLoudnessPlan,
|
|
58
|
+
decodeWellFormedEscapes,
|
|
58
59
|
measureAudio,
|
|
60
|
+
requestSubPath,
|
|
61
|
+
requestedProjectPath,
|
|
59
62
|
requiredFfmpeg,
|
|
60
63
|
resolveLocalAudioPath
|
|
61
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-32IH7WBY.js";
|
|
62
65
|
import {
|
|
63
66
|
injectMediaCodecMap,
|
|
64
67
|
isAutoProxyEnabled,
|
|
65
68
|
proxyEtagSalt,
|
|
66
69
|
resolvePreviewMediaCodecProbeCache
|
|
67
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-VE2AOX4K.js";
|
|
68
71
|
import {
|
|
69
72
|
CACHE_DIR_NAME,
|
|
70
73
|
PROXY_PENDING_RETRY_AFTER_SECONDS,
|
|
@@ -75,7 +78,7 @@ import {
|
|
|
75
78
|
proxyActivityMark,
|
|
76
79
|
resolveProxy,
|
|
77
80
|
waitForProxy
|
|
78
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-AZBGTB3N.js";
|
|
79
82
|
import {
|
|
80
83
|
PROXY_VARIANT_CONFIG,
|
|
81
84
|
decideMediaProxyEligibility,
|
|
@@ -84,7 +87,7 @@ import {
|
|
|
84
87
|
probeMediaMetadata,
|
|
85
88
|
recordProxyRequest,
|
|
86
89
|
resolveProxyVariantRequest
|
|
87
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-RN33UALN.js";
|
|
88
91
|
import {
|
|
89
92
|
folderGone,
|
|
90
93
|
isInHiddenOrVendorDir,
|
|
@@ -158,6 +161,12 @@ function registerProjectRoutes(api, adapter) {
|
|
|
158
161
|
}
|
|
159
162
|
|
|
160
163
|
// src/routes/files.ts
|
|
164
|
+
import { encodeUrlPath, decodedUrlPath, decodeCssEscapes } from "@hyperframes/parsers/asset-paths";
|
|
165
|
+
import {
|
|
166
|
+
scanHtmlOpeningTags,
|
|
167
|
+
decodeAuthoredAttribute,
|
|
168
|
+
HTML_ATTRIBUTE_ENTITIES
|
|
169
|
+
} from "@hyperframes/parsers";
|
|
161
170
|
import { bodyLimit } from "hono/body-limit";
|
|
162
171
|
import {
|
|
163
172
|
closeSync,
|
|
@@ -174,7 +183,7 @@ import {
|
|
|
174
183
|
readdirSync as readdirSync2,
|
|
175
184
|
realpathSync
|
|
176
185
|
} from "fs";
|
|
177
|
-
import { resolve as resolve2, dirname as dirname3, join as join6 } from "path";
|
|
186
|
+
import { resolve as resolve2, dirname as dirname3, join as join6, sep as sep2 } from "path";
|
|
178
187
|
|
|
179
188
|
// src/helpers/mime.ts
|
|
180
189
|
var MIME_TYPES = {
|
|
@@ -1191,14 +1200,6 @@ function resolveGsapWriter(env) {
|
|
|
1191
1200
|
throw new Error(`Invalid ${GSAP_WRITER_MIGRATION.flag}=${configured}; expected recast or acorn`);
|
|
1192
1201
|
}
|
|
1193
1202
|
|
|
1194
|
-
// src/helpers/requestSubPath.ts
|
|
1195
|
-
function requestSubPath(url, route) {
|
|
1196
|
-
const routeSegments = route.split("/");
|
|
1197
|
-
const segments = new URL(url).pathname.split("/");
|
|
1198
|
-
const start = segments.indexOf(routeSegments[0] ?? "");
|
|
1199
|
-
return decodeURIComponent(segments.slice(start + routeSegments.length).join("/"));
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
1203
|
// src/routes/files.ts
|
|
1203
1204
|
import {
|
|
1204
1205
|
hasCompositionOutsideTemplates,
|
|
@@ -1505,15 +1506,114 @@ var escapeRegExp = (text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
|
1505
1506
|
var SEPARATOR = String.raw`\\{0,2}[\\/]`;
|
|
1506
1507
|
var REFERENCE_START = String.raw`(?<![\w./\\+-])`;
|
|
1507
1508
|
var FILE_END = String.raw`(?![\w-]|\.\w)`;
|
|
1509
|
+
var ENTITY_SPELLINGS = HTML_ATTRIBUTE_ENTITIES;
|
|
1510
|
+
function referenceSpellings(char) {
|
|
1511
|
+
const spellings = [escapeRegExp(char)];
|
|
1512
|
+
const encoded = encodeUrlPath(char);
|
|
1513
|
+
if (encoded !== char)
|
|
1514
|
+
spellings.push(encoded.replace(/[A-F]/g, (hex) => `[${hex}${hex.toLowerCase()}]`));
|
|
1515
|
+
if (ENTITY_SPELLINGS[char]) spellings.push(...ENTITY_SPELLINGS[char]);
|
|
1516
|
+
if (["'", '"', "`", "$", " ", "(", ")"].includes(char)) {
|
|
1517
|
+
spellings.push(escapeRegExp(`\${char}`), escapeRegExp(`\${escapeHtmlReference(char)}`));
|
|
1518
|
+
}
|
|
1519
|
+
const scriptSpelling = escapeQuotedReference(char, '"');
|
|
1520
|
+
if (scriptSpelling !== char) spellings.push(escapeRegExp(scriptSpelling));
|
|
1521
|
+
if (["'", "`", "$", " ", "(", ")"].includes(char)) spellings.push(escapeRegExp(`\\${char}`));
|
|
1522
|
+
if (/[\t\n\f\r =<>`]/.test(char)) spellings.push(`&#${char.codePointAt(0)};`);
|
|
1523
|
+
if (char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127) {
|
|
1524
|
+
spellings.push(escapeRegExp(`\\${char.charCodeAt(0).toString(16)} `));
|
|
1525
|
+
}
|
|
1526
|
+
return spellings.length === 1 ? spellings[0] : `(?:${spellings.join("|")})`;
|
|
1527
|
+
}
|
|
1528
|
+
function escapeHtmlReference(path) {
|
|
1529
|
+
return path.replace(/[&"']/g, (char) => ENTITY_SPELLINGS[char][0]);
|
|
1530
|
+
}
|
|
1531
|
+
function escapeQuotedReference(path, quote) {
|
|
1532
|
+
const escaped = JSON.stringify(path).slice(1, -1);
|
|
1533
|
+
return quote === '"' ? escaped : escaped.replace(new RegExp(escapeRegExp(quote), "g"), `\\${quote}`);
|
|
1534
|
+
}
|
|
1535
|
+
function escapeRawUrlPath(path) {
|
|
1536
|
+
const encodeSpaces = path.startsWith(" ") || path.endsWith(" ");
|
|
1537
|
+
return path.replace(/[\s\S]/g, (char) => {
|
|
1538
|
+
if (char.charCodeAt(0) < 32 || ":?%#\\".includes(char) || encodeSpaces && char === " ") {
|
|
1539
|
+
return encodeUrlPath(char);
|
|
1540
|
+
}
|
|
1541
|
+
return char;
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
function spellLike(reference, path, oldPath, context) {
|
|
1545
|
+
const { syntax, quote, url, encodeAll } = context;
|
|
1546
|
+
if (url && (encodeAll || reference !== oldPath && /%[0-9A-Fa-f]{2}/.test(reference)))
|
|
1547
|
+
return encodeUrlPath(path);
|
|
1548
|
+
switch (syntax) {
|
|
1549
|
+
case "html": {
|
|
1550
|
+
const escaped = escapeHtmlReference(url ? escapeRawUrlPath(path) : path);
|
|
1551
|
+
return ["'", '"'].includes(quote) ? escaped : escaped.replace(/[\t\n\f\r =<>`]/g, (char) => `&#${char.codePointAt(0)};`);
|
|
1552
|
+
}
|
|
1553
|
+
case "script": {
|
|
1554
|
+
if (!["'", '"', "`"].includes(quote)) return path;
|
|
1555
|
+
const escaped = escapeQuotedReference(path, quote);
|
|
1556
|
+
return quote === "`" ? escaped.replace(/\$\{/g, "\\${") : escaped;
|
|
1557
|
+
}
|
|
1558
|
+
case "css":
|
|
1559
|
+
return url ? encodeUrlPath(path) : path;
|
|
1560
|
+
case "plain":
|
|
1561
|
+
return path;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
function referenceSyntax(file) {
|
|
1565
|
+
if (/\.html$/i.test(file)) return "html";
|
|
1566
|
+
if (/\.css$/i.test(file)) return "css";
|
|
1567
|
+
if (/\.(?:[cm]?js|jsx|tsx?|json)$/i.test(file)) return "script";
|
|
1568
|
+
return "plain";
|
|
1569
|
+
}
|
|
1570
|
+
function decodeQuotedReference(text2, syntax) {
|
|
1571
|
+
if (syntax === "css") return decodeCssEscapes(text2);
|
|
1572
|
+
if (syntax === "script") {
|
|
1573
|
+
const controls = { b: "\b", f: "\f", n: "\n", r: "\r", t: " " };
|
|
1574
|
+
return text2.replace(
|
|
1575
|
+
/\\(?:u([0-9a-fA-F]{4})|([bfnrt"'`$\\/]))/g,
|
|
1576
|
+
(_escaped, hex, char) => {
|
|
1577
|
+
if (hex !== void 0) return String.fromCharCode(Number.parseInt(hex, 16));
|
|
1578
|
+
return controls[char] ?? char;
|
|
1579
|
+
}
|
|
1580
|
+
);
|
|
1581
|
+
}
|
|
1582
|
+
return text2;
|
|
1583
|
+
}
|
|
1508
1584
|
function referencePattern(oldPath, isDirectory) {
|
|
1509
|
-
const name = oldPath.split("/").map(
|
|
1585
|
+
const name = oldPath.split("/").map((segment) => Array.from(segment, referenceSpellings).join("")).join(SEPARATOR);
|
|
1510
1586
|
const end = isDirectory ? `(?=${SEPARATOR})` : FILE_END;
|
|
1511
1587
|
return new RegExp(
|
|
1512
1588
|
String.raw`${REFERENCE_START}(?<lead>(?:\.{1,2}${SEPARATOR}|${SEPARATOR}){0,4})${name}${end}`,
|
|
1513
1589
|
"g"
|
|
1514
1590
|
);
|
|
1515
1591
|
}
|
|
1592
|
+
function rewriteEntityAttributes(text2, rewrite) {
|
|
1593
|
+
const parts = [];
|
|
1594
|
+
let cursor = 0;
|
|
1595
|
+
for (const tag of scanHtmlOpeningTags(text2)) {
|
|
1596
|
+
for (const attr of tag.attributes) {
|
|
1597
|
+
if (attr.kind !== "value") continue;
|
|
1598
|
+
const decoded = decodeAuthoredAttribute(attr.value);
|
|
1599
|
+
if (decoded === attr.value) continue;
|
|
1600
|
+
let value = escapeHtmlReference(decoded);
|
|
1601
|
+
if (!attr.quote)
|
|
1602
|
+
value = value.replace(/[\t\n\f\r =<>`]/g, (char) => `&#${char.codePointAt(0)};`);
|
|
1603
|
+
const source = `<x ${attr.name}=${attr.quote}${value}${attr.quote}>`;
|
|
1604
|
+
const updated = rewrite(source);
|
|
1605
|
+
if (updated === source) continue;
|
|
1606
|
+
const replacement = scanHtmlOpeningTags(updated)[0].attributes[0];
|
|
1607
|
+
if (replacement.kind !== "value") throw new Error("Reference rewrite removed its attribute");
|
|
1608
|
+
parts.push(text2.slice(cursor, attr.valueStart), replacement.value);
|
|
1609
|
+
cursor = attr.valueEnd;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
parts.push(text2.slice(cursor));
|
|
1613
|
+
return parts.join("");
|
|
1614
|
+
}
|
|
1516
1615
|
function referenceRewriter(oldPath, newPath, isDirectory, existing = []) {
|
|
1616
|
+
const oldIdentity = sep2 === "\\" ? oldPath.replace(/\\/g, "/") : oldPath;
|
|
1517
1617
|
const pattern = referencePattern(oldPath, isDirectory);
|
|
1518
1618
|
const around = /* @__PURE__ */ new Map();
|
|
1519
1619
|
const afterLengths = /* @__PURE__ */ new Set();
|
|
@@ -1527,24 +1627,123 @@ function referenceRewriter(oldPath, newPath, isDirectory, existing = []) {
|
|
|
1527
1627
|
before
|
|
1528
1628
|
);
|
|
1529
1629
|
afterLengths.add(after.length);
|
|
1530
|
-
window = Math.max(window, path.length *
|
|
1531
|
-
}
|
|
1532
|
-
}
|
|
1533
|
-
const normalized = (text2) =>
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1630
|
+
window = Math.max(window, path.length * 9);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
const normalized = (text2, context, full = false) => {
|
|
1634
|
+
let value = text2;
|
|
1635
|
+
if (context.syntax === "html" || context.htmlAttribute) {
|
|
1636
|
+
value = decodeAuthoredAttribute(value);
|
|
1637
|
+
}
|
|
1638
|
+
value = decodeQuotedReference(value, context.syntax);
|
|
1639
|
+
if (context.url) {
|
|
1640
|
+
value = value.replace(/\\/g, "/");
|
|
1641
|
+
value = full ? decodedUrlPath(value) : decodeWellFormedEscapes(value);
|
|
1642
|
+
}
|
|
1643
|
+
return sep2 === "\\" ? value.replace(/\\/g, "/") : value;
|
|
1644
|
+
};
|
|
1645
|
+
return function rewriteText(text2, syntax = "html", normalizeEntities = true) {
|
|
1646
|
+
if (syntax === "html" && normalizeEntities) {
|
|
1647
|
+
text2 = rewriteEntityAttributes(text2, (source) => rewriteText(source, "html", false));
|
|
1648
|
+
}
|
|
1649
|
+
pattern.lastIndex = 0;
|
|
1650
|
+
if (!pattern.test(text2)) return text2;
|
|
1651
|
+
pattern.lastIndex = 0;
|
|
1652
|
+
const tags = syntax === "html" ? scanHtmlOpeningTags(text2) : [];
|
|
1653
|
+
const inlineRegions = tags.filter(
|
|
1654
|
+
(tag) => tag.closed && ["script", "style"].includes(tag.name) && tag.rawTextEnd !== null
|
|
1655
|
+
).map((tag) => ({
|
|
1656
|
+
start: tag.end,
|
|
1657
|
+
end: tag.rawTextEnd,
|
|
1658
|
+
syntax: tag.name === "style" ? "css" : "script"
|
|
1659
|
+
}));
|
|
1660
|
+
const attributes = tags.flatMap(
|
|
1661
|
+
(tag) => tag.attributes.flatMap((attr) => attr.kind === "value" ? [attr] : [])
|
|
1662
|
+
);
|
|
1663
|
+
const cssContexts = syntax === "css" ? [{ start: 0, end: text2.length, htmlAttribute: false }] : [
|
|
1664
|
+
...inlineRegions.filter((region) => region.syntax === "css").map((region) => ({ ...region, htmlAttribute: false })),
|
|
1665
|
+
...attributes.filter((attr) => attr.name === "style").map((attr) => ({ ...attr, htmlAttribute: true }))
|
|
1666
|
+
];
|
|
1667
|
+
const cssFields = (pattern2) => cssContexts.flatMap(
|
|
1668
|
+
(context) => [...text2.slice(context.start, context.end).matchAll(pattern2)].map((match) => {
|
|
1669
|
+
const prefix = /^(?:url\(\s*|@import\s+)/i.exec(match[0])[0];
|
|
1670
|
+
const rest = match[0].slice(prefix.length);
|
|
1671
|
+
const delimiter = context.htmlAttribute ? /^(?:["']|"|'|')/.exec(rest)?.[0] : /^["']/.exec(rest)?.[0];
|
|
1672
|
+
const start = context.start + match.index + prefix.length + (delimiter?.length ?? 0);
|
|
1673
|
+
return {
|
|
1674
|
+
start,
|
|
1675
|
+
end: context.start + match.index + match[0].length,
|
|
1676
|
+
syntax: "css",
|
|
1677
|
+
htmlAttribute: context.htmlAttribute,
|
|
1678
|
+
encodeAll: true
|
|
1679
|
+
};
|
|
1680
|
+
})
|
|
1681
|
+
);
|
|
1682
|
+
const urlRegions = [
|
|
1683
|
+
...attributes.filter((attr) => ["src", "href", "poster", "xlink:href"].includes(attr.name)).map((attr) => ({
|
|
1684
|
+
start: attr.valueStart + (/^[\t\n\f\r ]*/.exec(attr.value)?.[0].length ?? 0),
|
|
1685
|
+
end: attr.valueEnd,
|
|
1686
|
+
syntax: "html",
|
|
1687
|
+
htmlAttribute: true,
|
|
1688
|
+
encodeAll: false
|
|
1689
|
+
})),
|
|
1690
|
+
...attributes.filter((attr) => attr.name === "srcset").flatMap(
|
|
1691
|
+
(attr) => [...attr.value.matchAll(/(^|,)[\t\n\f\r ]*([^,\t\n\f\r ]+)/g)].map((match) => ({
|
|
1692
|
+
start: attr.valueStart + match.index + match[0].length - match[2].length,
|
|
1693
|
+
end: attr.valueStart + match.index + match[0].length,
|
|
1694
|
+
syntax: "html",
|
|
1695
|
+
htmlAttribute: true,
|
|
1696
|
+
encodeAll: true
|
|
1697
|
+
}))
|
|
1698
|
+
),
|
|
1699
|
+
...cssFields(
|
|
1700
|
+
/\burl\((?:\s*"(?:\\.|[^"\\])*"\s*|\s*'(?:\\.|[^'\\])*'\s*|(?:\\.|[^)\\])*)\)/gi
|
|
1701
|
+
),
|
|
1702
|
+
...cssFields(/@import\s+(?:"[^"]*"|'[^']*')/gi)
|
|
1703
|
+
].sort((a, b) => a.start - b.start);
|
|
1704
|
+
let urlIndex = 0;
|
|
1705
|
+
let regionIndex = 0;
|
|
1706
|
+
return text2.replace(pattern, (...args) => {
|
|
1707
|
+
const [match, offset] = [args[0], args.at(-3)];
|
|
1708
|
+
const { lead } = args.at(-1);
|
|
1709
|
+
while (inlineRegions[regionIndex] && inlineRegions[regionIndex].end <= offset) regionIndex++;
|
|
1710
|
+
const region = inlineRegions[regionIndex];
|
|
1711
|
+
const matchSyntax = region && offset >= region.start ? region.syntax : syntax;
|
|
1712
|
+
while (urlRegions[urlIndex] && urlRegions[urlIndex].end <= offset) urlIndex++;
|
|
1713
|
+
const urlRegion = urlRegions[urlIndex];
|
|
1714
|
+
const url = matchSyntax !== "script" && !!urlRegion && offset >= urlRegion.start;
|
|
1715
|
+
const context = {
|
|
1716
|
+
syntax: url ? urlRegion.syntax : matchSyntax,
|
|
1717
|
+
url,
|
|
1718
|
+
htmlAttribute: url && urlRegion.htmlAttribute,
|
|
1719
|
+
encodeAll: url && urlRegion.encodeAll,
|
|
1720
|
+
quote: text2[offset - 1] ?? ""
|
|
1721
|
+
};
|
|
1722
|
+
if (url && offset !== urlRegion.start) return match;
|
|
1723
|
+
const reference = match.slice(lead.length);
|
|
1724
|
+
if (normalized(reference, context, true) !== oldIdentity) return match;
|
|
1725
|
+
if (isDirectory && !normalized(
|
|
1726
|
+
text2.slice(offset + match.length, offset + match.length + 4),
|
|
1727
|
+
context
|
|
1728
|
+
).startsWith("/"))
|
|
1729
|
+
return match;
|
|
1730
|
+
const at = offset + lead.length;
|
|
1731
|
+
const head = normalized(text2.slice(Math.max(0, at - window), at), context);
|
|
1732
|
+
const tail = normalized(
|
|
1733
|
+
text2.slice(offset + match.length, offset + match.length + window),
|
|
1734
|
+
context,
|
|
1735
|
+
context.url
|
|
1544
1736
|
);
|
|
1737
|
+
const inLonger = [...afterLengths].some((after) => {
|
|
1738
|
+
const befores = tail.length >= after && around.get(tail.slice(0, after));
|
|
1739
|
+
return befores && [...befores].some(
|
|
1740
|
+
([length, set]) => head.length >= length && set.has(head.slice(head.length - length))
|
|
1741
|
+
);
|
|
1742
|
+
});
|
|
1743
|
+
if (inLonger) return match;
|
|
1744
|
+
return `${lead}${spellLike(reference, newPath, oldPath, context)}`;
|
|
1545
1745
|
});
|
|
1546
|
-
|
|
1547
|
-
});
|
|
1746
|
+
};
|
|
1548
1747
|
}
|
|
1549
1748
|
function occurrences(text2, part) {
|
|
1550
1749
|
const at = [];
|
|
@@ -1577,7 +1776,7 @@ function updateReferences(projectDir, oldPath, newPath, isDirectory) {
|
|
|
1577
1776
|
if (!isSafePath(projectDir, file) || isPrivateProjectFile(projectDir, file)) continue;
|
|
1578
1777
|
const content = readableText(file);
|
|
1579
1778
|
if (content === null) continue;
|
|
1580
|
-
const updated = rewrite(content);
|
|
1779
|
+
const updated = rewrite(content, referenceSyntax(file));
|
|
1581
1780
|
if (updated !== content) {
|
|
1582
1781
|
replaceFileAtomically2(file, updated, statSync3(file).mode);
|
|
1583
1782
|
updatedCount++;
|
|
@@ -3454,10 +3653,11 @@ function registerFileRoutes(api, adapter) {
|
|
|
3454
3653
|
const res = await resolveProjectFile(c, adapter, { mustExist: true });
|
|
3455
3654
|
if ("error" in res) return res.error;
|
|
3456
3655
|
const body = await c.req.json();
|
|
3457
|
-
|
|
3656
|
+
const newPath = body !== null && typeof body === "object" && "newPath" in body ? body.newPath : void 0;
|
|
3657
|
+
if (typeof newPath !== "string" || !newPath || newPath.includes("\0") || /[\uD800-\uDFFF]/u.test(newPath)) {
|
|
3458
3658
|
return c.json({ error: "newPath required" }, 400);
|
|
3459
3659
|
}
|
|
3460
|
-
const newAbs = resolveWithinProject(res.project.dir,
|
|
3660
|
+
const newAbs = resolveWithinProject(res.project.dir, newPath);
|
|
3461
3661
|
if (!newAbs) {
|
|
3462
3662
|
return c.json({ error: "forbidden" }, 403);
|
|
3463
3663
|
}
|
|
@@ -3467,8 +3667,8 @@ function registerFileRoutes(api, adapter) {
|
|
|
3467
3667
|
const isDirectory = statSync3(res.absPath, { throwIfNoEntry: false })?.isDirectory() ?? false;
|
|
3468
3668
|
ensureDir(res.project.dir, newAbs);
|
|
3469
3669
|
renameSync2(res.absPath, newAbs);
|
|
3470
|
-
const updatedFiles = updateReferences(res.project.dir, res.filePath,
|
|
3471
|
-
return c.json({ ok: true, path:
|
|
3670
|
+
const updatedFiles = updateReferences(res.project.dir, res.filePath, newPath, isDirectory);
|
|
3671
|
+
return c.json({ ok: true, path: newPath, updatedReferences: updatedFiles });
|
|
3472
3672
|
});
|
|
3473
3673
|
api.post("/projects/:id/duplicate-file", async (c) => {
|
|
3474
3674
|
const project = await adapter.resolveProject(c.req.param("id"));
|
|
@@ -4306,7 +4506,11 @@ async function lintUncoveredHtml(adapter, projectDir, htmlFiles, coveredFiles) {
|
|
|
4306
4506
|
for (const file of htmlFiles) {
|
|
4307
4507
|
if (coveredFiles.has(file)) continue;
|
|
4308
4508
|
const content = readFileSync7(join9(projectDir, file), "utf-8");
|
|
4309
|
-
const result = await adapter.lint(content, {
|
|
4509
|
+
const result = await adapter.lint(content, {
|
|
4510
|
+
filePath: file,
|
|
4511
|
+
isSubComposition: true,
|
|
4512
|
+
compSrcPath: file
|
|
4513
|
+
});
|
|
4310
4514
|
for (const finding of result?.findings ?? []) {
|
|
4311
4515
|
findings.push({ ...finding, file });
|
|
4312
4516
|
}
|
|
@@ -4760,12 +4964,7 @@ async function generateThumbnail(path, signal) {
|
|
|
4760
4964
|
async function resolveSource(adapter, id, url) {
|
|
4761
4965
|
const project = await adapter.resolveProject(id);
|
|
4762
4966
|
if (!project) throw new HTTPException(404);
|
|
4763
|
-
|
|
4764
|
-
try {
|
|
4765
|
-
relative5 = requestSubPath(url, "projects/:id/image-thumbnail");
|
|
4766
|
-
} catch {
|
|
4767
|
-
throw new HTTPException(400);
|
|
4768
|
-
}
|
|
4967
|
+
const relative5 = requestSubPath(url, "projects/:id/image-thumbnail");
|
|
4769
4968
|
if (!/\.jpe?g$/i.test(relative5)) throw new HTTPException(415);
|
|
4770
4969
|
const path = pinWithinProject(project.dir, relative5);
|
|
4771
4970
|
if (!path) throw new HTTPException(404);
|
|
@@ -5079,7 +5278,7 @@ function registerWaveformRoutes(api, adapter) {
|
|
|
5079
5278
|
// src/routes/freezeFrame.ts
|
|
5080
5279
|
import { execFile } from "child_process";
|
|
5081
5280
|
import { readFileSync as readFileSync11, rmSync as rmSync5, statSync as statSync8 } from "fs";
|
|
5082
|
-
import { basename as basename2, dirname as dirname6, join as join13, relative as relative4, sep as
|
|
5281
|
+
import { basename as basename2, dirname as dirname6, join as join13, relative as relative4, sep as sep3 } from "path";
|
|
5083
5282
|
import { findFfBinary as findFfBinary3 } from "@hyperframes/parsers/ff-binaries";
|
|
5084
5283
|
import {
|
|
5085
5284
|
atomicTempPath,
|
|
@@ -5343,7 +5542,7 @@ async function extractStill(projectDir, absPath, source, playhead, tools) {
|
|
|
5343
5542
|
imagePath
|
|
5344
5543
|
);
|
|
5345
5544
|
if (failed) return failed;
|
|
5346
|
-
const depth = relative4(projectDir, fileDir).split(
|
|
5545
|
+
const depth = relative4(projectDir, fileDir).split(sep3).filter(Boolean).length;
|
|
5347
5546
|
const stillPath = `${FREEZE_DIR.join("/")}/${fileName}`;
|
|
5348
5547
|
return { imageSrc: `${"../".repeat(depth)}${stillPath}`, stillPath, imagePath };
|
|
5349
5548
|
}
|
|
@@ -5920,9 +6119,6 @@ function isVideoPath(path) {
|
|
|
5920
6119
|
function isImagePath(path) {
|
|
5921
6120
|
return IMAGE_EXTENSIONS.has(extname(path).toLowerCase());
|
|
5922
6121
|
}
|
|
5923
|
-
function normalizeProjectAssetPath(path) {
|
|
5924
|
-
return path.trim().replace(/^[.]\//, "").replace(/[?#].*$/, "");
|
|
5925
|
-
}
|
|
5926
6122
|
function containsNullByte(path) {
|
|
5927
6123
|
return path.includes("\0");
|
|
5928
6124
|
}
|
|
@@ -5987,7 +6183,7 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
5987
6183
|
api.get("/projects/:id/media/metadata", async (c) => {
|
|
5988
6184
|
const project = await adapter.resolveProject(c.req.param("id"));
|
|
5989
6185
|
if (!project) return c.json({ error: "not found" }, 404);
|
|
5990
|
-
const assetPath =
|
|
6186
|
+
const assetPath = requestedProjectPath(project.dir, c.req.query("path") ?? "");
|
|
5991
6187
|
if (!assetPath) return c.json({ error: "path required" }, 400);
|
|
5992
6188
|
if (containsNullByte(assetPath)) return c.json({ error: "forbidden" }, 403);
|
|
5993
6189
|
if (/^(?:https?:|data:|blob:)/i.test(assetPath)) {
|
|
@@ -6009,7 +6205,7 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
6009
6205
|
const project = await adapter.resolveProject(c.req.param("id"));
|
|
6010
6206
|
if (!project) return c.json({ error: "not found" }, 404);
|
|
6011
6207
|
const body = await c.req.json().catch(() => ({}));
|
|
6012
|
-
const inputAssetPath = body.inputPath ?
|
|
6208
|
+
const inputAssetPath = body.inputPath ? requestedProjectPath(project.dir, body.inputPath) : "";
|
|
6013
6209
|
if (!inputAssetPath) return c.json({ error: "inputPath required" }, 400);
|
|
6014
6210
|
if (containsNullByte(inputAssetPath)) return c.json({ error: "forbidden" }, 403);
|
|
6015
6211
|
if (/^(?:https?:|data:|blob:)/i.test(inputAssetPath)) {
|
|
@@ -6023,7 +6219,7 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
6023
6219
|
if (!inputIsVideo && !inputIsImage) {
|
|
6024
6220
|
return c.json({ error: "background removal supports video or image assets only" }, 400);
|
|
6025
6221
|
}
|
|
6026
|
-
const requestedOutput = body.outputPath ? posix.normalize(
|
|
6222
|
+
const requestedOutput = body.outputPath ? posix.normalize(requestedProjectPath(project.dir, body.outputPath)) : "";
|
|
6027
6223
|
if (requestedOutput && containsNullByte(requestedOutput)) {
|
|
6028
6224
|
return c.json({ error: "forbidden" }, 403);
|
|
6029
6225
|
}
|
|
@@ -6497,6 +6693,7 @@ export {
|
|
|
6497
6693
|
createStudioManualEditsRenderBodyScript,
|
|
6498
6694
|
createStudioMotionRenderBodyScript,
|
|
6499
6695
|
createStudioPositionSeekReapplyScript,
|
|
6696
|
+
decodeWellFormedEscapes,
|
|
6500
6697
|
dedupeClonedCompositionId,
|
|
6501
6698
|
duplicateElementInHtml,
|
|
6502
6699
|
fileContentVersion,
|