@hyperframes/studio-server 0.8.50 → 0.8.53-desktop.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +103 -172
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -290,77 +290,14 @@ function registerProjectRoutes(api, adapter) {
|
|
|
290
290
|
});
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
// src/routes/storyboard.ts
|
|
294
|
-
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
295
|
-
import {
|
|
296
|
-
parseStoryboard,
|
|
297
|
-
SCRIPT_FILENAME,
|
|
298
|
-
STORYBOARD_FILENAME
|
|
299
|
-
} from "@hyperframes/core/storyboard";
|
|
300
|
-
function resolveFrames(projectDir, frames) {
|
|
301
|
-
return frames.map((frame) => {
|
|
302
|
-
let srcExists = false;
|
|
303
|
-
if (frame.src) {
|
|
304
|
-
const abs = resolveWithinProject(projectDir, frame.src);
|
|
305
|
-
srcExists = abs ? existsSync(abs) : false;
|
|
306
|
-
}
|
|
307
|
-
return { ...frame, srcExists };
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
function readScript(projectDir) {
|
|
311
|
-
const abs = resolveWithinProject(projectDir, SCRIPT_FILENAME);
|
|
312
|
-
if (abs && existsSync(abs)) {
|
|
313
|
-
try {
|
|
314
|
-
return { exists: true, path: SCRIPT_FILENAME, content: readFileSync2(abs, "utf-8") };
|
|
315
|
-
} catch {
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return { exists: false, path: SCRIPT_FILENAME, content: "" };
|
|
319
|
-
}
|
|
320
|
-
function registerStoryboardRoutes(api, adapter) {
|
|
321
|
-
api.get("/projects/:id/storyboard", async (c) => {
|
|
322
|
-
const resolved = await resolveProjectAndSignature(adapter, c.req.param("id"));
|
|
323
|
-
if (!resolved) return c.json({ error: "not found" }, 404);
|
|
324
|
-
const { project, signature } = resolved;
|
|
325
|
-
const abs = resolveWithinProject(project.dir, STORYBOARD_FILENAME);
|
|
326
|
-
if (!abs || !existsSync(abs)) {
|
|
327
|
-
return c.json({
|
|
328
|
-
exists: false,
|
|
329
|
-
path: STORYBOARD_FILENAME,
|
|
330
|
-
globals: { extra: {} },
|
|
331
|
-
frames: [],
|
|
332
|
-
warnings: [],
|
|
333
|
-
script: readScript(project.dir),
|
|
334
|
-
signature
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
let source;
|
|
338
|
-
try {
|
|
339
|
-
source = readFileSync2(abs, "utf-8");
|
|
340
|
-
} catch {
|
|
341
|
-
return c.json({ error: "failed to read storyboard" }, 500);
|
|
342
|
-
}
|
|
343
|
-
const manifest = parseStoryboard(source);
|
|
344
|
-
return c.json({
|
|
345
|
-
exists: true,
|
|
346
|
-
path: STORYBOARD_FILENAME,
|
|
347
|
-
globals: manifest.globals,
|
|
348
|
-
frames: resolveFrames(project.dir, manifest.frames),
|
|
349
|
-
warnings: manifest.warnings,
|
|
350
|
-
script: readScript(project.dir),
|
|
351
|
-
signature
|
|
352
|
-
});
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
|
|
356
293
|
// src/routes/files.ts
|
|
357
294
|
import { bodyLimit } from "hono/body-limit";
|
|
358
295
|
import {
|
|
359
296
|
closeSync,
|
|
360
|
-
existsSync as
|
|
297
|
+
existsSync as existsSync3,
|
|
361
298
|
ftruncateSync,
|
|
362
299
|
openSync,
|
|
363
|
-
readFileSync as
|
|
300
|
+
readFileSync as readFileSync4,
|
|
364
301
|
writeFileSync as writeFileSync4,
|
|
365
302
|
writeSync,
|
|
366
303
|
mkdirSync as mkdirSync3,
|
|
@@ -421,7 +358,7 @@ function isAudioFile(name) {
|
|
|
421
358
|
// src/helpers/waveform.ts
|
|
422
359
|
import { spawn } from "child_process";
|
|
423
360
|
import {
|
|
424
|
-
existsSync
|
|
361
|
+
existsSync,
|
|
425
362
|
writeFileSync,
|
|
426
363
|
mkdirSync,
|
|
427
364
|
lstatSync as lstatSync2,
|
|
@@ -498,11 +435,11 @@ function decodeAudioPeaks(audioPath) {
|
|
|
498
435
|
}
|
|
499
436
|
async function generateWaveformCache(projectDir, assetPath) {
|
|
500
437
|
const audioPath = join3(projectDir, assetPath);
|
|
501
|
-
if (!
|
|
438
|
+
if (!existsSync(audioPath)) return;
|
|
502
439
|
const stats = statSync(audioPath);
|
|
503
440
|
const cacheDir = join3(projectDir, ".waveform-cache");
|
|
504
441
|
const cachePath = join3(cacheDir, buildWaveformCacheKey(assetPath, stats));
|
|
505
|
-
if (isWaveformCacheDirectory(cacheDir) &&
|
|
442
|
+
if (isWaveformCacheDirectory(cacheDir) && existsSync(cachePath)) return;
|
|
506
443
|
const peaks = await decodeAudioPeaks(audioPath);
|
|
507
444
|
writeWaveformCache(cachePath, peaks);
|
|
508
445
|
}
|
|
@@ -584,7 +521,7 @@ function validateUploadedMediaBuffer(fileName, buffer, runner = spawnSync) {
|
|
|
584
521
|
}
|
|
585
522
|
|
|
586
523
|
// src/helpers/backupJournal.ts
|
|
587
|
-
import { mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as
|
|
524
|
+
import { mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
588
525
|
import { Buffer as Buffer2 } from "buffer";
|
|
589
526
|
import { join as join5, relative as relative2 } from "path";
|
|
590
527
|
var DEFAULT_KEEP_PER_FILE = 10;
|
|
@@ -603,7 +540,7 @@ function backupPathForResponse(projectDir, backupPath) {
|
|
|
603
540
|
function snapshotBeforeWrite(projectDir, absPath, options = {}) {
|
|
604
541
|
if (!isSafePath(projectDir, absPath)) return { backupPath: null };
|
|
605
542
|
try {
|
|
606
|
-
const content =
|
|
543
|
+
const content = readFileSync2(absPath);
|
|
607
544
|
const relativePath = relative2(projectDir, absPath);
|
|
608
545
|
const backupDir = join5(projectDir, ".hyperframes", "backup");
|
|
609
546
|
mkdirSync2(backupDir, { recursive: true });
|
|
@@ -625,7 +562,7 @@ function nextBackupPath(backupDir, backupKey) {
|
|
|
625
562
|
let counter = 2;
|
|
626
563
|
while (true) {
|
|
627
564
|
try {
|
|
628
|
-
|
|
565
|
+
readFileSync2(candidate);
|
|
629
566
|
} catch (error) {
|
|
630
567
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
631
568
|
return candidate;
|
|
@@ -722,7 +659,7 @@ import {
|
|
|
722
659
|
import { parseHTML as parseHTML2 } from "linkedom";
|
|
723
660
|
|
|
724
661
|
// src/helpers/compositionInsertion.ts
|
|
725
|
-
import { existsSync as
|
|
662
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, realpathSync } from "fs";
|
|
726
663
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
727
664
|
import { dirname as dirname2, relative as relative3, resolve as resolve2, sep as sep2 } from "path";
|
|
728
665
|
import { parseHTML } from "linkedom";
|
|
@@ -757,7 +694,7 @@ function canonicalProjectPath(projectDir, candidate) {
|
|
|
757
694
|
if (!candidate) {
|
|
758
695
|
throw new CompositionInsertionError("Composition source escapes the project", 400);
|
|
759
696
|
}
|
|
760
|
-
if (!
|
|
697
|
+
if (!existsSync2(candidate)) {
|
|
761
698
|
throw new CompositionInsertionError("Composition source was not found", 404);
|
|
762
699
|
}
|
|
763
700
|
const canonical = realpathSync(candidate);
|
|
@@ -794,7 +731,7 @@ function validateDependencyGraph(projectDir, targetAbs, sourceAbs) {
|
|
|
794
731
|
}
|
|
795
732
|
if (visited.has(file)) return;
|
|
796
733
|
visiting.add(file);
|
|
797
|
-
const source =
|
|
734
|
+
const source = readFileSync3(file, "utf-8");
|
|
798
735
|
const { document } = compositionRoot(source);
|
|
799
736
|
for (const host of descendants(document, "[data-composition-src]")) {
|
|
800
737
|
const dependency = host.getAttribute("data-composition-src");
|
|
@@ -858,7 +795,7 @@ function insertCompositionIntoSource(input) {
|
|
|
858
795
|
const targetAbs = canonicalProjectFile(input.projectDir, input.targetPath);
|
|
859
796
|
const sourceAbs = canonicalProjectFile(input.projectDir, input.sourcePath);
|
|
860
797
|
validateDependencyGraph(input.projectDir, targetAbs, sourceAbs);
|
|
861
|
-
const source =
|
|
798
|
+
const source = readFileSync3(sourceAbs, "utf-8");
|
|
862
799
|
const sourceComposition = compositionRoot(source).root;
|
|
863
800
|
const duration = positiveAttribute(
|
|
864
801
|
sourceComposition,
|
|
@@ -988,7 +925,7 @@ async function resolveProjectPath(c, adapter, pathPrefix, opts) {
|
|
|
988
925
|
if (!absPath) {
|
|
989
926
|
return { error: c.json({ error: "forbidden" }, 403) };
|
|
990
927
|
}
|
|
991
|
-
if (opts?.mustExist && !
|
|
928
|
+
if (opts?.mustExist && !existsSync3(absPath)) {
|
|
992
929
|
return { error: c.json({ error: "not found" }, 404) };
|
|
993
930
|
}
|
|
994
931
|
return { project, filePath, absPath };
|
|
@@ -1056,7 +993,7 @@ function commitElementPatchBatches(projectDir, batches, writeFile = writeFileSyn
|
|
|
1056
993
|
resolvedPaths.add(absPath);
|
|
1057
994
|
let before;
|
|
1058
995
|
try {
|
|
1059
|
-
before =
|
|
996
|
+
before = readFileSync4(absPath, "utf-8");
|
|
1060
997
|
} catch {
|
|
1061
998
|
return { error: "not-found", sourceFile: batch.sourceFile };
|
|
1062
999
|
}
|
|
@@ -1196,7 +1133,7 @@ async function parseMutationBody(c) {
|
|
|
1196
1133
|
}
|
|
1197
1134
|
function ensureDir(filePath) {
|
|
1198
1135
|
const dir = dirname3(filePath);
|
|
1199
|
-
if (!
|
|
1136
|
+
if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
|
|
1200
1137
|
}
|
|
1201
1138
|
function generateCopyPath(projectDir, originalPath) {
|
|
1202
1139
|
const ext = originalPath.includes(".") ? "." + originalPath.split(".").pop() : "";
|
|
@@ -1205,7 +1142,7 @@ function generateCopyPath(projectDir, originalPath) {
|
|
|
1205
1142
|
const cleanBase = copyMatch ? base.slice(0, -copyMatch[0].length) : base;
|
|
1206
1143
|
let num = copyMatch ? copyMatch[1] ? parseInt(copyMatch[1]) + 1 : 2 : 1;
|
|
1207
1144
|
let candidate = num === 1 ? `${cleanBase} (copy)${ext}` : `${cleanBase} (copy ${num})${ext}`;
|
|
1208
|
-
while (
|
|
1145
|
+
while (existsSync3(resolve3(projectDir, candidate))) {
|
|
1209
1146
|
num++;
|
|
1210
1147
|
candidate = `${cleanBase} (copy ${num})${ext}`;
|
|
1211
1148
|
}
|
|
@@ -1233,7 +1170,7 @@ function updateReferences(projectDir, oldPath, newPath) {
|
|
|
1233
1170
|
let updatedCount = 0;
|
|
1234
1171
|
for (const file of textFiles) {
|
|
1235
1172
|
if (!isSafePath(projectDir, file)) continue;
|
|
1236
|
-
const content =
|
|
1173
|
+
const content = readFileSync4(file, "utf-8");
|
|
1237
1174
|
if (!content.includes(oldPath)) continue;
|
|
1238
1175
|
const updated = content.split(oldPath).join(newPath);
|
|
1239
1176
|
if (updated !== content) {
|
|
@@ -1507,7 +1444,7 @@ function containsRawGsapExpression(value) {
|
|
|
1507
1444
|
return Object.values(value).some(containsRawGsapExpression);
|
|
1508
1445
|
}
|
|
1509
1446
|
async function prepareGsapMutationScript(c, res, firstMutation) {
|
|
1510
|
-
const beforeHtml =
|
|
1447
|
+
const beforeHtml = readFileSync4(res.absPath, "utf-8");
|
|
1511
1448
|
let html = beforeHtml;
|
|
1512
1449
|
let block = extractGsapScriptBlock(html);
|
|
1513
1450
|
if (!block && (firstMutation.type === "add" || firstMutation.type === "add-with-keyframes")) {
|
|
@@ -1574,7 +1511,7 @@ async function applyGsapMutations(c, res, mutations) {
|
|
|
1574
1511
|
const changed = block.scriptText !== initialScript;
|
|
1575
1512
|
const newHtml = changed ? block.replaceScript(block.scriptText) : html;
|
|
1576
1513
|
let backupPath = null;
|
|
1577
|
-
if (
|
|
1514
|
+
if (readFileSync4(res.absPath, "utf-8") !== beforeHtml) {
|
|
1578
1515
|
return c.json({ error: "file changed during GSAP mutation", conflict: true }, 409);
|
|
1579
1516
|
}
|
|
1580
1517
|
if (changed) {
|
|
@@ -2335,9 +2272,9 @@ async function processUploadedFiles(formData, targetDir, projectDir) {
|
|
|
2335
2272
|
const base = dotIdx > 0 ? name.slice(0, dotIdx) : name;
|
|
2336
2273
|
const MAX_COPY_INDEX = 1e4;
|
|
2337
2274
|
let n = 1;
|
|
2338
|
-
if (
|
|
2275
|
+
if (existsSync3(finalPath)) {
|
|
2339
2276
|
n = 2;
|
|
2340
|
-
while (n < MAX_COPY_INDEX &&
|
|
2277
|
+
while (n < MAX_COPY_INDEX && existsSync3(resolve3(targetDir, `${base} (${n})${ext}`))) n++;
|
|
2341
2278
|
if (n >= MAX_COPY_INDEX) {
|
|
2342
2279
|
skipped.push(name);
|
|
2343
2280
|
continue;
|
|
@@ -2389,13 +2326,13 @@ function registerFileRoutes(api, adapter) {
|
|
|
2389
2326
|
api.get("/projects/:id/files/*", async (c) => {
|
|
2390
2327
|
const res = await resolveProjectFile(c, adapter);
|
|
2391
2328
|
if ("error" in res) return res.error;
|
|
2392
|
-
if (!
|
|
2329
|
+
if (!existsSync3(res.absPath)) {
|
|
2393
2330
|
if (c.req.query("optional") === "1") {
|
|
2394
2331
|
return c.json({ filename: res.filePath, content: "" });
|
|
2395
2332
|
}
|
|
2396
2333
|
return c.json({ error: "not found" }, 404);
|
|
2397
2334
|
}
|
|
2398
|
-
const content =
|
|
2335
|
+
const content = readFileSync4(res.absPath);
|
|
2399
2336
|
const version = fileContentVersion(content);
|
|
2400
2337
|
c.header("ETag", version);
|
|
2401
2338
|
return c.json({ filename: res.filePath, content: content.toString("utf-8"), version });
|
|
@@ -2409,7 +2346,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2409
2346
|
if (expectedVersion === null && !createOnly) {
|
|
2410
2347
|
let currentContent = null;
|
|
2411
2348
|
try {
|
|
2412
|
-
currentContent =
|
|
2349
|
+
currentContent = readFileSync4(res.absPath);
|
|
2413
2350
|
} catch (error) {
|
|
2414
2351
|
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
|
|
2415
2352
|
throw error;
|
|
@@ -2435,7 +2372,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2435
2372
|
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") {
|
|
2436
2373
|
throw error;
|
|
2437
2374
|
}
|
|
2438
|
-
const currentContent =
|
|
2375
|
+
const currentContent = readFileSync4(res.absPath);
|
|
2439
2376
|
return c.json(
|
|
2440
2377
|
{
|
|
2441
2378
|
error: "file conflict",
|
|
@@ -2470,7 +2407,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2470
2407
|
);
|
|
2471
2408
|
}
|
|
2472
2409
|
try {
|
|
2473
|
-
const currentContent =
|
|
2410
|
+
const currentContent = readFileSync4(fd);
|
|
2474
2411
|
const currentVersion = fileContentVersion(currentContent);
|
|
2475
2412
|
if (expectedVersion !== currentVersion) {
|
|
2476
2413
|
return c.json(
|
|
@@ -2543,7 +2480,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2543
2480
|
}
|
|
2544
2481
|
let before;
|
|
2545
2482
|
try {
|
|
2546
|
-
before =
|
|
2483
|
+
before = readFileSync4(ctx.absPath, "utf-8");
|
|
2547
2484
|
} catch (error) {
|
|
2548
2485
|
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
|
|
2549
2486
|
throw error;
|
|
@@ -2595,12 +2532,12 @@ function registerFileRoutes(api, adapter) {
|
|
|
2595
2532
|
api.post("/projects/:id/file-mutations/remove-element/*", async (c) => {
|
|
2596
2533
|
const ctx = await resolveFileMutationContext(c, adapter, "remove-element");
|
|
2597
2534
|
if ("error" in ctx) return ctx.error;
|
|
2598
|
-
if (!
|
|
2535
|
+
if (!existsSync3(ctx.absPath)) {
|
|
2599
2536
|
return c.json({ error: "not found" }, 404);
|
|
2600
2537
|
}
|
|
2601
2538
|
const parsed = await parseMutationBody(c);
|
|
2602
2539
|
if ("error" in parsed) return parsed.error;
|
|
2603
|
-
const originalContent =
|
|
2540
|
+
const originalContent = readFileSync4(ctx.absPath, "utf-8");
|
|
2604
2541
|
return writeIfChanged(
|
|
2605
2542
|
c,
|
|
2606
2543
|
ctx.project.dir,
|
|
@@ -2613,7 +2550,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2613
2550
|
api.post("/projects/:id/file-mutations/remove-elements/*", async (c) => {
|
|
2614
2551
|
const ctx = await resolveFileMutationContext(c, adapter, "remove-elements");
|
|
2615
2552
|
if ("error" in ctx) return ctx.error;
|
|
2616
|
-
if (!
|
|
2553
|
+
if (!existsSync3(ctx.absPath)) {
|
|
2617
2554
|
return c.json({ error: "not found" }, 404);
|
|
2618
2555
|
}
|
|
2619
2556
|
const body = await c.req.json().catch(() => null);
|
|
@@ -2621,7 +2558,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2621
2558
|
if (!Array.isArray(targets) || targets.length === 0) {
|
|
2622
2559
|
return c.json({ error: "targets required" }, 400);
|
|
2623
2560
|
}
|
|
2624
|
-
const originalContent =
|
|
2561
|
+
const originalContent = readFileSync4(ctx.absPath, "utf-8");
|
|
2625
2562
|
let next = originalContent;
|
|
2626
2563
|
for (const target of targets) {
|
|
2627
2564
|
next = removeElementFromHtml(next, target);
|
|
@@ -2654,7 +2591,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2654
2591
|
seen.add(absPath);
|
|
2655
2592
|
let before;
|
|
2656
2593
|
try {
|
|
2657
|
-
before =
|
|
2594
|
+
before = readFileSync4(absPath, "utf-8");
|
|
2658
2595
|
} catch {
|
|
2659
2596
|
return c.json({ error: `not found: ${file.path}` }, 404);
|
|
2660
2597
|
}
|
|
@@ -2681,7 +2618,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2681
2618
|
prepared.push(folded);
|
|
2682
2619
|
}
|
|
2683
2620
|
for (const file of prepared) {
|
|
2684
|
-
const current =
|
|
2621
|
+
const current = readFileSync4(file.absPath, "utf-8");
|
|
2685
2622
|
if (current !== file.before) {
|
|
2686
2623
|
return c.json(
|
|
2687
2624
|
{
|
|
@@ -2723,7 +2660,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2723
2660
|
const conflicts = [];
|
|
2724
2661
|
for (const file of written.reverse()) {
|
|
2725
2662
|
try {
|
|
2726
|
-
const current =
|
|
2663
|
+
const current = readFileSync4(file.absPath, "utf-8");
|
|
2727
2664
|
if (current !== file.after) {
|
|
2728
2665
|
conflicts.push(file.path);
|
|
2729
2666
|
continue;
|
|
@@ -2771,7 +2708,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2771
2708
|
const fallbackTiming = typeof parsed.body.elementStart === "number" && typeof parsed.body.elementDuration === "number" ? { start: parsed.body.elementStart, duration: parsed.body.elementDuration } : void 0;
|
|
2772
2709
|
let originalContent;
|
|
2773
2710
|
try {
|
|
2774
|
-
originalContent =
|
|
2711
|
+
originalContent = readFileSync4(ctx.absPath, "utf-8");
|
|
2775
2712
|
} catch {
|
|
2776
2713
|
return c.json({ error: "not found" }, 404);
|
|
2777
2714
|
}
|
|
@@ -2827,7 +2764,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2827
2764
|
}
|
|
2828
2765
|
let originalContent;
|
|
2829
2766
|
try {
|
|
2830
|
-
originalContent =
|
|
2767
|
+
originalContent = readFileSync4(ctx.absPath, "utf-8");
|
|
2831
2768
|
} catch {
|
|
2832
2769
|
return c.json({ error: "not found" }, 404);
|
|
2833
2770
|
}
|
|
@@ -2928,7 +2865,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2928
2865
|
}
|
|
2929
2866
|
let originalContent;
|
|
2930
2867
|
try {
|
|
2931
|
-
originalContent =
|
|
2868
|
+
originalContent = readFileSync4(ctx.absPath, "utf-8");
|
|
2932
2869
|
} catch {
|
|
2933
2870
|
return c.json({ error: "not found" }, 404);
|
|
2934
2871
|
}
|
|
@@ -2983,7 +2920,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2983
2920
|
).map((entry) => ({ target: entry.target, track: entry.track }));
|
|
2984
2921
|
let originalContent;
|
|
2985
2922
|
try {
|
|
2986
|
-
originalContent =
|
|
2923
|
+
originalContent = readFileSync4(ctx.absPath, "utf-8");
|
|
2987
2924
|
} catch {
|
|
2988
2925
|
return c.json({ error: "not found" }, 404);
|
|
2989
2926
|
}
|
|
@@ -3012,7 +2949,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3012
2949
|
if ("error" in parsed) return parsed.error;
|
|
3013
2950
|
let content;
|
|
3014
2951
|
try {
|
|
3015
|
-
content =
|
|
2952
|
+
content = readFileSync4(ctx.absPath, "utf-8");
|
|
3016
2953
|
} catch {
|
|
3017
2954
|
return c.json({ exists: false });
|
|
3018
2955
|
}
|
|
@@ -3030,7 +2967,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3030
2967
|
if (!newAbs) {
|
|
3031
2968
|
return c.json({ error: "forbidden" }, 403);
|
|
3032
2969
|
}
|
|
3033
|
-
if (
|
|
2970
|
+
if (existsSync3(newAbs)) {
|
|
3034
2971
|
return c.json({ error: "already exists" }, 409);
|
|
3035
2972
|
}
|
|
3036
2973
|
ensureDir(newAbs);
|
|
@@ -3046,7 +2983,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3046
2983
|
return c.json({ error: "path required" }, 400);
|
|
3047
2984
|
}
|
|
3048
2985
|
const srcAbs = resolveWithinProject(project.dir, body.path);
|
|
3049
|
-
if (!srcAbs || !
|
|
2986
|
+
if (!srcAbs || !existsSync3(srcAbs)) {
|
|
3050
2987
|
return c.json({ error: "not found" }, 404);
|
|
3051
2988
|
}
|
|
3052
2989
|
const copyPath = generateCopyPath(project.dir, body.path);
|
|
@@ -3055,7 +2992,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3055
2992
|
return c.json({ error: "forbidden" }, 403);
|
|
3056
2993
|
}
|
|
3057
2994
|
ensureDir(destAbs);
|
|
3058
|
-
writeFileSync4(destAbs,
|
|
2995
|
+
writeFileSync4(destAbs, readFileSync4(srcAbs));
|
|
3059
2996
|
return c.json({ ok: true, path: copyPath }, 201);
|
|
3060
2997
|
});
|
|
3061
2998
|
const MAX_UPLOAD_BYTES = 500 * 1024 * 1024;
|
|
@@ -3071,7 +3008,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3071
3008
|
const subDir = c.req.query("dir") ?? "";
|
|
3072
3009
|
const targetDir = subDir ? resolveWithinProject(project.dir, subDir) : project.dir;
|
|
3073
3010
|
if (!targetDir) return c.json({ error: "forbidden" }, 403);
|
|
3074
|
-
if (subDir && !
|
|
3011
|
+
if (subDir && !existsSync3(targetDir)) mkdirSync3(targetDir, { recursive: true });
|
|
3075
3012
|
const formData = await c.req.formData();
|
|
3076
3013
|
const result = await processUploadedFiles(formData, targetDir, project.dir);
|
|
3077
3014
|
return c.json(
|
|
@@ -3085,7 +3022,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3085
3022
|
mustExist: true
|
|
3086
3023
|
});
|
|
3087
3024
|
if ("error" in res) return res.error;
|
|
3088
|
-
const html =
|
|
3025
|
+
const html = readFileSync4(res.absPath, "utf-8");
|
|
3089
3026
|
const block = extractGsapScriptBlock(html);
|
|
3090
3027
|
if (!block) {
|
|
3091
3028
|
return c.json({
|
|
@@ -3144,7 +3081,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3144
3081
|
if (!body || typeof body.expected !== "string" || typeof body.restore !== "string") {
|
|
3145
3082
|
return c.json({ error: "expected and restore contents required" }, 400);
|
|
3146
3083
|
}
|
|
3147
|
-
const current =
|
|
3084
|
+
const current = readFileSync4(res.absPath, "utf-8");
|
|
3148
3085
|
if (current !== body.expected) {
|
|
3149
3086
|
return c.json({ ok: true, restored: false, conflict: true });
|
|
3150
3087
|
}
|
|
@@ -3154,7 +3091,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3154
3091
|
}
|
|
3155
3092
|
|
|
3156
3093
|
// src/routes/preview.ts
|
|
3157
|
-
import { createReadStream, existsSync as
|
|
3094
|
+
import { createReadStream, existsSync as existsSync5, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
|
|
3158
3095
|
import { Readable } from "stream";
|
|
3159
3096
|
import { join as join8, resolve as resolve4 } from "path";
|
|
3160
3097
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -3162,7 +3099,7 @@ import { injectScriptsIntoHtml, stripEmbeddedRuntimeScripts as stripEmbeddedRunt
|
|
|
3162
3099
|
import { isWithinProjectRoot } from "@hyperframes/parsers/asset-resolution";
|
|
3163
3100
|
|
|
3164
3101
|
// src/helpers/subComposition.ts
|
|
3165
|
-
import { existsSync as
|
|
3102
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
|
|
3166
3103
|
import { join as join7 } from "path";
|
|
3167
3104
|
import { parseHTML as parseHTML3 } from "linkedom";
|
|
3168
3105
|
import {
|
|
@@ -3175,7 +3112,7 @@ function isFullHtmlDocument(html) {
|
|
|
3175
3112
|
return /^\s*(?:<!doctype\s|<html[\s>])/i.test(html);
|
|
3176
3113
|
}
|
|
3177
3114
|
function rewriteRelativePaths(root, compPath, projectDir) {
|
|
3178
|
-
const assetExists = (path) =>
|
|
3115
|
+
const assetExists = (path) => existsSync4(join7(projectDir, path));
|
|
3179
3116
|
rewriteAssetPaths(
|
|
3180
3117
|
root.querySelectorAll("[src], [href]"),
|
|
3181
3118
|
compPath,
|
|
@@ -3273,8 +3210,8 @@ function tagRootCompositionFile(bodyHtml, compPath) {
|
|
|
3273
3210
|
}
|
|
3274
3211
|
function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, rawOverride) {
|
|
3275
3212
|
const compFile = join7(projectDir, compPath);
|
|
3276
|
-
if (!
|
|
3277
|
-
const rawComp = rawOverride ??
|
|
3213
|
+
if (!existsSync4(compFile)) return null;
|
|
3214
|
+
const rawComp = rawOverride ?? readFileSync5(compFile, "utf-8");
|
|
3278
3215
|
let compHeadContent = "";
|
|
3279
3216
|
let rewrittenContent;
|
|
3280
3217
|
let htmlAttrs = "";
|
|
@@ -3306,8 +3243,8 @@ function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, raw
|
|
|
3306
3243
|
rewrittenContent = tagRootCompositionFile(rewrittenContent, compPath);
|
|
3307
3244
|
const indexPath = join7(projectDir, "index.html");
|
|
3308
3245
|
let headContent = "";
|
|
3309
|
-
if (
|
|
3310
|
-
const indexHtml =
|
|
3246
|
+
if (existsSync4(indexPath)) {
|
|
3247
|
+
const indexHtml = readFileSync5(indexPath, "utf-8");
|
|
3311
3248
|
const headMatch = indexHtml.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
|
|
3312
3249
|
headContent = headMatch?.[1] ?? "";
|
|
3313
3250
|
}
|
|
@@ -3351,7 +3288,7 @@ import {
|
|
|
3351
3288
|
fstatSync,
|
|
3352
3289
|
ftruncateSync as ftruncateSync2,
|
|
3353
3290
|
openSync as openSync2,
|
|
3354
|
-
readFileSync as
|
|
3291
|
+
readFileSync as readFileSync6,
|
|
3355
3292
|
writeFileSync as writeFileSync5,
|
|
3356
3293
|
writeSync as writeSync2
|
|
3357
3294
|
} from "fs";
|
|
@@ -3361,7 +3298,7 @@ function persistHfIdsIfNeeded(filePath, html) {
|
|
|
3361
3298
|
const idsAfter = (normalized.match(/\bdata-hf-id=/g) ?? []).length;
|
|
3362
3299
|
if (idsAfter > idsBefore) {
|
|
3363
3300
|
try {
|
|
3364
|
-
const current =
|
|
3301
|
+
const current = readFileSync6(filePath, "utf-8");
|
|
3365
3302
|
if (current === html) {
|
|
3366
3303
|
writeFileSync5(filePath, normalized, "utf-8");
|
|
3367
3304
|
}
|
|
@@ -3389,7 +3326,7 @@ function stampFileHfIds(filePath) {
|
|
|
3389
3326
|
if (fd === null) return null;
|
|
3390
3327
|
try {
|
|
3391
3328
|
if (!fstatSync(fd).isFile()) return null;
|
|
3392
|
-
const html =
|
|
3329
|
+
const html = readFileSync6(fd, "utf-8");
|
|
3393
3330
|
const normalized = ensureHfIds(html);
|
|
3394
3331
|
const idsBefore = (html.match(/\bdata-hf-id=/g) ?? []).length;
|
|
3395
3332
|
const idsAfter = (normalized.match(/\bdata-hf-id=/g) ?? []).length;
|
|
@@ -3449,9 +3386,9 @@ ${html}`;
|
|
|
3449
3386
|
}
|
|
3450
3387
|
function readStudioMotionManifestContent(projectDir) {
|
|
3451
3388
|
const manifestPath = join8(projectDir, STUDIO_MOTION_PATH);
|
|
3452
|
-
if (!
|
|
3389
|
+
if (!existsSync5(manifestPath)) return "";
|
|
3453
3390
|
try {
|
|
3454
|
-
return
|
|
3391
|
+
return readFileSync7(manifestPath, "utf-8");
|
|
3455
3392
|
} catch {
|
|
3456
3393
|
return "";
|
|
3457
3394
|
}
|
|
@@ -3595,16 +3532,16 @@ async function transformPreviewHtml(html, adapter, project, activeCompositionPat
|
|
|
3595
3532
|
}
|
|
3596
3533
|
function resolveProjectMainHtml(projectDir, projectId) {
|
|
3597
3534
|
const indexPath = join8(projectDir, "index.html");
|
|
3598
|
-
if (
|
|
3535
|
+
if (existsSync5(indexPath)) {
|
|
3599
3536
|
return {
|
|
3600
|
-
html:
|
|
3537
|
+
html: readFileSync7(indexPath, "utf-8"),
|
|
3601
3538
|
compositionPath: "index.html"
|
|
3602
3539
|
};
|
|
3603
3540
|
}
|
|
3604
3541
|
const blockHtmlPath = join8(projectDir, `${projectId}.html`);
|
|
3605
|
-
if (
|
|
3542
|
+
if (existsSync5(blockHtmlPath)) {
|
|
3606
3543
|
return {
|
|
3607
|
-
html:
|
|
3544
|
+
html: readFileSync7(blockHtmlPath, "utf-8"),
|
|
3608
3545
|
compositionPath: `${projectId}.html`
|
|
3609
3546
|
};
|
|
3610
3547
|
}
|
|
@@ -3709,7 +3646,7 @@ ${runtimeTag}`;
|
|
|
3709
3646
|
c.req.path.replace(`/projects/${project.id}/preview/comp/`, "").split("?")[0] ?? ""
|
|
3710
3647
|
);
|
|
3711
3648
|
const compFile = resolveWithinProject(project.dir, compPath);
|
|
3712
|
-
if (!compFile || !
|
|
3649
|
+
if (!compFile || !existsSync5(compFile) || !statSync3(compFile).isFile()) {
|
|
3713
3650
|
return c.text("not found", 404);
|
|
3714
3651
|
}
|
|
3715
3652
|
const compPathHash = createHash3("sha1").update(compPath).digest("hex");
|
|
@@ -3749,7 +3686,7 @@ ${runtimeTag}`;
|
|
|
3749
3686
|
if (!file) {
|
|
3750
3687
|
return c.text("not found", 404);
|
|
3751
3688
|
}
|
|
3752
|
-
const stat =
|
|
3689
|
+
const stat = existsSync5(file) ? statSync3(file) : null;
|
|
3753
3690
|
if (!stat?.isFile()) {
|
|
3754
3691
|
return c.text("not found", 404);
|
|
3755
3692
|
}
|
|
@@ -3798,7 +3735,7 @@ ${runtimeTag}`;
|
|
|
3798
3735
|
}
|
|
3799
3736
|
servedContentType = PROXY_VARIANT_CONFIG[proxyVariant].contentType;
|
|
3800
3737
|
}
|
|
3801
|
-
const textBuffer = isText ? Buffer.from(
|
|
3738
|
+
const textBuffer = isText ? Buffer.from(readFileSync7(file, "utf-8"), "utf-8") : null;
|
|
3802
3739
|
const totalSize = textBuffer ? textBuffer.length : statSync3(servedPath).size;
|
|
3803
3740
|
const bodyFor = (start, end) => textBuffer ? new Uint8Array(textBuffer.subarray(start, end + 1)) : (
|
|
3804
3741
|
// Node's web stream type and the DOM one do not overlap for tsc on
|
|
@@ -3842,7 +3779,7 @@ ${runtimeTag}`;
|
|
|
3842
3779
|
}
|
|
3843
3780
|
|
|
3844
3781
|
// src/routes/lint.ts
|
|
3845
|
-
import { readFileSync as
|
|
3782
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
3846
3783
|
import { join as join9 } from "path";
|
|
3847
3784
|
async function collectProjectFindings(adapter, projectDir) {
|
|
3848
3785
|
const findings = [];
|
|
@@ -3861,7 +3798,7 @@ async function lintUncoveredHtml(adapter, projectDir, htmlFiles, coveredFiles) {
|
|
|
3861
3798
|
const findings = [];
|
|
3862
3799
|
for (const file of htmlFiles) {
|
|
3863
3800
|
if (coveredFiles.has(file)) continue;
|
|
3864
|
-
const content =
|
|
3801
|
+
const content = readFileSync8(join9(projectDir, file), "utf-8");
|
|
3865
3802
|
const result = await adapter.lint(content, { filePath: file, isSubComposition: true });
|
|
3866
3803
|
for (const finding of result?.findings ?? []) {
|
|
3867
3804
|
findings.push({ ...finding, file });
|
|
@@ -3894,7 +3831,7 @@ function registerLintRoutes(api, adapter) {
|
|
|
3894
3831
|
|
|
3895
3832
|
// src/routes/render.ts
|
|
3896
3833
|
import { streamSSE } from "hono/streaming";
|
|
3897
|
-
import { existsSync as
|
|
3834
|
+
import { existsSync as existsSync6, readFileSync as readFileSync9, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
3898
3835
|
import { join as join10 } from "path";
|
|
3899
3836
|
import { VALID_CANVAS_RESOLUTIONS } from "@hyperframes/parsers";
|
|
3900
3837
|
import { formatRenderOutputTimestamp, parseFps } from "@hyperframes/core";
|
|
@@ -3961,7 +3898,7 @@ function registerRenderRoutes(api, adapter) {
|
|
|
3961
3898
|
const now = /* @__PURE__ */ new Date();
|
|
3962
3899
|
const jobId = `${project.id}_${formatRenderOutputTimestamp(now)}`;
|
|
3963
3900
|
const rendersDir = adapter.rendersDir(project);
|
|
3964
|
-
if (!
|
|
3901
|
+
if (!existsSync6(rendersDir)) mkdirSync4(rendersDir, { recursive: true });
|
|
3965
3902
|
const ext = FORMAT_EXT[format] ?? ".mp4";
|
|
3966
3903
|
const outputPath = join10(rendersDir, `${jobId}${ext}`);
|
|
3967
3904
|
const jobState = adapter.startRender({
|
|
@@ -4027,12 +3964,12 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4027
3964
|
api.get("/render/:jobId/view", (c) => {
|
|
4028
3965
|
const { jobId } = c.req.param();
|
|
4029
3966
|
const job = renderJobs.get(jobId);
|
|
4030
|
-
if (!job?.outputPath || !
|
|
3967
|
+
if (!job?.outputPath || !existsSync6(job.outputPath)) {
|
|
4031
3968
|
return c.json({ error: "not found" }, 404);
|
|
4032
3969
|
}
|
|
4033
3970
|
const contentType = renderContentType(job.outputPath);
|
|
4034
3971
|
const filename = job.outputPath.split("/").pop() ?? `render.mp4`;
|
|
4035
|
-
const content =
|
|
3972
|
+
const content = readFileSync9(job.outputPath);
|
|
4036
3973
|
return new Response(content, {
|
|
4037
3974
|
headers: {
|
|
4038
3975
|
"Content-Type": contentType,
|
|
@@ -4045,12 +3982,12 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4045
3982
|
api.get("/render/:jobId/download", (c) => {
|
|
4046
3983
|
const { jobId } = c.req.param();
|
|
4047
3984
|
const job = renderJobs.get(jobId);
|
|
4048
|
-
if (!job?.outputPath || !
|
|
3985
|
+
if (!job?.outputPath || !existsSync6(job.outputPath)) {
|
|
4049
3986
|
return c.json({ error: "not found" }, 404);
|
|
4050
3987
|
}
|
|
4051
3988
|
const contentType = renderContentType(job.outputPath);
|
|
4052
3989
|
const filename = job.outputPath.split("/").pop() ?? `render.mp4`;
|
|
4053
|
-
const content =
|
|
3990
|
+
const content = readFileSync9(job.outputPath);
|
|
4054
3991
|
return new Response(content, {
|
|
4055
3992
|
headers: {
|
|
4056
3993
|
"Content-Type": contentType,
|
|
@@ -4065,7 +4002,7 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4065
4002
|
const dir = state.outputPath.replace(/\/[^/]+$/, "");
|
|
4066
4003
|
for (const ext of [".mp4", ".webm", ".mov", ".meta.json"]) {
|
|
4067
4004
|
const fp = join10(dir, `${jobId}${ext}`);
|
|
4068
|
-
if (
|
|
4005
|
+
if (existsSync6(fp)) unlinkSync3(fp);
|
|
4069
4006
|
}
|
|
4070
4007
|
break;
|
|
4071
4008
|
}
|
|
@@ -4081,9 +4018,9 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4081
4018
|
const rendersDir = adapter.rendersDir(project);
|
|
4082
4019
|
const fp = resolveWithinProject(rendersDir, filename);
|
|
4083
4020
|
if (!fp) return c.json({ error: "forbidden" }, 403);
|
|
4084
|
-
if (!
|
|
4021
|
+
if (!existsSync6(fp)) return c.json({ error: "not found" }, 404);
|
|
4085
4022
|
const contentType = renderContentType(fp);
|
|
4086
|
-
const content =
|
|
4023
|
+
const content = readFileSync9(fp);
|
|
4087
4024
|
return new Response(content, {
|
|
4088
4025
|
headers: {
|
|
4089
4026
|
"Content-Type": contentType,
|
|
@@ -4097,7 +4034,7 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4097
4034
|
const project = await adapter.resolveProject(c.req.param("id"));
|
|
4098
4035
|
if (!project) return c.json({ error: "not found" }, 404);
|
|
4099
4036
|
const rendersDir = adapter.rendersDir(project);
|
|
4100
|
-
if (!
|
|
4037
|
+
if (!existsSync6(rendersDir)) return c.json({ renders: [] });
|
|
4101
4038
|
const files = readdirSync5(rendersDir).filter((f) => f.endsWith(".mp4") || f.endsWith(".webm") || f.endsWith(".mov")).map((f) => {
|
|
4102
4039
|
const fp = join10(rendersDir, f);
|
|
4103
4040
|
const stat = statSync4(fp);
|
|
@@ -4105,10 +4042,10 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4105
4042
|
const metaPath = join10(rendersDir, `${rid}.meta.json`);
|
|
4106
4043
|
let status = "complete";
|
|
4107
4044
|
let durationMs;
|
|
4108
|
-
if (
|
|
4045
|
+
if (existsSync6(metaPath)) {
|
|
4109
4046
|
try {
|
|
4110
|
-
const meta = JSON.parse(
|
|
4111
|
-
if (meta.status === "failed" && !
|
|
4047
|
+
const meta = JSON.parse(readFileSync9(metaPath, "utf-8"));
|
|
4048
|
+
if (meta.status === "failed" && !existsSync6(fp)) status = "failed";
|
|
4112
4049
|
if (meta.durationMs) durationMs = meta.durationMs;
|
|
4113
4050
|
} catch {
|
|
4114
4051
|
}
|
|
@@ -4139,9 +4076,9 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4139
4076
|
|
|
4140
4077
|
// src/routes/thumbnail.ts
|
|
4141
4078
|
import {
|
|
4142
|
-
existsSync as
|
|
4079
|
+
existsSync as existsSync7,
|
|
4143
4080
|
mkdirSync as mkdirSync5,
|
|
4144
|
-
readFileSync as
|
|
4081
|
+
readFileSync as readFileSync10,
|
|
4145
4082
|
readdirSync as readdirSync6,
|
|
4146
4083
|
renameSync as renameSync3,
|
|
4147
4084
|
rmSync as rmSync4,
|
|
@@ -4259,12 +4196,11 @@ var thumbnailGenerationCoordinator = new ThumbnailGenerationCoordinator(1);
|
|
|
4259
4196
|
var THUMBNAIL_CACHE_VERSION = "v4";
|
|
4260
4197
|
var THUMBNAIL_MAX_OUTPUT_WIDTH = 240;
|
|
4261
4198
|
var THUMBNAIL_MAX_OUTPUT_HEIGHT = 135;
|
|
4262
|
-
var STORYBOARD_MAX_OUTPUT_DIMENSION = 1080;
|
|
4263
4199
|
var THUMBNAIL_CACHE_MAX_BYTES = 512 * 1024 * 1024;
|
|
4264
4200
|
var THUMBNAIL_CACHE_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
|
|
4265
4201
|
var prunedCacheDirs = /* @__PURE__ */ new Set();
|
|
4266
4202
|
function pruneThumbnailCache(cacheDir, protectedPaths, now = Date.now()) {
|
|
4267
|
-
if (!
|
|
4203
|
+
if (!existsSync7(cacheDir)) return;
|
|
4268
4204
|
const files = readdirSync6(cacheDir, { withFileTypes: true }).flatMap((entry) => {
|
|
4269
4205
|
if (!entry.isFile()) return [];
|
|
4270
4206
|
const path = join11(cacheDir, entry.name);
|
|
@@ -4325,7 +4261,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4325
4261
|
const format = url.searchParams.get("format") === "png" ? "png" : "jpeg";
|
|
4326
4262
|
const contentType = format === "png" ? "image/png" : "image/jpeg";
|
|
4327
4263
|
const requestedOutput = url.searchParams.get("output");
|
|
4328
|
-
const outputMode = requestedOutput === "source"
|
|
4264
|
+
const outputMode = requestedOutput === "source" || requestedOutput !== "preview" && format === "png" ? "source" : "preview";
|
|
4329
4265
|
const rawSelectorIndex = Number.parseInt(url.searchParams.get("selectorIndex") || "0", 10);
|
|
4330
4266
|
const selectorIndex = Number.isFinite(rawSelectorIndex) && rawSelectorIndex > 0 ? rawSelectorIndex : void 0;
|
|
4331
4267
|
const urlVersion = url.searchParams.get("v") || "";
|
|
@@ -4334,8 +4270,8 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4334
4270
|
let sourceMtime = 0;
|
|
4335
4271
|
let sourceKey = "";
|
|
4336
4272
|
const htmlFile = join11(project.dir, compPath);
|
|
4337
|
-
if (
|
|
4338
|
-
const html =
|
|
4273
|
+
if (existsSync7(htmlFile)) {
|
|
4274
|
+
const html = readFileSync10(htmlFile, "utf-8");
|
|
4339
4275
|
sourceKey = `_${createHash4("sha1").update(html).digest("hex").slice(0, 16)}`;
|
|
4340
4276
|
sourceMtime = Math.round(statSync5(htmlFile).mtimeMs);
|
|
4341
4277
|
if (!vpWidth) {
|
|
@@ -4347,15 +4283,15 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4347
4283
|
}
|
|
4348
4284
|
const manualEditsFile = join11(project.dir, STUDIO_MANUAL_EDITS_PATH);
|
|
4349
4285
|
let manualEditsKey = "";
|
|
4350
|
-
if (
|
|
4351
|
-
const manualEditsContent =
|
|
4286
|
+
if (existsSync7(manualEditsFile)) {
|
|
4287
|
+
const manualEditsContent = readFileSync10(manualEditsFile, "utf-8");
|
|
4352
4288
|
manualEditsKey = `_${createHash4("sha1").update(manualEditsContent).digest("hex").slice(0, 16)}`;
|
|
4353
4289
|
sourceMtime = Math.max(sourceMtime, Math.round(statSync5(manualEditsFile).mtimeMs));
|
|
4354
4290
|
}
|
|
4355
4291
|
const motionFile = join11(project.dir, STUDIO_MOTION_PATH);
|
|
4356
4292
|
let motionKey = "";
|
|
4357
|
-
if (
|
|
4358
|
-
const motionContent =
|
|
4293
|
+
if (existsSync7(motionFile)) {
|
|
4294
|
+
const motionContent = readFileSync10(motionFile, "utf-8");
|
|
4359
4295
|
motionKey = `_${createHash4("sha1").update(motionContent).digest("hex").slice(0, 16)}`;
|
|
4360
4296
|
sourceMtime = Math.max(sourceMtime, Math.round(statSync5(motionFile).mtimeMs));
|
|
4361
4297
|
}
|
|
@@ -4364,11 +4300,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4364
4300
|
const selectorKey = selector ? `_${selector.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 80)}_${selectorIndex ?? 0}` : "";
|
|
4365
4301
|
const urlVersionKey = urlVersion ? `_${urlVersion.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32)}` : "";
|
|
4366
4302
|
const projectSignatureKey = `_${createHash4("sha1").update(projectSignature).digest("hex").slice(0, 16)}`;
|
|
4367
|
-
const outputScale = outputMode === "source" ? 1 :
|
|
4368
|
-
1,
|
|
4369
|
-
STORYBOARD_MAX_OUTPUT_DIMENSION / compW,
|
|
4370
|
-
STORYBOARD_MAX_OUTPUT_DIMENSION / compH
|
|
4371
|
-
) : Math.min(1, THUMBNAIL_MAX_OUTPUT_WIDTH / compW, THUMBNAIL_MAX_OUTPUT_HEIGHT / compH);
|
|
4303
|
+
const outputScale = outputMode === "source" ? 1 : Math.min(1, THUMBNAIL_MAX_OUTPUT_WIDTH / compW, THUMBNAIL_MAX_OUTPUT_HEIGHT / compH);
|
|
4372
4304
|
const outputWidth = Math.max(1, Math.round(compW * outputScale));
|
|
4373
4305
|
const outputHeight = Math.max(1, Math.round(compH * outputScale));
|
|
4374
4306
|
const cacheKey = `${THUMBNAIL_CACHE_VERSION}${urlVersionKey}${projectSignatureKey}${manualEditsKey}${motionKey}${sourceKey}_${format}_${outputMode}_${compPath.replace(/\//g, "_")}_${compW}x${compH}_${outputWidth}x${outputHeight}_${sourceMtime}_${seekTime.toFixed(2)}${selectorKey}.${format === "png" ? "png" : "jpg"}`;
|
|
@@ -4380,8 +4312,8 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4380
4312
|
/* @__PURE__ */ new Set([...thumbnailGenerationCoordinator.protectedKeys(), cachePath])
|
|
4381
4313
|
);
|
|
4382
4314
|
}
|
|
4383
|
-
if (
|
|
4384
|
-
return new Response(new Uint8Array(
|
|
4315
|
+
if (existsSync7(cachePath)) {
|
|
4316
|
+
return new Response(new Uint8Array(readFileSync10(cachePath)), {
|
|
4385
4317
|
headers: { "Content-Type": contentType, "Cache-Control": "no-cache" }
|
|
4386
4318
|
});
|
|
4387
4319
|
}
|
|
@@ -4410,7 +4342,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4410
4342
|
if (!afterGeneration || afterGeneration.project.dir !== project.dir || afterGeneration.signature !== projectSignature || freshSignature !== projectSignature) {
|
|
4411
4343
|
return generated;
|
|
4412
4344
|
}
|
|
4413
|
-
if (!
|
|
4345
|
+
if (!existsSync7(cacheDir)) mkdirSync5(cacheDir, { recursive: true });
|
|
4414
4346
|
writeThumbnailAtomically(cachePath, generated);
|
|
4415
4347
|
return generated;
|
|
4416
4348
|
}
|
|
@@ -4436,7 +4368,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4436
4368
|
}
|
|
4437
4369
|
|
|
4438
4370
|
// src/routes/waveform.ts
|
|
4439
|
-
import { existsSync as
|
|
4371
|
+
import { existsSync as existsSync8, readFileSync as readFileSync11, statSync as statSync6 } from "fs";
|
|
4440
4372
|
import { join as join12 } from "path";
|
|
4441
4373
|
function registerWaveformRoutes(api, adapter) {
|
|
4442
4374
|
api.get("/projects/:id/waveform/*", async (c) => {
|
|
@@ -4451,8 +4383,8 @@ function registerWaveformRoutes(api, adapter) {
|
|
|
4451
4383
|
const cacheDir = join12(project.dir, ".waveform-cache");
|
|
4452
4384
|
const cachePath = join12(cacheDir, buildWaveformCacheKey(assetPath, stats));
|
|
4453
4385
|
try {
|
|
4454
|
-
if (isWaveformCacheDirectory(cacheDir) &&
|
|
4455
|
-
const peaks2 = JSON.parse(
|
|
4386
|
+
if (isWaveformCacheDirectory(cacheDir) && existsSync8(cachePath)) {
|
|
4387
|
+
const peaks2 = JSON.parse(readFileSync11(cachePath, "utf-8"));
|
|
4456
4388
|
return c.json({ peaks: peaks2 });
|
|
4457
4389
|
}
|
|
4458
4390
|
} catch {
|
|
@@ -4735,7 +4667,7 @@ function registerSelectionRoutes(api, adapter) {
|
|
|
4735
4667
|
|
|
4736
4668
|
// src/routes/media.ts
|
|
4737
4669
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
4738
|
-
import { existsSync as
|
|
4670
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
|
|
4739
4671
|
import { basename as basename2, dirname as dirname4, extname as extname2, join as join13 } from "path";
|
|
4740
4672
|
var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
4741
4673
|
".mp4",
|
|
@@ -4773,7 +4705,7 @@ function uniqueAssetPath(projectDir, assetPath) {
|
|
|
4773
4705
|
const ext = extname2(assetPath);
|
|
4774
4706
|
const withoutExt = assetPath.slice(0, -ext.length);
|
|
4775
4707
|
let candidate = assetPath;
|
|
4776
|
-
for (let index = 2;
|
|
4708
|
+
for (let index = 2; existsSync9(join13(projectDir, candidate)); index++) {
|
|
4777
4709
|
candidate = `${withoutExt}-${index}${ext}`;
|
|
4778
4710
|
}
|
|
4779
4711
|
return candidate;
|
|
@@ -4824,7 +4756,7 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
4824
4756
|
}
|
|
4825
4757
|
const filePath = resolveWithinProject(project.dir, assetPath);
|
|
4826
4758
|
if (!filePath) return c.json({ error: "forbidden" }, 403);
|
|
4827
|
-
if (!
|
|
4759
|
+
if (!existsSync9(filePath)) return c.json({ error: "media not found" }, 404);
|
|
4828
4760
|
return c.json({ path: assetPath, metadata: await readMediaMetadata(filePath) });
|
|
4829
4761
|
});
|
|
4830
4762
|
api.post(
|
|
@@ -4846,7 +4778,7 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
4846
4778
|
}
|
|
4847
4779
|
const inputPath = resolveWithinProject(project.dir, inputAssetPath);
|
|
4848
4780
|
if (!inputPath) return c.json({ error: "forbidden" }, 403);
|
|
4849
|
-
if (!
|
|
4781
|
+
if (!existsSync9(inputPath)) return c.json({ error: "input media not found" }, 404);
|
|
4850
4782
|
const inputIsVideo = isVideoPath(inputAssetPath);
|
|
4851
4783
|
const inputIsImage = isImagePath(inputAssetPath);
|
|
4852
4784
|
if (!inputIsVideo && !inputIsImage) {
|
|
@@ -4938,14 +4870,14 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
4938
4870
|
}
|
|
4939
4871
|
|
|
4940
4872
|
// src/routes/globalAssets.ts
|
|
4941
|
-
import { existsSync as
|
|
4873
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12 } from "fs";
|
|
4942
4874
|
import { homedir } from "os";
|
|
4943
4875
|
import { join as join14 } from "path";
|
|
4944
4876
|
function readGlobalAssets(home = homedir()) {
|
|
4945
4877
|
const manifestPath = join14(home, ".media", "manifest.jsonl");
|
|
4946
|
-
if (!
|
|
4878
|
+
if (!existsSync10(manifestPath)) return [];
|
|
4947
4879
|
const out = [];
|
|
4948
|
-
for (const line of
|
|
4880
|
+
for (const line of readFileSync12(manifestPath, "utf8").split("\n")) {
|
|
4949
4881
|
if (!line.trim()) continue;
|
|
4950
4882
|
try {
|
|
4951
4883
|
const rec = JSON.parse(line);
|
|
@@ -4966,7 +4898,6 @@ function registerGlobalAssetRoutes(api) {
|
|
|
4966
4898
|
function createStudioApi(adapter) {
|
|
4967
4899
|
const api = new Hono();
|
|
4968
4900
|
registerProjectRoutes(api, adapter);
|
|
4969
|
-
registerStoryboardRoutes(api, adapter);
|
|
4970
4901
|
registerFileRoutes(api, adapter);
|
|
4971
4902
|
registerPreviewRoutes(api, adapter);
|
|
4972
4903
|
registerLintRoutes(api, adapter);
|