@hyperframes/studio-server 0.8.82 → 0.8.83
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-G6IIFKXY.js → chunk-EL3UQI46.js} +21 -15
- package/dist/chunk-EL3UQI46.js.map +1 -0
- package/dist/{chunk-FTDTO3QP.js → chunk-HWBORC2U.js} +3 -3
- package/dist/{chunk-TJQ76IEA.js → chunk-WQ2WF2YJ.js} +29 -3
- package/dist/chunk-WQ2WF2YJ.js.map +1 -0
- package/dist/helpers/mediaCodecMap.js +1 -1
- package/dist/helpers/mediaProxyPreview.js +3 -3
- package/dist/helpers/proxyTranscoder.d.ts +1 -1
- package/dist/helpers/proxyTranscoder.js +2 -2
- package/dist/index.d.ts +11 -4
- package/dist/index.js +95 -58
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-G6IIFKXY.js.map +0 -1
- package/dist/chunk-TJQ76IEA.js.map +0 -1
- /package/dist/{chunk-FTDTO3QP.js.map → chunk-HWBORC2U.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -22,18 +22,21 @@ import {
|
|
|
22
22
|
isAutoProxyEnabled,
|
|
23
23
|
proxyEtagSalt,
|
|
24
24
|
resolvePreviewMediaCodecProbeCache
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-HWBORC2U.js";
|
|
26
26
|
import {
|
|
27
27
|
ProxyCapacityError,
|
|
28
28
|
ProxyTranscodeError,
|
|
29
29
|
resolveProxy
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-EL3UQI46.js";
|
|
31
31
|
import {
|
|
32
32
|
PROXY_VARIANT_CONFIG,
|
|
33
33
|
decideMediaProxyEligibility,
|
|
34
|
+
folderGone,
|
|
34
35
|
isInHiddenOrVendorDir,
|
|
36
|
+
isProjectRootMissing,
|
|
35
37
|
isProxyVariantRequest,
|
|
36
38
|
isSafePath,
|
|
39
|
+
mkdirWithinProject,
|
|
37
40
|
pinWithinProject,
|
|
38
41
|
probeAssetCodec,
|
|
39
42
|
probeMediaMetadata,
|
|
@@ -43,7 +46,7 @@ import {
|
|
|
43
46
|
resolveProxyVariantRequest,
|
|
44
47
|
resolveWithinProject,
|
|
45
48
|
walkDir
|
|
46
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-WQ2WF2YJ.js";
|
|
47
50
|
import {
|
|
48
51
|
STUDIO_MANUAL_EDITS_PATH,
|
|
49
52
|
createStudioManualEditsRenderBodyScript,
|
|
@@ -64,15 +67,7 @@ import { join as join2 } from "path";
|
|
|
64
67
|
// src/helpers/hfIdPersist.ts
|
|
65
68
|
import { ensureHfIds } from "@hyperframes/parsers/hf-ids";
|
|
66
69
|
import { createHash } from "crypto";
|
|
67
|
-
import {
|
|
68
|
-
closeSync,
|
|
69
|
-
constants,
|
|
70
|
-
fstatSync,
|
|
71
|
-
mkdirSync,
|
|
72
|
-
openSync,
|
|
73
|
-
readFileSync,
|
|
74
|
-
writeFileSync
|
|
75
|
-
} from "fs";
|
|
70
|
+
import { closeSync, constants, fstatSync, openSync, readFileSync, writeFileSync } from "fs";
|
|
76
71
|
import { dirname, join } from "path";
|
|
77
72
|
|
|
78
73
|
// src/helpers/atomicFile.ts
|
|
@@ -123,7 +118,7 @@ function stampProjectHfIds(projectDir) {
|
|
|
123
118
|
next[file] = hash;
|
|
124
119
|
}
|
|
125
120
|
try {
|
|
126
|
-
|
|
121
|
+
mkdirWithinProject(projectDir, dirname(recordPath));
|
|
127
122
|
writeFileSync(recordPath, JSON.stringify(next));
|
|
128
123
|
} catch {
|
|
129
124
|
}
|
|
@@ -408,7 +403,6 @@ import {
|
|
|
408
403
|
readlinkSync,
|
|
409
404
|
writeFileSync as writeFileSync5,
|
|
410
405
|
writeSync,
|
|
411
|
-
mkdirSync as mkdirSync4,
|
|
412
406
|
unlinkSync as unlinkSync2,
|
|
413
407
|
rmSync as rmSync3,
|
|
414
408
|
statSync as statSync3,
|
|
@@ -469,7 +463,7 @@ import { spawn } from "child_process";
|
|
|
469
463
|
import {
|
|
470
464
|
existsSync,
|
|
471
465
|
writeFileSync as writeFileSync2,
|
|
472
|
-
mkdirSync
|
|
466
|
+
mkdirSync,
|
|
473
467
|
lstatSync as lstatSync2,
|
|
474
468
|
mkdtempSync,
|
|
475
469
|
renameSync,
|
|
@@ -558,7 +552,7 @@ function isWaveformCacheDirectory(cacheDir) {
|
|
|
558
552
|
function writeWaveformCache(cachePath, peaks) {
|
|
559
553
|
const cacheDir = dirname2(cachePath);
|
|
560
554
|
try {
|
|
561
|
-
|
|
555
|
+
mkdirSync(cacheDir, { mode: 448 });
|
|
562
556
|
} catch (error) {
|
|
563
557
|
if (!(error instanceof Error && "code" in error && error.code === "EEXIST")) throw error;
|
|
564
558
|
}
|
|
@@ -585,7 +579,7 @@ import { tmpdir } from "os";
|
|
|
585
579
|
import { basename, join as join4 } from "path";
|
|
586
580
|
import { FFPROBE_PATH_ENV, findFfBinary as findFfBinary2 } from "@hyperframes/parsers/ff-binaries";
|
|
587
581
|
var VIDEO_EXT = /\.(mp4|webm|mov|mkv|avi|m4v|mxf|mts|m2ts|ts)$/i;
|
|
588
|
-
var AUDIO_EXT = /\.(mp3|wav|ogg|m4a|aac)$/i;
|
|
582
|
+
var AUDIO_EXT = /\.(mp3|wav|ogg|m4a|aac|flac)$/i;
|
|
589
583
|
var defaultRunner = spawnSync;
|
|
590
584
|
var FFPROBE_MISSING = "not checked: ffprobe was not found. Install FFmpeg or set HYPERFRAMES_FFPROBE_PATH.";
|
|
591
585
|
function validateUploadedMedia(filePath, runner = defaultRunner) {
|
|
@@ -644,7 +638,7 @@ function validateUploadedMediaBuffer(fileName, buffer, runner = defaultRunner) {
|
|
|
644
638
|
}
|
|
645
639
|
|
|
646
640
|
// src/helpers/backupJournal.ts
|
|
647
|
-
import {
|
|
641
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
|
|
648
642
|
import { Buffer as Buffer2 } from "buffer";
|
|
649
643
|
import { join as join5, relative as relative2 } from "path";
|
|
650
644
|
var DEFAULT_KEEP_PER_FILE = 10;
|
|
@@ -666,13 +660,14 @@ function snapshotBeforeWrite(projectDir, absPath, options = {}) {
|
|
|
666
660
|
const content = readFileSync3(absPath);
|
|
667
661
|
const relativePath = relative2(projectDir, absPath);
|
|
668
662
|
const backupDir = join5(projectDir, ".hyperframes", "backup");
|
|
669
|
-
|
|
663
|
+
mkdirWithinProject(projectDir, backupDir);
|
|
670
664
|
const backupKey = backupKeyForPath(relativePath);
|
|
671
665
|
const backupPath = nextBackupPath(backupDir, backupKey);
|
|
672
666
|
writeFileSync4(backupPath, content);
|
|
673
667
|
pruneBackups(backupDir, backupKey, options.keepPerFile ?? DEFAULT_KEEP_PER_FILE);
|
|
674
668
|
return { backupPath };
|
|
675
669
|
} catch (error) {
|
|
670
|
+
if (isProjectRootMissing(error)) throw error;
|
|
676
671
|
if (error && typeof error === "object" && "code" in error && (error.code === "ENOENT" || error.code === "EISDIR")) {
|
|
677
672
|
return { backupPath: null };
|
|
678
673
|
}
|
|
@@ -711,6 +706,15 @@ function pruneBackups(backupDir, backupKey, keepPerFile) {
|
|
|
711
706
|
}
|
|
712
707
|
}
|
|
713
708
|
|
|
709
|
+
// src/helpers/projectDirMissing.ts
|
|
710
|
+
var PROJECT_DIR_MISSING = { error: "not found", why: "project_dir_missing" };
|
|
711
|
+
var projectDirMissing = (c) => c.json(PROJECT_DIR_MISSING, 404);
|
|
712
|
+
function replaceWithProjectDirMissing(c, hostHeaders) {
|
|
713
|
+
hostHeaders.delete("content-type");
|
|
714
|
+
c.res = void 0;
|
|
715
|
+
c.res = Response.json(PROJECT_DIR_MISSING, { status: 404, headers: hostHeaders });
|
|
716
|
+
}
|
|
717
|
+
|
|
714
718
|
// src/helpers/fileVersion.ts
|
|
715
719
|
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
716
720
|
var RECEIPT_TTL_MS = 1e4;
|
|
@@ -905,6 +909,7 @@ import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
|
|
|
905
909
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
906
910
|
import { dirname as dirname3, relative as relative3, resolve as resolve2, sep as sep2 } from "path";
|
|
907
911
|
import { parseHTML } from "linkedom";
|
|
912
|
+
import { ProjectRootMissingError } from "@hyperframes/core";
|
|
908
913
|
var CompositionInsertionError = class extends Error {
|
|
909
914
|
constructor(message, status) {
|
|
910
915
|
super(message);
|
|
@@ -934,6 +939,7 @@ function positiveAttribute(root, ...names) {
|
|
|
934
939
|
}
|
|
935
940
|
function canonicalProjectPath(projectDir, candidate) {
|
|
936
941
|
if (!candidate) {
|
|
942
|
+
if (folderGone(projectDir)) throw new ProjectRootMissingError(projectDir);
|
|
937
943
|
throw new CompositionInsertionError("Composition source escapes the project", 400);
|
|
938
944
|
}
|
|
939
945
|
if (!existsSync2(candidate)) {
|
|
@@ -1186,10 +1192,8 @@ async function resolveProjectPath(c, adapter, route, opts) {
|
|
|
1186
1192
|
if (!project) {
|
|
1187
1193
|
return { error: c.json({ error: "not found" }, 404) };
|
|
1188
1194
|
}
|
|
1189
|
-
if (
|
|
1190
|
-
return {
|
|
1191
|
-
error: c.json({ error: "not found", why: "project_dir_missing" }, 404)
|
|
1192
|
-
};
|
|
1195
|
+
if (folderGone(project.dir)) {
|
|
1196
|
+
return { error: projectDirMissing(c) };
|
|
1193
1197
|
}
|
|
1194
1198
|
const filePath = requestSubPath(c.req.url, `projects/:id/${route}`);
|
|
1195
1199
|
if (filePath.includes("\0")) {
|
|
@@ -1393,9 +1397,8 @@ async function parseMutationBody(c) {
|
|
|
1393
1397
|
}
|
|
1394
1398
|
return { target: body.target, body };
|
|
1395
1399
|
}
|
|
1396
|
-
function ensureDir(filePath) {
|
|
1397
|
-
|
|
1398
|
-
if (!existsSync3(dir)) mkdirSync4(dir, { recursive: true });
|
|
1400
|
+
function ensureDir(projectDir, filePath) {
|
|
1401
|
+
mkdirWithinProject(projectDir, dirname4(filePath));
|
|
1399
1402
|
}
|
|
1400
1403
|
function generateCopyPath(projectDir, originalPath) {
|
|
1401
1404
|
const ext = originalPath.includes(".") ? "." + originalPath.split(".").pop() : "";
|
|
@@ -2645,7 +2648,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2645
2648
|
let backup = { backupPath: null };
|
|
2646
2649
|
let overwrote;
|
|
2647
2650
|
if (createOnly) {
|
|
2648
|
-
ensureDir(res.absPath);
|
|
2651
|
+
ensureDir(res.project.dir, res.absPath);
|
|
2649
2652
|
let fd;
|
|
2650
2653
|
try {
|
|
2651
2654
|
fd = openSync2(res.absPath, "wx");
|
|
@@ -2727,7 +2730,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
2727
2730
|
api.post("/projects/:id/files/*", async (c) => {
|
|
2728
2731
|
const res = await resolveProjectFile(c, adapter);
|
|
2729
2732
|
if ("error" in res) return res.error;
|
|
2730
|
-
ensureDir(res.absPath);
|
|
2733
|
+
ensureDir(res.project.dir, res.absPath);
|
|
2731
2734
|
const body = Buffer.from(await c.req.arrayBuffer());
|
|
2732
2735
|
try {
|
|
2733
2736
|
writeFileSync5(res.absPath, body, { flag: "wx" });
|
|
@@ -3268,7 +3271,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3268
3271
|
if (existsSync3(newAbs)) {
|
|
3269
3272
|
return c.json({ error: "already exists" }, 409);
|
|
3270
3273
|
}
|
|
3271
|
-
ensureDir(newAbs);
|
|
3274
|
+
ensureDir(res.project.dir, newAbs);
|
|
3272
3275
|
renameSync2(res.absPath, newAbs);
|
|
3273
3276
|
const updatedFiles = updateReferences(res.project.dir, res.filePath, body.newPath);
|
|
3274
3277
|
return c.json({ ok: true, path: body.newPath, updatedReferences: updatedFiles });
|
|
@@ -3289,7 +3292,7 @@ function registerFileRoutes(api, adapter) {
|
|
|
3289
3292
|
if (!destAbs) {
|
|
3290
3293
|
return c.json({ error: "forbidden" }, 403);
|
|
3291
3294
|
}
|
|
3292
|
-
ensureDir(destAbs);
|
|
3295
|
+
ensureDir(project.dir, destAbs);
|
|
3293
3296
|
try {
|
|
3294
3297
|
writeFileSync5(destAbs, readFileSync6(srcAbs), { flag: "wx" });
|
|
3295
3298
|
} catch (error) {
|
|
@@ -3310,12 +3313,14 @@ function registerFileRoutes(api, adapter) {
|
|
|
3310
3313
|
async (c) => {
|
|
3311
3314
|
const project = await adapter.resolveProject(c.req.param("id"));
|
|
3312
3315
|
if (!project) return c.json({ error: "not found" }, 404);
|
|
3316
|
+
if (folderGone(project.dir)) return projectDirMissing(c);
|
|
3313
3317
|
const subDir = c.req.query("dir") ?? "";
|
|
3314
3318
|
const targetDir = subDir ? resolveWithinProject(project.dir, subDir) : project.dir;
|
|
3315
3319
|
if (!targetDir) return c.json({ error: "forbidden" }, 403);
|
|
3316
|
-
if (subDir && !existsSync3(targetDir)) mkdirSync4(targetDir, { recursive: true });
|
|
3317
3320
|
const formData = await c.req.formData();
|
|
3321
|
+
mkdirWithinProject(project.dir, targetDir);
|
|
3318
3322
|
const result = await processUploadedFiles(formData, targetDir, project.dir);
|
|
3323
|
+
if (folderGone(project.dir)) return projectDirMissing(c);
|
|
3319
3324
|
return c.json(
|
|
3320
3325
|
{
|
|
3321
3326
|
ok: true,
|
|
@@ -4134,6 +4139,7 @@ ${etag}`;
|
|
|
4134
4139
|
if (err instanceof ProxyCapacityError) {
|
|
4135
4140
|
return c.text(err.message, 503, { "Retry-After": "5" });
|
|
4136
4141
|
}
|
|
4142
|
+
if (isProjectRootMissing(err)) throw err;
|
|
4137
4143
|
const message = err instanceof ProxyTranscodeError ? err.message : "proxy transcode failed";
|
|
4138
4144
|
return c.text(message, 502);
|
|
4139
4145
|
}
|
|
@@ -4235,7 +4241,7 @@ function registerLintRoutes(api, adapter) {
|
|
|
4235
4241
|
|
|
4236
4242
|
// src/routes/render.ts
|
|
4237
4243
|
import { streamSSE } from "hono/streaming";
|
|
4238
|
-
import { existsSync as existsSync6, readFileSync as readFileSync10,
|
|
4244
|
+
import { existsSync as existsSync6, readFileSync as readFileSync10, unlinkSync as unlinkSync3, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
|
|
4239
4245
|
import { join as join11 } from "path";
|
|
4240
4246
|
import { VALID_CANVAS_RESOLUTIONS } from "@hyperframes/parsers";
|
|
4241
4247
|
import { formatRenderOutputTimestamp, parseFps } from "@hyperframes/core";
|
|
@@ -4288,6 +4294,7 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4288
4294
|
let composition;
|
|
4289
4295
|
if (typeof body.composition === "string" && body.composition.length > 0) {
|
|
4290
4296
|
if (!resolveWithinProject(project.dir, body.composition)) {
|
|
4297
|
+
if (folderGone(project.dir)) return projectDirMissing(c);
|
|
4291
4298
|
return c.json({ error: "composition path must be within the project directory" }, 400);
|
|
4292
4299
|
}
|
|
4293
4300
|
composition = body.composition;
|
|
@@ -4302,7 +4309,7 @@ function registerRenderRoutes(api, adapter) {
|
|
|
4302
4309
|
const now = /* @__PURE__ */ new Date();
|
|
4303
4310
|
const jobId = `${project.id}_${formatRenderOutputTimestamp(now)}`;
|
|
4304
4311
|
const rendersDir = adapter.rendersDir(project);
|
|
4305
|
-
|
|
4312
|
+
mkdirWithinProject(project.dir, rendersDir);
|
|
4306
4313
|
const ext = FORMAT_EXT[format] ?? ".mp4";
|
|
4307
4314
|
const outputPath = join11(rendersDir, `${jobId}${ext}`);
|
|
4308
4315
|
const jobState = adapter.startRender({
|
|
@@ -4683,7 +4690,6 @@ import {
|
|
|
4683
4690
|
closeSync as closeSync3,
|
|
4684
4691
|
fstatSync as fstatSync3,
|
|
4685
4692
|
openSync as openSync3,
|
|
4686
|
-
mkdirSync as mkdirSync6,
|
|
4687
4693
|
readFileSync as readFileSync12,
|
|
4688
4694
|
readdirSync as readdirSync5,
|
|
4689
4695
|
renameSync as renameSync3,
|
|
@@ -4900,7 +4906,8 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4900
4906
|
headers: { "Content-Type": contentType, "Cache-Control": "no-cache" }
|
|
4901
4907
|
});
|
|
4902
4908
|
}
|
|
4903
|
-
if (url.searchParams.get("cached") === "1")
|
|
4909
|
+
if (url.searchParams.get("cached") === "1")
|
|
4910
|
+
return c.body(null, 204, { "Cache-Control": "no-cache" });
|
|
4904
4911
|
try {
|
|
4905
4912
|
const buffer = await thumbnailGenerationCoordinator.acquire(
|
|
4906
4913
|
cachePath,
|
|
@@ -4926,7 +4933,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4926
4933
|
if (afterGeneration?.project.dir !== project.dir || !inputsUnchanged(afterGeneration.signature) || !inputsUnchanged(createProjectSignature(project.dir))) {
|
|
4927
4934
|
return generated;
|
|
4928
4935
|
}
|
|
4929
|
-
|
|
4936
|
+
mkdirWithinProject(project.dir, cacheDir);
|
|
4930
4937
|
writeThumbnailAtomically(cachePath, generated);
|
|
4931
4938
|
return generated;
|
|
4932
4939
|
}
|
|
@@ -4945,6 +4952,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4945
4952
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
4946
4953
|
return new Response(null, { status: 499 });
|
|
4947
4954
|
}
|
|
4955
|
+
if (isProjectRootMissing(err)) throw err;
|
|
4948
4956
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4949
4957
|
return c.json({ error: `Thumbnail generation failed: ${msg}` }, 500);
|
|
4950
4958
|
}
|
|
@@ -5139,6 +5147,7 @@ function registerRegistryRoutes(api, adapter) {
|
|
|
5139
5147
|
}
|
|
5140
5148
|
const project = await adapter.resolveProject(c.req.param("id"));
|
|
5141
5149
|
if (!project) return c.json({ error: "Project not found" }, 404);
|
|
5150
|
+
if (folderGone(project.dir)) return projectDirMissing(c);
|
|
5142
5151
|
const body = await c.req.json().catch(() => null);
|
|
5143
5152
|
if (!body?.blockName) {
|
|
5144
5153
|
return c.json({ error: "blockName is required" }, 400);
|
|
@@ -5147,6 +5156,7 @@ function registerRegistryRoutes(api, adapter) {
|
|
|
5147
5156
|
const result = await adapter.installRegistryBlock({ project, blockName: body.blockName });
|
|
5148
5157
|
return c.json(result);
|
|
5149
5158
|
} catch (err) {
|
|
5159
|
+
if (isProjectRootMissing(err)) return projectDirMissing(c);
|
|
5150
5160
|
const message = err instanceof Error ? err.message : "Install failed";
|
|
5151
5161
|
return c.json({ error: message }, 500);
|
|
5152
5162
|
}
|
|
@@ -5253,7 +5263,7 @@ function registerSelectionRoutes(api, adapter) {
|
|
|
5253
5263
|
|
|
5254
5264
|
// src/routes/media.ts
|
|
5255
5265
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
5256
|
-
import { existsSync as existsSync8
|
|
5266
|
+
import { existsSync as existsSync8 } from "fs";
|
|
5257
5267
|
import { basename as basename2, dirname as dirname6, extname as extname2, join as join14, posix } from "path";
|
|
5258
5268
|
var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5259
5269
|
".mp4",
|
|
@@ -5410,8 +5420,8 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
5410
5420
|
return c.json({ error: "forbidden" }, 403);
|
|
5411
5421
|
}
|
|
5412
5422
|
}
|
|
5413
|
-
|
|
5414
|
-
if (backgroundOutputPath)
|
|
5423
|
+
mkdirWithinProject(project.dir, dirname6(outputPath));
|
|
5424
|
+
if (backgroundOutputPath) mkdirWithinProject(project.dir, dirname6(backgroundOutputPath));
|
|
5415
5425
|
const jobId = makeJobId(project.id, mediaJobs);
|
|
5416
5426
|
const state = adapter.startBackgroundRemoval({
|
|
5417
5427
|
project,
|
|
@@ -5499,7 +5509,7 @@ import {
|
|
|
5499
5509
|
existsSync as existsSync11,
|
|
5500
5510
|
lstatSync as lstatSync4,
|
|
5501
5511
|
statSync as statSync10,
|
|
5502
|
-
mkdirSync as
|
|
5512
|
+
mkdirSync as mkdirSync5,
|
|
5503
5513
|
readdirSync as readdirSync7,
|
|
5504
5514
|
readFileSync as readFileSync19,
|
|
5505
5515
|
realpathSync,
|
|
@@ -5584,7 +5594,7 @@ import { dirname as dirname8, join as join19 } from "path";
|
|
|
5584
5594
|
|
|
5585
5595
|
// src/history/historyId.ts
|
|
5586
5596
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
5587
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
5597
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync15, statSync as statSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
5588
5598
|
import { basename as basename3, join as join17, resolve as resolve7 } from "path";
|
|
5589
5599
|
var ID_PATH = join17(".hyperframes", "history-id");
|
|
5590
5600
|
var ID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
@@ -5632,7 +5642,7 @@ function projectHistoryId(projectDir, historyRoot) {
|
|
|
5632
5642
|
);
|
|
5633
5643
|
if (!id || existsSync10(join17(historyRoot, id)) && !isRecordedFolder(join17(historyRoot, id), folder)) {
|
|
5634
5644
|
id = randomUUID6();
|
|
5635
|
-
|
|
5645
|
+
mkdirWithinProject(dir, join17(dir, ".hyperframes"));
|
|
5636
5646
|
writeFileSync7(join17(dir, ID_PATH), `${id}
|
|
5637
5647
|
`);
|
|
5638
5648
|
}
|
|
@@ -5647,13 +5657,13 @@ var HistoryIdError = class extends Error {
|
|
|
5647
5657
|
};
|
|
5648
5658
|
function recordProject(historyDir, dir, folder) {
|
|
5649
5659
|
const record = { dir, ino: folder.ino, born: folder.birthtimeMs, dev: folder.dev };
|
|
5650
|
-
|
|
5660
|
+
mkdirSync2(historyDir, { recursive: true });
|
|
5651
5661
|
replaceFileAtomically(join17(historyDir, "project.json"), JSON.stringify(record), 420);
|
|
5652
5662
|
}
|
|
5653
5663
|
|
|
5654
5664
|
// src/history/ownerLock.ts
|
|
5655
5665
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
5656
|
-
import { linkSync, mkdirSync as
|
|
5666
|
+
import { linkSync, mkdirSync as mkdirSync3, readFileSync as readFileSync16, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
5657
5667
|
import { join as join18 } from "path";
|
|
5658
5668
|
var HistoryBusyError = class extends Error {
|
|
5659
5669
|
constructor(pid) {
|
|
@@ -5715,7 +5725,7 @@ function evictDeadOwner(file) {
|
|
|
5715
5725
|
async function takeHistoryOwnership(home, waitMs) {
|
|
5716
5726
|
const file = join18(home, "owner.pid");
|
|
5717
5727
|
const deadline = Date.now() + waitMs;
|
|
5718
|
-
|
|
5728
|
+
mkdirSync3(home, { recursive: true });
|
|
5719
5729
|
for (; ; ) {
|
|
5720
5730
|
if (claim(file)) {
|
|
5721
5731
|
let held = true;
|
|
@@ -5737,6 +5747,22 @@ var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
|
5737
5747
|
var KEEP_GONE_PROJECT_HISTORY_MS = 14 * DAY_MS;
|
|
5738
5748
|
var PRUNED_PREFIX = ".pruned-";
|
|
5739
5749
|
var LAST_PRUNE_FILE = ".last-prune";
|
|
5750
|
+
function listProjectHistories(historyRoot) {
|
|
5751
|
+
return namesIn(historyRoot).filter(isHistoryId).flatMap((id) => {
|
|
5752
|
+
const home = join19(historyRoot, id);
|
|
5753
|
+
const projectDir = readRecord(home)?.dir;
|
|
5754
|
+
if (typeof projectDir !== "string" || readId(projectDir) !== id) return [];
|
|
5755
|
+
return [{ id, projectDir, lastUsedMs: lastUsed(home) }];
|
|
5756
|
+
});
|
|
5757
|
+
}
|
|
5758
|
+
function namesIn(historyRoot) {
|
|
5759
|
+
try {
|
|
5760
|
+
return readdirSync6(historyRoot);
|
|
5761
|
+
} catch (error) {
|
|
5762
|
+
if (error.code === "ENOENT") return [];
|
|
5763
|
+
throw error;
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5740
5766
|
async function pruneGoneProjectHistories(historyRoot, options = {}) {
|
|
5741
5767
|
return (await pruneWithin(historyRoot, options)).pruned;
|
|
5742
5768
|
}
|
|
@@ -5749,13 +5775,7 @@ async function pruneWithin(historyRoot, {
|
|
|
5749
5775
|
measure = true
|
|
5750
5776
|
}) {
|
|
5751
5777
|
const started = performance.now();
|
|
5752
|
-
|
|
5753
|
-
try {
|
|
5754
|
-
names = readdirSync6(historyRoot);
|
|
5755
|
-
} catch (error) {
|
|
5756
|
-
if (error.code === "ENOENT") return { pruned: [], finished: true };
|
|
5757
|
-
throw error;
|
|
5758
|
-
}
|
|
5778
|
+
const names = namesIn(historyRoot);
|
|
5759
5779
|
const pruned = [];
|
|
5760
5780
|
for (const name of names) {
|
|
5761
5781
|
if (performance.now() - started >= budgetMs) return { pruned, finished: false };
|
|
@@ -5869,7 +5889,7 @@ function pruneGoneProjectHistoriesDaily(historyRoot, now, { onError, budgetMs }
|
|
|
5869
5889
|
}
|
|
5870
5890
|
|
|
5871
5891
|
// src/history/historyLog.ts
|
|
5872
|
-
import { closeSync as closeSync5, constants as constants4, mkdirSync as
|
|
5892
|
+
import { closeSync as closeSync5, constants as constants4, mkdirSync as mkdirSync4, openSync as openSync5, readFileSync as readFileSync18, writeSync as writeSync2 } from "fs";
|
|
5873
5893
|
import { dirname as dirname9 } from "path";
|
|
5874
5894
|
var START = "start";
|
|
5875
5895
|
function parseRecord(line) {
|
|
@@ -5922,7 +5942,7 @@ function writeLog(file, log) {
|
|
|
5922
5942
|
...log.entries.map((entry) => ({ type: "entry", entry })),
|
|
5923
5943
|
...[...log.pins].map((id) => ({ type: "pin", id, pinned: true }))
|
|
5924
5944
|
];
|
|
5925
|
-
|
|
5945
|
+
mkdirSync4(dirname9(file), { recursive: true });
|
|
5926
5946
|
replaceFileAtomically(file, records.map((r) => `${JSON.stringify(r)}
|
|
5927
5947
|
`).join(""), 420);
|
|
5928
5948
|
}
|
|
@@ -6170,7 +6190,7 @@ var Engine = class {
|
|
|
6170
6190
|
}
|
|
6171
6191
|
saveStatCache() {
|
|
6172
6192
|
const files = JSON.stringify(Object.fromEntries(this.tracked));
|
|
6173
|
-
|
|
6193
|
+
mkdirSync5(this.home, { recursive: true });
|
|
6174
6194
|
replaceFileAtomically(join20(this.home, "stat.json"), files, 420);
|
|
6175
6195
|
}
|
|
6176
6196
|
/**
|
|
@@ -6884,6 +6904,21 @@ function registerHistoryRoutes(api, adapter) {
|
|
|
6884
6904
|
// src/createStudioApi.ts
|
|
6885
6905
|
function createStudioApi(adapter) {
|
|
6886
6906
|
const api = new Hono();
|
|
6907
|
+
api.use(async function answerProjectDirMissingAfterErrorHandlers(c, next) {
|
|
6908
|
+
const hostHeaders = new Headers(c.res.headers);
|
|
6909
|
+
await next();
|
|
6910
|
+
if (isProjectRootMissing(c.error)) replaceWithProjectDirMissing(c, hostHeaders);
|
|
6911
|
+
});
|
|
6912
|
+
api.use("/projects/:id/*", async function answerProjectDirMissingForVanishedFolder(c, next) {
|
|
6913
|
+
const hostHeaders = new Headers(c.res.headers);
|
|
6914
|
+
const dirBeforeRoute = await Promise.resolve().then(() => adapter.resolveProject(c.req.param("id"))).then(
|
|
6915
|
+
(project) => project?.dir,
|
|
6916
|
+
() => void 0
|
|
6917
|
+
);
|
|
6918
|
+
await next();
|
|
6919
|
+
if (c.res.status >= 403 && dirBeforeRoute && folderGone(dirBeforeRoute))
|
|
6920
|
+
replaceWithProjectDirMissing(c, hostHeaders);
|
|
6921
|
+
});
|
|
6887
6922
|
registerProjectRoutes(api, adapter);
|
|
6888
6923
|
registerFileRoutes(api, adapter);
|
|
6889
6924
|
registerPreviewRoutes(api, adapter);
|
|
@@ -6980,9 +7015,10 @@ function numericAttribute(element, name) {
|
|
|
6980
7015
|
|
|
6981
7016
|
// src/helpers/previewDocumentStore.ts
|
|
6982
7017
|
import { createHash as createHash8 } from "crypto";
|
|
6983
|
-
import {
|
|
7018
|
+
import { readFileSync as readFileSync20, readdirSync as readdirSync8, rmSync as rmSync7 } from "fs";
|
|
6984
7019
|
import { join as join21 } from "path";
|
|
6985
|
-
function createPreviewDocumentStore(
|
|
7020
|
+
function createPreviewDocumentStore(projectDir, salt) {
|
|
7021
|
+
const dir = join21(projectDir, ".hyperframes", "preview");
|
|
6986
7022
|
const fileFor = (key) => join21(dir, `${createHash8("sha256").update(`${salt}
|
|
6987
7023
|
${key}`).digest("hex").slice(0, 32)}.html`);
|
|
6988
7024
|
return {
|
|
@@ -6995,7 +7031,7 @@ ${key}`).digest("hex").slice(0, 32)}.html`);
|
|
|
6995
7031
|
},
|
|
6996
7032
|
write(key, html) {
|
|
6997
7033
|
try {
|
|
6998
|
-
|
|
7034
|
+
mkdirWithinProject(projectDir, dir);
|
|
6999
7035
|
const file = fileFor(key);
|
|
7000
7036
|
replaceFileAtomically(file, html, 420);
|
|
7001
7037
|
for (const name of readdirSync8(dir)) {
|
|
@@ -7109,6 +7145,7 @@ export {
|
|
|
7109
7145
|
historyCache,
|
|
7110
7146
|
identifyFileWrite,
|
|
7111
7147
|
isSafePath,
|
|
7148
|
+
listProjectHistories,
|
|
7112
7149
|
openProjectHistory,
|
|
7113
7150
|
parseSourceDocument,
|
|
7114
7151
|
patchElementInHtml,
|