@hyperframes/studio-server 0.8.136 → 0.8.138

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.
@@ -2,12 +2,12 @@ import {
2
2
  PROXY_VARIANT_CONFIG,
3
3
  probeFirstFrameColour,
4
4
  probeMediaMetadata
5
- } from "./chunk-I3DRHG52.js";
5
+ } from "./chunk-74JVVCWL.js";
6
6
  import {
7
7
  mkdirWithinProject,
8
8
  realProjectRoot,
9
9
  realpath
10
- } from "./chunk-TTWLTYSQ.js";
10
+ } from "./chunk-DTKK7TKL.js";
11
11
 
12
12
  // src/helpers/proxyTranscoder.ts
13
13
  import { spawn } from "child_process";
@@ -481,4 +481,4 @@ export {
481
481
  proxyActivityMark,
482
482
  resolveProxy
483
483
  };
484
- //# sourceMappingURL=chunk-YOBWV2CY.js.map
484
+ //# sourceMappingURL=chunk-6RFZWPPN.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  realpath
3
- } from "./chunk-TTWLTYSQ.js";
3
+ } from "./chunk-DTKK7TKL.js";
4
4
 
5
5
  // src/helpers/mediaCodecMap.ts
6
6
  import { existsSync, statSync } from "fs";
@@ -346,4 +346,4 @@ export {
346
346
  probeAssetCodec,
347
347
  scanProjectMediaCodecMap
348
348
  };
349
- //# sourceMappingURL=chunk-I3DRHG52.js.map
349
+ //# sourceMappingURL=chunk-74JVVCWL.js.map
@@ -2,7 +2,7 @@ import {
2
2
  isInHiddenOrVendorDir,
3
3
  mkdirWithinProject,
4
4
  walkDir
5
- } from "./chunk-TTWLTYSQ.js";
5
+ } from "./chunk-DTKK7TKL.js";
6
6
 
7
7
  // src/helpers/hfIdPersist.ts
8
8
  import { ensureHfIds } from "@hyperframes/parsers/hf-ids";
@@ -95,4 +95,4 @@ export {
95
95
  stampProjectHfIds,
96
96
  stampFileHfIds
97
97
  };
98
- //# sourceMappingURL=chunk-7ON477WG.js.map
98
+ //# sourceMappingURL=chunk-DG2VOIGR.js.map
@@ -1,16 +1,26 @@
1
+ import {
2
+ STUDIO_MANUAL_EDITS_PATH
3
+ } from "./chunk-6XMC64FJ.js";
4
+ import {
5
+ STUDIO_MOTION_PATH
6
+ } from "./chunk-4ETS2LXI.js";
7
+
1
8
  // src/helpers/safePath.ts
2
9
  import { basename, dirname, isAbsolute, join, relative, sep } from "path";
3
10
  import { readdirSync } from "fs";
4
- import { realpath, resolveWithinProject } from "@hyperframes/core/safe-path";
11
+ import { realpath, resolveWithinProject as resolveInProject } from "@hyperframes/core/safe-path";
5
12
  import {
6
13
  folderGone,
7
14
  isProjectRootMissing,
8
15
  isSafePath,
9
16
  mkdirWithinProject,
10
17
  realpath as realpath2,
11
- realProjectRoot,
12
- resolveWithinProject as resolveWithinProject2
18
+ realProjectRoot
13
19
  } from "@hyperframes/core/safe-path";
