@hyperframes/studio-server 0.7.107 → 0.7.108

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 CHANGED
@@ -352,7 +352,7 @@ import {
352
352
  mkdirSync as mkdirSync3,
353
353
  unlinkSync as unlinkSync2,
354
354
  rmSync as rmSync2,
355
- statSync,
355
+ statSync as statSync2,
356
356
  renameSync,
357
357
  readdirSync as readdirSync4
358
358
  } from "fs";
@@ -406,14 +406,17 @@ function isAudioFile(name) {
406
406
 
407
407
  // src/helpers/waveform.ts
408
408
  import { spawn } from "child_process";
409
- import { existsSync as existsSync2, writeFileSync, mkdirSync } from "fs";
409
+ import { existsSync as existsSync2, writeFileSync, mkdirSync, statSync } from "fs";
410
410
  import { join as join3 } from "path";
411
411
  import { findFfBinary } from "@hyperframes/parsers/ff-binaries";
412
412
  var SAMPLE_RATE = 4e3;
413
413
  var PEAK_COUNT = 4e3;
414
414
  var WAVEFORM_CACHE_VERSION = "v2";
415
- function buildWaveformCacheKey(assetPath) {
416
- return `${WAVEFORM_CACHE_VERSION}_${assetPath.replace(/[/\\]/g, "_")}.json`;
415
+ function buildWaveformCacheKey(assetPath, fingerprint) {
416
+ const name = assetPath.replace(/[/\\]/g, "_");
417
+ if (!fingerprint) return `${WAVEFORM_CACHE_VERSION}_${name}.json`;
418
+ const stamp = `${fingerprint.size}-${Math.round(fingerprint.mtimeMs)}`;
419
+ return `${WAVEFORM_CACHE_VERSION}_${name}_${stamp}.json`;
417
420
  }
418
421
  function computePeaks(floats, count) {
419
422
  const step = floats.length / count;
@@ -473,8 +476,9 @@ function decodeAudioPeaks(audioPath) {
473
476
  async function generateWaveformCache(projectDir, assetPath) {
474
477
  const audioPath = join3(projectDir, assetPath);
475
478
  if (!existsSync2(audioPath)) return;
479
+ const stats = statSync(audioPath);
476
480
  const cacheDir = join3(projectDir, ".waveform-cache");
477
- const cachePath = join3(cacheDir, buildWaveformCacheKey(assetPath));
481
+ const cachePath = join3(cacheDir, buildWaveformCacheKey(assetPath, stats));
478
482
  if (existsSync2(cachePath)) return;
479
483
  const peaks = await decodeAudioPeaks(audioPath);
480
484
  mkdirSync(cacheDir, { recursive: true });
@@ -2426,7 +2430,7 @@ function registerFileRoutes(api, adapter) {
2426
2430
  api.delete("/projects/:id/files/*", async (c) => {
2427
2431
  const res = await resolveProjectFile(c, adapter, { mustExist: true });
2428
2432
  if ("error" in res) return res.error;
2429
- const stat = statSync(res.absPath);
2433
+ const stat = statSync2(res.absPath);
2430
2434
  const backup = snapshotBeforeWrite(res.project.dir, res.absPath);
2431
2435
  if (backup.error) console.warn(`Failed to create backup for ${res.filePath}: ${backup.error}`);
2432
2436
  if (stat.isDirectory()) {
@@ -3023,7 +3027,7 @@ function registerFileRoutes(api, adapter) {
3023
3027
  }
3024
3028
 
3025
3029
  // src/routes/preview.ts
3026
- import { existsSync as existsSync6, readFileSync as readFileSync8, statSync as statSync2 } from "fs";
3030
+ import { existsSync as existsSync6, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
3027
3031
  import { join as join8, resolve as resolve4 } from "path";
3028
3032
  import { createHash as createHash3 } from "crypto";
3029
3033
  import { injectScriptsIntoHtml, stripEmbeddedRuntimeScripts as stripEmbeddedRuntimeScripts2 } from "@hyperframes/core/compiler";
@@ -3573,7 +3577,7 @@ ${runtimeTag}`;
3573
3577
  c.req.path.replace(`/projects/${project.id}/preview/comp/`, "").split("?")[0] ?? ""
3574
3578
  );
3575
3579
  const compFile = resolveWithinProject(project.dir, compPath);
3576
- if (!compFile || !existsSync6(compFile) || !statSync2(compFile).isFile()) {
3580
+ if (!compFile || !existsSync6(compFile) || !statSync3(compFile).isFile()) {
3577
3581
  return c.text("not found", 404);
3578
3582
  }
3579
3583
  const etag = `"comp:v2:${compPath}:${signature}${variablesEtagSalt(vars.raw)}"`;
@@ -3612,7 +3616,7 @@ ${runtimeTag}`;
3612
3616
  if (!file) {
3613
3617
  return c.text("not found", 404);
3614
3618
  }
3615
- const stat = existsSync6(file) ? statSync2(file) : null;
3619
+ const stat = existsSync6(file) ? statSync3(file) : null;
3616
3620
  if (!stat?.isFile()) {
3617
3621
  return c.text("not found", 404);
3618
3622
  }
@@ -3724,7 +3728,7 @@ function registerLintRoutes(api, adapter) {
3724
3728
 
3725
3729
  // src/routes/render.ts
3726
3730
  import { streamSSE } from "hono/streaming";
3727
- import { existsSync as existsSync7, readFileSync as readFileSync10, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
3731
+ import { existsSync as existsSync7, readFileSync as readFileSync10, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
3728
3732
  import { join as join10 } from "path";
3729
3733
  import { VALID_CANVAS_RESOLUTIONS } from "@hyperframes/parsers";
3730
3734
  import { formatRenderOutputTimestamp, parseFps } from "@hyperframes/core";
@@ -3922,7 +3926,7 @@ function registerRenderRoutes(api, adapter) {
3922
3926
  if (!existsSync7(rendersDir)) return c.json({ renders: [] });
3923
3927
  const files = readdirSync5(rendersDir).filter((f) => f.endsWith(".mp4") || f.endsWith(".webm") || f.endsWith(".mov")).map((f) => {
3924
3928
  const fp = join10(rendersDir, f);
3925
- const stat = statSync3(fp);
3929
+ const stat = statSync4(fp);
3926
3930
  const rid = f.replace(/\.(mp4|webm|mov)$/, "");
3927
3931
  const metaPath = join10(rendersDir, `${rid}.meta.json`);
3928
3932
  let status = "complete";
@@ -3967,7 +3971,7 @@ import {
3967
3971
  readdirSync as readdirSync6,
3968
3972
  renameSync as renameSync2,
3969
3973
  rmSync as rmSync3,
3970
- statSync as statSync4,
3974
+ statSync as statSync5,
3971
3975
  unlinkSync as unlinkSync4,
3972
3976
  writeFileSync as writeFileSync6
3973
3977
  } from "fs";
@@ -4090,7 +4094,7 @@ function pruneThumbnailCache(cacheDir, protectedPaths, now = Date.now()) {
4090
4094
  if (!entry.isFile()) return [];
4091
4095
  const path = join11(cacheDir, entry.name);
4092
4096
  try {
4093
- const stats = statSync4(path);
4097
+ const stats = statSync5(path);
4094
4098
  return [{ path, bytes: stats.size, mtimeMs: stats.mtimeMs }];
4095
4099
  } catch {
4096
4100
  return [];
@@ -4157,7 +4161,7 @@ function registerThumbnailRoutes(api, adapter) {
4157
4161
  if (existsSync8(htmlFile)) {
4158
4162
  const html = readFileSync11(htmlFile, "utf-8");
4159
4163
  sourceKey = `_${createHash4("sha1").update(html).digest("hex").slice(0, 16)}`;
4160
- sourceMtime = Math.round(statSync4(htmlFile).mtimeMs);
4164
+ sourceMtime = Math.round(statSync5(htmlFile).mtimeMs);
4161
4165
  if (!vpWidth) {
4162
4166
  const wMatch = html.match(/data-width=["'](\d+)["']/);
4163
4167
  const hMatch = html.match(/data-height=["'](\d+)["']/);
@@ -4170,14 +4174,14 @@ function registerThumbnailRoutes(api, adapter) {
4170
4174
  if (existsSync8(manualEditsFile)) {
4171
4175
  const manualEditsContent = readFileSync11(manualEditsFile, "utf-8");
4172
4176
  manualEditsKey = `_${createHash4("sha1").update(manualEditsContent).digest("hex").slice(0, 16)}`;
4173
- sourceMtime = Math.max(sourceMtime, Math.round(statSync4(manualEditsFile).mtimeMs));
4177
+ sourceMtime = Math.max(sourceMtime, Math.round(statSync5(manualEditsFile).mtimeMs));
4174
4178
  }
4175
4179
  const motionFile = join11(project.dir, STUDIO_MOTION_PATH);
4176
4180
  let motionKey = "";
4177
4181
  if (existsSync8(motionFile)) {
4178
4182
  const motionContent = readFileSync11(motionFile, "utf-8");
4179
4183
  motionKey = `_${createHash4("sha1").update(motionContent).digest("hex").slice(0, 16)}`;
4180
- sourceMtime = Math.max(sourceMtime, Math.round(statSync4(motionFile).mtimeMs));
4184
+ sourceMtime = Math.max(sourceMtime, Math.round(statSync5(motionFile).mtimeMs));
4181
4185
  }
4182
4186
  const previewUrl = compPath === "index.html" ? `http://${c.req.header("host")}/api/projects/${project.id}/preview` : `http://${c.req.header("host")}/api/projects/${project.id}/preview/comp/${compPath}`;
4183
4187
  const cacheDir = join11(project.dir, ".thumbnails");
@@ -4246,7 +4250,7 @@ function registerThumbnailRoutes(api, adapter) {
4246
4250
  }
4247
4251
 
4248
4252
  // src/routes/waveform.ts
4249
- import { existsSync as existsSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6 } from "fs";
4253
+ import { existsSync as existsSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, statSync as statSync6 } from "fs";
4250
4254
  import { join as join12 } from "path";
4251
4255
  function registerWaveformRoutes(api, adapter) {
4252
4256
  api.get("/projects/:id/waveform/*", async (c) => {
@@ -4256,9 +4260,10 @@ function registerWaveformRoutes(api, adapter) {
4256
4260
  c.req.path.replace(`/projects/${project.id}/waveform/`, "").split("?")[0] ?? ""
4257
4261
  );
4258
4262
  const audioPath = join12(project.dir, assetPath);
4259
- if (!existsSync9(audioPath)) return c.json({ error: "file not found" }, 404);
4263
+ const stats = statSync6(audioPath, { throwIfNoEntry: false });
4264
+ if (!stats) return c.json({ error: "file not found" }, 404);
4260
4265
  const cacheDir = join12(project.dir, ".waveform-cache");
4261
- const cachePath = join12(cacheDir, buildWaveformCacheKey(assetPath));
4266
+ const cachePath = join12(cacheDir, buildWaveformCacheKey(assetPath, stats));
4262
4267
  if (existsSync9(cachePath)) {
4263
4268
  try {
4264
4269
  const peaks2 = JSON.parse(readFileSync12(cachePath, "utf-8"));