@hyperframes/studio-server 0.8.57 → 0.8.59
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-BVSPLTYT.js → chunk-45POWYFS.js} +2 -2
- package/dist/{chunk-QJ73CZFQ.js → chunk-4MNTZVVZ.js} +6 -2
- package/dist/{chunk-QJ73CZFQ.js.map → chunk-4MNTZVVZ.js.map} +1 -1
- package/dist/{chunk-MMVFN2BI.js → chunk-KFPWE4Q7.js} +23 -19
- package/dist/chunk-KFPWE4Q7.js.map +1 -0
- package/dist/{chunk-QN7UPP3B.js → chunk-PGL4S2ZU.js} +3 -3
- package/dist/helpers/mediaCodecMap.js +1 -1
- package/dist/helpers/mediaProxyPreview.js +3 -3
- package/dist/helpers/proxyTranscoder.js +2 -2
- package/dist/helpers/sourceMutation.d.ts +7 -1
- package/dist/helpers/sourceMutation.js +7 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +237 -125
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-MMVFN2BI.js.map +0 -1
- /package/dist/{chunk-BVSPLTYT.js.map → chunk-45POWYFS.js.map} +0 -0
- /package/dist/{chunk-QN7UPP3B.js.map → chunk-PGL4S2ZU.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -3,14 +3,17 @@ import {
|
|
|
3
3
|
findUnsafeMutationValues
|
|
4
4
|
} from "./chunk-X62ASOGO.js";
|
|
5
5
|
import {
|
|
6
|
+
dedupeClonedCompositionId,
|
|
7
|
+
findTargetElement,
|
|
6
8
|
isHTMLElement,
|
|
9
|
+
parseSourceDocument,
|
|
7
10
|
patchElementInHtml,
|
|
8
11
|
probeElementInSource,
|
|
9
12
|
removeElementFromHtml,
|
|
10
13
|
splitElementInHtml,
|
|
11
14
|
unwrapElementsFromHtml,
|
|
12
15
|
wrapElementsInHtml
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-KFPWE4Q7.js";
|
|
14
17
|
import {
|
|
15
18
|
getElementScreenshotClip
|
|
16
19
|
} from "./chunk-W2SBTCO2.js";
|
|
@@ -19,12 +22,12 @@ import {
|
|
|
19
22
|
isAutoProxyEnabled,
|
|
20
23
|
proxyEtagSalt,
|
|
21
24
|
resolvePreviewMediaCodecProbeCache
|
|
22
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-PGL4S2ZU.js";
|
|
23
26
|
import {
|
|
24
27
|
ProxyCapacityError,
|
|
25
28
|
ProxyTranscodeError,
|
|
26
29
|
resolveProxy
|
|
27
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-45POWYFS.js";
|
|
28
31
|
import {
|
|
29
32
|
PROXY_VARIANT_CONFIG,
|
|
30
33
|
decideMediaProxyEligibility,
|
|
@@ -33,7 +36,7 @@ import {
|
|
|
33
36
|
probeMediaMetadata,
|
|
34
37
|
recordProxyRequest,
|
|
35
38
|
resolveProxyVariantRequest
|
|
36
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-4MNTZVVZ.js";
|
|
37
40
|
import {
|
|
38
41
|
STUDIO_MANUAL_EDITS_PATH,
|
|
39
42
|
createStudioManualEditsRenderBodyScript,
|
|
@@ -297,8 +300,8 @@ import {
|
|
|
297
300
|
existsSync as existsSync3,
|
|
298
301
|
ftruncateSync,
|
|
299
302
|
openSync,
|
|
300
|
-
readFileSync as
|
|
301
|
-
writeFileSync as
|
|
303
|
+
readFileSync as readFileSync5,
|
|
304
|
+
writeFileSync as writeFileSync5,
|
|
302
305
|
writeSync,
|
|
303
306
|
mkdirSync as mkdirSync3,
|
|
304
307
|
unlinkSync as unlinkSync2,
|
|
@@ -607,6 +610,13 @@ function recordFileWriteReceipt(absPath, receipt) {
|
|
|
607
610
|
current.push({ ...receipt, recordedAt: now });
|
|
608
611
|
receipts.set(absPath, current);
|
|
609
612
|
}
|
|
613
|
+
function clearFileWriteReceipt(absPath, version, writeToken) {
|
|
614
|
+
const current = (receipts.get(absPath) ?? []).filter(
|
|
615
|
+
(entry) => entry.version !== version || entry.writeToken !== writeToken
|
|
616
|
+
);
|
|
617
|
+
if (current.length > 0) receipts.set(absPath, current);
|
|
618
|
+
else receipts.delete(absPath);
|
|
619
|
+
}
|
|
610
620
|
function identifyFileWrite(absPath, expectedVersion) {
|
|
611
621
|
const now = Date.now();
|
|
612
622
|
const current = (receipts.get(absPath) ?? []).filter(
|
|
@@ -624,6 +634,79 @@ function identifyFileWrite(absPath, expectedVersion) {
|
|
|
624
634
|
}
|
|
625
635
|
var consumeFileWriteReceipt = identifyFileWrite;
|
|
626
636
|
|
|
637
|
+
// src/helpers/applyFileMutations.ts
|
|
638
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
639
|
+
function applyFileMutations(projectDir, mutations, requestToken, writeFile = writeFileSync4) {
|
|
640
|
+
const prepared = mutations.map((mutation) => ({
|
|
641
|
+
...mutation,
|
|
642
|
+
before: mutation.before ?? readFileSync3(mutation.absPath, "utf-8")
|
|
643
|
+
}));
|
|
644
|
+
const results = [];
|
|
645
|
+
const attempted = [];
|
|
646
|
+
try {
|
|
647
|
+
for (const mutation of prepared) {
|
|
648
|
+
results.push(applyOneMutation(projectDir, mutation, requestToken, writeFile, attempted));
|
|
649
|
+
}
|
|
650
|
+
return results;
|
|
651
|
+
} catch (error) {
|
|
652
|
+
const rollbackErrors = [];
|
|
653
|
+
for (const mutation of attempted.reverse()) {
|
|
654
|
+
try {
|
|
655
|
+
writeFile(mutation.absPath, mutation.before, "utf-8");
|
|
656
|
+
clearFileWriteReceipt(mutation.absPath, mutation.version, mutation.writeToken);
|
|
657
|
+
} catch (rollbackError) {
|
|
658
|
+
rollbackErrors.push(rollbackError);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (rollbackErrors.length > 0) {
|
|
662
|
+
throw new AggregateError(
|
|
663
|
+
[error, ...rollbackErrors],
|
|
664
|
+
"File mutation failed and rollback did not complete"
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
throw error;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
function applyOneMutation(projectDir, mutation, requestToken, writeFile, attempted) {
|
|
671
|
+
const current = readFileSync3(mutation.absPath, "utf-8");
|
|
672
|
+
assertExpectedVersion(mutation.expectedVersion, current);
|
|
673
|
+
if (mutation.after === mutation.before) {
|
|
674
|
+
return {
|
|
675
|
+
...mutation,
|
|
676
|
+
before: current,
|
|
677
|
+
changed: false,
|
|
678
|
+
backupPath: null,
|
|
679
|
+
version: fileContentVersion(mutation.before),
|
|
680
|
+
writeToken: null
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
const backup = snapshotBeforeWrite(projectDir, mutation.absPath);
|
|
684
|
+
if (backup.error) throw new Error(`backup failed: ${backup.error}`);
|
|
685
|
+
const before = current;
|
|
686
|
+
const version = fileContentVersion(mutation.after);
|
|
687
|
+
const writeToken = createWriteToken(requestToken);
|
|
688
|
+
attempted.push({ ...mutation, before, version, writeToken });
|
|
689
|
+
writeFile(mutation.absPath, mutation.after, "utf-8");
|
|
690
|
+
recordFileWriteReceipt(mutation.absPath, {
|
|
691
|
+
path: mutation.sourceFile,
|
|
692
|
+
version,
|
|
693
|
+
writeToken
|
|
694
|
+
});
|
|
695
|
+
return {
|
|
696
|
+
...mutation,
|
|
697
|
+
before,
|
|
698
|
+
changed: true,
|
|
699
|
+
backupPath: backupPathForResponse(projectDir, backup.backupPath),
|
|
700
|
+
version,
|
|
701
|
+
writeToken
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
function assertExpectedVersion(expectedVersion, current) {
|
|
705
|
+
if (expectedVersion !== void 0 && fileContentVersion(current) !== expectedVersion) {
|
|
706
|
+
throw new Error("file changed since the timeline was read");
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
627
710
|
// src/routes/files.ts
|
|
628
711
|
import { classifyPropertyGroup } from "@hyperframes/parsers/gsap-constants";
|
|
629
712
|
import { parseGsapScriptAcorn } from "@hyperframes/parsers/gsap-parser-acorn";
|
|
@@ -659,7 +742,7 @@ import {
|
|
|
659
742
|
import { parseHTML as parseHTML2 } from "linkedom";
|
|
660
743
|
|
|
661
744
|
// src/helpers/compositionInsertion.ts
|
|
662
|
-
import { existsSync as existsSync2, readFileSync as
|
|
745
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4, realpathSync } from "fs";
|
|
663
746
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
664
747
|
import { dirname as dirname2, relative as relative3, resolve as resolve2, sep as sep2 } from "path";
|
|
665
748
|
import { parseHTML } from "linkedom";
|
|
@@ -731,7 +814,7 @@ function validateDependencyGraph(projectDir, targetAbs, sourceAbs) {
|
|
|
731
814
|
}
|
|
732
815
|
if (visited.has(file)) return;
|
|
733
816
|
visiting.add(file);
|
|
734
|
-
const source =
|
|
817
|
+
const source = readFileSync4(file, "utf-8");
|
|
735
818
|
const { document } = compositionRoot(source);
|
|
736
819
|
for (const host of descendants(document, "[data-composition-src]")) {
|
|
737
820
|
const dependency = host.getAttribute("data-composition-src");
|
|
@@ -795,7 +878,7 @@ function insertCompositionIntoSource(input) {
|
|
|
795
878
|
const targetAbs = canonicalProjectFile(input.projectDir, input.targetPath);
|
|
796
879
|
const sourceAbs = canonicalProjectFile(input.projectDir, input.sourcePath);
|
|
797
880
|
validateDependencyGraph(input.projectDir, targetAbs, sourceAbs);
|
|
798
|
-
const source =
|
|
881
|
+
const source = readFileSync4(sourceAbs, "utf-8");
|
|
799
882
|
const sourceComposition = compositionRoot(source).root;
|
|
800
883
|
const duration = positiveAttribute(
|
|
801
884
|
sourceComposition,
|
|
@@ -983,7 +1066,7 @@ function foldElementPatches(originalContent, patches) {
|
|
|
983
1066
|
}
|
|
984
1067
|
return { content, matched };
|
|
985
1068
|
}
|
|
986
|
-
function commitElementPatchBatches(projectDir, batches, writeFile =
|
|
1069
|
+
function commitElementPatchBatches(projectDir, batches, writeFile = writeFileSync5, requestToken) {
|
|
987
1070
|
const resolvedPaths = /* @__PURE__ */ new Set();
|
|
988
1071
|
const prepared = [];
|
|
989
1072
|
for (const batch of batches) {
|
|
@@ -993,7 +1076,7 @@ function commitElementPatchBatches(projectDir, batches, writeFile = writeFileSyn
|
|
|
993
1076
|
resolvedPaths.add(absPath);
|
|
994
1077
|
let before;
|
|
995
1078
|
try {
|
|
996
|
-
before =
|
|
1079
|
+
before = readFileSync5(absPath, "utf-8");
|
|
997
1080
|
} catch {
|
|
998
1081
|
return { error: "not-found", sourceFile: batch.sourceFile };
|
|
999
1082
|
}
|
|
@@ -1019,64 +1102,34 @@ function commitElementPatchBatches(projectDir, batches, writeFile = writeFileSyn
|
|
|
1019
1102
|
}))
|
|
1020
1103
|
};
|
|
1021
1104
|
}
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
writeFile(file.absPath, file.after, "utf-8");
|
|
1042
|
-
files.push({
|
|
1043
|
-
sourceFile: file.sourceFile,
|
|
1044
|
-
changed: true,
|
|
1045
|
-
matched: file.matched,
|
|
1046
|
-
before: file.before,
|
|
1047
|
-
after: file.after,
|
|
1048
|
-
backupPath: backupPathForResponse(projectDir, backup.backupPath)
|
|
1049
|
-
});
|
|
1050
|
-
}
|
|
1051
|
-
} catch (error) {
|
|
1052
|
-
const rollbackErrors = [];
|
|
1053
|
-
for (const file of attemptedWrites.reverse()) {
|
|
1054
|
-
try {
|
|
1055
|
-
writeFile(file.absPath, file.before, "utf-8");
|
|
1056
|
-
} catch (rollbackError) {
|
|
1057
|
-
rollbackErrors.push(rollbackError);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
if (rollbackErrors.length > 0) {
|
|
1061
|
-
throw new AggregateError(
|
|
1062
|
-
[error, ...rollbackErrors],
|
|
1063
|
-
"Element patch batch failed and rollback did not complete"
|
|
1064
|
-
);
|
|
1065
|
-
}
|
|
1066
|
-
throw error;
|
|
1067
|
-
}
|
|
1105
|
+
const applied = applyFileMutations(
|
|
1106
|
+
projectDir,
|
|
1107
|
+
prepared.map(({ sourceFile, absPath, before, after }) => ({
|
|
1108
|
+
sourceFile,
|
|
1109
|
+
absPath,
|
|
1110
|
+
before,
|
|
1111
|
+
after
|
|
1112
|
+
})),
|
|
1113
|
+
requestToken,
|
|
1114
|
+
writeFile
|
|
1115
|
+
);
|
|
1116
|
+
const files = applied.map((file, index) => ({
|
|
1117
|
+
sourceFile: file.sourceFile,
|
|
1118
|
+
changed: file.changed,
|
|
1119
|
+
matched: prepared[index]?.matched ?? [],
|
|
1120
|
+
before: file.before,
|
|
1121
|
+
after: file.after,
|
|
1122
|
+
backupPath: file.backupPath ?? void 0
|
|
1123
|
+
}));
|
|
1068
1124
|
return { durable: true, files };
|
|
1069
1125
|
}
|
|
1070
1126
|
function commitElementPatchBatchesWithReceipts(c, projectDir, batches) {
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
recordMutationReceipt(c, file.sourceFile, absPath, file.after);
|
|
1078
|
-
}
|
|
1079
|
-
return result;
|
|
1127
|
+
return commitElementPatchBatches(
|
|
1128
|
+
projectDir,
|
|
1129
|
+
batches,
|
|
1130
|
+
writeFileSync5,
|
|
1131
|
+
c.req.header("X-Hyperframes-Write-Token")
|
|
1132
|
+
);
|
|
1080
1133
|
}
|
|
1081
1134
|
function recordMutationReceipt(c, filePath, absPath, html) {
|
|
1082
1135
|
const version = fileContentVersion(html);
|
|
@@ -1085,7 +1138,7 @@ function recordMutationReceipt(c, filePath, absPath, html) {
|
|
|
1085
1138
|
return { version, writeToken };
|
|
1086
1139
|
}
|
|
1087
1140
|
function writeFileWithReceipt(c, filePath, absPath, html) {
|
|
1088
|
-
|
|
1141
|
+
writeFileSync5(absPath, html, "utf-8");
|
|
1089
1142
|
return recordMutationReceipt(c, filePath, absPath, html);
|
|
1090
1143
|
}
|
|
1091
1144
|
function writeMutationResult(c, projectDir, filePath, absPath, html) {
|
|
@@ -1170,11 +1223,11 @@ function updateReferences(projectDir, oldPath, newPath) {
|
|
|
1170
1223
|
let updatedCount = 0;
|
|
1171
1224
|
for (const file of textFiles) {
|
|
1172
1225
|
if (!isSafePath(projectDir, file)) continue;
|
|
1173
|
-
const content =
|
|
1226
|
+
const content = readFileSync5(file, "utf-8");
|
|
1174
1227
|
if (!content.includes(oldPath)) continue;
|
|
1175
1228
|
const updated = content.split(oldPath).join(newPath);
|
|
1176
1229
|
if (updated !== content) {
|
|
1177
|
-
|
|
1230
|
+
writeFileSync5(file, updated, "utf-8");
|
|
1178
1231
|
updatedCount++;
|
|
1179
1232
|
}
|
|
1180
1233
|
}
|
|
@@ -1444,7 +1497,7 @@ function containsRawGsapExpression(value) {
|
|
|
1444
1497
|
return Object.values(value).some(containsRawGsapExpression);
|
|
1445
1498
|
}
|
|
1446
1499
|
async function prepareGsapMutationScript(c, res, firstMutation) {
|
|
1447
|
-
const beforeHtml =
|
|
1500
|
+
const beforeHtml = readFileSync5(res.absPath, "utf-8");
|
|
1448
1501
|
let html = beforeHtml;
|
|
1449
1502
|
let block = extractGsapScriptBlock(html);
|
|
1450
1503
|
if (!block && (firstMutation.type === "add" || firstMutation.type === "add-with-keyframes")) {
|
|
@@ -1511,7 +1564,7 @@ async function applyGsapMutations(c, res, mutations) {
|
|
|
1511
1564
|
const changed = block.scriptText !== initialScript;
|
|
1512
1565
|
const newHtml = changed ? block.replaceScript(block.scriptText) : html;
|
|
1513
1566
|
let backupPath = null;
|
|
1514
|
-
if (
|
|
1567
|
+
if (readFileSync5(res.absPath, "utf-8") !== beforeHtml) {
|
|
1515
1568
|
return c.json({ error: "file changed during GSAP mutation", conflict: true }, 409);
|
|
1516
1569
|
}
|
|
1517
1570
|
if (changed) {
|
|
@@ -2294,7 +2347,7 @@ async function processUploadedFiles(formData, targetDir, projectDir) {
|
|
|
2294
2347
|
try {
|
|
2295
2348
|
const fd = openSync(finalPath, "wx");
|
|
2296
2349
|
try {
|
|
2297
|
-
|
|
2350
|
+
writeFileSync5(fd, buffer);
|
|
2298
2351
|
} finally {
|
|
2299
2352
|
closeSync(fd);
|
|
2300
2353
|
}
|
|
@@ -2332,7 +2385,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2332
2385
|
}
|
|
2333
2386
|
return c.json({ error: "not found" }, 404);
|
|
2334
2387
|
}
|
|
2335
|
-
const content =
|
|
2388
|
+
const content = readFileSync5(res.absPath);
|
|
2336
2389
|
const version = fileContentVersion(content);
|
|
2337
2390
|
c.header("ETag", version);
|
|
2338
2391
|
return c.json({ filename: res.filePath, content: content.toString("utf-8"), version });
|
|
@@ -2346,7 +2399,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2346
2399
|
if (expectedVersion === null && !createOnly) {
|
|
2347
2400
|
let currentContent = null;
|
|
2348
2401
|
try {
|
|
2349
|
-
currentContent =
|
|
2402
|
+
currentContent = readFileSync5(res.absPath);
|
|
2350
2403
|
} catch (error) {
|
|
2351
2404
|
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
|
|
2352
2405
|
throw error;
|
|
@@ -2372,7 +2425,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2372
2425
|
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") {
|
|
2373
2426
|
throw error;
|
|
2374
2427
|
}
|
|
2375
|
-
const currentContent =
|
|
2428
|
+
const currentContent = readFileSync5(res.absPath);
|
|
2376
2429
|
return c.json(
|
|
2377
2430
|
{
|
|
2378
2431
|
error: "file conflict",
|
|
@@ -2407,7 +2460,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2407
2460
|
);
|
|
2408
2461
|
}
|
|
2409
2462
|
try {
|
|
2410
|
-
const currentContent =
|
|
2463
|
+
const currentContent = readFileSync5(fd);
|
|
2411
2464
|
const currentVersion = fileContentVersion(currentContent);
|
|
2412
2465
|
if (expectedVersion !== currentVersion) {
|
|
2413
2466
|
return c.json(
|
|
@@ -2446,7 +2499,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2446
2499
|
ensureDir(res.absPath);
|
|
2447
2500
|
const body = Buffer.from(await c.req.arrayBuffer());
|
|
2448
2501
|
try {
|
|
2449
|
-
|
|
2502
|
+
writeFileSync5(res.absPath, body, { flag: "wx" });
|
|
2450
2503
|
} catch (error) {
|
|
2451
2504
|
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") {
|
|
2452
2505
|
throw error;
|
|
@@ -2480,7 +2533,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2480
2533
|
}
|
|
2481
2534
|
let before;
|
|
2482
2535
|
try {
|
|
2483
|
-
before =
|
|
2536
|
+
before = readFileSync5(ctx.absPath, "utf-8");
|
|
2484
2537
|
} catch (error) {
|
|
2485
2538
|
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
|
|
2486
2539
|
throw error;
|
|
@@ -2537,7 +2590,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2537
2590
|
}
|
|
2538
2591
|
const parsed = await parseMutationBody(c);
|
|
2539
2592
|
if ("error" in parsed) return parsed.error;
|
|
2540
|
-
const originalContent =
|
|
2593
|
+
const originalContent = readFileSync5(ctx.absPath, "utf-8");
|
|
2541
2594
|
return writeIfChanged(
|
|
2542
2595
|
c,
|
|
2543
2596
|
ctx.project.dir,
|
|
@@ -2558,7 +2611,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2558
2611
|
if (!Array.isArray(targets) || targets.length === 0) {
|
|
2559
2612
|
return c.json({ error: "targets required" }, 400);
|
|
2560
2613
|
}
|
|
2561
|
-
const originalContent =
|
|
2614
|
+
const originalContent = readFileSync5(ctx.absPath, "utf-8");
|
|
2562
2615
|
let next = originalContent;
|
|
2563
2616
|
for (const target of targets) {
|
|
2564
2617
|
next = removeElementFromHtml(next, target);
|
|
@@ -2591,7 +2644,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2591
2644
|
seen.add(absPath);
|
|
2592
2645
|
let before;
|
|
2593
2646
|
try {
|
|
2594
|
-
before =
|
|
2647
|
+
before = readFileSync5(absPath, "utf-8");
|
|
2595
2648
|
} catch {
|
|
2596
2649
|
return c.json({ error: `not found: ${file.path}` }, 404);
|
|
2597
2650
|
}
|
|
@@ -2618,7 +2671,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2618
2671
|
prepared.push(folded);
|
|
2619
2672
|
}
|
|
2620
2673
|
for (const file of prepared) {
|
|
2621
|
-
const current =
|
|
2674
|
+
const current = readFileSync5(file.absPath, "utf-8");
|
|
2622
2675
|
if (current !== file.before) {
|
|
2623
2676
|
return c.json(
|
|
2624
2677
|
{
|
|
@@ -2648,7 +2701,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2648
2701
|
const written = [];
|
|
2649
2702
|
try {
|
|
2650
2703
|
for (const file of prepared) {
|
|
2651
|
-
|
|
2704
|
+
writeFileSync5(file.absPath, file.after, "utf-8");
|
|
2652
2705
|
written.push(file);
|
|
2653
2706
|
recordFileWriteReceipt(file.absPath, {
|
|
2654
2707
|
path: file.path,
|
|
@@ -2660,12 +2713,12 @@ function registerFileRoutes(api, adapter) {
|
|
|
2660
2713
|
const conflicts = [];
|
|
2661
2714
|
for (const file of written.reverse()) {
|
|
2662
2715
|
try {
|
|
2663
|
-
const current =
|
|
2716
|
+
const current = readFileSync5(file.absPath, "utf-8");
|
|
2664
2717
|
if (current !== file.after) {
|
|
2665
2718
|
conflicts.push(file.path);
|
|
2666
2719
|
continue;
|
|
2667
2720
|
}
|
|
2668
|
-
|
|
2721
|
+
writeFileSync5(file.absPath, file.before, "utf-8");
|
|
2669
2722
|
recordFileWriteReceipt(file.absPath, {
|
|
2670
2723
|
path: file.path,
|
|
2671
2724
|
version: fileContentVersion(file.before),
|
|
@@ -2708,7 +2761,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2708
2761
|
const fallbackTiming = typeof parsed.body.elementStart === "number" && typeof parsed.body.elementDuration === "number" ? { start: parsed.body.elementStart, duration: parsed.body.elementDuration } : void 0;
|
|
2709
2762
|
let originalContent;
|
|
2710
2763
|
try {
|
|
2711
|
-
originalContent =
|
|
2764
|
+
originalContent = readFileSync5(ctx.absPath, "utf-8");
|
|
2712
2765
|
} catch {
|
|
2713
2766
|
return c.json({ error: "not found" }, 404);
|
|
2714
2767
|
}
|
|
@@ -2764,7 +2817,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2764
2817
|
}
|
|
2765
2818
|
let originalContent;
|
|
2766
2819
|
try {
|
|
2767
|
-
originalContent =
|
|
2820
|
+
originalContent = readFileSync5(ctx.absPath, "utf-8");
|
|
2768
2821
|
} catch {
|
|
2769
2822
|
return c.json({ error: "not found" }, 404);
|
|
2770
2823
|
}
|
|
@@ -2865,7 +2918,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2865
2918
|
}
|
|
2866
2919
|
let originalContent;
|
|
2867
2920
|
try {
|
|
2868
|
-
originalContent =
|
|
2921
|
+
originalContent = readFileSync5(ctx.absPath, "utf-8");
|
|
2869
2922
|
} catch {
|
|
2870
2923
|
return c.json({ error: "not found" }, 404);
|
|
2871
2924
|
}
|
|
@@ -2920,7 +2973,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2920
2973
|
).map((entry) => ({ target: entry.target, track: entry.track }));
|
|
2921
2974
|
let originalContent;
|
|
2922
2975
|
try {
|
|
2923
|
-
originalContent =
|
|
2976
|
+
originalContent = readFileSync5(ctx.absPath, "utf-8");
|
|
2924
2977
|
} catch {
|
|
2925
2978
|
return c.json({ error: "not found" }, 404);
|
|
2926
2979
|
}
|
|
@@ -2949,7 +3002,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2949
3002
|
if ("error" in parsed) return parsed.error;
|
|
2950
3003
|
let content;
|
|
2951
3004
|
try {
|
|
2952
|
-
content =
|
|
3005
|
+
content = readFileSync5(ctx.absPath, "utf-8");
|
|
2953
3006
|
} catch {
|
|
2954
3007
|
return c.json({ exists: false });
|
|
2955
3008
|
}
|
|
@@ -2992,7 +3045,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2992
3045
|
return c.json({ error: "forbidden" }, 403);
|
|
2993
3046
|
}
|
|
2994
3047
|
ensureDir(destAbs);
|
|
2995
|
-
|
|
3048
|
+
writeFileSync5(destAbs, readFileSync5(srcAbs));
|
|
2996
3049
|
return c.json({ ok: true, path: copyPath }, 201);
|
|
2997
3050
|
});
|
|
2998
3051
|
const MAX_UPLOAD_BYTES = 500 * 1024 * 1024;
|
|
@@ -3022,7 +3075,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3022
3075
|
mustExist: true
|
|
3023
3076
|
});
|
|
3024
3077
|
if ("error" in res) return res.error;
|
|
3025
|
-
const html =
|
|
3078
|
+
const html = readFileSync5(res.absPath, "utf-8");
|
|
3026
3079
|
const block = extractGsapScriptBlock(html);
|
|
3027
3080
|
if (!block) {
|
|
3028
3081
|
return c.json({
|
|
@@ -3081,17 +3134,17 @@ function registerFileRoutes(api, adapter) {
|
|
|
3081
3134
|
if (!body || typeof body.expected !== "string" || typeof body.restore !== "string") {
|
|
3082
3135
|
return c.json({ error: "expected and restore contents required" }, 400);
|
|
3083
3136
|
}
|
|
3084
|
-
const current =
|
|
3137
|
+
const current = readFileSync5(res.absPath, "utf-8");
|
|
3085
3138
|
if (current !== body.expected) {
|
|
3086
3139
|
return c.json({ ok: true, restored: false, conflict: true });
|
|
3087
3140
|
}
|
|
3088
|
-
|
|
3141
|
+
writeFileSync5(res.absPath, body.restore, "utf-8");
|
|
3089
3142
|
return c.json({ ok: true, restored: true, conflict: false });
|
|
3090
3143
|
});
|
|
3091
3144
|
}
|
|
3092
3145
|
|
|
3093
3146
|
// src/routes/preview.ts
|
|
3094
|
-
import { createReadStream, existsSync as existsSync5, readFileSync as
|
|
3147
|
+
import { createReadStream, existsSync as existsSync5, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
|
|
3095
3148
|
import { Readable } from "stream";
|
|
3096
3149
|
import { join as join8, resolve as resolve4 } from "path";
|
|
3097
3150
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -3099,7 +3152,7 @@ import { injectScriptsIntoHtml, stripEmbeddedRuntimeScripts as stripEmbeddedRunt
|
|
|
3099
3152
|
import { isWithinProjectRoot } from "@hyperframes/parsers/asset-resolution";
|
|
3100
3153
|
|
|
3101
3154
|
// src/helpers/subComposition.ts
|
|
3102
|
-
import { existsSync as existsSync4, readFileSync as
|
|
3155
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
|
|
3103
3156
|
import { join as join7 } from "path";
|
|
3104
3157
|
import { parseHTML as parseHTML3 } from "linkedom";
|
|
3105
3158
|
import {
|
|
@@ -3211,7 +3264,7 @@ function tagRootCompositionFile(bodyHtml, compPath) {
|
|
|
3211
3264
|
function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, rawOverride) {
|
|
3212
3265
|
const compFile = join7(projectDir, compPath);
|
|
3213
3266
|
if (!existsSync4(compFile)) return null;
|
|
3214
|
-
const rawComp = rawOverride ??
|
|
3267
|
+
const rawComp = rawOverride ?? readFileSync6(compFile, "utf-8");
|
|
3215
3268
|
let compHeadContent = "";
|
|
3216
3269
|
let rewrittenContent;
|
|
3217
3270
|
let htmlAttrs = "";
|
|
@@ -3244,7 +3297,7 @@ function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, raw
|
|
|
3244
3297
|
const indexPath = join7(projectDir, "index.html");
|
|
3245
3298
|
let headContent = "";
|
|
3246
3299
|
if (existsSync4(indexPath)) {
|
|
3247
|
-
const indexHtml =
|
|
3300
|
+
const indexHtml = readFileSync6(indexPath, "utf-8");
|
|
3248
3301
|
const headMatch = indexHtml.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
|
|
3249
3302
|
headContent = headMatch?.[1] ?? "";
|
|
3250
3303
|
}
|
|
@@ -3288,8 +3341,8 @@ import {
|
|
|
3288
3341
|
fstatSync,
|
|
3289
3342
|
ftruncateSync as ftruncateSync2,
|
|
3290
3343
|
openSync as openSync2,
|
|
3291
|
-
readFileSync as
|
|
3292
|
-
writeFileSync as
|
|
3344
|
+
readFileSync as readFileSync7,
|
|
3345
|
+
writeFileSync as writeFileSync6,
|
|
3293
3346
|
writeSync as writeSync2
|
|
3294
3347
|
} from "fs";
|
|
3295
3348
|
function persistHfIdsIfNeeded(filePath, html) {
|
|
@@ -3298,9 +3351,9 @@ function persistHfIdsIfNeeded(filePath, html) {
|
|
|
3298
3351
|
const idsAfter = (normalized.match(/\bdata-hf-id=/g) ?? []).length;
|
|
3299
3352
|
if (idsAfter > idsBefore) {
|
|
3300
3353
|
try {
|
|
3301
|
-
const current =
|
|
3354
|
+
const current = readFileSync7(filePath, "utf-8");
|
|
3302
3355
|
if (current === html) {
|
|
3303
|
-
|
|
3356
|
+
writeFileSync6(filePath, normalized, "utf-8");
|
|
3304
3357
|
}
|
|
3305
3358
|
} catch (err) {
|
|
3306
3359
|
console.warn("[hyperframes] persistHfIdsIfNeeded: failed to write ids to disk:", err);
|
|
@@ -3326,7 +3379,7 @@ function stampFileHfIds(filePath) {
|
|
|
3326
3379
|
if (fd === null) return null;
|
|
3327
3380
|
try {
|
|
3328
3381
|
if (!fstatSync(fd).isFile()) return null;
|
|
3329
|
-
const html =
|
|
3382
|
+
const html = readFileSync7(fd, "utf-8");
|
|
3330
3383
|
const normalized = ensureHfIds(html);
|
|
3331
3384
|
const idsBefore = (html.match(/\bdata-hf-id=/g) ?? []).length;
|
|
3332
3385
|
const idsAfter = (normalized.match(/\bdata-hf-id=/g) ?? []).length;
|
|
@@ -3388,7 +3441,7 @@ function readStudioMotionManifestContent(projectDir) {
|
|
|
3388
3441
|
const manifestPath = join8(projectDir, STUDIO_MOTION_PATH);
|
|
3389
3442
|
if (!existsSync5(manifestPath)) return "";
|
|
3390
3443
|
try {
|
|
3391
|
-
return
|
|
3444
|
+
return readFileSync8(manifestPath, "utf-8");
|
|
3392
3445
|
} catch {
|
|
3393
3446
|
return "";
|
|
3394
3447
|
}
|
|
@@ -3534,14 +3587,14 @@ function resolveProjectMainHtml(projectDir, projectId) {
|
|
|
3534
3587
|
const indexPath = join8(projectDir, "index.html");
|
|
3535
3588
|
if (existsSync5(indexPath)) {
|
|
3536
3589
|
return {
|
|
3537
|
-
html:
|
|
3590
|
+
html: readFileSync8(indexPath, "utf-8"),
|
|
3538
3591
|
compositionPath: "index.html"
|
|
3539
3592
|
};
|
|
3540
3593
|
}
|
|
3541
3594
|
const blockHtmlPath = join8(projectDir, `${projectId}.html`);
|
|
3542
3595
|
if (existsSync5(blockHtmlPath)) {
|
|
3543
3596
|
return {
|
|
3544
|
-
html:
|
|
3597
|
+
html: readFileSync8(blockHtmlPath, "utf-8"),
|
|
3545
3598
|
compositionPath: `${projectId}.html`
|
|
3546
3599
|
};
|
|
3547
3600
|
}
|
|
@@ -3735,7 +3788,7 @@ ${runtimeTag}`;
|
|
|
3735
3788
|
}
|
|
3736
3789
|
servedContentType = PROXY_VARIANT_CONFIG[proxyVariant].contentType;
|
|
3737
3790
|
}
|
|
3738
|
-
const textBuffer = isText ? Buffer.from(
|
|
3791
|
+
const textBuffer = isText ? Buffer.from(readFileSync8(file, "utf-8"), "utf-8") : null;
|
|
3739
3792
|
const totalSize = textBuffer ? textBuffer.length : statSync3(servedPath).size;
|
|
3740
3793
|
const bodyFor = (start, end) => textBuffer ? new Uint8Array(textBuffer.subarray(start, end + 1)) : (
|
|
3741
3794
|
// Node's web stream type and the DOM one do not overlap for tsc on
|
|
@@ -3779,7 +3832,7 @@ ${runtimeTag}`;
|
|
|
3779
3832
|
}
|
|
3780
3833
|
|
|
3781
3834
|
// src/routes/lint.ts
|
|
3782
|
-
import { readFileSync as
|
|
3835
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
3783
3836
|
import { join as join9 } from "path";
|
|
3784
3837
|
async function collectProjectFindings(adapter, projectDir) {
|
|
3785
3838
|
const findings = [];
|
|
@@ -3798,7 +3851,7 @@ async function lintUncoveredHtml(adapter, projectDir, htmlFiles, coveredFiles) {
|
|
|
3798
3851
|
const findings = [];
|
|
3799
3852
|
for (const file of htmlFiles) {
|
|
3800
3853
|
if (coveredFiles.has(file)) continue;
|
|
3801
|
-
const content =
|
|
3854
|
+
const content = readFileSync9(join9(projectDir, file), "utf-8");
|
|
3802
3855
|
const result = await adapter.lint(content, { filePath: file, isSubComposition: true });
|
|
3803
3856
|
for (const finding of result?.findings ?? []) {
|
|
3804
3857
|
findings.push({ ...finding, file });
|
|
@@ -3831,7 +3884,7 @@ function registerLintRoutes(api, adapter) {
|
|
|
3831
3884
|
|
|
3832
3885
|
// src/routes/render.ts
|
|
3833
3886
|
import { streamSSE } from "hono/streaming";
|
|
3834
|
-
import { existsSync as existsSync6, readFileSync as
|
|
3887
|
+
import { existsSync as existsSync6, readFileSync as readFileSync10, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
3835
3888
|
import { join as join10 } from "path";
|
|
3836
3889
|
import { VALID_CANVAS_RESOLUTIONS } from "@hyperframes/parsers";
|
|
3837
3890
|
import { formatRenderOutputTimestamp, parseFps } from "@hyperframes/core";
|
|
@@ -3969,7 +4022,7 @@ function registerRenderRoutes(api, adapter) {
|
|
|
3969
4022
|
}
|
|
3970
4023
|
const contentType = renderContentType(job.outputPath);
|
|
3971
4024
|
const filename = job.outputPath.split("/").pop() ?? `render.mp4`;
|
|
3972
|
-
const content =
|
|
4025
|
+
const content = readFileSync10(job.outputPath);
|
|
3973
4026
|
return new Response(content, {
|
|
3974
4027
|
headers: {
|
|
3975
4028
|
"Content-Type": contentType,
|
|
@@ -3987,7 +4040,7 @@ function registerRenderRoutes(api, adapter) {
|
|
|
3987
4040
|
}
|
|
3988
4041
|
const contentType = renderContentType(job.outputPath);
|
|
3989
4042
|
const filename = job.outputPath.split("/").pop() ?? `render.mp4`;
|
|
3990
|
-
const content =
|
|
4043
|
+
const content = readFileSync10(job.outputPath);
|
|
3991
4044
|
return new Response(content, {
|
|
3992
4045
|
headers: {
|
|
3993
4046
|
"Content-Type": contentType,
|
|
@@ -4020,7 +4073,7 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4020
4073
|
if (!fp) return c.json({ error: "forbidden" }, 403);
|
|
4021
4074
|
if (!existsSync6(fp)) return c.json({ error: "not found" }, 404);
|
|
4022
4075
|
const contentType = renderContentType(fp);
|
|
4023
|
-
const content =
|
|
4076
|
+
const content = readFileSync10(fp);
|
|
4024
4077
|
return new Response(content, {
|
|
4025
4078
|
headers: {
|
|
4026
4079
|
"Content-Type": contentType,
|
|
@@ -4044,7 +4097,7 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4044
4097
|
let durationMs;
|
|
4045
4098
|
if (existsSync6(metaPath)) {
|
|
4046
4099
|
try {
|
|
4047
|
-
const meta = JSON.parse(
|
|
4100
|
+
const meta = JSON.parse(readFileSync10(metaPath, "utf-8"));
|
|
4048
4101
|
if (meta.status === "failed" && !existsSync6(fp)) status = "failed";
|
|
4049
4102
|
if (meta.durationMs) durationMs = meta.durationMs;
|
|
4050
4103
|
} catch {
|
|
@@ -4078,13 +4131,13 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4078
4131
|
import {
|
|
4079
4132
|
existsSync as existsSync7,
|
|
4080
4133
|
mkdirSync as mkdirSync5,
|
|
4081
|
-
readFileSync as
|
|
4134
|
+
readFileSync as readFileSync11,
|
|
4082
4135
|
readdirSync as readdirSync6,
|
|
4083
4136
|
renameSync as renameSync3,
|
|
4084
4137
|
rmSync as rmSync4,
|
|
4085
4138
|
statSync as statSync5,
|
|
4086
4139
|
unlinkSync as unlinkSync4,
|
|
4087
|
-
writeFileSync as
|
|
4140
|
+
writeFileSync as writeFileSync7
|
|
4088
4141
|
} from "fs";
|
|
4089
4142
|
import { join as join11 } from "path";
|
|
4090
4143
|
import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
|
|
@@ -4233,7 +4286,7 @@ function pruneThumbnailCache(cacheDir, protectedPaths, now = Date.now()) {
|
|
|
4233
4286
|
function writeThumbnailAtomically(path, buffer) {
|
|
4234
4287
|
const temporaryPath = `${path}.${process.pid}.${randomUUID3()}.tmp`;
|
|
4235
4288
|
try {
|
|
4236
|
-
|
|
4289
|
+
writeFileSync7(temporaryPath, buffer, { flag: "wx" });
|
|
4237
4290
|
renameSync3(temporaryPath, path);
|
|
4238
4291
|
} finally {
|
|
4239
4292
|
rmSync4(temporaryPath, { force: true });
|
|
@@ -4271,7 +4324,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4271
4324
|
let sourceKey = "";
|
|
4272
4325
|
const htmlFile = join11(project.dir, compPath);
|
|
4273
4326
|
if (existsSync7(htmlFile)) {
|
|
4274
|
-
const html =
|
|
4327
|
+
const html = readFileSync11(htmlFile, "utf-8");
|
|
4275
4328
|
sourceKey = `_${createHash4("sha1").update(html).digest("hex").slice(0, 16)}`;
|
|
4276
4329
|
sourceMtime = Math.round(statSync5(htmlFile).mtimeMs);
|
|
4277
4330
|
if (!vpWidth) {
|
|
@@ -4284,14 +4337,14 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4284
4337
|
const manualEditsFile = join11(project.dir, STUDIO_MANUAL_EDITS_PATH);
|
|
4285
4338
|
let manualEditsKey = "";
|
|
4286
4339
|
if (existsSync7(manualEditsFile)) {
|
|
4287
|
-
const manualEditsContent =
|
|
4340
|
+
const manualEditsContent = readFileSync11(manualEditsFile, "utf-8");
|
|
4288
4341
|
manualEditsKey = `_${createHash4("sha1").update(manualEditsContent).digest("hex").slice(0, 16)}`;
|
|
4289
4342
|
sourceMtime = Math.max(sourceMtime, Math.round(statSync5(manualEditsFile).mtimeMs));
|
|
4290
4343
|
}
|
|
4291
4344
|
const motionFile = join11(project.dir, STUDIO_MOTION_PATH);
|
|
4292
4345
|
let motionKey = "";
|
|
4293
4346
|
if (existsSync7(motionFile)) {
|
|
4294
|
-
const motionContent =
|
|
4347
|
+
const motionContent = readFileSync11(motionFile, "utf-8");
|
|
4295
4348
|
motionKey = `_${createHash4("sha1").update(motionContent).digest("hex").slice(0, 16)}`;
|
|
4296
4349
|
sourceMtime = Math.max(sourceMtime, Math.round(statSync5(motionFile).mtimeMs));
|
|
4297
4350
|
}
|
|
@@ -4313,7 +4366,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4313
4366
|
);
|
|
4314
4367
|
}
|
|
4315
4368
|
if (existsSync7(cachePath)) {
|
|
4316
|
-
return new Response(new Uint8Array(
|
|
4369
|
+
return new Response(new Uint8Array(readFileSync11(cachePath)), {
|
|
4317
4370
|
headers: { "Content-Type": contentType, "Cache-Control": "no-cache" }
|
|
4318
4371
|
});
|
|
4319
4372
|
}
|
|
@@ -4368,7 +4421,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4368
4421
|
}
|
|
4369
4422
|
|
|
4370
4423
|
// src/routes/waveform.ts
|
|
4371
|
-
import { existsSync as existsSync8, readFileSync as
|
|
4424
|
+
import { existsSync as existsSync8, readFileSync as readFileSync12, statSync as statSync6 } from "fs";
|
|
4372
4425
|
import { join as join12 } from "path";
|
|
4373
4426
|
function registerWaveformRoutes(api, adapter) {
|
|
4374
4427
|
api.get("/projects/:id/waveform/*", async (c) => {
|
|
@@ -4384,7 +4437,7 @@ function registerWaveformRoutes(api, adapter) {
|
|
|
4384
4437
|
const cachePath = join12(cacheDir, buildWaveformCacheKey(assetPath, stats));
|
|
4385
4438
|
try {
|
|
4386
4439
|
if (isWaveformCacheDirectory(cacheDir) && existsSync8(cachePath)) {
|
|
4387
|
-
const peaks2 = JSON.parse(
|
|
4440
|
+
const peaks2 = JSON.parse(readFileSync12(cachePath, "utf-8"));
|
|
4388
4441
|
return c.json({ peaks: peaks2 });
|
|
4389
4442
|
}
|
|
4390
4443
|
} catch {
|
|
@@ -4870,14 +4923,14 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
4870
4923
|
}
|
|
4871
4924
|
|
|
4872
4925
|
// src/routes/globalAssets.ts
|
|
4873
|
-
import { existsSync as existsSync10, readFileSync as
|
|
4926
|
+
import { existsSync as existsSync10, readFileSync as readFileSync13 } from "fs";
|
|
4874
4927
|
import { homedir } from "os";
|
|
4875
4928
|
import { join as join14 } from "path";
|
|
4876
4929
|
function readGlobalAssets(home = homedir()) {
|
|
4877
4930
|
const manifestPath = join14(home, ".media", "manifest.jsonl");
|
|
4878
4931
|
if (!existsSync10(manifestPath)) return [];
|
|
4879
4932
|
const out = [];
|
|
4880
|
-
for (const line of
|
|
4933
|
+
for (const line of readFileSync13(manifestPath, "utf8").split("\n")) {
|
|
4881
4934
|
if (!line.trim()) continue;
|
|
4882
4935
|
try {
|
|
4883
4936
|
const rec = JSON.parse(line);
|
|
@@ -4912,6 +4965,57 @@ function createStudioApi(adapter) {
|
|
|
4912
4965
|
return api;
|
|
4913
4966
|
}
|
|
4914
4967
|
|
|
4968
|
+
// src/helpers/duplicateElement.ts
|
|
4969
|
+
import { ensureHfIds as ensureHfIds3 } from "@hyperframes/parsers/hf-ids";
|
|
4970
|
+
function duplicateElementInHtml(source, target, newId, at) {
|
|
4971
|
+
const { document, wrappedFragment } = parseSourceDocument(source);
|
|
4972
|
+
const element = findTargetElement(document, target);
|
|
4973
|
+
if (!element || !element.parentElement) return { html: source, matched: false, newId: null };
|
|
4974
|
+
const duration = numericAttribute(element, "data-duration");
|
|
4975
|
+
const track = numericAttribute(element, "data-track-index") ?? 0;
|
|
4976
|
+
if (duration === null || numericAttribute(element, "data-start") === null) {
|
|
4977
|
+
return { html: source, matched: false, newId: null };
|
|
4978
|
+
}
|
|
4979
|
+
const uniqueId = nextUniqueId(document, newId);
|
|
4980
|
+
rippleElements(document, element, at, duration, track);
|
|
4981
|
+
const clone = element.cloneNode(true);
|
|
4982
|
+
if (!isHTMLElement(clone)) return { html: source, matched: false, newId: null };
|
|
4983
|
+
clone.setAttribute("id", uniqueId);
|
|
4984
|
+
dedupeClonedCompositionId(document, clone);
|
|
4985
|
+
stripStableIds(clone);
|
|
4986
|
+
clone.setAttribute("data-start", String(at));
|
|
4987
|
+
element.parentElement.insertBefore(clone, element.nextSibling);
|
|
4988
|
+
const html = wrappedFragment ? document.body.innerHTML || "" : document.toString();
|
|
4989
|
+
return { html: ensureHfIds3(html), matched: true, newId: uniqueId };
|
|
4990
|
+
}
|
|
4991
|
+
function nextUniqueId(document, newId) {
|
|
4992
|
+
let uniqueId = newId;
|
|
4993
|
+
let suffix = 2;
|
|
4994
|
+
while (document.getElementById(uniqueId)) uniqueId = `${newId}-${suffix++}`;
|
|
4995
|
+
return uniqueId;
|
|
4996
|
+
}
|
|
4997
|
+
function rippleElements(document, element, at, duration, track) {
|
|
4998
|
+
for (const candidate of Array.from(document.querySelectorAll("[data-start][data-duration]"))) {
|
|
4999
|
+
if (candidate === element || candidate.getAttribute("data-track-index") !== String(track)) {
|
|
5000
|
+
continue;
|
|
5001
|
+
}
|
|
5002
|
+
const start = numericAttribute(candidate, "data-start");
|
|
5003
|
+
if (start !== null && start >= at) {
|
|
5004
|
+
candidate.setAttribute("data-start", String(start + duration));
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
5007
|
+
}
|
|
5008
|
+
function stripStableIds(element) {
|
|
5009
|
+
element.removeAttribute("data-hf-id");
|
|
5010
|
+
for (const child of Array.from(element.querySelectorAll("[data-hf-id]"))) {
|
|
5011
|
+
child.removeAttribute("data-hf-id");
|
|
5012
|
+
}
|
|
5013
|
+
}
|
|
5014
|
+
function numericAttribute(element, name) {
|
|
5015
|
+
const value = Number(element.getAttribute(name));
|
|
5016
|
+
return Number.isFinite(value) ? value : null;
|
|
5017
|
+
}
|
|
5018
|
+
|
|
4915
5019
|
// src/helpers/thumbnailOutput.ts
|
|
4916
5020
|
function thumbnailDeviceScaleFactor({
|
|
4917
5021
|
width,
|
|
@@ -4984,6 +5088,7 @@ export {
|
|
|
4984
5088
|
STUDIO_MANUAL_EDITS_PATH,
|
|
4985
5089
|
STUDIO_MOTION_PATH,
|
|
4986
5090
|
affectsProjectSignature,
|
|
5091
|
+
applyFileMutations,
|
|
4987
5092
|
buildSubCompositionHtml,
|
|
4988
5093
|
consumeFileWriteReceipt,
|
|
4989
5094
|
createBackgroundRemovalJob,
|
|
@@ -4992,11 +5097,18 @@ export {
|
|
|
4992
5097
|
createStudioManualEditsRenderBodyScript,
|
|
4993
5098
|
createStudioMotionRenderBodyScript,
|
|
4994
5099
|
createStudioPositionSeekReapplyScript,
|
|
5100
|
+
dedupeClonedCompositionId,
|
|
5101
|
+
duplicateElementInHtml,
|
|
4995
5102
|
fileContentVersion,
|
|
5103
|
+
findTargetElement,
|
|
4996
5104
|
getElementScreenshotClip,
|
|
4997
5105
|
getMimeType,
|
|
4998
5106
|
identifyFileWrite,
|
|
4999
5107
|
isSafePath,
|
|
5108
|
+
parseSourceDocument,
|
|
5109
|
+
patchElementInHtml,
|
|
5110
|
+
removeElementFromHtml,
|
|
5111
|
+
splitElementInHtml,
|
|
5000
5112
|
thumbnailDeviceScaleFactor,
|
|
5001
5113
|
walkDir
|
|
5002
5114
|
};
|