20
+ function resolveWithinProject(base, relativePath) {
21
+ const resolved = resolveInProject(base, relativePath);
22
+ return resolved && !isPrivateProjectFile(base, resolved) ? resolved : null;
23
+ }
14
24
  function realFilePath(filePath) {
15
25
  try {
16
26
  return realpath(filePath);
@@ -19,6 +29,18 @@ function realFilePath(filePath) {
19
29
  return dir === filePath ? filePath : join(realFilePath(dir), basename(filePath));
20
30
  }
21
31
  }
32
+ var STUDIO_FILES = /* @__PURE__ */ new Set([STUDIO_MOTION_PATH, STUDIO_MANUAL_EDITS_PATH]);
33
+ var STUDIO_FOLDERS = [".hyperframes/prepared-assets", ".hyperframes/requests"];
34
+ function isPrivateProjectPath(relPath) {
35
+ const path = relPath.toLowerCase();
36
+ return (path === ".hyperframes" || path.startsWith(".hyperframes/")) && !STUDIO_FILES.has(path) && !STUDIO_FOLDERS.some((folder) => path === folder || path.startsWith(`${folder}/`));
37
+ }
38
+ function isPrivateProjectFile(projectDir, filePath) {
39
+ const rel = (from, to) => relative(from, to).split(sep).join("/");
40
+ return [rel(projectDir, filePath), rel(realFilePath(projectDir), realFilePath(filePath))].some(
41
+ isPrivateProjectPath
42
+ );
43
+ }
22
44
  function pinWithinProject(base, relativePath) {
23
45
  const checked = resolveWithinProject(base, relativePath);
24
46
  if (!checked) return null;
@@ -64,7 +86,10 @@ function walkDir(dir, prefix = "") {
64
86
  }
65
87
 
66
88
  export {
89
+ resolveWithinProject,
67
90
  realFilePath,
91
+ isPrivateProjectPath,
92
+ isPrivateProjectFile,
68
93
  pinWithinProject,
69
94
  isInHiddenOrVendorDir,
70
95
  walkDir,
@@ -73,7 +98,6 @@ export {
73
98
  isSafePath,
74
99
  mkdirWithinProject,
75
100
  realpath2 as realpath,
76
- realProjectRoot,
77
- resolveWithinProject2 as resolveWithinProject
101
+ realProjectRoot
78
102
  };
79
- //# sourceMappingURL=chunk-TTWLTYSQ.js.map
103
+ //# sourceMappingURL=chunk-DTKK7TKL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/helpers/safePath.ts"],"sourcesContent":["import { basename, dirname, isAbsolute, join, relative, sep } from \"node:path\";\nimport { readdirSync, type Dirent } from \"node:fs\";\nimport { realpath, resolveWithinProject as resolveInProject } from \"@hyperframes/core/safe-path\";\nimport { STUDIO_MANUAL_EDITS_PATH } from \"./manualEditsRenderScript.js\";\nimport { STUDIO_MOTION_PATH } from \"./studioMotionRenderScript.js\";\n\n// `isSafePath` lives at the package root so non-studio-api layers (compiler,\n// CLI, engine) can share it without a backwards dependency on studio-api.\n// Re-exported here for back-compat with existing `../helpers/safePath.js` imports.\nexport {\n folderGone,\n isProjectRootMissing,\n isSafePath,\n mkdirWithinProject,\n realpath,\n realProjectRoot,\n} from \"@hyperframes/core/safe-path\";\n\n/** Core's `resolveWithinProject`, closed to the desktop app's private files for every route a request path reaches. */\nexport function resolveWithinProject(base: string, relativePath: string): string | null {\n const resolved = resolveInProject(base, relativePath);\n return resolved && !isPrivateProjectFile(base, resolved) ? resolved : null;\n}\n\n/** The real path; for a path not there (yet, or any more), the nearest existing folder's real path plus the rest. */\nexport function realFilePath(filePath: string): string {\n try {\n return realpath(filePath);\n } catch {\n const dir = dirname(filePath);\n return dir === filePath ? filePath : join(realFilePath(dir), basename(filePath));\n }\n}\n\n// `.hyperframes/` holds the desktop app's and the CLI's own records (a hand-off, chat history, read marks): no route\n// reaches any of it, the folder itself included, but Studio's own files and the app's request pictures.\nconst STUDIO_FILES = new Set([STUDIO_MOTION_PATH, STUDIO_MANUAL_EDITS_PATH]);\nconst STUDIO_FOLDERS = [\".hyperframes/prepared-assets\", \".hyperframes/requests\"];\n\n/** For a project-relative path with `/` separators. */\nexport function isPrivateProjectPath(relPath: string): boolean {\n const path = relPath.toLowerCase();\n return (\n (path === \".hyperframes\" || path.startsWith(\".hyperframes/\")) &&\n !STUDIO_FILES.has(path) &&\n !STUDIO_FOLDERS.some((folder) => path === folder || path.startsWith(`${folder}/`))\n );\n}\n\nexport function isPrivateProjectFile(projectDir: string, filePath: string): boolean {\n const rel = (from: string, to: string) => relative(from, to).split(sep).join(\"/\");\n return [rel(projectDir, filePath), rel(realFilePath(projectDir), realFilePath(filePath))].some(\n isPrivateProjectPath,\n );\n}\n\n/**\n * `resolveWithinProject` with every link inside the project resolved now, so a write to the result lands on the\n * file checked here even if a link on the way is retargeted before the write.\n */\nexport function pinWithinProject(base: string, relativePath: string): string | null {\n const checked = resolveWithinProject(base, relativePath);\n if (!checked) return null;\n const inside = relative(realFilePath(base), realFilePath(checked));\n const escapes = inside === \"..\" || inside.startsWith(`..${sep}`) || isAbsolute(inside);\n return escapes ? null : join(base, inside);\n}\n\nconst IGNORE_DIRS = new Set([\n \".thumbnails\",\n \".transcode-cache\",\n \".waveform-cache\",\n \"node_modules\",\n \".git\",\n]);\n\nfunction shouldIgnoreDir(rel: string): boolean {\n return rel === \".hyperframes/backup\";\n}\n\n/**\n * True when any directory segment of a relative path is a dot-directory or\n * node_modules. Projects that vendor tooling assets under dot-directories\n * (.hyperframes/, .cache/, …) ship example/preset HTML that must not surface\n * as project compositions or studio lint targets (#1384). The file tree is\n * deliberately not filtered — this only gates discovery.\n */\nexport function isInHiddenOrVendorDir(relPath: string): boolean {\n const segments = relPath.split(\"/\");\n return segments.slice(0, -1).some((seg) => seg.startsWith(\".\") || seg === \"node_modules\");\n}\n\nconst UNREADABLE_DIR_CODES = new Set([\"EACCES\", \"EPERM\", \"ENOENT\", \"ENOTDIR\"]);\n\n/** Recursively walk a directory and return relative file paths. A subfolder that is unreadable\n * or removed mid-walk is skipped; any other failure, or an unreadable `dir` itself, throws. */\nexport function walkDir(dir: string, prefix = \"\"): string[] {\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (!prefix || !code || !UNREADABLE_DIR_CODES.has(code)) throw err;\n return [];\n }\n const files: string[] = [];\n for (const entry of entries) {\n const rel = prefix ? `${prefix}/${entry.name}` : entry.name;\n if (IGNORE_DIRS.has(entry.name) || shouldIgnoreDir(rel)) continue;\n if (entry.isDirectory()) {\n files.push(...walkDir(join(dir, entry.name), rel));\n } else {\n files.push(rel);\n }\n }\n return files;\n}\n"],"mappings":"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","names":["realpath"]}
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  PROXY_PARAMS_VERSION,
3
3
  resolveProxy
4
- } from "./chunk-YOBWV2CY.js";
4
+ } from "./chunk-6RFZWPPN.js";
5
5
  import {
6
6
  createMediaCodecProbeCache,
7
7
  proxyVariantFor,
8
8
  recordProxyPrewarm,
9
9
  scanProjectMediaCodecMap,
10
10
  shouldPrewarmProxy
11
- } from "./chunk-I3DRHG52.js";
11
+ } from "./chunk-74JVVCWL.js";
12
12
 
13
13
  // src/helpers/mediaProxyPreview.ts
14
14
  import { resolve } from "path";
@@ -63,4 +63,4 @@ export {
63
63
  injectMediaCodecMapIntoHtml,
64
64
  injectMediaCodecMap
65
65
  };
66
- //# sourceMappingURL=chunk-WK3ZL3SN.js.map
66
+ //# sourceMappingURL=chunk-G7FEKQ32.js.map
@@ -2,7 +2,7 @@ import {
2
2
  isSafePath,
3
3
  mkdirWithinProject,
4
4
  realFilePath
5
- } from "./chunk-TTWLTYSQ.js";
5
+ } from "./chunk-DTKK7TKL.js";
6
6
 
7
7
  // src/history/projectHistory.ts
8
8
  import { randomUUID as randomUUID6 } from "crypto";
@@ -1750,4 +1750,4 @@ export {
1750
1750
  openProjectHistory,
1751
1751
  historyCache
1752
1752
  };
1753
- //# sourceMappingURL=chunk-36F5I56K.js.map
1753
+ //# sourceMappingURL=chunk-K3H6HTVC.js.map
@@ -2,8 +2,10 @@ import {
2
2
  isCompositionSource,
3
3
  stampFileHfIds,
4
4
  stampProjectHfIds
5
- } from "../chunk-7ON477WG.js";
6
- import "../chunk-TTWLTYSQ.js";
5
+ } from "../chunk-DG2VOIGR.js";
6
+ import "../chunk-DTKK7TKL.js";
7
+ import "../chunk-6XMC64FJ.js";
8
+ import "../chunk-4ETS2LXI.js";
7
9
  export {
8
10
  isCompositionSource,
9
11
  stampFileHfIds,
@@ -13,8 +13,10 @@ import {
13
13
  resolveProxyVariantRequest,
14
14
  scanProjectMediaCodecMap,
15
15
  shouldPrewarmProxy
16
- } from "../chunk-I3DRHG52.js";
17
- import "../chunk-TTWLTYSQ.js";
16
+ } from "../chunk-74JVVCWL.js";
17
+ import "../chunk-DTKK7TKL.js";
18
+ import "../chunk-6XMC64FJ.js";
19
+ import "../chunk-4ETS2LXI.js";
18
20
  export {
19
21
  BROWSER_HOSTILE_CODECS,
20
22
  PROXY_VARIANT_CONFIG,
@@ -4,10 +4,12 @@ import {
4
4
  isAutoProxyEnabled,
5
5
  proxyEtagSalt,
6
6
  resolvePreviewMediaCodecProbeCache
7
- } from "../chunk-WK3ZL3SN.js";
8
- import "../chunk-YOBWV2CY.js";
9
- import "../chunk-I3DRHG52.js";
10
- import "../chunk-TTWLTYSQ.js";
7
+ } from "../chunk-G7FEKQ32.js";
8
+ import "../chunk-6RFZWPPN.js";
9
+ import "../chunk-74JVVCWL.js";
10
+ import "../chunk-DTKK7TKL.js";
11
+ import "../chunk-6XMC64FJ.js";
12
+ import "../chunk-4ETS2LXI.js";
11
13
  export {
12
14
  injectMediaCodecMap,
13
15
  injectMediaCodecMapIntoHtml,
@@ -14,9 +14,11 @@ import {
14
14
  proxyActivityMark,
15
15
  resolveProxy,
16
16
  waitForProxy
17
- } from "../chunk-YOBWV2CY.js";
18
- import "../chunk-I3DRHG52.js";
19
- import "../chunk-TTWLTYSQ.js";
17
+ } from "../chunk-6RFZWPPN.js";
18
+ import "../chunk-74JVVCWL.js";
19
+ import "../chunk-DTKK7TKL.js";
20
+ import "../chunk-6XMC64FJ.js";
21
+ import "../chunk-4ETS2LXI.js";
20
22
  export {
21
23
  CACHE_DIR_NAME,
22
24
  DEFAULT_PROXY_WAIT_TIMEOUT_MS,
@@ -1,7 +1,12 @@
1
- export { folderGone, isProjectRootMissing, isSafePath, mkdirWithinProject, realProjectRoot, realpath, resolveWithinProject } from '@hyperframes/core/safe-path';
1
+ export { folderGone, isProjectRootMissing, isSafePath, mkdirWithinProject, realProjectRoot, realpath } from '@hyperframes/core/safe-path';
2
2
 
3
+ /** Core's `resolveWithinProject`, closed to the desktop app's private files for every route a request path reaches. */
4
+ declare function resolveWithinProject(base: string, relativePath: string): string | null;
3
5
  /** The real path; for a path not there (yet, or any more), the nearest existing folder's real path plus the rest. */
4
6
  declare function realFilePath(filePath: string): string;
7
+ /** For a project-relative path with `/` separators. */
8
+ declare function isPrivateProjectPath(relPath: string): boolean;
9
+ declare function isPrivateProjectFile(projectDir: string, filePath: string): boolean;
5
10
  /**
6
11
  * `resolveWithinProject` with every link inside the project resolved now, so a write to the result lands on the
7
12
  * file checked here even if a link on the way is retargeted before the write.
@@ -19,4 +24,4 @@ declare function isInHiddenOrVendorDir(relPath: string): boolean;
19
24
  * or removed mid-walk is skipped; any other failure, or an unreadable `dir` itself, throws. */
20
25
  declare function walkDir(dir: string, prefix?: string): string[];
21
26
 
22
- export { isInHiddenOrVendorDir, pinWithinProject, realFilePath, walkDir };
27
+ export { isInHiddenOrVendorDir, isPrivateProjectFile, isPrivateProjectPath, pinWithinProject, realFilePath, resolveWithinProject, walkDir };
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  folderGone,
3
3
  isInHiddenOrVendorDir,
4
+ isPrivateProjectFile,
5
+ isPrivateProjectPath,
4
6
  isProjectRootMissing,
5
7
  isSafePath,
6
8
  mkdirWithinProject,
@@ -10,10 +12,14 @@ import {
10
12
  realpath,
11
13
  resolveWithinProject,
12
14
  walkDir
13
- } from "../chunk-TTWLTYSQ.js";
15
+ } from "../chunk-DTKK7TKL.js";
16
+ import "../chunk-6XMC64FJ.js";
17
+ import "../chunk-4ETS2LXI.js";
14
18
  export {
15
19
  folderGone,
16
20
  isInHiddenOrVendorDir,
21
+ isPrivateProjectFile,
22
+ isPrivateProjectPath,
17
23
  isProjectRootMissing,
18
24
  isSafePath,
19
25
  mkdirWithinProject,
@@ -10,8 +10,10 @@ import {
10
10
  listProjectHistories,
11
11
  openProjectHistory,
12
12
  pruneGoneProjectHistories
13
- } from "../chunk-36F5I56K.js";
14
- import "../chunk-TTWLTYSQ.js";
13
+ } from "../chunk-K3H6HTVC.js";
14
+ import "../chunk-DTKK7TKL.js";
15
+ import "../chunk-6XMC64FJ.js";
16
+ import "../chunk-4ETS2LXI.js";
15
17
  export {
16
18
  DEFAULT_HISTORY_ROOT,
17
19
  START as HISTORY_START,
package/dist/index.d.ts CHANGED
@@ -25,6 +25,8 @@ import './helpers/mediaCodecMap.js';
25
25
  */
26
26
  declare function createStudioApi(adapter: StudioApiAdapter): Hono;
27
27
 
28
+ declare const MAX_UPLOAD_BYTES: number;
29
+
28
30
  /**
29
31
  * Whether a write at `changedPath` can change `createProjectSignature(projectDir)`.
30
32
  *
@@ -226,4 +228,4 @@ type BackgroundRemovalRender = (options: {
226
228
  }>;
227
229
  declare function createBackgroundRemovalJob(opts: BackgroundRemovalJobOptions, render: BackgroundRemovalRender): MediaProcessingJobState;
228
230
 
229
- export { type AppliedFileMutation, type BackgroundRemovalRender, DELETED_VERSION, type DuplicateElementResult, type FileMutationInput, type FileWriteReceipt, MIME_TYPES, MediaProcessingJobState, PREVIEW_BUNDLE_OPTIONS, SourceMutationTarget, StudioApiAdapter, type ThumbnailOutputDimensions, affectsPreview, affectsProjectSignature, applyFileMutations, buildSubCompositionHtml, cleanupProxyCache, compositionsAffectedBy, consumeFileWriteReceipt, createBackgroundRemovalJob, createPreviewDocumentStore, createProjectSignature, createStudioApi, duplicateElementInHtml, fileContentVersion, getMimeType, identifyFileWrite, requestSubPath, settledFileTag, thumbnailDeviceScaleFactor };
231
+ export { type AppliedFileMutation, type BackgroundRemovalRender, DELETED_VERSION, type DuplicateElementResult, type FileMutationInput, type FileWriteReceipt, MAX_UPLOAD_BYTES, MIME_TYPES, MediaProcessingJobState, PREVIEW_BUNDLE_OPTIONS, SourceMutationTarget, StudioApiAdapter, type ThumbnailOutputDimensions, affectsPreview, affectsProjectSignature, applyFileMutations, buildSubCompositionHtml, cleanupProxyCache, compositionsAffectedBy, consumeFileWriteReceipt, createBackgroundRemovalJob, createPreviewDocumentStore, createProjectSignature, createStudioApi, duplicateElementInHtml, fileContentVersion, getMimeType, identifyFileWrite, requestSubPath, settledFileTag, thumbnailDeviceScaleFactor };
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  isCompositionSource,
23
23
  stampProjectHfIds
24
- } from "./chunk-7ON477WG.js";
24
+ } from "./chunk-DG2VOIGR.js";
25
25
  import {
26
26
  DEFAULT_HISTORY_ROOT,
27
27
  DELETED_VERSION,
@@ -47,7 +47,7 @@ import {
47
47
  resolveProjectAndSignature,
48
48
  resolveProjectSignature,
49
49
  settledFileTag
50
- } from "./chunk-36F5I56K.js";
50
+ } from "./chunk-K3H6HTVC.js";
51
51
  import {
52
52
  clearElementScreenshotIsolation,
53
53
  getElementScreenshotClip
@@ -64,7 +64,7 @@ import {
64
64
  isAutoProxyEnabled,
65
65
  proxyEtagSalt,
66
66
  resolvePreviewMediaCodecProbeCache
67
- } from "./chunk-WK3ZL3SN.js";
67
+ } from "./chunk-G7FEKQ32.js";
68
68
  import {
69
69
  CACHE_DIR_NAME,
70
70
  PROXY_PENDING_RETRY_AFTER_SECONDS,
@@ -75,7 +75,7 @@ import {
75
75
  proxyActivityMark,
76
76
  resolveProxy,
77
77
  waitForProxy
78
- } from "./chunk-YOBWV2CY.js";
78
+ } from "./chunk-6RFZWPPN.js";
79
79
  import {
80
80
  PROXY_VARIANT_CONFIG,
81
81
  decideMediaProxyEligibility,
@@ -84,10 +84,12 @@ import {
84
84
  probeMediaMetadata,
85
85
  recordProxyRequest,
86
86
  resolveProxyVariantRequest
87
- } from "./chunk-I3DRHG52.js";
87
+ } from "./chunk-74JVVCWL.js";
88
88
  import {
89
89
  folderGone,
90
90
  isInHiddenOrVendorDir,
91
+ isPrivateProjectFile,
92
+ isPrivateProjectPath,
91
93
  isProjectRootMissing,
92
94
  isSafePath,
93
95
  mkdirWithinProject,
@@ -96,7 +98,7 @@ import {
96
98
  realpath,
97
99
  resolveWithinProject,
98
100
  walkDir
99
- } from "./chunk-TTWLTYSQ.js";
101
+ } from "./chunk-DTKK7TKL.js";
100
102
  import {
101
103
  STUDIO_MANUAL_EDITS_PATH,
102
104
  createStudioManualEditsRenderBodyScript,
@@ -149,7 +151,7 @@ function registerProjectRoutes(api, adapter) {
149
151
  api.get("/projects/:id", async (c) => {
150
152
  const project = await adapter.resolveProject(c.req.param("id"));
151
153
  if (!project) return c.json({ error: "not found" }, 404);
152
- const files = walkDir(project.dir);
154
+ const files = walkDir(project.dir).filter((file) => !isPrivateProjectPath(file));
153
155
  const compositions = await filterCompositionFiles(project.dir, files);
154
156
  return c.json({ id: project.id, dir: project.dir, title: project.title, files, compositions });
155
157
  });
@@ -1572,7 +1574,7 @@ function updateReferences(projectDir, oldPath, newPath, isDirectory) {
1572
1574
  const rewrite = referenceRewriter(oldPath, newPath, isDirectory, projectPaths(projectDir));
1573
1575
  let updatedCount = 0;
1574
1576
  for (const file of textFiles) {
1575
- if (!isSafePath(projectDir, file)) continue;
1577
+ if (!isSafePath(projectDir, file) || isPrivateProjectFile(projectDir, file)) continue;
1576
1578
  const content = readableText(file);
1577
1579
  if (content === null) continue;
1578
1580
  const updated = rewrite(content);
@@ -2681,8 +2683,8 @@ async function foldAtomicCutFile(c, file, absPath, before, writer) {
2681
2683
  skippedSelectors: [...skippedSelectors]
2682
2684
  };
2683
2685
  }
2686
+ var MAX_UPLOAD_BYTES = 500 * 1024 * 1024;
2684
2687
  async function processUploadedFiles(formData, targetDir, projectDir) {
2685
- const MAX_UPLOAD_BYTES = 500 * 1024 * 1024;
2686
2688
  const uploaded = [];
2687
2689
  const skipped = [];
2688
2690
  const invalid = [];
@@ -2716,7 +2718,7 @@ async function processUploadedFiles(formData, targetDir, projectDir) {
2716
2718
  finalName = `${base} (${n})${ext}`;
2717
2719
  finalPath = resolve2(targetDir, finalName);
2718
2720
  }
2719
- if (!isSafePath(projectDir, finalPath)) continue;
2721
+ if (!isSafePath(projectDir, finalPath) || isPrivateProjectFile(projectDir, finalPath)) continue;
2720
2722
  const buffer = Buffer.from(await value.arrayBuffer());
2721
2723
  const validation = validateUploadedMediaBuffer(finalName, buffer);
2722
2724
  if (!validation.ok) {
@@ -3495,7 +3497,6 @@ function registerFileRoutes(api, adapter) {
3495
3497
  }
3496
3498
  return c.json({ ok: true, path: copyPath }, 201);
3497
3499
  });
3498
- const MAX_UPLOAD_BYTES = 500 * 1024 * 1024;
3499
3500
  api.post(
3500
3501
  "/projects/:id/upload",
3501
3502
  bodyLimit({
@@ -3610,7 +3611,10 @@ import {
3610
3611
  insertRuntimeTag,
3611
3612
  stripEmbeddedRuntimeScripts as stripEmbeddedRuntimeScripts2
3612
3613
  } from "@hyperframes/core/compiler";
3613
- import { STUDIO_PREVIEW_MARK_META } from "@hyperframes/core/studio-preview-mark";
3614
+ import {
3615
+ STUDIO_PREVIEW_MARK_META,
3616
+ STUDIO_PREVIEW_ERRORS
3617
+ } from "@hyperframes/core/studio-preview-mark";
3614
3618
  import { gsapCdnDist as gsapCdnDist2, motionPathPluginUrl } from "@hyperframes/core/gsap-cdn";
3615
3619
  import { findStartTags as findStartTags2, injectTagsAtHeadStart } from "@hyperframes/core/compiler/html-document";
3616
3620
  import { isWithinProjectRoot } from "@hyperframes/parsers/asset-resolution";
@@ -3905,15 +3909,17 @@ var GSAP_CDN_FALLBACK_SCRIPT = `<script ${GSAP_FALLBACK_ATTR}>
3905
3909
  if(loaded[file])return loaded[file];
3906
3910
  return loaded[file]=new Promise(function(ok,fail){
3907
3911
  var s=document.createElement("script");
3908
- s.src=cdnBase+file;s.onload=ok;s.onerror=fail;
3912
+ s.src=cdnBase+file;s.hfGsapFallback=true;s.onload=ok;s.onerror=fail;
3909
3913
  document.head.appendChild(s);
3910
3914
  });
3911
3915
  }
3912
3916
  document.addEventListener("error",function(e){
3913
3917
  var t=e.target;
3914
- if(!t||t.tagName!=="SCRIPT"||!t.src)return;
3918
+ if(!t||t.tagName!=="SCRIPT"||!t.src||t.hfGsapFallback)return;
3915
3919
  var m=t.src.match(/gsap[^/]*\\/dist\\/(.+\\.js)/);
3916
- if(m)loadFallback(m[1]);
3920
+ if(m)loadFallback(m[1]).catch(function(){
3921
+ reportError(new Error("GSAP could not load from "+t.src+" or "+cdnBase+m[1]+", so this preview's animations will not play."));
3922
+ });
3917
3923
  },true);
3918
3924
  })();
3919
3925
  </script>`;
@@ -3945,8 +3951,13 @@ function previewVariablesFromRequest(rawVariables) {
3945
3951
  return { raw: rawVariables, values: parse2.values };
3946
3952
  }
3947
3953
  var PREVIEW_CAPTURE_PARAM = "hf-capture";
3954
+ var EARLY_ERRORS_SCRIPT = `<script>(function(){var seen=window.${STUDIO_PREVIEW_ERRORS}=[];
3955
+ addEventListener("error",function(e){seen.push(e.message||String(e))});})();</script>`;
3948
3956
  function injectStudioPreviewAugmentations(html, adapter, projectDir, activeCompositionPath, capture) {
3949
- const marked = capture ? html : injectTagsAtHeadStart(lazyPreviewImages(html), `<meta name="${STUDIO_PREVIEW_MARK_META}">`);
3957
+ const marked = capture ? html : injectTagsAtHeadStart(
3958
+ lazyPreviewImages(html),
3959
+ `<meta name="${STUDIO_PREVIEW_MARK_META}">${EARLY_ERRORS_SCRIPT}`
3960
+ );
3950
3961
  return injectStudioMotionScript(
3951
3962
  injectMotionPathPluginIfNeeded(
3952
3963
  injectGsapCdnFallback(
@@ -4167,7 +4178,7 @@ ${etag}`;
4167
4178
  const subPath = requestSubPath(c.req.url, "projects/:id/preview");
4168
4179
  const candidate = resolve4(project.dir, subPath);
4169
4180
  const file = isWithinProjectRoot(project.dir, candidate) ? candidate : null;
4170
- if (!file) {
4181
+ if (!file || isPrivateProjectFile(project.dir, file)) {
4171
4182
  return c.text("not found", 404);
4172
4183
  }
4173
4184
  recordPreviewRead(project.dir, file);
@@ -4321,7 +4332,7 @@ function registerLintRoutes(api, adapter) {
4321
4332
 
4322
4333
  // src/routes/render.ts
4323
4334
  import { streamSSE } from "hono/streaming";
4324
- import { existsSync as existsSync6, readFileSync as readFileSync8, unlinkSync as unlinkSync3, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
4335
+ import { existsSync as existsSync6, lstatSync as lstatSync3, readFileSync as readFileSync8, unlinkSync as unlinkSync3, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
4325
4336
  import { join as join10 } from "path";
4326
4337
  import { VALID_CANVAS_RESOLUTIONS } from "@hyperframes/parsers";
4327
4338
  import { formatRenderOutputTimestamp, parseFps } from "@hyperframes/core";
@@ -4449,6 +4460,13 @@ function registerRenderRoutes(api, adapter) {
4449
4460
  ".mov": "video/quicktime"
4450
4461
  };
4451
4462
  const RENDER_EXTENSIONS = Object.keys(RENDER_MIME);
4463
+ const isPlainFile = (path) => {
4464
+ try {
4465
+ return lstatSync3(path).isFile();
4466
+ } catch {
4467
+ return false;
4468
+ }
4469
+ };
4452
4470
  function renderContentType(filePath) {
4453
4471
  const ext = RENDER_EXTENSIONS.find((e) => filePath.endsWith(e));
4454
4472
  return (ext && RENDER_MIME[ext]) ?? "video/mp4";
@@ -4456,7 +4474,7 @@ function registerRenderRoutes(api, adapter) {
4456
4474
  api.get("/render/:jobId/view", (c) => {
4457
4475
  const { jobId } = c.req.param();
4458
4476
  const job = renderJobs.get(jobId);
4459
- if (!job?.outputPath || !existsSync6(job.outputPath)) {
4477
+ if (!job?.outputPath || !isPlainFile(job.outputPath)) {
4460
4478
  return c.json({ error: "not found" }, 404);
4461
4479
  }
4462
4480
  const contentType = renderContentType(job.outputPath);
@@ -4474,7 +4492,7 @@ function registerRenderRoutes(api, adapter) {
4474
4492
  api.get("/render/:jobId/download", (c) => {
4475
4493
  const { jobId } = c.req.param();
4476
4494
  const job = renderJobs.get(jobId);
4477
- if (!job?.outputPath || !existsSync6(job.outputPath)) {
4495
+ if (!job?.outputPath || !isPlainFile(job.outputPath)) {
4478
4496
  return c.json({ error: "not found" }, 404);
4479
4497
  }
4480
4498
  const contentType = renderContentType(job.outputPath);
@@ -4509,7 +4527,7 @@ function registerRenderRoutes(api, adapter) {
4509
4527
  if (!filename) return c.json({ error: "missing filename" }, 400);
4510
4528
  const rendersDir = adapter.rendersDir(project);
4511
4529
  const fp = resolveWithinProject(rendersDir, filename);
4512
- if (!fp) return c.json({ error: "forbidden" }, 403);
4530
+ if (!fp || isPrivateProjectFile(project.dir, fp)) return c.json({ error: "forbidden" }, 403);
4513
4531
  if (!existsSync6(fp)) return c.json({ error: "not found" }, 404);
4514
4532
  const contentType = renderContentType(fp);
4515
4533
  const content = readFileSync8(fp);
@@ -4527,8 +4545,10 @@ function registerRenderRoutes(api, adapter) {
4527
4545
  if (!project) return c.json({ error: "not found" }, 404);
4528
4546
  const rendersDir = adapter.rendersDir(project);
4529
4547
  if (!existsSync6(rendersDir)) return c.json({ renders: [] });
4530
- const files = readdirSync3(rendersDir).filter((f) => f.endsWith(".mp4") || f.endsWith(".webm") || f.endsWith(".mov")).map((f) => {
4531
- const fp = join10(rendersDir, f);
4548
+ const files = readdirSync3(rendersDir).filter((f) => f.endsWith(".mp4") || f.endsWith(".webm") || f.endsWith(".mov")).flatMap((f) => {
4549
+ const fp = resolveWithinProject(rendersDir, f);
4550
+ return fp && !isPrivateProjectFile(project.dir, fp) ? [{ f, fp }] : [];
4551
+ }).map(({ f, fp }) => {
4532
4552
  const stat2 = statSync5(fp);
4533
4553
  const rid = f.replace(/\.(mp4|webm|mov)$/, "");
4534
4554
  const metaPath = join10(rendersDir, `${rid}.meta.json`);
@@ -4547,6 +4567,7 @@ function registerRenderRoutes(api, adapter) {
4547
4567
  return {
4548
4568
  id: rid,
4549
4569
  filename: f,
4570
+ path: fp,
4550
4571
  size: stat2.size,
4551
4572
  createdAt: stat2.mtimeMs,
4552
4573
  status,
@@ -4560,12 +4581,12 @@ function registerRenderRoutes(api, adapter) {
4560
4581
  id: file.id,
4561
4582
  status: file.status,
4562
4583
  progress: 100,
4563
- outputPath: join10(rendersDir, file.filename),
4584
+ outputPath: file.path,
4564
4585
  createdAt: file.createdAt
4565
4586
  });
4566
4587
  }
4567
4588
  }
4568
- return c.json({ renders: files });
4589
+ return c.json({ renders: files.map(({ path: _path, ...render }) => render) });
4569
4590
  });
4570
4591
  }
4571
4592
 
@@ -6432,6 +6453,7 @@ export {
6432
6453
  HistoryBusyError,
6433
6454
  HistoryClosedError,
6434
6455
  HistoryIdError,
6456
+ MAX_UPLOAD_BYTES,
6435
6457
  MAX_WINDOW_IDLE_MS,
6436
6458
  MIME_TYPES,
6437
6459
  PREVIEW_BUNDLE_OPTIONS,