hyperframes 0.5.0-alpha.1 → 0.5.0-alpha.3

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/cli.js CHANGED
@@ -54,7 +54,7 @@ var VERSION;
54
54
  var init_version = __esm({
55
55
  "src/version.ts"() {
56
56
  "use strict";
57
- VERSION = true ? "0.5.0-alpha.1" : "0.0.0-dev";
57
+ VERSION = true ? "0.5.0-alpha.3" : "0.0.0-dev";
58
58
  }
59
59
  });
60
60
 
@@ -5723,24 +5723,6 @@ var init_composition = __esm({
5723
5723
  }
5724
5724
  return findings;
5725
5725
  },
5726
- // root_composition_missing_data_duration
5727
- ({ rootTag }) => {
5728
- const findings = [];
5729
- if (!rootTag) return findings;
5730
- const compId = readAttr(rootTag.raw, "data-composition-id");
5731
- if (!compId) return findings;
5732
- const hasDuration = readAttr(rootTag.raw, "data-duration") !== null;
5733
- if (!hasDuration) {
5734
- findings.push({
5735
- code: "root_composition_missing_data_duration",
5736
- severity: "warning",
5737
- message: `Root composition "${compId}" is missing data-duration. Without an explicit duration, the runtime may infer Infinity for compositions with repeating animations, causing playback issues.`,
5738
- fixHint: 'Add data-duration="X" to the root composition element, where X is the total duration in seconds.',
5739
- snippet: truncateSnippet(rootTag.raw)
5740
- });
5741
- }
5742
- return findings;
5743
- },
5744
5726
  // standalone_composition_wrapped_in_template
5745
5727
  ({ rawSource, options }) => {
5746
5728
  const findings = [];
@@ -9868,8 +9850,8 @@ function flushSync() {
9868
9850
  eventQueue = [];
9869
9851
  const payload = JSON.stringify({ api_key: POSTHOG_API_KEY, batch });
9870
9852
  try {
9871
- const { spawn: spawn13 } = __require("child_process");
9872
- const child = spawn13(
9853
+ const { spawn: spawn14 } = __require("child_process");
9854
+ const child = spawn14(
9873
9855
  process.execPath,
9874
9856
  [
9875
9857
  "-e",
@@ -10521,9 +10503,9 @@ function findWavDataChunk(buf) {
10521
10503
  return null;
10522
10504
  }
10523
10505
  function detectSpeechOnset(wavPath) {
10524
- const SAMPLE_RATE = 16e3;
10506
+ const SAMPLE_RATE2 = 16e3;
10525
10507
  const WINDOW_SECONDS = 0.5;
10526
- const WINDOW_SAMPLES = SAMPLE_RATE * WINDOW_SECONDS;
10508
+ const WINDOW_SAMPLES = SAMPLE_RATE2 * WINDOW_SECONDS;
10527
10509
  const SUSTAINED_WINDOWS = 3;
10528
10510
  const SILENCE_THRESHOLD_RATIO = 0.6;
10529
10511
  const MIN_INTRO_SECONDS = 3;
@@ -10802,14 +10784,14 @@ function lintProject(project) {
10802
10784
  totalErrors += rootResult.errorCount;
10803
10785
  totalWarnings += rootResult.warningCount;
10804
10786
  totalInfos += rootResult.infoCount;
10805
- const allHtmlSources = [rootHtml];
10787
+ const allHtmlSources = [{ html: rootHtml }];
10806
10788
  const compositionsDir = resolve5(project.dir, "compositions");
10807
10789
  if (existsSync7(compositionsDir)) {
10808
10790
  const files = readdirSync2(compositionsDir).filter((f3) => f3.endsWith(".html"));
10809
10791
  for (const file of files) {
10810
10792
  const filePath = join9(compositionsDir, file);
10811
10793
  const html = readFileSync7(filePath, "utf-8");
10812
- allHtmlSources.push(html);
10794
+ allHtmlSources.push({ html, compSrcPath: `compositions/${file}` });
10813
10795
  const result = lintHyperframeHtml(html, { filePath, isSubComposition: true });
10814
10796
  results.push({ file: `compositions/${file}`, result });
10815
10797
  totalErrors += result.errorCount;
@@ -10852,7 +10834,7 @@ function lintProjectAudioFiles(projectDir, htmlSources) {
10852
10834
  return findings;
10853
10835
  }
10854
10836
  if (audioFiles.length === 0) return findings;
10855
- const hasAudioElement = htmlSources.some((html) => /<audio\b/i.test(html));
10837
+ const hasAudioElement = htmlSources.some(({ html }) => /<audio\b/i.test(html));
10856
10838
  if (!hasAudioElement) {
10857
10839
  findings.push({
10858
10840
  code: "audio_file_without_element",
@@ -10867,13 +10849,14 @@ function lintAudioSrcNotFound(projectDir, htmlSources) {
10867
10849
  const findings = [];
10868
10850
  const audioSrcRe = /<audio\b[^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi;
10869
10851
  const missingSrcs = [];
10870
- for (const html of htmlSources) {
10852
+ for (const { html, compSrcPath } of htmlSources) {
10871
10853
  let match;
10872
10854
  while ((match = audioSrcRe.exec(html)) !== null) {
10873
10855
  const src = match[1];
10874
10856
  if (/^(https?:|data:|blob:)/i.test(src)) continue;
10875
10857
  if (/^__[A-Z_]+__$/.test(src)) continue;
10876
- const resolved = resolve5(projectDir, src);
10858
+ const rootRelative = compSrcPath ? rewriteAssetPath(compSrcPath, src) : src;
10859
+ const resolved = resolve5(projectDir, rootRelative);
10877
10860
  if (!existsSync7(resolved)) {
10878
10861
  missingSrcs.push(src);
10879
10862
  }
@@ -10922,7 +10905,7 @@ function lintDuplicateAudioTracks(htmlSources) {
10922
10905
  }
10923
10906
  const tracks = [];
10924
10907
  const seen = /* @__PURE__ */ new Set();
10925
- for (const html of htmlSources) {
10908
+ for (const { html } of htmlSources) {
10926
10909
  const audioTagRe = /<audio\b[^>]*>/gi;
10927
10910
  let match;
10928
10911
  while ((match = audioTagRe.exec(html)) !== null) {
@@ -10966,6 +10949,7 @@ var init_lintProject = __esm({
10966
10949
  "src/utils/lintProject.ts"() {
10967
10950
  "use strict";
10968
10951
  init_lint();
10952
+ init_src();
10969
10953
  AUDIO_EXTENSIONS2 = /* @__PURE__ */ new Set([".mp3", ".wav", ".aac", ".ogg", ".m4a", ".flac", ".opus"]);
10970
10954
  }
10971
10955
  });
@@ -11418,11 +11402,138 @@ var init_projects = __esm({
11418
11402
  }
11419
11403
  });
11420
11404
 
11405
+ // ../core/src/studio-api/helpers/mime.ts
11406
+ function getMimeType(path2) {
11407
+ const ext = path2.slice(path2.lastIndexOf(".")).toLowerCase();
11408
+ return MIME_TYPES[ext] || "application/octet-stream";
11409
+ }
11410
+ function isAudioFile2(name) {
11411
+ return (getMimeType(name) ?? "").startsWith("audio/");
11412
+ }
11413
+ var MIME_TYPES;
11414
+ var init_mime = __esm({
11415
+ "../core/src/studio-api/helpers/mime.ts"() {
11416
+ "use strict";
11417
+ MIME_TYPES = {
11418
+ ".html": "text/html",
11419
+ ".css": "text/css",
11420
+ ".js": "text/javascript",
11421
+ ".mjs": "text/javascript",
11422
+ ".json": "application/json",
11423
+ ".svg": "image/svg+xml",
11424
+ ".png": "image/png",
11425
+ ".jpg": "image/jpeg",
11426
+ ".jpeg": "image/jpeg",
11427
+ ".gif": "image/gif",
11428
+ ".webp": "image/webp",
11429
+ ".ico": "image/x-icon",
11430
+ ".mp4": "video/mp4",
11431
+ ".mov": "video/quicktime",
11432
+ ".webm": "video/webm",
11433
+ ".mp3": "audio/mpeg",
11434
+ ".wav": "audio/wav",
11435
+ ".ogg": "audio/ogg",
11436
+ ".m4a": "audio/mp4",
11437
+ ".aac": "audio/aac",
11438
+ ".flac": "audio/flac",
11439
+ ".opus": "audio/ogg",
11440
+ ".woff": "font/woff",
11441
+ ".woff2": "font/woff2",
11442
+ ".ttf": "font/ttf",
11443
+ ".otf": "font/otf",
11444
+ ".txt": "text/plain",
11445
+ ".md": "text/markdown"
11446
+ };
11447
+ }
11448
+ });
11449
+
11450
+ // ../core/src/studio-api/helpers/waveform.ts
11451
+ import { spawn as spawn2 } from "child_process";
11452
+ import { existsSync as existsSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
11453
+ import { join as join11 } from "path";
11454
+ function buildWaveformCacheKey(assetPath) {
11455
+ return `${WAVEFORM_CACHE_VERSION}_${assetPath.replace(/[/\\]/g, "_")}.json`;
11456
+ }
11457
+ function computePeaks(floats, count) {
11458
+ const step = floats.length / count;
11459
+ const peaks = [];
11460
+ for (let i2 = 0; i2 < count; i2++) {
11461
+ const start = Math.floor(i2 * step);
11462
+ const end = Math.min(Math.floor((i2 + 1) * step), floats.length);
11463
+ let max = 0;
11464
+ for (let j2 = start; j2 < end; j2++) {
11465
+ const abs = Math.abs(floats[j2] ?? 0);
11466
+ if (abs > max) max = abs;
11467
+ }
11468
+ peaks.push(max);
11469
+ }
11470
+ const maxPeak = Math.max(...peaks, 1e-3);
11471
+ return peaks.map((p) => p / maxPeak);
11472
+ }
11473
+ function decodeAudioPeaks(audioPath) {
11474
+ return new Promise((resolve39, reject) => {
11475
+ const proc = spawn2(
11476
+ "ffmpeg",
11477
+ [
11478
+ "-i",
11479
+ audioPath,
11480
+ "-af",
11481
+ "atrim=start_sample=1152",
11482
+ "-f",
11483
+ "f32le",
11484
+ "-ac",
11485
+ "1",
11486
+ "-ar",
11487
+ String(SAMPLE_RATE),
11488
+ "-vn",
11489
+ "pipe:1"
11490
+ ],
11491
+ { stdio: ["ignore", "pipe", "ignore"] }
11492
+ );
11493
+ const chunks = [];
11494
+ proc.stdout?.on("data", (chunk) => chunks.push(chunk));
11495
+ proc.on("close", (code) => {
11496
+ if (code !== 0 && chunks.length === 0) {
11497
+ reject(new Error(`ffmpeg exited with code ${code}`));
11498
+ return;
11499
+ }
11500
+ const buf = Buffer.concat(chunks);
11501
+ const numSamples = Math.floor(buf.length / 4);
11502
+ if (numSamples === 0) {
11503
+ reject(new Error("ffmpeg produced no audio samples"));
11504
+ return;
11505
+ }
11506
+ const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + numSamples * 4);
11507
+ resolve39(computePeaks(new Float32Array(ab), PEAK_COUNT));
11508
+ });
11509
+ proc.on("error", reject);
11510
+ });
11511
+ }
11512
+ async function generateWaveformCache(projectDir, assetPath) {
11513
+ const audioPath = join11(projectDir, assetPath);
11514
+ if (!existsSync9(audioPath)) return;
11515
+ const cacheDir = join11(projectDir, ".waveform-cache");
11516
+ const cachePath2 = join11(cacheDir, buildWaveformCacheKey(assetPath));
11517
+ if (existsSync9(cachePath2)) return;
11518
+ const peaks = await decodeAudioPeaks(audioPath);
11519
+ mkdirSync5(cacheDir, { recursive: true });
11520
+ writeFileSync5(cachePath2, JSON.stringify(peaks));
11521
+ }
11522
+ var SAMPLE_RATE, PEAK_COUNT, WAVEFORM_CACHE_VERSION;
11523
+ var init_waveform = __esm({
11524
+ "../core/src/studio-api/helpers/waveform.ts"() {
11525
+ "use strict";
11526
+ SAMPLE_RATE = 4e3;
11527
+ PEAK_COUNT = 4e3;
11528
+ WAVEFORM_CACHE_VERSION = "v2";
11529
+ }
11530
+ });
11531
+
11421
11532
  // ../core/src/studio-api/helpers/mediaValidation.ts
11422
11533
  import { spawnSync } from "child_process";
11423
- import { mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
11534
+ import { mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
11424
11535
  import { tmpdir as tmpdir2 } from "os";
11425
- import { basename, join as join11 } from "path";
11536
+ import { basename, join as join12 } from "path";
11426
11537
  function validateUploadedMedia(filePath, runner = spawnSync) {
11427
11538
  const isVideo2 = VIDEO_EXT.test(filePath);
11428
11539
  const isAudio = AUDIO_EXT.test(filePath);
@@ -11461,10 +11572,10 @@ function validateUploadedMedia(filePath, runner = spawnSync) {
11461
11572
  }
11462
11573
  }
11463
11574
  function validateUploadedMediaBuffer(fileName, buffer, runner = spawnSync) {
11464
- const tempDir = mkdtempSync(join11(tmpdir2(), "hyperframes-upload-"));
11465
- const tempPath = join11(tempDir, basename(fileName));
11575
+ const tempDir = mkdtempSync(join12(tmpdir2(), "hyperframes-upload-"));
11576
+ const tempPath = join12(tempDir, basename(fileName));
11466
11577
  try {
11467
- writeFileSync5(tempPath, buffer);
11578
+ writeFileSync6(tempPath, buffer);
11468
11579
  return validateUploadedMedia(tempPath, runner);
11469
11580
  } finally {
11470
11581
  rmSync2(tempDir, { recursive: true, force: true });
@@ -23317,7 +23428,7 @@ var init_html_classes = __esm({
23317
23428
 
23318
23429
  // ../../node_modules/.bun/linkedom@0.18.12/node_modules/linkedom/esm/shared/mime.js
23319
23430
  var voidElements2, Mime;
23320
- var init_mime = __esm({
23431
+ var init_mime2 = __esm({
23321
23432
  "../../node_modules/.bun/linkedom@0.18.12/node_modules/linkedom/esm/shared/mime.js"() {
23322
23433
  "use strict";
23323
23434
  voidElements2 = { test: () => true };
@@ -23582,7 +23693,7 @@ var init_document = __esm({
23582
23693
  init_symbols();
23583
23694
  init_facades();
23584
23695
  init_html_classes();
23585
- init_mime();
23696
+ init_mime2();
23586
23697
  init_utils2();
23587
23698
  init_object();
23588
23699
  init_non_element_parent_node();
@@ -24121,17 +24232,17 @@ var init_sourceMutation = __esm({
24121
24232
  // ../core/src/studio-api/routes/files.ts
24122
24233
  import { bodyLimit } from "hono/body-limit";
24123
24234
  import {
24124
- existsSync as existsSync9,
24235
+ existsSync as existsSync10,
24125
24236
  readFileSync as readFileSync9,
24126
- writeFileSync as writeFileSync6,
24127
- mkdirSync as mkdirSync5,
24237
+ writeFileSync as writeFileSync7,
24238
+ mkdirSync as mkdirSync6,
24128
24239
  unlinkSync as unlinkSync3,
24129
24240
  rmSync as rmSync3,
24130
24241
  statSync,
24131
24242
  renameSync as renameSync2,
24132
24243
  readdirSync as readdirSync4
24133
24244
  } from "fs";
24134
- import { resolve as resolve9, dirname as dirname5, join as join12 } from "path";
24245
+ import { resolve as resolve9, dirname as dirname5, join as join13 } from "path";
24135
24246
  async function resolveProjectFile(c2, adapter2, opts) {
24136
24247
  const id = c2.req.param("id");
24137
24248
  const project = await adapter2.resolveProject(id);
@@ -24146,14 +24257,14 @@ async function resolveProjectFile(c2, adapter2, opts) {
24146
24257
  if (!isSafePath(project.dir, absPath)) {
24147
24258
  return { error: c2.json({ error: "forbidden" }, 403) };
24148
24259
  }
24149
- if (opts?.mustExist && !existsSync9(absPath)) {
24260
+ if (opts?.mustExist && !existsSync10(absPath)) {
24150
24261
  return { error: c2.json({ error: "not found" }, 404) };
24151
24262
  }
24152
24263
  return { project, filePath, absPath };
24153
24264
  }
24154
24265
  function ensureDir(filePath) {
24155
24266
  const dir = dirname5(filePath);
24156
- if (!existsSync9(dir)) mkdirSync5(dir, { recursive: true });
24267
+ if (!existsSync10(dir)) mkdirSync6(dir, { recursive: true });
24157
24268
  }
24158
24269
  function generateCopyPath(projectDir, originalPath) {
24159
24270
  const ext = originalPath.includes(".") ? "." + originalPath.split(".").pop() : "";
@@ -24162,7 +24273,7 @@ function generateCopyPath(projectDir, originalPath) {
24162
24273
  const cleanBase = copyMatch ? base.slice(0, -copyMatch[0].length) : base;
24163
24274
  let num = copyMatch ? copyMatch[1] ? parseInt(copyMatch[1]) + 1 : 2 : 1;
24164
24275
  let candidate = num === 1 ? `${cleanBase} (copy)${ext}` : `${cleanBase} (copy ${num})${ext}`;
24165
- while (existsSync9(resolve9(projectDir, candidate))) {
24276
+ while (existsSync10(resolve9(projectDir, candidate))) {
24166
24277
  num++;
24167
24278
  candidate = `${cleanBase} (copy ${num})${ext}`;
24168
24279
  }
@@ -24171,7 +24282,7 @@ function generateCopyPath(projectDir, originalPath) {
24171
24282
  function walkFiles(dir, filter2) {
24172
24283
  const results = [];
24173
24284
  for (const entry of readdirSync4(dir, { withFileTypes: true })) {
24174
- const full = join12(dir, entry.name);
24285
+ const full = join13(dir, entry.name);
24175
24286
  if (entry.isDirectory()) {
24176
24287
  if (entry.name === "node_modules" || entry.name === ".thumbnails" || entry.name === "renders")
24177
24288
  continue;
@@ -24193,7 +24304,7 @@ function updateReferences(projectDir, oldPath, newPath) {
24193
24304
  if (!content.includes(oldPath)) continue;
24194
24305
  const updated = content.split(oldPath).join(newPath);
24195
24306
  if (updated !== content) {
24196
- writeFileSync6(file, updated, "utf-8");
24307
+ writeFileSync7(file, updated, "utf-8");
24197
24308
  updatedCount++;
24198
24309
  }
24199
24310
  }
@@ -24211,18 +24322,18 @@ function registerFileRoutes(api, adapter2) {
24211
24322
  if ("error" in res) return res.error;
24212
24323
  ensureDir(res.absPath);
24213
24324
  const body = await c2.req.text();
24214
- writeFileSync6(res.absPath, body, "utf-8");
24325
+ writeFileSync7(res.absPath, body, "utf-8");
24215
24326
  return c2.json({ ok: true });
24216
24327
  });
24217
24328
  api.post("/projects/:id/files/*", async (c2) => {
24218
24329
  const res = await resolveProjectFile(c2, adapter2);
24219
24330
  if ("error" in res) return res.error;
24220
- if (existsSync9(res.absPath)) {
24331
+ if (existsSync10(res.absPath)) {
24221
24332
  return c2.json({ error: "already exists" }, 409);
24222
24333
  }
24223
24334
  ensureDir(res.absPath);
24224
24335
  const body = await c2.req.text().catch(() => "");
24225
- writeFileSync6(res.absPath, body, "utf-8");
24336
+ writeFileSync7(res.absPath, body, "utf-8");
24226
24337
  return c2.json({ ok: true, path: res.filePath }, 201);
24227
24338
  });
24228
24339
  api.delete("/projects/:id/files/*", async (c2) => {
@@ -24250,7 +24361,7 @@ function registerFileRoutes(api, adapter2) {
24250
24361
  if (!isSafePath(project.dir, absPath)) {
24251
24362
  return c2.json({ error: "forbidden" }, 403);
24252
24363
  }
24253
- if (!existsSync9(absPath)) {
24364
+ if (!existsSync10(absPath)) {
24254
24365
  return c2.json({ error: "not found" }, 404);
24255
24366
  }
24256
24367
  const body = await c2.req.json().catch(() => null);
@@ -24262,7 +24373,7 @@ function registerFileRoutes(api, adapter2) {
24262
24373
  if (patchedContent === originalContent) {
24263
24374
  return c2.json({ ok: true, changed: false, content: originalContent });
24264
24375
  }
24265
- writeFileSync6(absPath, patchedContent, "utf-8");
24376
+ writeFileSync7(absPath, patchedContent, "utf-8");
24266
24377
  return c2.json({ ok: true, changed: true, content: patchedContent });
24267
24378
  });
24268
24379
  api.patch("/projects/:id/files/*", async (c2) => {
@@ -24276,7 +24387,7 @@ function registerFileRoutes(api, adapter2) {
24276
24387
  if (!isSafePath(res.project.dir, newAbs)) {
24277
24388
  return c2.json({ error: "forbidden" }, 403);
24278
24389
  }
24279
- if (existsSync9(newAbs)) {
24390
+ if (existsSync10(newAbs)) {
24280
24391
  return c2.json({ error: "already exists" }, 409);
24281
24392
  }
24282
24393
  ensureDir(newAbs);
@@ -24292,7 +24403,7 @@ function registerFileRoutes(api, adapter2) {
24292
24403
  return c2.json({ error: "path required" }, 400);
24293
24404
  }
24294
24405
  const srcAbs = resolve9(project.dir, body.path);
24295
- if (!isSafePath(project.dir, srcAbs) || !existsSync9(srcAbs)) {
24406
+ if (!isSafePath(project.dir, srcAbs) || !existsSync10(srcAbs)) {
24296
24407
  return c2.json({ error: "not found" }, 404);
24297
24408
  }
24298
24409
  const copyPath = generateCopyPath(project.dir, body.path);
@@ -24301,7 +24412,7 @@ function registerFileRoutes(api, adapter2) {
24301
24412
  return c2.json({ error: "forbidden" }, 403);
24302
24413
  }
24303
24414
  ensureDir(destAbs);
24304
- writeFileSync6(destAbs, readFileSync9(srcAbs));
24415
+ writeFileSync7(destAbs, readFileSync9(srcAbs));
24305
24416
  return c2.json({ ok: true, path: copyPath }, 201);
24306
24417
  });
24307
24418
  const MAX_UPLOAD_BYTES = 500 * 1024 * 1024;
@@ -24317,7 +24428,7 @@ function registerFileRoutes(api, adapter2) {
24317
24428
  const subDir = c2.req.query("dir") ?? "";
24318
24429
  const targetDir = subDir ? resolve9(project.dir, subDir) : project.dir;
24319
24430
  if (!isSafePath(project.dir, targetDir)) return c2.json({ error: "forbidden" }, 403);
24320
- if (subDir && !existsSync9(targetDir)) mkdirSync5(targetDir, { recursive: true });
24431
+ if (subDir && !existsSync10(targetDir)) mkdirSync6(targetDir, { recursive: true });
24321
24432
  const formData = await c2.req.formData();
24322
24433
  const uploaded = [];
24323
24434
  const skipped = [];
@@ -24334,12 +24445,12 @@ function registerFileRoutes(api, adapter2) {
24334
24445
  if (!isSafePath(project.dir, destPath)) continue;
24335
24446
  let finalPath = destPath;
24336
24447
  let finalName = name;
24337
- if (existsSync9(finalPath)) {
24448
+ if (existsSync10(finalPath)) {
24338
24449
  const dotIdx = name.indexOf(".", name.startsWith(".") ? 1 : 0);
24339
24450
  const ext = dotIdx > 0 ? name.slice(dotIdx) : "";
24340
24451
  const base = dotIdx > 0 ? name.slice(0, dotIdx) : name;
24341
24452
  let n = 2;
24342
- while (n < 1e4 && existsSync9(resolve9(targetDir, `${base} (${n})${ext}`))) n++;
24453
+ while (n < 1e4 && existsSync10(resolve9(targetDir, `${base} (${n})${ext}`))) n++;
24343
24454
  if (n >= 1e4) {
24344
24455
  skipped.push(name);
24345
24456
  continue;
@@ -24353,8 +24464,13 @@ function registerFileRoutes(api, adapter2) {
24353
24464
  invalid.push({ name: finalName, reason: validation.reason });
24354
24465
  continue;
24355
24466
  }
24356
- writeFileSync6(finalPath, buffer);
24357
- uploaded.push(subDir ? join12(subDir, finalName) : finalName);
24467
+ writeFileSync7(finalPath, buffer);
24468
+ const relativePath = subDir ? join13(subDir, finalName) : finalName;
24469
+ uploaded.push(relativePath);
24470
+ if (isAudioFile2(finalName)) {
24471
+ generateWaveformCache(project.dir, relativePath).catch(() => {
24472
+ });
24473
+ }
24358
24474
  }
24359
24475
  return c2.json({ ok: true, files: uploaded, skipped, invalid }, 201);
24360
24476
  }
@@ -24363,57 +24479,20 @@ function registerFileRoutes(api, adapter2) {
24363
24479
  var init_files = __esm({
24364
24480
  "../core/src/studio-api/routes/files.ts"() {
24365
24481
  "use strict";
24482
+ init_mime();
24483
+ init_waveform();
24366
24484
  init_mediaValidation();
24367
24485
  init_safePath();
24368
24486
  init_sourceMutation();
24369
24487
  }
24370
24488
  });
24371
24489
 
24372
- // ../core/src/studio-api/helpers/mime.ts
24373
- function getMimeType(path2) {
24374
- const ext = path2.slice(path2.lastIndexOf(".")).toLowerCase();
24375
- return MIME_TYPES[ext] || "application/octet-stream";
24376
- }
24377
- var MIME_TYPES;
24378
- var init_mime2 = __esm({
24379
- "../core/src/studio-api/helpers/mime.ts"() {
24380
- "use strict";
24381
- MIME_TYPES = {
24382
- ".html": "text/html",
24383
- ".css": "text/css",
24384
- ".js": "text/javascript",
24385
- ".mjs": "text/javascript",
24386
- ".json": "application/json",
24387
- ".svg": "image/svg+xml",
24388
- ".png": "image/png",
24389
- ".jpg": "image/jpeg",
24390
- ".jpeg": "image/jpeg",
24391
- ".gif": "image/gif",
24392
- ".webp": "image/webp",
24393
- ".ico": "image/x-icon",
24394
- ".mp4": "video/mp4",
24395
- ".mov": "video/quicktime",
24396
- ".webm": "video/webm",
24397
- ".mp3": "audio/mpeg",
24398
- ".wav": "audio/wav",
24399
- ".ogg": "audio/ogg",
24400
- ".m4a": "audio/mp4",
24401
- ".woff": "font/woff",
24402
- ".woff2": "font/woff2",
24403
- ".ttf": "font/ttf",
24404
- ".otf": "font/otf",
24405
- ".txt": "text/plain",
24406
- ".md": "text/markdown"
24407
- };
24408
- }
24409
- });
24410
-
24411
24490
  // ../core/src/studio-api/helpers/subComposition.ts
24412
- import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
24413
- import { join as join13 } from "path";
24491
+ import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
24492
+ import { join as join14 } from "path";
24414
24493
  function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref) {
24415
- const compFile = join13(projectDir, compPath);
24416
- if (!existsSync10(compFile)) return null;
24494
+ const compFile = join14(projectDir, compPath);
24495
+ if (!existsSync11(compFile)) return null;
24417
24496
  const rawComp = readFileSync10(compFile, "utf-8");
24418
24497
  const templateMatch = rawComp.match(/<template[^>]*>([\s\S]*)<\/template>/i);
24419
24498
  const content = templateMatch?.[1] ?? rawComp;
@@ -24440,9 +24519,9 @@ function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref) {
24440
24519
  styleEl.textContent = rewriteCssAssetUrls(styleEl.textContent || "", compPath);
24441
24520
  }
24442
24521
  const rewrittenContent = contentDoc.body.innerHTML || content;
24443
- const indexPath = join13(projectDir, "index.html");
24522
+ const indexPath = join14(projectDir, "index.html");
24444
24523
  let headContent = "";
24445
- if (existsSync10(indexPath)) {
24524
+ if (existsSync11(indexPath)) {
24446
24525
  const indexHtml = readFileSync10(indexPath, "utf-8");
24447
24526
  const headMatch = indexHtml.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
24448
24527
  headContent = headMatch?.[1] ?? "";
@@ -24479,7 +24558,7 @@ var init_subComposition = __esm({
24479
24558
  });
24480
24559
 
24481
24560
  // ../core/src/studio-api/routes/preview.ts
24482
- import { existsSync as existsSync11, readFileSync as readFileSync11, statSync as statSync2 } from "fs";
24561
+ import { existsSync as existsSync12, readFileSync as readFileSync11, statSync as statSync2 } from "fs";
24483
24562
  import { resolve as resolve10 } from "path";
24484
24563
  function registerPreviewRoutes(api, adapter2) {
24485
24564
  api.get("/projects/:id/preview", async (c2) => {
@@ -24489,7 +24568,7 @@ function registerPreviewRoutes(api, adapter2) {
24489
24568
  let bundled = await adapter2.bundle(project.dir);
24490
24569
  if (!bundled) {
24491
24570
  const indexPath = resolve10(project.dir, "index.html");
24492
- if (!existsSync11(indexPath)) return c2.text("not found", 404);
24571
+ if (!existsSync12(indexPath)) return c2.text("not found", 404);
24493
24572
  bundled = readFileSync11(indexPath, "utf-8");
24494
24573
  }
24495
24574
  if (!bundled.includes("hyperframe.runtime") && !bundled.includes("hyperframes-preview-runtime")) {
@@ -24505,7 +24584,7 @@ ${runtimeTag}`;
24505
24584
  return c2.html(bundled);
24506
24585
  } catch {
24507
24586
  const file = resolve10(project.dir, "index.html");
24508
- if (existsSync11(file)) return c2.html(readFileSync11(file, "utf-8"));
24587
+ if (existsSync12(file)) return c2.html(readFileSync11(file, "utf-8"));
24509
24588
  return c2.text("not found", 404);
24510
24589
  }
24511
24590
  });
@@ -24516,7 +24595,7 @@ ${runtimeTag}`;
24516
24595
  c2.req.path.replace(`/projects/${project.id}/preview/comp/`, "").split("?")[0] ?? ""
24517
24596
  );
24518
24597
  const compFile = resolve10(project.dir, compPath);
24519
- if (!isSafePath(project.dir, compFile) || !existsSync11(compFile) || !statSync2(compFile).isFile()) {
24598
+ if (!isSafePath(project.dir, compFile) || !existsSync12(compFile) || !statSync2(compFile).isFile()) {
24520
24599
  return c2.text("not found", 404);
24521
24600
  }
24522
24601
  const baseHref = `/api/projects/${project.id}/preview/`;
@@ -24531,7 +24610,7 @@ ${runtimeTag}`;
24531
24610
  c2.req.path.replace(`/projects/${project.id}/preview/`, "").split("?")[0] ?? ""
24532
24611
  );
24533
24612
  const file = resolve10(project.dir, subPath);
24534
- if (!isSafePath(project.dir, file) || !existsSync11(file) || !statSync2(file).isFile()) {
24613
+ if (!isSafePath(project.dir, file) || !existsSync12(file) || !statSync2(file).isFile()) {
24535
24614
  return c2.text("not found", 404);
24536
24615
  }
24537
24616
  const contentType = getMimeType(subPath);
@@ -24570,14 +24649,14 @@ var init_preview = __esm({
24570
24649
  "../core/src/studio-api/routes/preview.ts"() {
24571
24650
  "use strict";
24572
24651
  init_safePath();
24573
- init_mime2();
24652
+ init_mime();
24574
24653
  init_subComposition();
24575
24654
  }
24576
24655
  });
24577
24656
 
24578
24657
  // ../core/src/studio-api/routes/lint.ts
24579
24658
  import { readFileSync as readFileSync12 } from "fs";
24580
- import { join as join14 } from "path";
24659
+ import { join as join15 } from "path";
24581
24660
  function registerLintRoutes(api, adapter2) {
24582
24661
  api.get("/projects/:id/lint", async (c2) => {
24583
24662
  const project = await adapter2.resolveProject(c2.req.param("id"));
@@ -24586,7 +24665,7 @@ function registerLintRoutes(api, adapter2) {
24586
24665
  const htmlFiles = walkDir(project.dir).filter((f3) => f3.endsWith(".html"));
24587
24666
  const allFindings = [];
24588
24667
  for (const file of htmlFiles) {
24589
- const content = readFileSync12(join14(project.dir, file), "utf-8");
24668
+ const content = readFileSync12(join15(project.dir, file), "utf-8");
24590
24669
  const result = await adapter2.lint(content, { filePath: file });
24591
24670
  if (result?.findings) {
24592
24671
  for (const f3 of result.findings) {
@@ -24610,8 +24689,8 @@ var init_lint2 = __esm({
24610
24689
 
24611
24690
  // ../core/src/studio-api/routes/render.ts
24612
24691
  import { streamSSE } from "hono/streaming";
24613
- import { existsSync as existsSync12, readFileSync as readFileSync13, mkdirSync as mkdirSync6, unlinkSync as unlinkSync4, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
24614
- import { join as join15 } from "path";
24692
+ import { existsSync as existsSync13, readFileSync as readFileSync13, mkdirSync as mkdirSync7, unlinkSync as unlinkSync4, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
24693
+ import { join as join16 } from "path";
24615
24694
  function registerRenderRoutes(api, adapter2) {
24616
24695
  const renderJobs = /* @__PURE__ */ new Map();
24617
24696
  const TTL_MS = 3e5;
@@ -24648,9 +24727,9 @@ function registerRenderRoutes(api, adapter2) {
24648
24727
  const timePart = now.toTimeString().slice(0, 8).replace(/:/g, "-");
24649
24728
  const jobId = `${project.id}_${datePart}_${timePart}`;
24650
24729
  const rendersDir = adapter2.rendersDir(project);
24651
- if (!existsSync12(rendersDir)) mkdirSync6(rendersDir, { recursive: true });
24730
+ if (!existsSync13(rendersDir)) mkdirSync7(rendersDir, { recursive: true });
24652
24731
  const ext = FORMAT_EXT2[format] ?? ".mp4";
24653
- const outputPath = join15(rendersDir, `${jobId}${ext}`);
24732
+ const outputPath = join16(rendersDir, `${jobId}${ext}`);
24654
24733
  const jobState = adapter2.startRender({
24655
24734
  project,
24656
24735
  outputPath,
@@ -24715,7 +24794,7 @@ function registerRenderRoutes(api, adapter2) {
24715
24794
  api.get("/render/:jobId/view", (c2) => {
24716
24795
  const { jobId } = c2.req.param();
24717
24796
  const job = renderJobs.get(jobId);
24718
- if (!job?.outputPath || !existsSync12(job.outputPath)) {
24797
+ if (!job?.outputPath || !existsSync13(job.outputPath)) {
24719
24798
  return c2.json({ error: "not found" }, 404);
24720
24799
  }
24721
24800
  const contentType = renderContentType(job.outputPath);
@@ -24733,7 +24812,7 @@ function registerRenderRoutes(api, adapter2) {
24733
24812
  api.get("/render/:jobId/download", (c2) => {
24734
24813
  const { jobId } = c2.req.param();
24735
24814
  const job = renderJobs.get(jobId);
24736
- if (!job?.outputPath || !existsSync12(job.outputPath)) {
24815
+ if (!job?.outputPath || !existsSync13(job.outputPath)) {
24737
24816
  return c2.json({ error: "not found" }, 404);
24738
24817
  }
24739
24818
  const contentType = renderContentType(job.outputPath);
@@ -24752,8 +24831,8 @@ function registerRenderRoutes(api, adapter2) {
24752
24831
  if (state.id === jobId && state.outputPath) {
24753
24832
  const dir = state.outputPath.replace(/\/[^/]+$/, "");
24754
24833
  for (const ext of [".mp4", ".webm", ".mov", ".meta.json"]) {
24755
- const fp = join15(dir, `${jobId}${ext}`);
24756
- if (existsSync12(fp)) unlinkSync4(fp);
24834
+ const fp = join16(dir, `${jobId}${ext}`);
24835
+ if (existsSync13(fp)) unlinkSync4(fp);
24757
24836
  }
24758
24837
  break;
24759
24838
  }
@@ -24767,8 +24846,8 @@ function registerRenderRoutes(api, adapter2) {
24767
24846
  const filename = c2.req.path.split("/renders/file/")[1];
24768
24847
  if (!filename) return c2.json({ error: "missing filename" }, 400);
24769
24848
  const rendersDir = adapter2.rendersDir(project);
24770
- const fp = join15(rendersDir, filename);
24771
- if (!existsSync12(fp)) return c2.json({ error: "not found" }, 404);
24849
+ const fp = join16(rendersDir, filename);
24850
+ if (!existsSync13(fp)) return c2.json({ error: "not found" }, 404);
24772
24851
  const contentType = renderContentType(fp);
24773
24852
  const content = readFileSync13(fp);
24774
24853
  return new Response(content, {
@@ -24784,15 +24863,15 @@ function registerRenderRoutes(api, adapter2) {
24784
24863
  const project = await adapter2.resolveProject(c2.req.param("id"));
24785
24864
  if (!project) return c2.json({ error: "not found" }, 404);
24786
24865
  const rendersDir = adapter2.rendersDir(project);
24787
- if (!existsSync12(rendersDir)) return c2.json({ renders: [] });
24866
+ if (!existsSync13(rendersDir)) return c2.json({ renders: [] });
24788
24867
  const files = readdirSync5(rendersDir).filter((f3) => f3.endsWith(".mp4") || f3.endsWith(".webm") || f3.endsWith(".mov")).map((f3) => {
24789
- const fp = join15(rendersDir, f3);
24868
+ const fp = join16(rendersDir, f3);
24790
24869
  const stat3 = statSync3(fp);
24791
24870
  const rid = f3.replace(/\.(mp4|webm|mov)$/, "");
24792
- const metaPath = join15(rendersDir, `${rid}.meta.json`);
24871
+ const metaPath = join16(rendersDir, `${rid}.meta.json`);
24793
24872
  let status = "complete";
24794
24873
  let durationMs;
24795
- if (existsSync12(metaPath)) {
24874
+ if (existsSync13(metaPath)) {
24796
24875
  try {
24797
24876
  const meta = JSON.parse(readFileSync13(metaPath, "utf-8"));
24798
24877
  if (meta.status === "failed") status = "failed";
@@ -24815,7 +24894,7 @@ function registerRenderRoutes(api, adapter2) {
24815
24894
  id: file.id,
24816
24895
  status: file.status,
24817
24896
  progress: 100,
24818
- outputPath: join15(rendersDir, file.filename),
24897
+ outputPath: join16(rendersDir, file.filename),
24819
24898
  createdAt: file.createdAt
24820
24899
  });
24821
24900
  }
@@ -24830,8 +24909,8 @@ var init_render = __esm({
24830
24909
  });
24831
24910
 
24832
24911
  // ../core/src/studio-api/routes/thumbnail.ts
24833
- import { existsSync as existsSync13, readFileSync as readFileSync14, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, statSync as statSync4 } from "fs";
24834
- import { join as join16 } from "path";
24912
+ import { existsSync as existsSync14, readFileSync as readFileSync14, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, statSync as statSync4 } from "fs";
24913
+ import { join as join17 } from "path";
24835
24914
  function registerThumbnailRoutes(api, adapter2) {
24836
24915
  api.get("/projects/:id/thumbnail/*", async (c2) => {
24837
24916
  if (!adapter2.generateThumbnail) {
@@ -24853,8 +24932,8 @@ function registerThumbnailRoutes(api, adapter2) {
24853
24932
  let compH = vpHeight || 1080;
24854
24933
  let sourceMtime = 0;
24855
24934
  if (!vpWidth) {
24856
- const htmlFile = join16(project.dir, compPath);
24857
- if (existsSync13(htmlFile)) {
24935
+ const htmlFile = join17(project.dir, compPath);
24936
+ if (existsSync14(htmlFile)) {
24858
24937
  sourceMtime = Math.round(statSync4(htmlFile).mtimeMs);
24859
24938
  const html = readFileSync14(htmlFile, "utf-8");
24860
24939
  const wMatch = html.match(/data-width=["'](\d+)["']/);
@@ -24864,12 +24943,12 @@ function registerThumbnailRoutes(api, adapter2) {
24864
24943
  }
24865
24944
  }
24866
24945
  const previewUrl = compPath === "index.html" ? `http://${c2.req.header("host")}/api/projects/${project.id}/preview` : `http://${c2.req.header("host")}/api/projects/${project.id}/preview/comp/${compPath}`;
24867
- const cacheDir = join16(project.dir, ".thumbnails");
24946
+ const cacheDir = join17(project.dir, ".thumbnails");
24868
24947
  const selectorKey = selector ? `_${selector.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 80)}` : "";
24869
24948
  const urlVersionKey = urlVersion ? `_${urlVersion.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32)}` : "";
24870
24949
  const cacheKey = `${THUMBNAIL_CACHE_VERSION}${urlVersionKey}_${compPath.replace(/\//g, "_")}_${compW}x${compH}_${sourceMtime}_${seekTime.toFixed(2)}${selectorKey}.jpg`;
24871
- const cachePath2 = join16(cacheDir, cacheKey);
24872
- if (existsSync13(cachePath2)) {
24950
+ const cachePath2 = join17(cacheDir, cacheKey);
24951
+ if (existsSync14(cachePath2)) {
24873
24952
  return new Response(new Uint8Array(readFileSync14(cachePath2)), {
24874
24953
  headers: { "Content-Type": "image/jpeg", "Cache-Control": "public, max-age=60" }
24875
24954
  });
@@ -24887,8 +24966,8 @@ function registerThumbnailRoutes(api, adapter2) {
24887
24966
  if (!buffer) {
24888
24967
  return c2.json({ error: "Thumbnail generation returned null" }, 500);
24889
24968
  }
24890
- if (!existsSync13(cacheDir)) mkdirSync7(cacheDir, { recursive: true });
24891
- writeFileSync7(cachePath2, buffer);
24969
+ if (!existsSync14(cacheDir)) mkdirSync8(cacheDir, { recursive: true });
24970
+ writeFileSync8(cachePath2, buffer);
24892
24971
  return new Response(new Uint8Array(buffer), {
24893
24972
  headers: { "Content-Type": "image/jpeg", "Cache-Control": "public, max-age=60" }
24894
24973
  });
@@ -24906,11 +24985,55 @@ var init_thumbnail = __esm({
24906
24985
  }
24907
24986
  });
24908
24987
 
24988
+ // ../core/src/studio-api/routes/waveform.ts
24989
+ import { existsSync as existsSync15, readFileSync as readFileSync15, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9 } from "fs";
24990
+ import { join as join18 } from "path";
24991
+ function registerWaveformRoutes(api, adapter2) {
24992
+ api.get("/projects/:id/waveform/*", async (c2) => {
24993
+ const project = await adapter2.resolveProject(c2.req.param("id"));
24994
+ if (!project) return c2.json({ error: "not found" }, 404);
24995
+ const assetPath = decodeURIComponent(
24996
+ c2.req.path.replace(`/projects/${project.id}/waveform/`, "").split("?")[0] ?? ""
24997
+ );
24998
+ const audioPath = join18(project.dir, assetPath);
24999
+ if (!existsSync15(audioPath)) return c2.json({ error: "file not found" }, 404);
25000
+ const cacheDir = join18(project.dir, ".waveform-cache");
25001
+ const cachePath2 = join18(cacheDir, buildWaveformCacheKey(assetPath));
25002
+ if (existsSync15(cachePath2)) {
25003
+ try {
25004
+ const peaks2 = JSON.parse(readFileSync15(cachePath2, "utf-8"));
25005
+ return c2.json({ peaks: peaks2 });
25006
+ } catch {
25007
+ }
25008
+ }
25009
+ let peaks;
25010
+ try {
25011
+ peaks = await decodeAudioPeaks(audioPath);
25012
+ } catch {
25013
+ return c2.json({ error: "failed to decode audio" }, 500);
25014
+ }
25015
+ try {
25016
+ mkdirSync9(cacheDir, { recursive: true });
25017
+ writeFileSync9(cachePath2, JSON.stringify(peaks));
25018
+ } catch {
25019
+ }
25020
+ return c2.json({ peaks });
25021
+ });
25022
+ }
25023
+ var init_waveform2 = __esm({
25024
+ "../core/src/studio-api/routes/waveform.ts"() {
25025
+ "use strict";
25026
+ init_waveform();
25027
+ init_mime();
25028
+ init_waveform();
25029
+ }
25030
+ });
25031
+
24909
25032
  // ../core/src/studio-api/routes/fonts.ts
24910
25033
  import { execFileSync as execFileSync4 } from "child_process";
24911
- import { existsSync as existsSync14, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
25034
+ import { existsSync as existsSync16, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
24912
25035
  import { homedir as homedir4, platform as platform3 } from "os";
24913
- import { join as join17 } from "path";
25036
+ import { join as join19 } from "path";
24914
25037
  function isRecord(value) {
24915
25038
  return typeof value === "object" && value !== null;
24916
25039
  }
@@ -24918,18 +25041,18 @@ function fontDirectories() {
24918
25041
  const home = homedir4();
24919
25042
  if (platform3() === "darwin") {
24920
25043
  return [
24921
- join17(home, "Library", "Fonts"),
25044
+ join19(home, "Library", "Fonts"),
24922
25045
  "/Library/Fonts",
24923
25046
  "/System/Library/Fonts",
24924
25047
  "/System/Library/Fonts/Supplemental"
24925
25048
  ];
24926
25049
  }
24927
25050
  if (platform3() === "win32") {
24928
- return [join17(process.env.WINDIR || "C:\\Windows", "Fonts")];
25051
+ return [join19(process.env.WINDIR || "C:\\Windows", "Fonts")];
24929
25052
  }
24930
25053
  return [
24931
- join17(home, ".fonts"),
24932
- join17(home, ".local", "share", "fonts"),
25054
+ join19(home, ".fonts"),
25055
+ join19(home, ".local", "share", "fonts"),
24933
25056
  "/usr/local/share/fonts",
24934
25057
  "/usr/share/fonts"
24935
25058
  ];
@@ -24981,10 +25104,10 @@ function collectMacSystemProfilerFonts() {
24981
25104
  return fonts;
24982
25105
  }
24983
25106
  function collectFontsFromDir(dir, depth = 0) {
24984
- if (!existsSync14(dir) || depth > 2) return [];
25107
+ if (!existsSync16(dir) || depth > 2) return [];
24985
25108
  const fonts = [];
24986
25109
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
24987
- const fullPath = join17(dir, entry.name);
25110
+ const fullPath = join19(dir, entry.name);
24988
25111
  if (entry.isDirectory()) {
24989
25112
  fonts.push(...collectFontsFromDir(fullPath, depth + 1));
24990
25113
  continue;
@@ -25128,6 +25251,7 @@ function createStudioApi(adapter2) {
25128
25251
  registerLintRoutes(api, adapter2);
25129
25252
  registerRenderRoutes(api, adapter2);
25130
25253
  registerThumbnailRoutes(api, adapter2);
25254
+ registerWaveformRoutes(api, adapter2);
25131
25255
  registerFontRoutes(api);
25132
25256
  return api;
25133
25257
  }
@@ -25140,6 +25264,7 @@ var init_createStudioApi = __esm({
25140
25264
  init_lint2();
25141
25265
  init_render();
25142
25266
  init_thumbnail();
25267
+ init_waveform2();
25143
25268
  init_fonts();
25144
25269
  }
25145
25270
  });
@@ -25159,7 +25284,7 @@ var init_studio_api = __esm({
25159
25284
  "use strict";
25160
25285
  init_createStudioApi();
25161
25286
  init_safePath();
25162
- init_mime2();
25287
+ init_mime();
25163
25288
  init_subComposition();
25164
25289
  }
25165
25290
  });
@@ -25175,9 +25300,9 @@ __export(manager_exports2, {
25175
25300
  setBrowserPath: () => setBrowserPath
25176
25301
  });
25177
25302
  import { execSync } from "child_process";
25178
- import { existsSync as existsSync15, rmSync as rmSync4 } from "fs";
25303
+ import { existsSync as existsSync17, rmSync as rmSync4 } from "fs";
25179
25304
  import { homedir as homedir5 } from "os";
25180
- import { join as join18 } from "path";
25305
+ import { join as join20 } from "path";
25181
25306
  import { Browser, detectBrowserPlatform, getInstalledBrowsers, install } from "@puppeteer/browsers";
25182
25307
  function setBrowserPath(path2) {
25183
25308
  _browserPathOverride = path2;
@@ -25197,17 +25322,17 @@ function whichBinary2(name) {
25197
25322
  }
25198
25323
  }
25199
25324
  function findFromEnv2() {
25200
- if (_browserPathOverride && existsSync15(_browserPathOverride)) {
25325
+ if (_browserPathOverride && existsSync17(_browserPathOverride)) {
25201
25326
  return { executablePath: _browserPathOverride, source: "env" };
25202
25327
  }
25203
25328
  const envPath = process.env["HYPERFRAMES_BROWSER_PATH"];
25204
- if (envPath && existsSync15(envPath)) {
25329
+ if (envPath && existsSync17(envPath)) {
25205
25330
  return { executablePath: envPath, source: "env" };
25206
25331
  }
25207
25332
  return void 0;
25208
25333
  }
25209
25334
  async function findFromCache() {
25210
- if (!existsSync15(CACHE_DIR2)) {
25335
+ if (!existsSync17(CACHE_DIR2)) {
25211
25336
  return void 0;
25212
25337
  }
25213
25338
  const installed = await getInstalledBrowsers({ cacheDir: CACHE_DIR2 });
@@ -25219,7 +25344,7 @@ async function findFromCache() {
25219
25344
  }
25220
25345
  function findFromSystem2() {
25221
25346
  for (const p of SYSTEM_CHROME_PATHS) {
25222
- if (existsSync15(p)) {
25347
+ if (existsSync17(p)) {
25223
25348
  return { executablePath: p, source: "system" };
25224
25349
  }
25225
25350
  }
@@ -25253,7 +25378,7 @@ async function ensureBrowser(options) {
25253
25378
  return { executablePath: installed.executablePath, source: "download" };
25254
25379
  }
25255
25380
  function clearBrowser() {
25256
- if (!existsSync15(CACHE_DIR2)) {
25381
+ if (!existsSync17(CACHE_DIR2)) {
25257
25382
  return false;
25258
25383
  }
25259
25384
  rmSync4(CACHE_DIR2, { recursive: true, force: true });
@@ -25264,7 +25389,7 @@ var init_manager2 = __esm({
25264
25389
  "src/browser/manager.ts"() {
25265
25390
  "use strict";
25266
25391
  CHROME_VERSION = "131.0.6778.85";
25267
- CACHE_DIR2 = join18(homedir5(), ".cache", "hyperframes", "chrome");
25392
+ CACHE_DIR2 = join20(homedir5(), ".cache", "hyperframes", "chrome");
25268
25393
  SYSTEM_CHROME_PATHS = process.platform === "darwin" ? ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"] : [
25269
25394
  "/usr/bin/google-chrome",
25270
25395
  "/usr/bin/google-chrome-stable",
@@ -25391,8 +25516,8 @@ var init_config2 = __esm({
25391
25516
  });
25392
25517
 
25393
25518
  // ../engine/src/services/browserManager.ts
25394
- import { existsSync as existsSync16, readdirSync as readdirSync7 } from "fs";
25395
- import { join as join19 } from "path";
25519
+ import { existsSync as existsSync18, readdirSync as readdirSync7 } from "fs";
25520
+ import { join as join21 } from "path";
25396
25521
  import { homedir as homedir6 } from "os";
25397
25522
  async function getPuppeteer() {
25398
25523
  if (_puppeteer) return _puppeteer;
@@ -25413,19 +25538,19 @@ function resolveHeadlessShellPath(config) {
25413
25538
  if (process.env.PRODUCER_HEADLESS_SHELL_PATH) {
25414
25539
  return process.env.PRODUCER_HEADLESS_SHELL_PATH;
25415
25540
  }
25416
- const baseDir = join19(homedir6(), ".cache", "puppeteer", "chrome-headless-shell");
25417
- if (!existsSync16(baseDir)) return void 0;
25541
+ const baseDir = join21(homedir6(), ".cache", "puppeteer", "chrome-headless-shell");
25542
+ if (!existsSync18(baseDir)) return void 0;
25418
25543
  try {
25419
25544
  const versions = readdirSync7(baseDir).sort().reverse();
25420
25545
  for (const version of versions) {
25421
25546
  const candidates = [
25422
- join19(baseDir, version, "chrome-headless-shell-linux64", "chrome-headless-shell"),
25423
- join19(baseDir, version, "chrome-headless-shell-mac-arm64", "chrome-headless-shell"),
25424
- join19(baseDir, version, "chrome-headless-shell-mac-x64", "chrome-headless-shell"),
25425
- join19(baseDir, version, "chrome-headless-shell-win64", "chrome-headless-shell.exe")
25547
+ join21(baseDir, version, "chrome-headless-shell-linux64", "chrome-headless-shell"),
25548
+ join21(baseDir, version, "chrome-headless-shell-mac-arm64", "chrome-headless-shell"),
25549
+ join21(baseDir, version, "chrome-headless-shell-mac-x64", "chrome-headless-shell"),
25550
+ join21(baseDir, version, "chrome-headless-shell-win64", "chrome-headless-shell.exe")
25426
25551
  ];
25427
25552
  for (const binary of candidates) {
25428
- if (existsSync16(binary)) return binary;
25553
+ if (existsSync18(binary)) return binary;
25429
25554
  }
25430
25555
  }
25431
25556
  } catch {
@@ -25888,10 +26013,10 @@ var init_screenshotService = __esm({
25888
26013
  });
25889
26014
 
25890
26015
  // ../engine/src/services/frameCapture.ts
25891
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "fs";
25892
- import { join as join20 } from "path";
26016
+ import { existsSync as existsSync19, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
26017
+ import { join as join22 } from "path";
25893
26018
  async function createCaptureSession(serverUrl, outputDir, options, onBeforeCapture = null, config) {
25894
- if (!existsSync17(outputDir)) mkdirSync8(outputDir, { recursive: true });
26019
+ if (!existsSync19(outputDir)) mkdirSync10(outputDir, { recursive: true });
25895
26020
  const headlessShell = resolveHeadlessShellPath(config);
25896
26021
  const isLinux = process.platform === "linux";
25897
26022
  const forceScreenshot = config?.forceScreenshot ?? DEFAULT_CONFIG2.forceScreenshot;
@@ -26095,13 +26220,13 @@ async function initializeSession(session) {
26095
26220
  }
26096
26221
  async function captureFrameErrorDiagnostics(session, frameIndex, time, error) {
26097
26222
  try {
26098
- const diagnosticsDir = join20(session.outputDir, "diagnostics");
26099
- if (!existsSync17(diagnosticsDir)) mkdirSync8(diagnosticsDir, { recursive: true });
26100
- const base = join20(diagnosticsDir, `frame-error-${frameIndex}`);
26223
+ const diagnosticsDir = join22(session.outputDir, "diagnostics");
26224
+ if (!existsSync19(diagnosticsDir)) mkdirSync10(diagnosticsDir, { recursive: true });
26225
+ const base = join22(diagnosticsDir, `frame-error-${frameIndex}`);
26101
26226
  await session.page.screenshot({ path: `${base}.png`, type: "png", fullPage: true });
26102
26227
  const html = await session.page.content();
26103
- writeFileSync8(`${base}.html`, html, "utf-8");
26104
- writeFileSync8(
26228
+ writeFileSync10(`${base}.html`, html, "utf-8");
26229
+ writeFileSync10(
26105
26230
  `${base}.json`,
26106
26231
  JSON.stringify(
26107
26232
  {
@@ -26195,8 +26320,8 @@ async function captureFrame(session, frameIndex, time) {
26195
26320
  );
26196
26321
  const ext = options.format === "png" ? "png" : "jpg";
26197
26322
  const frameName = `frame_${String(frameIndex).padStart(6, "0")}.${ext}`;
26198
- const framePath = join20(outputDir, frameName);
26199
- writeFileSync8(framePath, buffer);
26323
+ const framePath = join22(outputDir, frameName);
26324
+ writeFileSync10(framePath, buffer);
26200
26325
  return { frameIndex, time: quantizedTime, path: framePath, captureTimeMs };
26201
26326
  }
26202
26327
  async function captureFrameToBuffer(session, frameIndex, time) {
@@ -26216,8 +26341,8 @@ async function closeCaptureSession(session) {
26216
26341
  session.isInitialized = false;
26217
26342
  }
26218
26343
  function prepareCaptureSessionForReuse(session, outputDir, onBeforeCapture) {
26219
- if (!existsSync17(outputDir)) {
26220
- mkdirSync8(outputDir, { recursive: true });
26344
+ if (!existsSync19(outputDir)) {
26345
+ mkdirSync10(outputDir, { recursive: true });
26221
26346
  }
26222
26347
  session.outputDir = outputDir;
26223
26348
  session.onBeforeCapture = onBeforeCapture;
@@ -26260,10 +26385,10 @@ var init_frameCapture = __esm({
26260
26385
  });
26261
26386
 
26262
26387
  // ../engine/src/utils/gpuEncoder.ts
26263
- import { spawn as spawn2 } from "child_process";
26388
+ import { spawn as spawn3 } from "child_process";
26264
26389
  async function detectGpuEncoder() {
26265
26390
  return new Promise((resolve39) => {
26266
- const ffmpeg = spawn2("ffmpeg", ["-encoders"], {
26391
+ const ffmpeg = spawn3("ffmpeg", ["-encoders"], {
26267
26392
  stdio: ["pipe", "pipe", "pipe"]
26268
26393
  });
26269
26394
  let stdout2 = "";
@@ -26383,7 +26508,7 @@ var init_hdr = __esm({
26383
26508
  });
26384
26509
 
26385
26510
  // ../engine/src/utils/runFfmpeg.ts
26386
- import { spawn as spawn3 } from "child_process";
26511
+ import { spawn as spawn4 } from "child_process";
26387
26512
  function formatFfmpegError(exitCode, stderr, tailLines = DEFAULT_STDERR_TAIL_LINES) {
26388
26513
  const tail = (stderr ?? "").split(/\r?\n/).filter((line) => line.length > 0).slice(-tailLines).join("\n");
26389
26514
  if (exitCode === null) {
@@ -26399,7 +26524,7 @@ async function runFfmpeg(args, opts) {
26399
26524
  const timeout = opts?.timeout ?? DEFAULT_TIMEOUT;
26400
26525
  const onStderr = opts?.onStderr;
26401
26526
  return new Promise((resolve39) => {
26402
- const ffmpeg = spawn3("ffmpeg", args);
26527
+ const ffmpeg = spawn4("ffmpeg", args);
26403
26528
  let stderr = "";
26404
26529
  const onAbort = () => {
26405
26530
  ffmpeg.kill("SIGTERM");
@@ -26453,9 +26578,9 @@ var init_runFfmpeg = __esm({
26453
26578
  });
26454
26579
 
26455
26580
  // ../engine/src/services/chunkEncoder.ts
26456
- import { spawn as spawn4 } from "child_process";
26457
- import { copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync as readdirSync8, statSync as statSync6, writeFileSync as writeFileSync9 } from "fs";
26458
- import { join as join21, dirname as dirname6 } from "path";
26581
+ import { spawn as spawn5 } from "child_process";
26582
+ import { copyFileSync, existsSync as existsSync20, mkdirSync as mkdirSync11, readdirSync as readdirSync8, statSync as statSync6, writeFileSync as writeFileSync11 } from "fs";
26583
+ import { join as join23, dirname as dirname6 } from "path";
26459
26584
  function getEncoderPreset(quality, format = "mp4", hdr) {
26460
26585
  const base = ENCODER_PRESETS[quality];
26461
26586
  if (format === "webm") {
@@ -26606,7 +26731,7 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
26606
26731
  async function encodeFramesFromDir(framesDir, framePattern, outputPath, options, signal, config) {
26607
26732
  const startTime = Date.now();
26608
26733
  const outputDir = dirname6(outputPath);
26609
- if (!existsSync18(outputDir)) mkdirSync9(outputDir, { recursive: true });
26734
+ if (!existsSync20(outputDir)) mkdirSync11(outputDir, { recursive: true });
26610
26735
  const files = readdirSync8(framesDir).filter((f3) => f3.match(/\.(jpg|jpeg|png)$/i));
26611
26736
  const frameCount = files.length;
26612
26737
  if (frameCount === 0) {
@@ -26623,11 +26748,11 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
26623
26748
  if (options.useGpu) {
26624
26749
  gpuEncoder = await getCachedGpuEncoder();
26625
26750
  }
26626
- const inputPath = join21(framesDir, framePattern);
26751
+ const inputPath = join23(framesDir, framePattern);
26627
26752
  const inputArgs = ["-framerate", String(options.fps), "-i", inputPath];
26628
26753
  const args = buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder);
26629
26754
  return new Promise((resolve39) => {
26630
- const ffmpeg = spawn4("ffmpeg", args);
26755
+ const ffmpeg = spawn5("ffmpeg", args);
26631
26756
  let stderr = "";
26632
26757
  const onAbort = () => {
26633
26758
  ffmpeg.kill("SIGTERM");
@@ -26672,7 +26797,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
26672
26797
  });
26673
26798
  return;
26674
26799
  }
26675
- const fileSize = existsSync18(outputPath) ? statSync6(outputPath).size : 0;
26800
+ const fileSize = existsSync20(outputPath) ? statSync6(outputPath).size : 0;
26676
26801
  resolve39({ success: true, outputPath, durationMs, framesEncoded: frameCount, fileSize });
26677
26802
  });
26678
26803
  ffmpeg.on("error", (err) => {
@@ -26704,8 +26829,8 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26704
26829
  }
26705
26830
  const chunkSize = Math.max(30, Math.floor(chunkSizeFrames));
26706
26831
  const chunkCount = Math.ceil(files.length / chunkSize);
26707
- const chunkDir = join21(dirname6(outputPath), "chunk-encode");
26708
- if (!existsSync18(chunkDir)) mkdirSync9(chunkDir, { recursive: true });
26832
+ const chunkDir = join23(dirname6(outputPath), "chunk-encode");
26833
+ if (!existsSync20(chunkDir)) mkdirSync11(chunkDir, { recursive: true });
26709
26834
  const chunkPaths = [];
26710
26835
  for (let i2 = 0; i2 < chunkCount; i2++) {
26711
26836
  if (signal?.aborted) {
@@ -26721,8 +26846,8 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26721
26846
  const startNumber = i2 * chunkSize;
26722
26847
  const framesInChunk = Math.min(chunkSize, files.length - startNumber);
26723
26848
  const ext = outputPath.endsWith(".webm") ? ".webm" : outputPath.endsWith(".mov") ? ".mov" : ".mp4";
26724
- const chunkPath = join21(chunkDir, `chunk_${String(i2).padStart(4, "0")}${ext}`);
26725
- const inputPath = join21(framesDir, framePattern);
26849
+ const chunkPath = join23(chunkDir, `chunk_${String(i2).padStart(4, "0")}${ext}`);
26850
+ const inputPath = join23(framesDir, framePattern);
26726
26851
  const inputArgs = [
26727
26852
  "-framerate",
26728
26853
  String(options.fps),
@@ -26737,7 +26862,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26737
26862
  if (options.useGpu) gpuEncoder = await getCachedGpuEncoder();
26738
26863
  const args = buildEncoderArgs(options, inputArgs, chunkPath, gpuEncoder);
26739
26864
  const chunkResult = await new Promise((resolve39) => {
26740
- const ffmpeg = spawn4("ffmpeg", args);
26865
+ const ffmpeg = spawn5("ffmpeg", args);
26741
26866
  let stderr = "";
26742
26867
  ffmpeg.stderr.on("data", (d) => {
26743
26868
  stderr += d.toString();
@@ -26762,9 +26887,9 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26762
26887
  }
26763
26888
  chunkPaths.push(chunkPath);
26764
26889
  }
26765
- const concatListPath = join21(chunkDir, "concat-list.txt");
26890
+ const concatListPath = join23(chunkDir, "concat-list.txt");
26766
26891
  const concatInput = chunkPaths.map((path2) => `file '${path2.replace(/'/g, "'\\''")}'`).join("\n");
26767
- writeFileSync9(concatListPath, concatInput, "utf-8");
26892
+ writeFileSync11(concatListPath, concatInput, "utf-8");
26768
26893
  const concatArgs = [
26769
26894
  "-f",
26770
26895
  "concat",
@@ -26778,7 +26903,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26778
26903
  outputPath
26779
26904
  ];
26780
26905
  const concatResult = await new Promise((resolve39) => {
26781
- const ffmpeg = spawn4("ffmpeg", concatArgs);
26906
+ const ffmpeg = spawn5("ffmpeg", concatArgs);
26782
26907
  let stderr = "";
26783
26908
  ffmpeg.stderr.on("data", (d) => {
26784
26909
  stderr += d.toString();
@@ -26801,7 +26926,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26801
26926
  error: concatResult.error
26802
26927
  };
26803
26928
  }
26804
- const fileSize = existsSync18(outputPath) ? statSync6(outputPath).size : 0;
26929
+ const fileSize = existsSync20(outputPath) ? statSync6(outputPath).size : 0;
26805
26930
  return {
26806
26931
  success: true,
26807
26932
  outputPath,
@@ -26812,7 +26937,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26812
26937
  }
26813
26938
  async function muxVideoWithAudio(videoPath, audioPath, outputPath, signal, config) {
26814
26939
  const outputDir = dirname6(outputPath);
26815
- if (!existsSync18(outputDir)) mkdirSync9(outputDir, { recursive: true });
26940
+ if (!existsSync20(outputDir)) mkdirSync11(outputDir, { recursive: true });
26816
26941
  const isWebm = outputPath.endsWith(".webm");
26817
26942
  const isMov = outputPath.endsWith(".mov");
26818
26943
  const args = ["-i", videoPath, "-i", audioPath, "-c:v", "copy"];
@@ -26882,8 +27007,8 @@ var init_chunkEncoder = __esm({
26882
27007
  });
26883
27008
 
26884
27009
  // ../engine/src/services/streamingEncoder.ts
26885
- import { spawn as spawn5 } from "child_process";
26886
- import { existsSync as existsSync19, mkdirSync as mkdirSync10, statSync as statSync7 } from "fs";
27010
+ import { spawn as spawn6 } from "child_process";
27011
+ import { existsSync as existsSync21, mkdirSync as mkdirSync12, statSync as statSync7 } from "fs";
26887
27012
  import { dirname as dirname7 } from "path";
26888
27013
  function createFrameReorderBuffer(startFrame, endFrame) {
26889
27014
  let cursor = startFrame;
@@ -27066,14 +27191,14 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
27066
27191
  }
27067
27192
  async function spawnStreamingEncoder(outputPath, options, signal, config) {
27068
27193
  const outputDir = dirname7(outputPath);
27069
- if (!existsSync19(outputDir)) mkdirSync10(outputDir, { recursive: true });
27194
+ if (!existsSync21(outputDir)) mkdirSync12(outputDir, { recursive: true });
27070
27195
  let gpuEncoder = null;
27071
27196
  if (options.useGpu) {
27072
27197
  gpuEncoder = await getCachedGpuEncoder();
27073
27198
  }
27074
27199
  const args = buildStreamingArgs(options, outputPath, gpuEncoder);
27075
27200
  const startTime = Date.now();
27076
- const ffmpeg = spawn5("ffmpeg", args, {
27201
+ const ffmpeg = spawn6("ffmpeg", args, {
27077
27202
  stdio: ["pipe", "pipe", "pipe"]
27078
27203
  });
27079
27204
  let exitStatus = "running";
@@ -27148,7 +27273,7 @@ Process error: ${err.message}`;
27148
27273
  error: formatFfmpegError(exitCode, stderr)
27149
27274
  };
27150
27275
  }
27151
- const fileSize = existsSync19(outputPath) ? statSync7(outputPath).size : 0;
27276
+ const fileSize = existsSync21(outputPath) ? statSync7(outputPath).size : 0;
27152
27277
  return { success: true, durationMs, fileSize };
27153
27278
  },
27154
27279
  getExitStatus: () => exitStatus
@@ -27166,12 +27291,12 @@ var init_streamingEncoder = __esm({
27166
27291
  });
27167
27292
 
27168
27293
  // ../engine/src/utils/ffprobe.ts
27169
- import { spawn as spawn6 } from "child_process";
27170
- import { readFileSync as readFileSync15 } from "fs";
27294
+ import { spawn as spawn7 } from "child_process";
27295
+ import { readFileSync as readFileSync16 } from "fs";
27171
27296
  import { extname as extname4 } from "path";
27172
27297
  function runFfprobe(args) {
27173
27298
  return new Promise((resolve39, reject) => {
27174
- const proc = spawn6("ffprobe", args);
27299
+ const proc = spawn7("ffprobe", args);
27175
27300
  let stdout2 = "";
27176
27301
  let stderr = "";
27177
27302
  proc.stdout.on("data", (data) => {
@@ -27261,7 +27386,7 @@ function extractPngMetadataFromBuffer(buf) {
27261
27386
  function extractStillImageMetadata(filePath) {
27262
27387
  if (extname4(filePath).toLowerCase() !== ".png") return null;
27263
27388
  try {
27264
- return extractPngMetadataFromBuffer(readFileSync15(filePath));
27389
+ return extractPngMetadataFromBuffer(readFileSync16(filePath));
27265
27390
  } catch {
27266
27391
  return null;
27267
27392
  }
@@ -27440,9 +27565,9 @@ var init_ffprobe = __esm({
27440
27565
  });
27441
27566
 
27442
27567
  // ../engine/src/utils/urlDownloader.ts
27443
- import { createWriteStream as createWriteStream2, existsSync as existsSync20, mkdirSync as mkdirSync11 } from "fs";
27568
+ import { createWriteStream as createWriteStream2, existsSync as existsSync22, mkdirSync as mkdirSync13 } from "fs";
27444
27569
  import { createHash } from "crypto";
27445
- import { join as join22, extname as extname5 } from "path";
27570
+ import { join as join24, extname as extname5 } from "path";
27446
27571
  import { Readable } from "stream";
27447
27572
  import { finished } from "stream/promises";
27448
27573
  function getFilenameFromUrl(url) {
@@ -27453,19 +27578,19 @@ function getFilenameFromUrl(url) {
27453
27578
  }
27454
27579
  async function downloadToTemp(url, destDir, timeoutMs = 3e5) {
27455
27580
  const cachedPath = downloadPathCache.get(url);
27456
- if (cachedPath && existsSync20(cachedPath)) {
27581
+ if (cachedPath && existsSync22(cachedPath)) {
27457
27582
  return cachedPath;
27458
27583
  }
27459
27584
  const inFlight = inFlightDownloads.get(url);
27460
27585
  if (inFlight) {
27461
27586
  return inFlight;
27462
27587
  }
27463
- if (!existsSync20(destDir)) {
27464
- mkdirSync11(destDir, { recursive: true });
27588
+ if (!existsSync22(destDir)) {
27589
+ mkdirSync13(destDir, { recursive: true });
27465
27590
  }
27466
27591
  const filename = getFilenameFromUrl(url);
27467
- const localPath = join22(destDir, filename);
27468
- if (existsSync20(localPath)) {
27592
+ const localPath = join24(destDir, filename);
27593
+ if (existsSync22(localPath)) {
27469
27594
  downloadPathCache.set(url, localPath);
27470
27595
  return localPath;
27471
27596
  }
@@ -27557,9 +27682,9 @@ var init_htmlTemplate = __esm({
27557
27682
 
27558
27683
  // ../engine/src/services/extractionCache.ts
27559
27684
  import { createHash as createHash2 } from "crypto";
27560
- import { mkdirSync as mkdirSync12, readdirSync as readdirSync9, statSync as statSync8, writeFileSync as writeFileSync10 } from "fs";
27561
- import { existsSync as existsSync21 } from "fs";
27562
- import { join as join23 } from "path";
27685
+ import { mkdirSync as mkdirSync14, readdirSync as readdirSync9, statSync as statSync8, writeFileSync as writeFileSync12 } from "fs";
27686
+ import { existsSync as existsSync23 } from "fs";
27687
+ import { join as join25 } from "path";
27563
27688
  function readKeyStat(videoPath) {
27564
27689
  try {
27565
27690
  const stat3 = statSync8(videoPath);
@@ -27588,15 +27713,15 @@ function cacheEntryDirName(keyHash) {
27588
27713
  }
27589
27714
  function lookupCacheEntry(rootDir, input) {
27590
27715
  const keyHash = computeCacheKey(input);
27591
- const dir = join23(rootDir, cacheEntryDirName(keyHash));
27592
- const complete = existsSync21(join23(dir, COMPLETE_SENTINEL));
27716
+ const dir = join25(rootDir, cacheEntryDirName(keyHash));
27717
+ const complete = existsSync23(join25(dir, COMPLETE_SENTINEL));
27593
27718
  return { entry: { dir, keyHash }, hit: complete };
27594
27719
  }
27595
27720
  function ensureCacheEntryDir(entry) {
27596
- mkdirSync12(entry.dir, { recursive: true });
27721
+ mkdirSync14(entry.dir, { recursive: true });
27597
27722
  }
27598
27723
  function markCacheEntryComplete(entry) {
27599
- writeFileSync10(join23(entry.dir, COMPLETE_SENTINEL), "", "utf-8");
27724
+ writeFileSync12(join25(entry.dir, COMPLETE_SENTINEL), "", "utf-8");
27600
27725
  }
27601
27726
  function rehydrateCacheEntry(entry, options) {
27602
27727
  const framePattern = `${FRAME_FILENAME_PREFIX}%05d.${options.format}`;
@@ -27604,7 +27729,7 @@ function rehydrateCacheEntry(entry, options) {
27604
27729
  const suffix = `.${options.format}`;
27605
27730
  const files = readdirSync9(entry.dir).filter((f3) => f3.startsWith(FRAME_FILENAME_PREFIX) && f3.endsWith(suffix)).sort();
27606
27731
  files.forEach((file, idx) => {
27607
- framePaths.set(idx, join23(entry.dir, file));
27732
+ framePaths.set(idx, join25(entry.dir, file));
27608
27733
  });
27609
27734
  return {
27610
27735
  videoId: options.videoId,
@@ -27629,9 +27754,9 @@ var init_extractionCache = __esm({
27629
27754
  });
27630
27755
 
27631
27756
  // ../engine/src/services/videoFrameExtractor.ts
27632
- import { spawn as spawn7 } from "child_process";
27633
- import { existsSync as existsSync22, mkdirSync as mkdirSync13, readdirSync as readdirSync10, rmSync as rmSync5 } from "fs";
27634
- import { isAbsolute as isAbsolute2, join as join24 } from "path";
27757
+ import { spawn as spawn8 } from "child_process";
27758
+ import { existsSync as existsSync24, mkdirSync as mkdirSync15, readdirSync as readdirSync10, rmSync as rmSync5 } from "fs";
27759
+ import { isAbsolute as isAbsolute2, join as join26 } from "path";
27635
27760
  function parseVideoElements(html) {
27636
27761
  const videos = [];
27637
27762
  const { document: document2 } = parseHTML(unwrapTemplate(html));
@@ -27701,12 +27826,12 @@ function parseImageElements(html) {
27701
27826
  async function extractVideoFramesRange(videoPath, videoId, startTime, duration, options, signal, config, outputDirOverride) {
27702
27827
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
27703
27828
  const { fps, outputDir, quality = 95 } = options;
27704
- const videoOutputDir = outputDirOverride ?? join24(outputDir, videoId);
27705
- if (!existsSync22(videoOutputDir)) mkdirSync13(videoOutputDir, { recursive: true });
27829
+ const videoOutputDir = outputDirOverride ?? join26(outputDir, videoId);
27830
+ if (!existsSync24(videoOutputDir)) mkdirSync15(videoOutputDir, { recursive: true });
27706
27831
  const metadata = await extractMediaMetadata(videoPath);
27707
27832
  const format = resolveFrameFormat(metadata, options.format);
27708
27833
  const framePattern = `${FRAME_FILENAME_PREFIX}%05d.${format}`;
27709
- const outputPattern = join24(videoOutputDir, framePattern);
27834
+ const outputPattern = join26(videoOutputDir, framePattern);
27710
27835
  const isHdr = isHdrColorSpace(metadata.colorSpace);
27711
27836
  const isMacOS = process.platform === "darwin";
27712
27837
  const args = [];
@@ -27727,7 +27852,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
27727
27852
  if (format === "png") args.push("-compression_level", "6");
27728
27853
  args.push("-y", outputPattern);
27729
27854
  return new Promise((resolve39, reject) => {
27730
- const ffmpeg = spawn7("ffmpeg", args);
27855
+ const ffmpeg = spawn8("ffmpeg", args);
27731
27856
  let stderr = "";
27732
27857
  const onAbort = () => {
27733
27858
  ffmpeg.kill("SIGTERM");
@@ -27759,7 +27884,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
27759
27884
  const framePaths = /* @__PURE__ */ new Map();
27760
27885
  const files = readdirSync10(videoOutputDir).filter((f3) => f3.startsWith(FRAME_FILENAME_PREFIX) && f3.endsWith(`.${format}`)).sort();
27761
27886
  files.forEach((file, index) => {
27762
- framePaths.set(index, join24(videoOutputDir, file));
27887
+ framePaths.set(index, join26(videoOutputDir, file));
27763
27888
  });
27764
27889
  resolve39({
27765
27890
  videoId,
@@ -27886,15 +28011,15 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
27886
28011
  try {
27887
28012
  let videoPath = video.src;
27888
28013
  if (!isAbsolute2(videoPath) && !isHttpUrl(videoPath)) {
27889
- const fromCompiled = compiledDir ? join24(compiledDir, videoPath) : null;
27890
- videoPath = fromCompiled && existsSync22(fromCompiled) ? fromCompiled : join24(baseDir, videoPath);
28014
+ const fromCompiled = compiledDir ? join26(compiledDir, videoPath) : null;
28015
+ videoPath = fromCompiled && existsSync24(fromCompiled) ? fromCompiled : join26(baseDir, videoPath);
27891
28016
  }
27892
28017
  if (isHttpUrl(videoPath)) {
27893
- const downloadDir = join24(options.outputDir, "_downloads");
27894
- mkdirSync13(downloadDir, { recursive: true });
28018
+ const downloadDir = join26(options.outputDir, "_downloads");
28019
+ mkdirSync15(downloadDir, { recursive: true });
27895
28020
  videoPath = await downloadToTemp(videoPath, downloadDir);
27896
28021
  }
27897
- if (!existsSync22(videoPath)) {
28022
+ if (!existsSync24(videoPath)) {
27898
28023
  errors.push({ videoId: video.id, error: `Video file not found: ${videoPath}` });
27899
28024
  continue;
27900
28025
  }
@@ -27927,8 +28052,8 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
27927
28052
  const hdrSkippedIndices = /* @__PURE__ */ new Set();
27928
28053
  if (hdrInfo.hasHdr && hdrInfo.dominantTransfer) {
27929
28054
  const targetTransfer = hdrInfo.dominantTransfer;
27930
- const convertDir = join24(options.outputDir, "_hdr_normalized");
27931
- mkdirSync13(convertDir, { recursive: true });
28055
+ const convertDir = join26(options.outputDir, "_hdr_normalized");
28056
+ mkdirSync15(convertDir, { recursive: true });
27932
28057
  for (let i2 = 0; i2 < resolvedVideos.length; i2++) {
27933
28058
  if (signal?.aborted) break;
27934
28059
  const cs = videoColorSpaces[i2] ?? null;
@@ -27949,7 +28074,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
27949
28074
  const sourceRemaining = metadata.durationSeconds - entry.video.mediaStart;
27950
28075
  segDuration = sourceRemaining > 0 ? sourceRemaining : metadata.durationSeconds;
27951
28076
  }
27952
- const convertedPath = join24(convertDir, `${entry.video.id}_hdr.mp4`);
28077
+ const convertedPath = join26(convertDir, `${entry.video.id}_hdr.mp4`);
27953
28078
  try {
27954
28079
  await convertSdrToHdr(
27955
28080
  entry.videoPath,
@@ -27984,7 +28109,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
27984
28109
  }
27985
28110
  }
27986
28111
  const vfrPreflightStart = Date.now();
27987
- const vfrNormDir = join24(options.outputDir, "_vfr_normalized");
28112
+ const vfrNormDir = join26(options.outputDir, "_vfr_normalized");
27988
28113
  for (let i2 = 0; i2 < resolvedVideos.length; i2++) {
27989
28114
  if (signal?.aborted) break;
27990
28115
  const entry = resolvedVideos[i2];
@@ -27998,8 +28123,8 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
27998
28123
  const sourceRemaining = metadata.durationSeconds - entry.video.mediaStart;
27999
28124
  segDuration = sourceRemaining > 0 ? sourceRemaining : metadata.durationSeconds;
28000
28125
  }
28001
- mkdirSync13(vfrNormDir, { recursive: true });
28002
- const normalizedPath = join24(vfrNormDir, `${entry.video.id}_cfr.mp4`);
28126
+ mkdirSync15(vfrNormDir, { recursive: true });
28127
+ const normalizedPath = join26(vfrNormDir, `${entry.video.id}_cfr.mp4`);
28003
28128
  try {
28004
28129
  await convertVfrToCfr(
28005
28130
  entry.videoPath,
@@ -28255,7 +28380,7 @@ var init_videoFrameExtractor = __esm({
28255
28380
  cleanup() {
28256
28381
  for (const video of this.videos.values()) {
28257
28382
  if (video.extracted.ownedByLookup) continue;
28258
- if (existsSync22(video.extracted.outputDir)) {
28383
+ if (existsSync24(video.extracted.outputDir)) {
28259
28384
  rmSync5(video.extracted.outputDir, { recursive: true, force: true });
28260
28385
  }
28261
28386
  }
@@ -28563,8 +28688,8 @@ var init_videoFrameInjector = __esm({
28563
28688
  });
28564
28689
 
28565
28690
  // ../engine/src/services/audioMixer.ts
28566
- import { existsSync as existsSync23, mkdirSync as mkdirSync14, rmSync as rmSync6 } from "fs";
28567
- import { isAbsolute as isAbsolute3, join as join25, dirname as dirname8 } from "path";
28691
+ import { existsSync as existsSync25, mkdirSync as mkdirSync16, rmSync as rmSync6 } from "fs";
28692
+ import { isAbsolute as isAbsolute3, join as join27, dirname as dirname8 } from "path";
28568
28693
  function parseAudioElements(html) {
28569
28694
  const elements = [];
28570
28695
  const { document: document2 } = parseHTML(unwrapTemplate(html));
@@ -28615,7 +28740,7 @@ function parseAudioElements(html) {
28615
28740
  async function extractAudioFromVideo(videoPath, outputPath, options, signal, config) {
28616
28741
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
28617
28742
  const outputDir = dirname8(outputPath);
28618
- if (!existsSync23(outputDir)) mkdirSync14(outputDir, { recursive: true });
28743
+ if (!existsSync25(outputDir)) mkdirSync16(outputDir, { recursive: true });
28619
28744
  const args = ["-i", videoPath];
28620
28745
  if (options?.startTime !== void 0) args.push("-ss", String(options.startTime));
28621
28746
  if (options?.duration !== void 0) args.push("-t", String(options.duration));
@@ -28642,7 +28767,7 @@ async function extractAudioFromVideo(videoPath, outputPath, options, signal, con
28642
28767
  async function prepareAudioTrack(srcPath, outputPath, mediaStart, duration, signal, config) {
28643
28768
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
28644
28769
  const outputDir = dirname8(outputPath);
28645
- if (!existsSync23(outputDir)) mkdirSync14(outputDir, { recursive: true });
28770
+ if (!existsSync25(outputDir)) mkdirSync16(outputDir, { recursive: true });
28646
28771
  const args = [
28647
28772
  "-ss",
28648
28773
  String(mediaStart),
@@ -28678,7 +28803,7 @@ async function prepareAudioTrack(srcPath, outputPath, mediaStart, duration, sign
28678
28803
  async function generateSilence(outputPath, duration, signal, config) {
28679
28804
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
28680
28805
  const outputDir = dirname8(outputPath);
28681
- if (!existsSync23(outputDir)) mkdirSync14(outputDir, { recursive: true });
28806
+ if (!existsSync25(outputDir)) mkdirSync16(outputDir, { recursive: true });
28682
28807
  const args = [
28683
28808
  "-f",
28684
28809
  "lavfi",
@@ -28721,7 +28846,7 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config)
28721
28846
  };
28722
28847
  }
28723
28848
  const outputDir = dirname8(outputPath);
28724
- if (!existsSync23(outputDir)) mkdirSync14(outputDir, { recursive: true });
28849
+ if (!existsSync25(outputDir)) mkdirSync16(outputDir, { recursive: true });
28725
28850
  const inputs = [];
28726
28851
  const filterParts = [];
28727
28852
  tracks.forEach((track, i2) => {
@@ -28782,7 +28907,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
28782
28907
  const startMs = Date.now();
28783
28908
  const tracks = [];
28784
28909
  const errors = [];
28785
- if (!existsSync23(workDir)) mkdirSync14(workDir, { recursive: true });
28910
+ if (!existsSync25(workDir)) mkdirSync16(workDir, { recursive: true });
28786
28911
  await Promise.all(
28787
28912
  elements.map(async (element) => {
28788
28913
  if (signal?.aborted) {
@@ -28792,8 +28917,8 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
28792
28917
  try {
28793
28918
  let srcPath = element.src;
28794
28919
  if (!isAbsolute3(srcPath) && !isHttpUrl(srcPath)) {
28795
- const fromCompiled = compiledDir ? join25(compiledDir, srcPath) : null;
28796
- srcPath = fromCompiled && existsSync23(fromCompiled) ? fromCompiled : join25(baseDir, srcPath);
28920
+ const fromCompiled = compiledDir ? join27(compiledDir, srcPath) : null;
28921
+ srcPath = fromCompiled && existsSync25(fromCompiled) ? fromCompiled : join27(baseDir, srcPath);
28797
28922
  }
28798
28923
  if (isHttpUrl(srcPath)) {
28799
28924
  try {
@@ -28805,7 +28930,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
28805
28930
  return;
28806
28931
  }
28807
28932
  }
28808
- if (!existsSync23(srcPath)) {
28933
+ if (!existsSync25(srcPath)) {
28809
28934
  errors.push(`Source not found: ${element.id} (${element.src})`);
28810
28935
  return;
28811
28936
  }
@@ -28816,7 +28941,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
28816
28941
  }
28817
28942
  let audioSrcPath = srcPath;
28818
28943
  if (element.type === "video") {
28819
- const extractedPath = join25(workDir, `${element.id}-extracted.wav`);
28944
+ const extractedPath = join27(workDir, `${element.id}-extracted.wav`);
28820
28945
  const extractResult = await extractAudioFromVideo(
28821
28946
  srcPath,
28822
28947
  extractedPath,
@@ -28833,7 +28958,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
28833
28958
  }
28834
28959
  audioSrcPath = extractedPath;
28835
28960
  } else {
28836
- const trimmedPath = join25(workDir, `${element.id}-trimmed.wav`);
28961
+ const trimmedPath = join27(workDir, `${element.id}-trimmed.wav`);
28837
28962
  const prepResult = await prepareAudioTrack(
28838
28963
  srcPath,
28839
28964
  trimmedPath,
@@ -28887,9 +29012,9 @@ var init_audioMixer = __esm({
28887
29012
 
28888
29013
  // ../engine/src/services/parallelCoordinator.ts
28889
29014
  import { cpus as cpus2, freemem, totalmem as totalmem2 } from "os";
28890
- import { existsSync as existsSync24, mkdirSync as mkdirSync15, readdirSync as readdirSync11 } from "fs";
29015
+ import { existsSync as existsSync26, mkdirSync as mkdirSync17, readdirSync as readdirSync11 } from "fs";
28891
29016
  import { copyFile, rename } from "fs/promises";
28892
- import { join as join26 } from "path";
29017
+ import { join as join28 } from "path";
28893
29018
  function calculateOptimalWorkers(totalFrames, requested, config) {
28894
29019
  const effectiveMaxWorkers = (() => {
28895
29020
  const concurrency = config?.concurrency ?? DEFAULT_CONFIG2.concurrency;
@@ -28932,7 +29057,7 @@ function distributeFrames(totalFrames, workerCount, workDir) {
28932
29057
  workerId: i2,
28933
29058
  startFrame,
28934
29059
  endFrame,
28935
- outputDir: join26(workDir, `worker-${i2}`)
29060
+ outputDir: join28(workDir, `worker-${i2}`)
28936
29061
  });
28937
29062
  }
28938
29063
  return tasks;
@@ -28940,7 +29065,7 @@ function distributeFrames(totalFrames, workerCount, workDir) {
28940
29065
  async function executeWorkerTask(task, serverUrl, captureOptions, createBeforeCaptureHook, signal, onFrameCaptured, onFrameBuffer, config) {
28941
29066
  const startTime = Date.now();
28942
29067
  let framesCaptured = 0;
28943
- if (!existsSync24(task.outputDir)) mkdirSync15(task.outputDir, { recursive: true });
29068
+ if (!existsSync26(task.outputDir)) mkdirSync17(task.outputDir, { recursive: true });
28944
29069
  let session = null;
28945
29070
  let perf;
28946
29071
  try {
@@ -29030,17 +29155,17 @@ async function executeParallelCapture(serverUrl, workDir, tasks, captureOptions,
29030
29155
  return results;
29031
29156
  }
29032
29157
  async function mergeWorkerFrames(workDir, tasks, outputDir) {
29033
- if (!existsSync24(outputDir)) mkdirSync15(outputDir, { recursive: true });
29158
+ if (!existsSync26(outputDir)) mkdirSync17(outputDir, { recursive: true });
29034
29159
  let totalFrames = 0;
29035
29160
  const sortedTasks = [...tasks].sort((a, b) => a.startFrame - b.startFrame);
29036
29161
  for (const task of sortedTasks) {
29037
- if (!existsSync24(task.outputDir)) {
29162
+ if (!existsSync26(task.outputDir)) {
29038
29163
  continue;
29039
29164
  }
29040
29165
  const files = readdirSync11(task.outputDir).filter((f3) => f3.startsWith("frame_") && (f3.endsWith(".jpg") || f3.endsWith(".png"))).sort();
29041
29166
  const copyTasks = files.map(async (file) => {
29042
- const sourcePath = join26(task.outputDir, file);
29043
- const targetPath = join26(outputDir, file);
29167
+ const sourcePath = join28(task.outputDir, file);
29168
+ const targetPath = join28(outputDir, file);
29044
29169
  try {
29045
29170
  await rename(sourcePath, targetPath);
29046
29171
  } catch {
@@ -29077,8 +29202,8 @@ var init_parallelCoordinator = __esm({
29077
29202
  // ../engine/src/services/fileServer.ts
29078
29203
  import { Hono as Hono2 } from "hono";
29079
29204
  import { serve } from "@hono/node-server";
29080
- import { readFileSync as readFileSync16, existsSync as existsSync25, statSync as statSync9 } from "fs";
29081
- import { join as join27, extname as extname6 } from "path";
29205
+ import { readFileSync as readFileSync17, existsSync as existsSync27, statSync as statSync9 } from "fs";
29206
+ import { join as join29, extname as extname6 } from "path";
29082
29207
  function stripEmbeddedRuntimeScripts(html) {
29083
29208
  if (!html) return html;
29084
29209
  const scriptRe = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
@@ -29147,22 +29272,22 @@ function createFileServer(options) {
29147
29272
  let requestPath = c2.req.path;
29148
29273
  if (requestPath === "/") requestPath = "/index.html";
29149
29274
  const relativePath = requestPath.replace(/^\//, "");
29150
- const compiledPath = compiledDir ? join27(compiledDir, relativePath) : null;
29275
+ const compiledPath = compiledDir ? join29(compiledDir, relativePath) : null;
29151
29276
  const hasCompiledFile = Boolean(
29152
- compiledPath && existsSync25(compiledPath) && statSync9(compiledPath).isFile()
29277
+ compiledPath && existsSync27(compiledPath) && statSync9(compiledPath).isFile()
29153
29278
  );
29154
- const filePath = hasCompiledFile ? compiledPath : join27(projectDir, relativePath);
29155
- if (!existsSync25(filePath) || !statSync9(filePath).isFile()) {
29279
+ const filePath = hasCompiledFile ? compiledPath : join29(projectDir, relativePath);
29280
+ if (!existsSync27(filePath) || !statSync9(filePath).isFile()) {
29156
29281
  return c2.text("Not found", 404);
29157
29282
  }
29158
29283
  const ext = extname6(filePath).toLowerCase();
29159
29284
  const contentType = MIME_TYPES2[ext] || "application/octet-stream";
29160
29285
  if (ext === ".html") {
29161
- const rawHtml = readFileSync16(filePath, "utf-8");
29286
+ const rawHtml = readFileSync17(filePath, "utf-8");
29162
29287
  const html = relativePath === "index.html" ? injectScriptsIntoHtml(rawHtml, headScripts, bodyScripts, stripEmbeddedRuntime) : rawHtml;
29163
29288
  return c2.text(html, 200, { "Content-Type": contentType });
29164
29289
  }
29165
- const content = readFileSync16(filePath);
29290
+ const content = readFileSync17(filePath);
29166
29291
  return new Response(content, {
29167
29292
  status: 200,
29168
29293
  headers: { "Content-Type": contentType }
@@ -30430,8 +30555,8 @@ var init_shaderTransitions = __esm({
30430
30555
  });
30431
30556
 
30432
30557
  // ../engine/src/services/hdrCapture.ts
30433
- import { existsSync as existsSync26, readdirSync as readdirSync12 } from "fs";
30434
- import { join as join28 } from "path";
30558
+ import { existsSync as existsSync28, readdirSync as readdirSync12 } from "fs";
30559
+ import { join as join30 } from "path";
30435
30560
  import { homedir as homedir7 } from "os";
30436
30561
  function linearToPQ(L2) {
30437
30562
  const Lp = Math.max(0, L2 * SDR_NITS / PQ_MAX_NITS);
@@ -30547,12 +30672,12 @@ function float16ToPqRgb(rawBuffer, bytesPerRow, width, height) {
30547
30672
  return output;
30548
30673
  }
30549
30674
  function resolveHeadedChromePath() {
30550
- const baseDir = join28(homedir7(), ".cache", "puppeteer", "chrome");
30551
- if (!existsSync26(baseDir)) return void 0;
30675
+ const baseDir = join30(homedir7(), ".cache", "puppeteer", "chrome");
30676
+ if (!existsSync28(baseDir)) return void 0;
30552
30677
  const versions = readdirSync12(baseDir).sort().reverse();
30553
30678
  for (const version of versions) {
30554
30679
  const candidates = [
30555
- join28(
30680
+ join30(
30556
30681
  baseDir,
30557
30682
  version,
30558
30683
  "chrome-mac-arm64",
@@ -30561,7 +30686,7 @@ function resolveHeadedChromePath() {
30561
30686
  "MacOS",
30562
30687
  "Google Chrome for Testing"
30563
30688
  ),
30564
- join28(
30689
+ join30(
30565
30690
  baseDir,
30566
30691
  version,
30567
30692
  "chrome-mac-x64",
@@ -30570,11 +30695,11 @@ function resolveHeadedChromePath() {
30570
30695
  "MacOS",
30571
30696
  "Google Chrome for Testing"
30572
30697
  ),
30573
- join28(baseDir, version, "chrome-linux64", "chrome"),
30574
- join28(baseDir, version, "chrome-win64", "chrome.exe")
30698
+ join30(baseDir, version, "chrome-linux64", "chrome"),
30699
+ join30(baseDir, version, "chrome-win64", "chrome.exe")
30575
30700
  ];
30576
30701
  for (const binary of candidates) {
30577
- if (existsSync26(binary)) return binary;
30702
+ if (existsSync28(binary)) return binary;
30578
30703
  }
30579
30704
  }
30580
30705
  return void 0;
@@ -30846,8 +30971,8 @@ var init_staticGuard = __esm({
30846
30971
  });
30847
30972
 
30848
30973
  // ../core/src/compiler/htmlBundler.ts
30849
- import { readFileSync as readFileSync17, existsSync as existsSync27 } from "fs";
30850
- import { join as join29, resolve as resolve12, isAbsolute as isAbsolute4, sep as sep2 } from "path";
30974
+ import { readFileSync as readFileSync18, existsSync as existsSync29 } from "fs";
30975
+ import { join as join31, resolve as resolve12, isAbsolute as isAbsolute4, sep as sep2 } from "path";
30851
30976
  import { transformSync } from "esbuild";
30852
30977
  function parseHTMLContent2(html) {
30853
30978
  const trimmed = html.trimStart().toLowerCase();
@@ -30915,17 +31040,17 @@ function isRelativeUrl(url) {
30915
31040
  return !url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("//") && !url.startsWith("data:") && !isAbsolute4(url);
30916
31041
  }
30917
31042
  function safeReadFile(filePath) {
30918
- if (!existsSync27(filePath)) return null;
31043
+ if (!existsSync29(filePath)) return null;
30919
31044
  try {
30920
- return readFileSync17(filePath, "utf-8");
31045
+ return readFileSync18(filePath, "utf-8");
30921
31046
  } catch {
30922
31047
  return null;
30923
31048
  }
30924
31049
  }
30925
31050
  function safeReadFileBuffer(filePath) {
30926
- if (!existsSync27(filePath)) return null;
31051
+ if (!existsSync29(filePath)) return null;
30927
31052
  try {
30928
- return readFileSync17(filePath);
31053
+ return readFileSync18(filePath);
30929
31054
  } catch {
30930
31055
  return null;
30931
31056
  }
@@ -31116,9 +31241,9 @@ function stripJsCommentsParserSafe(source) {
31116
31241
  }
31117
31242
  }
31118
31243
  async function bundleToSingleHtml(projectDir, options) {
31119
- const indexPath = join29(projectDir, "index.html");
31120
- if (!existsSync27(indexPath)) throw new Error("index.html not found in project directory");
31121
- const rawHtml = readFileSync17(indexPath, "utf-8");
31244
+ const indexPath = join31(projectDir, "index.html");
31245
+ if (!existsSync29(indexPath)) throw new Error("index.html not found in project directory");
31246
+ const rawHtml = readFileSync18(indexPath, "utf-8");
31122
31247
  const compiled = await compileHtml(rawHtml, projectDir, options?.probeMediaDuration);
31123
31248
  const staticGuard = validateHyperframeHtmlContract(compiled);
31124
31249
  if (!staticGuard.isValid) {
@@ -31402,7 +31527,7 @@ var init_compiler = __esm({
31402
31527
 
31403
31528
  // ../producer/src/services/hyperframeRuntimeLoader.ts
31404
31529
  import { createHash as createHash3 } from "crypto";
31405
- import { existsSync as existsSync28, readFileSync as readFileSync18 } from "fs";
31530
+ import { existsSync as existsSync30, readFileSync as readFileSync19 } from "fs";
31406
31531
  import { dirname as dirname9, resolve as resolve13 } from "path";
31407
31532
  import { fileURLToPath as fileURLToPath2 } from "url";
31408
31533
  function resolveHyperframeManifestPath() {
@@ -31415,7 +31540,7 @@ function resolveHyperframeManifestPath() {
31415
31540
  MODULE_RELATIVE_MANIFEST_PATH
31416
31541
  ];
31417
31542
  for (const candidate of candidates) {
31418
- if (existsSync28(candidate)) {
31543
+ if (existsSync30(candidate)) {
31419
31544
  return candidate;
31420
31545
  }
31421
31546
  }
@@ -31426,12 +31551,12 @@ function getVerifiedHyperframeRuntimeSource() {
31426
31551
  }
31427
31552
  function resolveVerifiedHyperframeRuntime() {
31428
31553
  const manifestPath = resolveHyperframeManifestPath();
31429
- if (!existsSync28(manifestPath)) {
31554
+ if (!existsSync30(manifestPath)) {
31430
31555
  throw new Error(
31431
31556
  `[HyperframeRuntimeLoader] Missing manifest at ${manifestPath}. Build core runtime artifacts before rendering.`
31432
31557
  );
31433
31558
  }
31434
- const manifestRaw = readFileSync18(manifestPath, "utf8");
31559
+ const manifestRaw = readFileSync19(manifestPath, "utf8");
31435
31560
  const manifest = JSON.parse(manifestRaw);
31436
31561
  const runtimeFileName = manifest.artifacts?.iife;
31437
31562
  if (!runtimeFileName || !manifest.sha256) {
@@ -31440,10 +31565,10 @@ function resolveVerifiedHyperframeRuntime() {
31440
31565
  );
31441
31566
  }
31442
31567
  const runtimePath = resolve13(dirname9(manifestPath), runtimeFileName);
31443
- if (!existsSync28(runtimePath)) {
31568
+ if (!existsSync30(runtimePath)) {
31444
31569
  throw new Error(`[HyperframeRuntimeLoader] Missing runtime artifact at ${runtimePath}.`);
31445
31570
  }
31446
- const runtimeSource = readFileSync18(runtimePath, "utf8");
31571
+ const runtimeSource = readFileSync19(runtimePath, "utf8");
31447
31572
  const runtimeSha = createHash3("sha256").update(runtimeSource, "utf8").digest("hex");
31448
31573
  if (runtimeSha !== manifest.sha256) {
31449
31574
  throw new Error(
@@ -31482,16 +31607,16 @@ var init_hyperframeRuntimeLoader = __esm({
31482
31607
  // ../producer/src/services/fileServer.ts
31483
31608
  import { Hono as Hono3 } from "hono";
31484
31609
  import { serve as serve2 } from "@hono/node-server";
31485
- import { readFileSync as readFileSync19, existsSync as existsSync29, realpathSync, statSync as statSync10 } from "fs";
31486
- import { join as join30, extname as extname7, resolve as resolve14, sep as sep3 } from "path";
31610
+ import { readFileSync as readFileSync20, existsSync as existsSync31, realpathSync, statSync as statSync10 } from "fs";
31611
+ import { join as join32, extname as extname7, resolve as resolve14, sep as sep3 } from "path";
31487
31612
  function isPathInside(child, parent, options = {}) {
31488
31613
  const { resolveSymlinks = false, pathModule } = options;
31489
31614
  const resolveFn = pathModule?.resolve ?? resolve14;
31490
31615
  const separator = pathModule?.sep ?? sep3;
31491
31616
  const resolvedChild = resolveFn(child);
31492
31617
  const resolvedParent = resolveFn(parent);
31493
- const normalizedChild = resolveSymlinks && existsSync29(resolvedChild) ? realpathSync.native(resolvedChild) : resolvedChild;
31494
- const normalizedParent = resolveSymlinks && existsSync29(resolvedParent) ? realpathSync.native(resolvedParent) : resolvedParent;
31618
+ const normalizedChild = resolveSymlinks && existsSync31(resolvedChild) ? realpathSync.native(resolvedChild) : resolvedChild;
31619
+ const normalizedParent = resolveSymlinks && existsSync31(resolvedParent) ? realpathSync.native(resolvedParent) : resolvedParent;
31495
31620
  if (normalizedChild === normalizedParent) return true;
31496
31621
  const parentWithSep = normalizedParent.endsWith(separator) ? normalizedParent : normalizedParent + separator;
31497
31622
  return normalizedChild.startsWith(parentWithSep);
@@ -31580,14 +31705,14 @@ function createFileServer2(options) {
31580
31705
  const relativePath = requestPath.replace(/^\//, "");
31581
31706
  let filePath = null;
31582
31707
  if (compiledDir) {
31583
- const candidate = join30(compiledDir, relativePath);
31584
- if (existsSync29(candidate) && isPathInside(candidate, compiledDir, { resolveSymlinks: true }) && statSync10(candidate).isFile()) {
31708
+ const candidate = join32(compiledDir, relativePath);
31709
+ if (existsSync31(candidate) && isPathInside(candidate, compiledDir) && statSync10(candidate).isFile()) {
31585
31710
  filePath = candidate;
31586
31711
  }
31587
31712
  }
31588
31713
  if (!filePath) {
31589
- const candidate = join30(projectDir, relativePath);
31590
- if (existsSync29(candidate) && isPathInside(candidate, projectDir, { resolveSymlinks: true }) && statSync10(candidate).isFile()) {
31714
+ const candidate = join32(projectDir, relativePath);
31715
+ if (existsSync31(candidate) && isPathInside(candidate, projectDir) && statSync10(candidate).isFile()) {
31591
31716
  filePath = candidate;
31592
31717
  }
31593
31718
  }
@@ -31600,7 +31725,7 @@ function createFileServer2(options) {
31600
31725
  const ext = extname7(filePath).toLowerCase();
31601
31726
  const contentType = MIME_TYPES3[ext] || "application/octet-stream";
31602
31727
  if (ext === ".html") {
31603
- const rawHtml = readFileSync19(filePath, "utf-8");
31728
+ const rawHtml = readFileSync20(filePath, "utf-8");
31604
31729
  const isIndex = relativePath === "index.html";
31605
31730
  let html = rawHtml;
31606
31731
  if (preHeadScripts.length > 0) {
@@ -31609,7 +31734,7 @@ function createFileServer2(options) {
31609
31734
  html = isIndex ? injectScriptsIntoHtml2(html, headScripts, bodyScripts, stripEmbeddedRuntime) : html;
31610
31735
  return c2.text(html, 200, { "Content-Type": contentType });
31611
31736
  }
31612
- const content = readFileSync19(filePath);
31737
+ const content = readFileSync20(filePath);
31613
31738
  return new Response(content, {
31614
31739
  status: 200,
31615
31740
  headers: { "Content-Type": contentType }
@@ -31961,7 +32086,7 @@ var init_ffprobe2 = __esm({
31961
32086
  });
31962
32087
 
31963
32088
  // ../producer/src/utils/paths.ts
31964
- import { resolve as resolve15, basename as basename2, join as join31, relative as relative2, isAbsolute as isAbsolute5 } from "path";
32089
+ import { resolve as resolve15, basename as basename2, join as join33, relative as relative2, isAbsolute as isAbsolute5 } from "path";
31965
32090
  function isPathInside2(childPath, parentPath) {
31966
32091
  const absChild = resolve15(childPath);
31967
32092
  const absParent = resolve15(parentPath);
@@ -31982,7 +32107,7 @@ function toExternalAssetKey(absPath) {
31982
32107
  function resolveRenderPaths(projectDir, outputPath, rendersDir = DEFAULT_RENDERS_DIR) {
31983
32108
  const absoluteProjectDir = resolve15(projectDir);
31984
32109
  const projectName = basename2(absoluteProjectDir);
31985
- const resolvedOutputPath = outputPath ?? join31(rendersDir, `${projectName}.mp4`);
32110
+ const resolvedOutputPath = outputPath ?? join33(rendersDir, `${projectName}.mp4`);
31986
32111
  const absoluteOutputPath = resolve15(resolvedOutputPath);
31987
32112
  return { absoluteProjectDir, absoluteOutputPath };
31988
32113
  }
@@ -32055,9 +32180,9 @@ var init_fontData_generated = __esm({
32055
32180
  });
32056
32181
 
32057
32182
  // ../producer/src/services/deterministicFonts.ts
32058
- import { existsSync as existsSync30, mkdirSync as mkdirSync16, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
32183
+ import { existsSync as existsSync32, mkdirSync as mkdirSync18, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "fs";
32059
32184
  import { homedir as homedir8 } from "os";
32060
- import { join as join32 } from "path";
32185
+ import { join as join34 } from "path";
32061
32186
  function normalizeFamilyName(family) {
32062
32187
  return family.trim().replace(/^['"]|['"]$/g, "").trim().toLowerCase();
32063
32188
  }
@@ -32168,14 +32293,14 @@ function fontSlug(familyName) {
32168
32293
  return familyName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
32169
32294
  }
32170
32295
  function fontCacheDir(slug) {
32171
- const dir = join32(GOOGLE_FONTS_CACHE_DIR, slug);
32172
- if (!existsSync30(dir)) {
32173
- mkdirSync16(dir, { recursive: true });
32296
+ const dir = join34(GOOGLE_FONTS_CACHE_DIR, slug);
32297
+ if (!existsSync32(dir)) {
32298
+ mkdirSync18(dir, { recursive: true });
32174
32299
  }
32175
32300
  return dir;
32176
32301
  }
32177
32302
  function cachedWoff2Path(slug, weight, style) {
32178
- return join32(fontCacheDir(slug), `${weight}-${style}.woff2`);
32303
+ return join34(fontCacheDir(slug), `${weight}-${style}.woff2`);
32179
32304
  }
32180
32305
  async function fetchGoogleFont(familyName) {
32181
32306
  const slug = fontSlug(familyName);
@@ -32201,17 +32326,17 @@ async function fetchGoogleFont(familyName) {
32201
32326
  const woff2Url = match[3] || "";
32202
32327
  if (!woff2Url) continue;
32203
32328
  const cachePath2 = cachedWoff2Path(slug, weight, style);
32204
- if (!existsSync30(cachePath2)) {
32329
+ if (!existsSync32(cachePath2)) {
32205
32330
  try {
32206
32331
  const fontRes = await fetch(woff2Url);
32207
32332
  if (!fontRes.ok) continue;
32208
32333
  const buffer = Buffer.from(await fontRes.arrayBuffer());
32209
- writeFileSync11(cachePath2, buffer);
32334
+ writeFileSync13(cachePath2, buffer);
32210
32335
  } catch {
32211
32336
  continue;
32212
32337
  }
32213
32338
  }
32214
- const fontBytes = readFileSync20(cachePath2);
32339
+ const fontBytes = readFileSync21(cachePath2);
32215
32340
  const dataUri = `data:font/woff2;base64,${fontBytes.toString("base64")}`;
32216
32341
  faces.push({ weight, style, dataUri });
32217
32342
  }
@@ -32387,14 +32512,14 @@ var init_deterministicFonts = __esm({
32387
32512
  poppins: "poppins",
32388
32513
  "segoe ui": "roboto"
32389
32514
  };
32390
- GOOGLE_FONTS_CACHE_DIR = join32(homedir8(), ".cache", "hyperframes", "fonts");
32515
+ GOOGLE_FONTS_CACHE_DIR = join34(homedir8(), ".cache", "hyperframes", "fonts");
32391
32516
  WOFF2_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
32392
32517
  }
32393
32518
  });
32394
32519
 
32395
32520
  // ../producer/src/services/htmlCompiler.ts
32396
- import { readFileSync as readFileSync21, existsSync as existsSync31, mkdirSync as mkdirSync17 } from "fs";
32397
- import { join as join33, dirname as dirname10, resolve as resolve16 } from "path";
32521
+ import { readFileSync as readFileSync22, existsSync as existsSync33, mkdirSync as mkdirSync19 } from "fs";
32522
+ import { join as join35, dirname as dirname10, resolve as resolve16 } from "path";
32398
32523
  import postcss from "postcss";
32399
32524
  function dedupeElementsById(elements) {
32400
32525
  const deduped = /* @__PURE__ */ new Map();
@@ -32445,16 +32570,16 @@ function detectRenderModeHints(html) {
32445
32570
  async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagName19) {
32446
32571
  let filePath = src;
32447
32572
  if (isHttpUrl(src)) {
32448
- if (!existsSync31(downloadDir)) mkdirSync17(downloadDir, { recursive: true });
32573
+ if (!existsSync33(downloadDir)) mkdirSync19(downloadDir, { recursive: true });
32449
32574
  try {
32450
32575
  filePath = await downloadToTemp(src, downloadDir);
32451
32576
  } catch {
32452
32577
  return { duration: 0, resolvedPath: src };
32453
32578
  }
32454
32579
  } else if (!filePath.startsWith("/")) {
32455
- filePath = join33(baseDir, filePath);
32580
+ filePath = join35(baseDir, filePath);
32456
32581
  }
32457
- if (!existsSync31(filePath)) {
32582
+ if (!existsSync33(filePath)) {
32458
32583
  return { duration: 0, resolvedPath: filePath };
32459
32584
  }
32460
32585
  const metadata = tagName19 === "video" ? await extractMediaMetadata(filePath) : await extractAudioMetadata(filePath);
@@ -32523,10 +32648,10 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
32523
32648
  if (visited.has(filePath)) {
32524
32649
  continue;
32525
32650
  }
32526
- if (!existsSync31(filePath)) {
32651
+ if (!existsSync33(filePath)) {
32527
32652
  continue;
32528
32653
  }
32529
- const rawSubHtml = readFileSync21(filePath, "utf-8");
32654
+ const rawSubHtml = readFileSync22(filePath, "utf-8");
32530
32655
  const nestedVisited = new Set(visited);
32531
32656
  nestedVisited.add(filePath);
32532
32657
  workItems.push({ srcPath, absoluteStart, absoluteEnd, filePath, rawSubHtml, nestedVisited });
@@ -32733,8 +32858,8 @@ function inlineSubCompositions(html, subCompositions, projectDir) {
32733
32858
  let compHtml = subCompositions.get(srcPath) || null;
32734
32859
  if (!compHtml) {
32735
32860
  const filePath = resolve16(projectDir, srcPath);
32736
- if (existsSync31(filePath)) {
32737
- compHtml = readFileSync21(filePath, "utf-8");
32861
+ if (existsSync33(filePath)) {
32862
+ compHtml = readFileSync22(filePath, "utf-8");
32738
32863
  }
32739
32864
  }
32740
32865
  if (!compHtml) {
@@ -32961,7 +33086,7 @@ function collectExternalAssets(html, projectDir) {
32961
33086
  if (isPathInside2(absPath, absProjectDir)) {
32962
33087
  return null;
32963
33088
  }
32964
- if (!existsSync31(absPath)) return null;
33089
+ if (!existsSync33(absPath)) return null;
32965
33090
  const safeKey = toExternalAssetKey(absPath);
32966
33091
  externalAssets.set(safeKey, absPath);
32967
33092
  return safeKey;
@@ -33006,7 +33131,7 @@ function collectExternalAssets(html, projectDir) {
33006
33131
  };
33007
33132
  }
33008
33133
  async function compileForRender(projectDir, htmlPath, downloadDir) {
33009
- const rawHtml = readFileSync21(htmlPath, "utf-8");
33134
+ const rawHtml = readFileSync22(htmlPath, "utf-8");
33010
33135
  const { html: compiledHtml, unresolvedCompositions } = await compileHtmlFile(
33011
33136
  rawHtml,
33012
33137
  projectDir,
@@ -33346,17 +33471,17 @@ var init_hdrImageTransferCache = __esm({
33346
33471
 
33347
33472
  // ../producer/src/services/renderOrchestrator.ts
33348
33473
  import {
33349
- existsSync as existsSync32,
33350
- mkdirSync as mkdirSync18,
33474
+ existsSync as existsSync34,
33475
+ mkdirSync as mkdirSync20,
33351
33476
  rmSync as rmSync7,
33352
- readFileSync as readFileSync22,
33477
+ readFileSync as readFileSync23,
33353
33478
  readdirSync as readdirSync14,
33354
33479
  statSync as statSync11,
33355
- writeFileSync as writeFileSync12,
33480
+ writeFileSync as writeFileSync14,
33356
33481
  copyFileSync as copyFileSync2,
33357
33482
  appendFileSync
33358
33483
  } from "fs";
33359
- import { join as join34, dirname as dirname11, resolve as resolve17 } from "path";
33484
+ import { join as join36, dirname as dirname11, resolve as resolve17 } from "path";
33360
33485
  import { randomUUID as randomUUID2 } from "crypto";
33361
33486
  import { freemem as freemem2 } from "os";
33362
33487
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -33382,7 +33507,7 @@ function sampleDirectoryBytes(dir) {
33382
33507
  continue;
33383
33508
  }
33384
33509
  for (const name of entries2) {
33385
- const full = join34(current, name);
33510
+ const full = join36(current, name);
33386
33511
  try {
33387
33512
  const st2 = statSync11(full);
33388
33513
  if (st2.isDirectory()) {
@@ -33457,21 +33582,21 @@ function installDebugLogger(logPath, log2 = defaultLogger) {
33457
33582
  };
33458
33583
  }
33459
33584
  function writeCompiledArtifacts(compiled, workDir, includeSummary) {
33460
- const compileDir = join34(workDir, "compiled");
33461
- mkdirSync18(compileDir, { recursive: true });
33462
- writeFileSync12(join34(compileDir, "index.html"), compiled.html, "utf-8");
33585
+ const compileDir = join36(workDir, "compiled");
33586
+ mkdirSync20(compileDir, { recursive: true });
33587
+ writeFileSync14(join36(compileDir, "index.html"), compiled.html, "utf-8");
33463
33588
  for (const [srcPath, html] of compiled.subCompositions) {
33464
- const outPath = join34(compileDir, srcPath);
33465
- mkdirSync18(dirname11(outPath), { recursive: true });
33466
- writeFileSync12(outPath, html, "utf-8");
33589
+ const outPath = join36(compileDir, srcPath);
33590
+ mkdirSync20(dirname11(outPath), { recursive: true });
33591
+ writeFileSync14(outPath, html, "utf-8");
33467
33592
  }
33468
33593
  for (const [relativePath, absolutePath] of compiled.externalAssets) {
33469
- const outPath = resolve17(join34(compileDir, relativePath));
33594
+ const outPath = resolve17(join36(compileDir, relativePath));
33470
33595
  if (!isPathInside2(outPath, compileDir)) {
33471
33596
  console.warn(`[Render] Skipping external asset with unsafe path: ${relativePath}`);
33472
33597
  continue;
33473
33598
  }
33474
- mkdirSync18(dirname11(outPath), { recursive: true });
33599
+ mkdirSync20(dirname11(outPath), { recursive: true });
33475
33600
  copyFileSync2(absolutePath, outPath);
33476
33601
  }
33477
33602
  if (includeSummary) {
@@ -33496,7 +33621,7 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
33496
33621
  subCompositions: Array.from(compiled.subCompositions.keys()),
33497
33622
  renderModeHints: compiled.renderModeHints
33498
33623
  };
33499
- writeFileSync12(join34(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
33624
+ writeFileSync14(join36(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
33500
33625
  }
33501
33626
  }
33502
33627
  function applyRenderModeHints(cfg, compiled, log2 = defaultLogger) {
@@ -33517,12 +33642,12 @@ function blitHdrVideoLayer(canvas, el, time, fps, hdrFrameDirs, hdrStartTimes, w
33517
33642
  if (videoFrameIndex < 1) return;
33518
33643
  const maxIndex = getMaxFrameIndex(frameDir);
33519
33644
  const effectiveIndex = maxIndex > 0 ? Math.min(videoFrameIndex, maxIndex) : videoFrameIndex;
33520
- const framePath = join34(frameDir, `frame_${String(effectiveIndex).padStart(4, "0")}.png`);
33521
- if (!existsSync32(framePath)) {
33645
+ const framePath = join36(frameDir, `frame_${String(effectiveIndex).padStart(4, "0")}.png`);
33646
+ if (!existsSync34(framePath)) {
33522
33647
  return;
33523
33648
  }
33524
33649
  try {
33525
- const { data: hdrRgb, width: srcW, height: srcH } = decodePngToRgb48le(readFileSync22(framePath));
33650
+ const { data: hdrRgb, width: srcW, height: srcH } = decodePngToRgb48le(readFileSync23(framePath));
33526
33651
  if (sourceTransfer && targetTransfer && sourceTransfer !== targetTransfer) {
33527
33652
  convertTransfer(hdrRgb, sourceTransfer, targetTransfer);
33528
33653
  }
@@ -33701,7 +33826,7 @@ async function compositeHdrFrame(ctx, canvas, time, fullStacking, elementFilter,
33701
33826
  const startTime = hdrVideoStartTimes.get(layer.element.id) ?? 0;
33702
33827
  const localTime = time - startTime;
33703
33828
  const frameNum = Math.floor(localTime * fps) + 1;
33704
- const expectedFrame = frameDir ? join34(frameDir, `frame_${String(frameNum).padStart(4, "0")}.png`) : null;
33829
+ const expectedFrame = frameDir ? join36(frameDir, `frame_${String(frameNum).padStart(4, "0")}.png`) : null;
33705
33830
  log2.info("[diag] hdr layer blit", {
33706
33831
  frame: debugFrameIndex,
33707
33832
  layerIdx,
@@ -33713,7 +33838,7 @@ async function compositeHdrFrame(ctx, canvas, time, fullStacking, elementFilter,
33713
33838
  localTime: localTime.toFixed(3),
33714
33839
  hdrFrameNum: frameNum,
33715
33840
  expectedFrame,
33716
- expectedFrameExists: expectedFrame ? existsSync32(expectedFrame) : false
33841
+ expectedFrameExists: expectedFrame ? existsSync34(expectedFrame) : false
33717
33842
  });
33718
33843
  }
33719
33844
  }
@@ -33738,8 +33863,8 @@ async function compositeHdrFrame(ctx, canvas, time, fullStacking, elementFilter,
33738
33863
  if (shouldLog && debugDumpDir) {
33739
33864
  const after2 = countNonZeroRgb48(canvas);
33740
33865
  const dumpName = `frame_${String(debugFrameIndex).padStart(4, "0")}_layer_${String(layerIdx).padStart(2, "0")}_dom.png`;
33741
- const dumpPath = join34(debugDumpDir, dumpName);
33742
- writeFileSync12(dumpPath, domPng);
33866
+ const dumpPath = join36(debugDumpDir, dumpName);
33867
+ writeFileSync14(dumpPath, domPng);
33743
33868
  log2.info("[diag] dom layer blit", {
33744
33869
  frame: debugFrameIndex,
33745
33870
  layerIdx,
@@ -33812,8 +33937,8 @@ function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
33812
33937
  async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSignal) {
33813
33938
  const moduleDir = dirname11(fileURLToPath3(import.meta.url));
33814
33939
  const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve17(process.env.PRODUCER_RENDERS_DIR, "..") : resolve17(moduleDir, "../..");
33815
- const debugDir = join34(producerRoot, ".debug");
33816
- const workDir = job.config.debug ? join34(debugDir, job.id) : join34(dirname11(outputPath), `work-${job.id}`);
33940
+ const debugDir = join36(producerRoot, ".debug");
33941
+ const workDir = job.config.debug ? join36(debugDir, job.id) : join36(dirname11(outputPath), `work-${job.id}`);
33817
33942
  const pipelineStart = Date.now();
33818
33943
  const log2 = job.config.logger ?? defaultLogger;
33819
33944
  let fileServer = null;
@@ -33825,7 +33950,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
33825
33950
  videoExtractionFailures: 0,
33826
33951
  imageDecodeFailures: 0
33827
33952
  };
33828
- const perfOutputPath = join34(workDir, "perf-summary.json");
33953
+ const perfOutputPath = join36(workDir, "perf-summary.json");
33829
33954
  const cfg = { ...job.config.producerConfig ?? resolveConfig() };
33830
33955
  const outputFormat = job.config.format ?? "mp4";
33831
33956
  const isWebm = outputFormat === "webm";
@@ -33858,28 +33983,28 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
33858
33983
  };
33859
33984
  job.startedAt = /* @__PURE__ */ new Date();
33860
33985
  assertNotAborted();
33861
- if (!existsSync32(workDir)) mkdirSync18(workDir, { recursive: true });
33986
+ if (!existsSync34(workDir)) mkdirSync20(workDir, { recursive: true });
33862
33987
  if (job.config.debug) {
33863
- const logPath = join34(workDir, "render.log");
33988
+ const logPath = join36(workDir, "render.log");
33864
33989
  restoreLogger = installDebugLogger(logPath, log2);
33865
33990
  }
33866
33991
  const entryFile = job.config.entryFile || "index.html";
33867
- let htmlPath = join34(projectDir, entryFile);
33868
- if (!existsSync32(htmlPath)) {
33992
+ let htmlPath = join36(projectDir, entryFile);
33993
+ if (!existsSync34(htmlPath)) {
33869
33994
  throw new Error(`Entry file not found: ${htmlPath}`);
33870
33995
  }
33871
33996
  assertNotAborted();
33872
- const rawEntry = readFileSync22(htmlPath, "utf-8");
33997
+ const rawEntry = readFileSync23(htmlPath, "utf-8");
33873
33998
  if (entryFile !== "index.html" && rawEntry.trimStart().startsWith("<template")) {
33874
- const wrapperPath = join34(workDir, "standalone-entry.html");
33875
- const projectIndexPath = join34(projectDir, "index.html");
33876
- if (!existsSync32(projectIndexPath)) {
33999
+ const wrapperPath = join36(workDir, "standalone-entry.html");
34000
+ const projectIndexPath = join36(projectDir, "index.html");
34001
+ if (!existsSync34(projectIndexPath)) {
33877
34002
  throw new Error(
33878
34003
  `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
33879
34004
  );
33880
34005
  }
33881
34006
  const standaloneHtml = extractStandaloneEntryFromIndex(
33882
- readFileSync22(projectIndexPath, "utf-8"),
34007
+ readFileSync23(projectIndexPath, "utf-8"),
33883
34008
  entryFile
33884
34009
  );
33885
34010
  if (!standaloneHtml) {
@@ -33887,7 +34012,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
33887
34012
  `Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
33888
34013
  );
33889
34014
  }
33890
- writeFileSync12(wrapperPath, standaloneHtml, "utf-8");
34015
+ writeFileSync14(wrapperPath, standaloneHtml, "utf-8");
33891
34016
  htmlPath = wrapperPath;
33892
34017
  log2.info("Extracted standalone entry from index.html host context", {
33893
34018
  entryFile
@@ -33896,7 +34021,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
33896
34021
  const stage1Start = Date.now();
33897
34022
  updateJobStatus(job, "preprocessing", "Compiling composition", 5, onProgress);
33898
34023
  const compileStart = Date.now();
33899
- let compiled = await compileForRender(projectDir, htmlPath, join34(workDir, "downloads"));
34024
+ let compiled = await compileForRender(projectDir, htmlPath, join36(workDir, "downloads"));
33900
34025
  assertNotAborted();
33901
34026
  perfStages.compileOnlyMs = Date.now() - compileStart;
33902
34027
  applyRenderModeHints(cfg, compiled, log2);
@@ -33928,7 +34053,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
33928
34053
  reasons.push(`${compiled.unresolvedCompositions.length} unresolved composition(s)`);
33929
34054
  fileServer = await createFileServer2({
33930
34055
  projectDir,
33931
- compiledDir: join34(workDir, "compiled"),
34056
+ compiledDir: join36(workDir, "compiled"),
33932
34057
  port: 0,
33933
34058
  preHeadScripts: [VIRTUAL_TIME_SHIM]
33934
34059
  });
@@ -33942,7 +34067,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
33942
34067
  };
33943
34068
  probeSession = await createCaptureSession(
33944
34069
  fileServer.url,
33945
- join34(workDir, "probe"),
34070
+ join36(workDir, "probe"),
33946
34071
  captureOpts,
33947
34072
  null,
33948
34073
  cfg
@@ -33974,7 +34099,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
33974
34099
  compiled,
33975
34100
  resolutions,
33976
34101
  projectDir,
33977
- join34(workDir, "downloads")
34102
+ join36(workDir, "downloads")
33978
34103
  );
33979
34104
  assertNotAborted();
33980
34105
  composition.videos = compiled.videos;
@@ -34128,7 +34253,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34128
34253
  const stage2Start = Date.now();
34129
34254
  updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress);
34130
34255
  let frameLookup = null;
34131
- const compiledDir = join34(workDir, "compiled");
34256
+ const compiledDir = join36(workDir, "compiled");
34132
34257
  let extractionResult = null;
34133
34258
  const nativeHdrVideoIds = /* @__PURE__ */ new Set();
34134
34259
  const videoTransfers = /* @__PURE__ */ new Map();
@@ -34137,10 +34262,10 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34137
34262
  composition.videos.map(async (v) => {
34138
34263
  let videoPath = v.src;
34139
34264
  if (!videoPath.startsWith("/")) {
34140
- const fromCompiled = existsSync32(join34(compiledDir, videoPath)) ? join34(compiledDir, videoPath) : join34(projectDir, videoPath);
34265
+ const fromCompiled = existsSync34(join36(compiledDir, videoPath)) ? join36(compiledDir, videoPath) : join36(projectDir, videoPath);
34141
34266
  videoPath = fromCompiled;
34142
34267
  }
34143
- if (!existsSync32(videoPath)) return;
34268
+ if (!existsSync34(videoPath)) return;
34144
34269
  const meta = await extractMediaMetadata(videoPath);
34145
34270
  if (isHdrColorSpace(meta.colorSpace)) {
34146
34271
  nativeHdrVideoIds.add(v.id);
@@ -34158,10 +34283,10 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34158
34283
  composition.images.map(async (img) => {
34159
34284
  let imgPath = img.src;
34160
34285
  if (!imgPath.startsWith("/")) {
34161
- const fromCompiled = existsSync32(join34(compiledDir, imgPath)) ? join34(compiledDir, imgPath) : join34(projectDir, imgPath);
34286
+ const fromCompiled = existsSync34(join36(compiledDir, imgPath)) ? join36(compiledDir, imgPath) : join36(projectDir, imgPath);
34162
34287
  imgPath = fromCompiled;
34163
34288
  }
34164
- if (!existsSync32(imgPath)) return null;
34289
+ if (!existsSync34(imgPath)) return null;
34165
34290
  const meta = await extractMediaMetadata(imgPath);
34166
34291
  if (isHdrColorSpace(meta.colorSpace)) {
34167
34292
  nativeHdrImageIds.add(img.id);
@@ -34177,7 +34302,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34177
34302
  extractionResult = await extractAllVideoFrames(
34178
34303
  composition.videos,
34179
34304
  projectDir,
34180
- { fps: job.config.fps, outputDir: join34(workDir, "video-frames") },
34305
+ { fps: job.config.fps, outputDir: join36(workDir, "video-frames") },
34181
34306
  abortSignal,
34182
34307
  { extractCacheDir: cfg.extractCacheDir },
34183
34308
  compiledDir
@@ -34235,13 +34360,13 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34235
34360
  }
34236
34361
  const stage3Start = Date.now();
34237
34362
  updateJobStatus(job, "preprocessing", "Processing audio tracks", 20, onProgress);
34238
- const audioOutputPath = join34(workDir, "audio.aac");
34363
+ const audioOutputPath = join36(workDir, "audio.aac");
34239
34364
  let hasAudio = false;
34240
34365
  if (composition.audios.length > 0) {
34241
34366
  const audioResult = await processCompositionAudio(
34242
34367
  composition.audios,
34243
34368
  projectDir,
34244
- join34(workDir, "audio-work"),
34369
+ join36(workDir, "audio-work"),
34245
34370
  audioOutputPath,
34246
34371
  job.duration,
34247
34372
  abortSignal,
@@ -34259,14 +34384,14 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34259
34384
  if (!fileServer) {
34260
34385
  fileServer = await createFileServer2({
34261
34386
  projectDir,
34262
- compiledDir: join34(workDir, "compiled"),
34387
+ compiledDir: join36(workDir, "compiled"),
34263
34388
  port: 0,
34264
34389
  preHeadScripts: [VIRTUAL_TIME_SHIM]
34265
34390
  });
34266
34391
  assertNotAborted();
34267
34392
  }
34268
- const framesDir = join34(workDir, "captured-frames");
34269
- if (!existsSync32(framesDir)) mkdirSync18(framesDir, { recursive: true });
34393
+ const framesDir = join36(workDir, "captured-frames");
34394
+ if (!existsSync34(framesDir)) mkdirSync20(framesDir, { recursive: true });
34270
34395
  const captureOptions = {
34271
34396
  width,
34272
34397
  height,
@@ -34281,7 +34406,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34281
34406
  const workerCount = calculateOptimalWorkers(totalFrames, job.config.workers, cfg);
34282
34407
  const FORMAT_EXT2 = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
34283
34408
  const videoExt = FORMAT_EXT2[outputFormat] ?? ".mp4";
34284
- const videoOnlyPath = join34(workDir, `video-only${videoExt}`);
34409
+ const videoOnlyPath = join36(workDir, `video-only${videoExt}`);
34285
34410
  const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
34286
34411
  const hasHdrContent = effectiveHdr && nativeHdrIds.size > 0;
34287
34412
  const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
@@ -34303,8 +34428,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34303
34428
  if (!hdrVideoIds.includes(v.id)) continue;
34304
34429
  let srcPath = v.src;
34305
34430
  if (!srcPath.startsWith("/")) {
34306
- const fromCompiled = join34(compiledDir, srcPath);
34307
- srcPath = existsSync32(fromCompiled) ? fromCompiled : join34(projectDir, srcPath);
34431
+ const fromCompiled = join36(compiledDir, srcPath);
34432
+ srcPath = existsSync34(fromCompiled) ? fromCompiled : join36(projectDir, srcPath);
34308
34433
  }
34309
34434
  hdrVideoSrcPaths.set(v.id, srcPath);
34310
34435
  }
@@ -34434,8 +34559,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34434
34559
  for (const [videoId, srcPath] of hdrVideoSrcPaths) {
34435
34560
  const video = composition.videos.find((v) => v.id === videoId);
34436
34561
  if (!video) continue;
34437
- const frameDir = join34(framesDir, `hdr_${videoId}`);
34438
- mkdirSync18(frameDir, { recursive: true });
34562
+ const frameDir = join36(framesDir, `hdr_${videoId}`);
34563
+ mkdirSync20(frameDir, { recursive: true });
34439
34564
  const duration = video.end - video.start;
34440
34565
  const dims = hdrExtractionDims.get(videoId) ?? { width, height };
34441
34566
  const ffmpegArgs = [
@@ -34454,7 +34579,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34454
34579
  "-c:v",
34455
34580
  "png",
34456
34581
  "-y",
34457
- join34(frameDir, "frame_%04d.png")
34582
+ join36(frameDir, "frame_%04d.png")
34458
34583
  ];
34459
34584
  const result = await runFfmpeg(ffmpegArgs, { signal: abortSignal });
34460
34585
  if (!result.success) {
@@ -34473,7 +34598,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34473
34598
  const hdrImageBuffers = /* @__PURE__ */ new Map();
34474
34599
  for (const [imageId, srcPath] of hdrImageSrcPaths) {
34475
34600
  try {
34476
- const decoded = decodePngToRgb48le(readFileSync22(srcPath));
34601
+ const decoded = decodePngToRgb48le(readFileSync23(srcPath));
34477
34602
  const layout2 = hdrExtractionDims.get(imageId);
34478
34603
  const fitInfo = hdrImageFitInfo.get(imageId);
34479
34604
  if (layout2 && (layout2.width !== decoded.width || layout2.height !== decoded.height)) {
@@ -34522,9 +34647,9 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34522
34647
  }
34523
34648
  }
34524
34649
  const debugDumpEnabled = process.env.KEEP_TEMP === "1";
34525
- const debugDumpDir = debugDumpEnabled ? join34(framesDir, "debug-composite") : null;
34526
- if (debugDumpDir && !existsSync32(debugDumpDir)) {
34527
- mkdirSync18(debugDumpDir, { recursive: true });
34650
+ const debugDumpDir = debugDumpEnabled ? join36(framesDir, "debug-composite") : null;
34651
+ if (debugDumpDir && !existsSync34(debugDumpDir)) {
34652
+ mkdirSync20(debugDumpDir, { recursive: true });
34528
34653
  }
34529
34654
  if (!effectiveHdr) {
34530
34655
  throw new Error(
@@ -34670,11 +34795,11 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34670
34795
  i2
34671
34796
  );
34672
34797
  if (debugDumpEnabled && debugDumpDir && i2 % 30 === 0) {
34673
- const previewPath = join34(
34798
+ const previewPath = join36(
34674
34799
  debugDumpDir,
34675
34800
  `frame_${String(i2).padStart(4, "0")}_final_rgb48le.bin`
34676
34801
  );
34677
- writeFileSync12(previewPath, normalCanvas);
34802
+ writeFileSync14(previewPath, normalCanvas);
34678
34803
  }
34679
34804
  hdrEncoder.writeFrame(normalCanvas);
34680
34805
  }
@@ -35017,7 +35142,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35017
35142
  updateJobStatus(job, "complete", "Render complete", 100, onProgress);
35018
35143
  const totalElapsed = Date.now() - pipelineStart;
35019
35144
  sampleMemory();
35020
- const tmpPeakBytes = existsSync32(workDir) ? sampleDirectoryBytes(workDir) : 0;
35145
+ const tmpPeakBytes = existsSync34(workDir) ? sampleDirectoryBytes(workDir) : 0;
35021
35146
  const perfSummary = {
35022
35147
  renderId: job.id,
35023
35148
  totalElapsedMs: totalElapsed,
@@ -35042,7 +35167,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35042
35167
  job.perfSummary = perfSummary;
35043
35168
  if (job.config.debug) {
35044
35169
  try {
35045
- writeFileSync12(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
35170
+ writeFileSync14(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
35046
35171
  } catch (err) {
35047
35172
  log2.debug("Failed to write perf summary", {
35048
35173
  perfOutputPath,
@@ -35051,8 +35176,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35051
35176
  }
35052
35177
  }
35053
35178
  if (job.config.debug) {
35054
- if (existsSync32(outputPath)) {
35055
- const debugOutput = join34(workDir, `output${videoExt}`);
35179
+ if (existsSync34(outputPath)) {
35180
+ const debugOutput = join36(workDir, `output${videoExt}`);
35056
35181
  copyFileSync2(outputPath, debugOutput);
35057
35182
  }
35058
35183
  } else if (process.env.KEEP_TEMP === "1") {
@@ -35138,7 +35263,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35138
35263
  await safeCleanup(
35139
35264
  "remove workDir (error)",
35140
35265
  () => {
35141
- if (existsSync32(workDir)) rmSync7(workDir, { recursive: true, force: true });
35266
+ if (existsSync34(workDir)) rmSync7(workDir, { recursive: true, force: true });
35142
35267
  },
35143
35268
  log2
35144
35269
  );
@@ -35198,8 +35323,8 @@ var init_config3 = __esm({
35198
35323
  });
35199
35324
 
35200
35325
  // ../producer/src/services/hyperframeLint.ts
35201
- import { existsSync as existsSync33, readFileSync as readFileSync23, statSync as statSync12 } from "fs";
35202
- import { resolve as resolve18, join as join35 } from "path";
35326
+ import { existsSync as existsSync35, readFileSync as readFileSync24, statSync as statSync12 } from "fs";
35327
+ import { resolve as resolve18, join as join37 } from "path";
35203
35328
  function isStringRecord(value) {
35204
35329
  if (!value || typeof value !== "object" || Array.isArray(value)) {
35205
35330
  return false;
@@ -35227,7 +35352,7 @@ function pickEntryFile(files, preferredEntryFile) {
35227
35352
  }
35228
35353
  function readProjectEntryFile(projectDir, preferredEntryFile) {
35229
35354
  const absProjectDir = resolve18(projectDir);
35230
- if (!existsSync33(absProjectDir) || !statSync12(absProjectDir).isDirectory()) {
35355
+ if (!existsSync35(absProjectDir) || !statSync12(absProjectDir).isDirectory()) {
35231
35356
  return { error: `Project directory not found: ${absProjectDir}` };
35232
35357
  }
35233
35358
  const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
@@ -35238,16 +35363,16 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
35238
35363
  if (!absoluteEntryPath.startsWith(absProjectDir)) {
35239
35364
  return { error: `Entry file must stay inside project directory: ${entryFile}` };
35240
35365
  }
35241
- if (existsSync33(absoluteEntryPath) && statSync12(absoluteEntryPath).isFile()) {
35366
+ if (existsSync35(absoluteEntryPath) && statSync12(absoluteEntryPath).isFile()) {
35242
35367
  return {
35243
35368
  entryFile,
35244
- html: readFileSync23(absoluteEntryPath, "utf-8"),
35369
+ html: readFileSync24(absoluteEntryPath, "utf-8"),
35245
35370
  source: "projectDir"
35246
35371
  };
35247
35372
  }
35248
35373
  }
35249
35374
  return {
35250
- error: `No HTML entry file found in project directory: ${join35(absProjectDir, preferredEntryFile || "index.html")}`
35375
+ error: `No HTML entry file found in project directory: ${join37(absProjectDir, preferredEntryFile || "index.html")}`
35251
35376
  };
35252
35377
  }
35253
35378
  function prepareHyperframeLintBody(body) {
@@ -35335,15 +35460,15 @@ var init_semaphore = __esm({
35335
35460
 
35336
35461
  // ../producer/src/server.ts
35337
35462
  import {
35338
- existsSync as existsSync34,
35339
- mkdirSync as mkdirSync19,
35463
+ existsSync as existsSync36,
35464
+ mkdirSync as mkdirSync21,
35340
35465
  statSync as statSync13,
35341
35466
  mkdtempSync as mkdtempSync2,
35342
- writeFileSync as writeFileSync13,
35467
+ writeFileSync as writeFileSync15,
35343
35468
  rmSync as rmSync8,
35344
35469
  createReadStream
35345
35470
  } from "fs";
35346
- import { resolve as resolve19, dirname as dirname12, join as join36 } from "path";
35471
+ import { resolve as resolve19, dirname as dirname12, join as join38 } from "path";
35347
35472
  import { tmpdir as tmpdir3 } from "os";
35348
35473
  import { parseArgs as parseArgs2 } from "util";
35349
35474
  import crypto2 from "crypto";
@@ -35366,11 +35491,11 @@ async function prepareRenderBody(body) {
35366
35491
  const projectDir = typeof body.projectDir === "string" ? body.projectDir : void 0;
35367
35492
  if (projectDir) {
35368
35493
  const absProjectDir = resolve19(projectDir);
35369
- if (!existsSync34(absProjectDir) || !statSync13(absProjectDir).isDirectory()) {
35494
+ if (!existsSync36(absProjectDir) || !statSync13(absProjectDir).isDirectory()) {
35370
35495
  return { error: `Project directory not found: ${absProjectDir}` };
35371
35496
  }
35372
35497
  const entry = options.entryFile || "index.html";
35373
- if (!existsSync34(resolve19(absProjectDir, entry))) {
35498
+ if (!existsSync36(resolve19(absProjectDir, entry))) {
35374
35499
  return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
35375
35500
  }
35376
35501
  return { prepared: { input: { projectDir: absProjectDir, ...options } } };
@@ -35395,8 +35520,8 @@ async function prepareRenderBody(body) {
35395
35520
  }
35396
35521
  }
35397
35522
  const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir3();
35398
- const tempProjectDir = mkdtempSync2(join36(tempRoot, "producer-project-"));
35399
- writeFileSync13(join36(tempProjectDir, "index.html"), htmlContent, "utf-8");
35523
+ const tempProjectDir = mkdtempSync2(join38(tempRoot, "producer-project-"));
35524
+ writeFileSync15(join38(tempProjectDir, "index.html"), htmlContent, "utf-8");
35400
35525
  return {
35401
35526
  prepared: {
35402
35527
  input: {
@@ -35519,7 +35644,7 @@ function createRenderHandlers(options = {}) {
35519
35644
  log2
35520
35645
  );
35521
35646
  const outputDir = dirname12(absoluteOutputPath);
35522
- if (!existsSync34(outputDir)) mkdirSync19(outputDir, { recursive: true });
35647
+ if (!existsSync36(outputDir)) mkdirSync21(outputDir, { recursive: true });
35523
35648
  const release2 = await renderSemaphore.acquire();
35524
35649
  log2.info("render started", {
35525
35650
  requestId,
@@ -35546,7 +35671,7 @@ function createRenderHandlers(options = {}) {
35546
35671
  log2.info(`render progress ${pct}%`, { requestId, stage: j2.currentStage, message });
35547
35672
  }
35548
35673
  });
35549
- const fileSize = existsSync34(absoluteOutputPath) ? statSync13(absoluteOutputPath).size : 0;
35674
+ const fileSize = existsSync36(absoluteOutputPath) ? statSync13(absoluteOutputPath).size : 0;
35550
35675
  const durationMs = Date.now() - t0;
35551
35676
  const outputToken = store.register(absoluteOutputPath);
35552
35677
  const outputUrl = `${outputUrlPrefix}/${outputToken}`;
@@ -35630,7 +35755,7 @@ function createRenderHandlers(options = {}) {
35630
35755
  log2
35631
35756
  );
35632
35757
  const outputDir = dirname12(absoluteOutputPath);
35633
- if (!existsSync34(outputDir)) mkdirSync19(outputDir, { recursive: true });
35758
+ if (!existsSync36(outputDir)) mkdirSync21(outputDir, { recursive: true });
35634
35759
  log2.info("render-stream started", { requestId, projectDir: input.projectDir });
35635
35760
  const job = createRenderJob({
35636
35761
  fps: input.fps,
@@ -35675,7 +35800,7 @@ function createRenderHandlers(options = {}) {
35675
35800
  },
35676
35801
  abortController.signal
35677
35802
  );
35678
- const fileSize = existsSync34(absoluteOutputPath) ? statSync13(absoluteOutputPath).size : 0;
35803
+ const fileSize = existsSync36(absoluteOutputPath) ? statSync13(absoluteOutputPath).size : 0;
35679
35804
  const outputToken = store.register(absoluteOutputPath);
35680
35805
  const outputUrl = `${outputUrlPrefix}/${outputToken}`;
35681
35806
  log2.info("render-stream completed", { requestId, fileSize, perf: job.perfSummary ?? null });
@@ -35734,7 +35859,7 @@ function createRenderHandlers(options = {}) {
35734
35859
  if (!artifact) {
35735
35860
  return c2.json({ success: false, error: "Output artifact not found or expired" }, 404);
35736
35861
  }
35737
- if (!existsSync34(artifact.path)) {
35862
+ if (!existsSync36(artifact.path)) {
35738
35863
  store.delete(token);
35739
35864
  return c2.json({ success: false, error: "Output artifact file missing" }, 404);
35740
35865
  }
@@ -35872,20 +35997,20 @@ __export(studioServer_exports, {
35872
35997
  });
35873
35998
  import { Hono as Hono5 } from "hono";
35874
35999
  import { streamSSE as streamSSE3 } from "hono/streaming";
35875
- import { existsSync as existsSync35, readFileSync as readFileSync24, writeFileSync as writeFileSync14, statSync as statSync14 } from "fs";
35876
- import { resolve as resolve20, join as join37, basename as basename3 } from "path";
36000
+ import { existsSync as existsSync37, readFileSync as readFileSync25, writeFileSync as writeFileSync16, statSync as statSync14 } from "fs";
36001
+ import { resolve as resolve20, join as join39, basename as basename3 } from "path";
35877
36002
  function resolveDistDir() {
35878
36003
  return resolveStudioBundle().dir;
35879
36004
  }
35880
36005
  function resolveStudioBundle() {
35881
36006
  const builtPath = resolve20(__dirname, "studio");
35882
36007
  const builtIndex = resolve20(builtPath, "index.html");
35883
- if (existsSync35(builtIndex)) {
36008
+ if (existsSync37(builtIndex)) {
35884
36009
  return { dir: builtPath, indexPath: builtIndex, available: true, checkedPaths: [builtIndex] };
35885
36010
  }
35886
36011
  const devPath = resolve20(__dirname, "..", "..", "..", "studio", "dist");
35887
36012
  const devIndex = resolve20(devPath, "index.html");
35888
- if (existsSync35(devIndex)) {
36013
+ if (existsSync37(devIndex)) {
35889
36014
  return {
35890
36015
  dir: devPath,
35891
36016
  indexPath: devIndex,
@@ -35902,9 +36027,9 @@ function resolveStudioBundle() {
35902
36027
  }
35903
36028
  function resolveRuntimePath() {
35904
36029
  const builtPath = resolve20(__dirname, "hyperframe-runtime.js");
35905
- if (existsSync35(builtPath)) return builtPath;
36030
+ if (existsSync37(builtPath)) return builtPath;
35906
36031
  const iifePath = resolve20(__dirname, "hyperframe.runtime.iife.js");
35907
- if (existsSync35(iifePath)) return iifePath;
36032
+ if (existsSync37(iifePath)) return iifePath;
35908
36033
  const devPath = resolve20(
35909
36034
  __dirname,
35910
36035
  "..",
@@ -35914,7 +36039,7 @@ function resolveRuntimePath() {
35914
36039
  "dist",
35915
36040
  "hyperframe.runtime.iife.js"
35916
36041
  );
35917
- if (existsSync35(devPath)) return devPath;
36042
+ if (existsSync37(devPath)) return devPath;
35918
36043
  return builtPath;
35919
36044
  }
35920
36045
  async function getThumbnailBrowser() {
@@ -35976,7 +36101,7 @@ function createStudioServer(options) {
35976
36101
  return lintHyperframeHtml2(html, opts);
35977
36102
  },
35978
36103
  runtimeUrl: "/api/runtime.js",
35979
- rendersDir: () => join37(projectDir, "renders"),
36104
+ rendersDir: () => join39(projectDir, "renders"),
35980
36105
  startRender(opts) {
35981
36106
  const state = {
35982
36107
  id: opts.jobId,
@@ -36009,7 +36134,7 @@ function createStudioServer(options) {
36009
36134
  state.status = "complete";
36010
36135
  state.progress = 100;
36011
36136
  const metaPath = opts.outputPath.replace(/\.(mp4|webm|mov)$/, ".meta.json");
36012
- writeFileSync14(
36137
+ writeFileSync16(
36013
36138
  metaPath,
36014
36139
  JSON.stringify({ status: "complete", durationMs: Date.now() - startTime })
36015
36140
  );
@@ -36018,7 +36143,7 @@ function createStudioServer(options) {
36018
36143
  state.error = err instanceof Error ? err.message : String(err);
36019
36144
  try {
36020
36145
  const metaPath = opts.outputPath.replace(/\.(mp4|webm|mov)$/, ".meta.json");
36021
- writeFileSync14(metaPath, JSON.stringify({ status: "failed" }));
36146
+ writeFileSync16(metaPath, JSON.stringify({ status: "failed" }));
36022
36147
  } catch {
36023
36148
  }
36024
36149
  }
@@ -36091,7 +36216,7 @@ function createStudioServer(options) {
36091
36216
  });
36092
36217
  app.get("/api/runtime.js", (c2) => {
36093
36218
  const serve4 = async () => {
36094
- const runtimeSource = await loadRuntimeSource() ?? (existsSync35(runtimePath) ? readFileSync24(runtimePath, "utf-8") : null);
36219
+ const runtimeSource = await loadRuntimeSource() ?? (existsSync37(runtimePath) ? readFileSync25(runtimePath, "utf-8") : null);
36095
36220
  if (!runtimeSource) return c2.text("runtime not available", 404);
36096
36221
  return c2.body(runtimeSource, 200, {
36097
36222
  "Content-Type": "text/javascript",
@@ -36127,23 +36252,23 @@ function createStudioServer(options) {
36127
36252
  });
36128
36253
  app.get("/assets/*", (c2) => {
36129
36254
  const filePath = resolve20(studioDir, c2.req.path.slice(1));
36130
- if (!existsSync35(filePath) || !statSync14(filePath).isFile()) return c2.text("not found", 404);
36131
- const content = readFileSync24(filePath);
36255
+ if (!existsSync37(filePath) || !statSync14(filePath).isFile()) return c2.text("not found", 404);
36256
+ const content = readFileSync25(filePath);
36132
36257
  return new Response(content, {
36133
36258
  headers: { "Content-Type": getMimeType(filePath), "Cache-Control": "no-store" }
36134
36259
  });
36135
36260
  });
36136
36261
  app.get("/icons/*", (c2) => {
36137
36262
  const filePath = resolve20(studioDir, c2.req.path.slice(1));
36138
- if (!existsSync35(filePath) || !statSync14(filePath).isFile()) return c2.text("not found", 404);
36139
- const content = readFileSync24(filePath);
36263
+ if (!existsSync37(filePath) || !statSync14(filePath).isFile()) return c2.text("not found", 404);
36264
+ const content = readFileSync25(filePath);
36140
36265
  return new Response(content, {
36141
36266
  headers: { "Content-Type": getMimeType(filePath), "Cache-Control": "no-store" }
36142
36267
  });
36143
36268
  });
36144
36269
  app.get("*", (c2) => {
36145
36270
  const indexPath = resolve20(studioDir, "index.html");
36146
- if (!existsSync35(indexPath)) {
36271
+ if (!existsSync37(indexPath)) {
36147
36272
  return c2.html(
36148
36273
  `<!doctype html>
36149
36274
  <html>
@@ -36199,7 +36324,7 @@ function createStudioServer(options) {
36199
36324
  500
36200
36325
  );
36201
36326
  }
36202
- return c2.html(readFileSync24(indexPath, "utf-8"));
36327
+ return c2.html(readFileSync25(indexPath, "utf-8"));
36203
36328
  });
36204
36329
  return { app, watcher };
36205
36330
  }
@@ -36222,21 +36347,21 @@ __export(preview_exports, {
36222
36347
  default: () => preview_default,
36223
36348
  examples: () => examples
36224
36349
  });
36225
- import { spawn as spawn8 } from "child_process";
36226
- import { existsSync as existsSync36, lstatSync, symlinkSync, unlinkSync as unlinkSync5, readlinkSync, mkdirSync as mkdirSync20 } from "fs";
36227
- import { resolve as resolve21, dirname as dirname13, basename as basename4, join as join38 } from "path";
36350
+ import { spawn as spawn9 } from "child_process";
36351
+ import { existsSync as existsSync38, lstatSync, symlinkSync, unlinkSync as unlinkSync5, readlinkSync, mkdirSync as mkdirSync22 } from "fs";
36352
+ import { resolve as resolve21, dirname as dirname13, basename as basename4, join as join40 } from "path";
36228
36353
  import { fileURLToPath as fileURLToPath4 } from "url";
36229
36354
  import { createRequire } from "module";
36230
36355
  async function runDevMode(dir, projectName) {
36231
36356
  const thisFile = fileURLToPath4(import.meta.url);
36232
36357
  const repoRoot = resolve21(dirname13(thisFile), "..", "..", "..", "..");
36233
- const projectsDir = join38(repoRoot, "packages", "studio", "data", "projects");
36358
+ const projectsDir = join40(repoRoot, "packages", "studio", "data", "projects");
36234
36359
  const pName = projectName ?? basename4(dir);
36235
- const symlinkPath = join38(projectsDir, pName);
36236
- mkdirSync20(projectsDir, { recursive: true });
36360
+ const symlinkPath = join40(projectsDir, pName);
36361
+ mkdirSync22(projectsDir, { recursive: true });
36237
36362
  let createdSymlink = false;
36238
36363
  if (dir !== symlinkPath) {
36239
- if (existsSync36(symlinkPath)) {
36364
+ if (existsSync38(symlinkPath)) {
36240
36365
  try {
36241
36366
  const stat3 = lstatSync(symlinkPath);
36242
36367
  if (stat3.isSymbolicLink()) {
@@ -36248,7 +36373,7 @@ async function runDevMode(dir, projectName) {
36248
36373
  } catch {
36249
36374
  }
36250
36375
  }
36251
- if (!existsSync36(symlinkPath)) {
36376
+ if (!existsSync38(symlinkPath)) {
36252
36377
  symlinkSync(dir, symlinkPath, "dir");
36253
36378
  createdSymlink = true;
36254
36379
  }
@@ -36256,8 +36381,8 @@ async function runDevMode(dir, projectName) {
36256
36381
  Wt2(c.bold("hyperframes preview"));
36257
36382
  const s2 = be();
36258
36383
  s2.start("Starting studio...");
36259
- const studioPkgDir = join38(repoRoot, "packages", "studio");
36260
- const child = spawn8("pnpm", ["exec", "vite"], {
36384
+ const studioPkgDir = join40(repoRoot, "packages", "studio");
36385
+ const child = spawn9("pnpm", ["exec", "vite"], {
36261
36386
  cwd: studioPkgDir,
36262
36387
  stdio: ["ignore", "pipe", "pipe"]
36263
36388
  });
@@ -36290,7 +36415,7 @@ async function runDevMode(dir, projectName) {
36290
36415
  if (createdSymlink) {
36291
36416
  process.on("exit", () => {
36292
36417
  try {
36293
- if (existsSync36(symlinkPath)) unlinkSync5(symlinkPath);
36418
+ if (existsSync38(symlinkPath)) unlinkSync5(symlinkPath);
36294
36419
  } catch {
36295
36420
  }
36296
36421
  });
@@ -36301,7 +36426,7 @@ async function runDevMode(dir, projectName) {
36301
36426
  }
36302
36427
  function hasLocalStudio(dir) {
36303
36428
  try {
36304
- const req = createRequire(join38(dir, "package.json"));
36429
+ const req = createRequire(join40(dir, "package.json"));
36305
36430
  req.resolve("@hyperframes/studio/package.json");
36306
36431
  return true;
36307
36432
  } catch {
@@ -36309,20 +36434,20 @@ function hasLocalStudio(dir) {
36309
36434
  }
36310
36435
  }
36311
36436
  async function runLocalStudioMode(dir, projectName) {
36312
- const req = createRequire(join38(dir, "package.json"));
36437
+ const req = createRequire(join40(dir, "package.json"));
36313
36438
  const studioPkgPath = dirname13(req.resolve("@hyperframes/studio/package.json"));
36314
36439
  const pName = projectName ?? basename4(dir);
36315
- const projectsDir = join38(studioPkgPath, "data", "projects");
36316
- const symlinkPath = join38(projectsDir, pName);
36317
- mkdirSync20(projectsDir, { recursive: true });
36440
+ const projectsDir = join40(studioPkgPath, "data", "projects");
36441
+ const symlinkPath = join40(projectsDir, pName);
36442
+ mkdirSync22(projectsDir, { recursive: true });
36318
36443
  let createdSymlink = false;
36319
36444
  if (dir !== symlinkPath) {
36320
- if (existsSync36(symlinkPath) && lstatSync(symlinkPath).isSymbolicLink()) {
36445
+ if (existsSync38(symlinkPath) && lstatSync(symlinkPath).isSymbolicLink()) {
36321
36446
  if (resolve21(readlinkSync(symlinkPath)) !== resolve21(dir)) {
36322
36447
  unlinkSync5(symlinkPath);
36323
36448
  }
36324
36449
  }
36325
- if (!existsSync36(symlinkPath)) {
36450
+ if (!existsSync38(symlinkPath)) {
36326
36451
  symlinkSync(dir, symlinkPath, "dir");
36327
36452
  createdSymlink = true;
36328
36453
  }
@@ -36330,7 +36455,7 @@ async function runLocalStudioMode(dir, projectName) {
36330
36455
  Wt2(c.bold("hyperframes preview") + c.dim(" (local studio)"));
36331
36456
  const s2 = be();
36332
36457
  s2.start("Starting studio...");
36333
- const child = spawn8("npx", ["vite"], {
36458
+ const child = spawn9("npx", ["vite"], {
36334
36459
  cwd: studioPkgPath,
36335
36460
  stdio: ["ignore", "pipe", "pipe"]
36336
36461
  });
@@ -36361,7 +36486,7 @@ async function runLocalStudioMode(dir, projectName) {
36361
36486
  if (createdSymlink) {
36362
36487
  process.on("exit", () => {
36363
36488
  try {
36364
- if (existsSync36(symlinkPath)) unlinkSync5(symlinkPath);
36489
+ if (existsSync38(symlinkPath)) unlinkSync5(symlinkPath);
36365
36490
  } catch {
36366
36491
  }
36367
36492
  });
@@ -36537,8 +36662,8 @@ var init_preview2 = __esm({
36537
36662
  const dir = resolve21(rawArg ?? ".");
36538
36663
  const isImplicitCwd = !rawArg || rawArg === "." || rawArg === "./";
36539
36664
  const projectName = isImplicitCwd ? basename4(process.env.PWD ?? dir) : basename4(dir);
36540
- const indexPath = join38(dir, "index.html");
36541
- if (existsSync36(indexPath)) {
36665
+ const indexPath = join40(dir, "index.html");
36666
+ if (existsSync38(indexPath)) {
36542
36667
  const project = { dir, name: projectName, indexPath };
36543
36668
  const lintResult = lintProject(project);
36544
36669
  if (lintResult.totalErrors > 0 || lintResult.totalWarnings > 0) {
@@ -36567,17 +36692,17 @@ __export(init_exports, {
36567
36692
  examples: () => examples2
36568
36693
  });
36569
36694
  import {
36570
- existsSync as existsSync37,
36571
- mkdirSync as mkdirSync21,
36695
+ existsSync as existsSync39,
36696
+ mkdirSync as mkdirSync23,
36572
36697
  copyFileSync as copyFileSync3,
36573
36698
  cpSync,
36574
- writeFileSync as writeFileSync15,
36575
- readFileSync as readFileSync25,
36699
+ writeFileSync as writeFileSync17,
36700
+ readFileSync as readFileSync26,
36576
36701
  readdirSync as readdirSync15
36577
36702
  } from "fs";
36578
- import { resolve as resolve22, basename as basename5, join as join39, dirname as dirname14 } from "path";
36703
+ import { resolve as resolve22, basename as basename5, join as join41, dirname as dirname14 } from "path";
36579
36704
  import { fileURLToPath as fileURLToPath5 } from "url";
36580
- import { execFileSync as execFileSync5, spawn as spawn9 } from "child_process";
36705
+ import { execFileSync as execFileSync5, spawn as spawn10 } from "child_process";
36581
36706
  function probeVideo(filePath) {
36582
36707
  try {
36583
36708
  const raw = execFileSync5(
@@ -36619,7 +36744,7 @@ function isWebCompatible(codec) {
36619
36744
  }
36620
36745
  function transcodeToMp4(inputPath, outputPath) {
36621
36746
  return new Promise((resolvePromise) => {
36622
- const child = spawn9(
36747
+ const child = spawn10(
36623
36748
  "ffmpeg",
36624
36749
  [
36625
36750
  "-i",
@@ -36647,7 +36772,7 @@ function resolveAssetDir(devSegments, builtSegments) {
36647
36772
  const base = dirname14(fileURLToPath5(import.meta.url));
36648
36773
  const devPath = resolve22(base, ...devSegments);
36649
36774
  const builtPath = resolve22(base, ...builtSegments);
36650
- return existsSync37(devPath) ? devPath : builtPath;
36775
+ return existsSync39(devPath) ? devPath : builtPath;
36651
36776
  }
36652
36777
  function getStaticTemplateDir(templateId) {
36653
36778
  return resolveAssetDir(["..", "templates", templateId], ["templates", templateId]);
@@ -36656,9 +36781,9 @@ function getSharedTemplateDir() {
36656
36781
  return resolveAssetDir(["..", "templates", "_shared"], ["templates", "_shared"]);
36657
36782
  }
36658
36783
  function patchVideoSrc(dir, videoFilename, durationSeconds) {
36659
- const htmlFiles = readdirSync15(dir, { withFileTypes: true, recursive: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".html")).map((e2) => join39(e2.parentPath ?? e2.path, e2.name));
36784
+ const htmlFiles = readdirSync15(dir, { withFileTypes: true, recursive: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".html")).map((e2) => join41(e2.parentPath ?? e2.path, e2.name));
36660
36785
  for (const file of htmlFiles) {
36661
- let content = readFileSync25(file, "utf-8");
36786
+ let content = readFileSync26(file, "utf-8");
36662
36787
  if (videoFilename) {
36663
36788
  content = content.replaceAll("__VIDEO_SRC__", videoFilename);
36664
36789
  } else {
@@ -36669,7 +36794,7 @@ function patchVideoSrc(dir, videoFilename, durationSeconds) {
36669
36794
  }
36670
36795
  const dur = durationSeconds ? String(Math.round(durationSeconds * 100) / 100) : "10";
36671
36796
  content = content.replaceAll("__VIDEO_DURATION__", dur);
36672
- writeFileSync15(file, content, "utf-8");
36797
+ writeFileSync17(file, content, "utf-8");
36673
36798
  }
36674
36799
  }
36675
36800
  async function patchTranscript(dir, transcriptPath) {
@@ -36761,15 +36886,15 @@ async function handleVideoFile(videoPath, destDir, interactive) {
36761
36886
  return { meta, localVideoName };
36762
36887
  }
36763
36888
  async function scaffoldProject(destDir, name, templateId, localVideoName, durationSeconds) {
36764
- mkdirSync21(destDir, { recursive: true });
36889
+ mkdirSync23(destDir, { recursive: true });
36765
36890
  const templateDir = getStaticTemplateDir(templateId);
36766
- if (existsSync37(templateDir)) {
36891
+ if (existsSync39(templateDir)) {
36767
36892
  cpSync(templateDir, destDir, { recursive: true });
36768
36893
  } else {
36769
36894
  await fetchRemoteTemplate(templateId, destDir);
36770
36895
  }
36771
36896
  patchVideoSrc(destDir, localVideoName, durationSeconds);
36772
- writeFileSync15(
36897
+ writeFileSync17(
36773
36898
  resolve22(destDir, "meta.json"),
36774
36899
  JSON.stringify(
36775
36900
  {
@@ -36782,14 +36907,14 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
36782
36907
  ),
36783
36908
  "utf-8"
36784
36909
  );
36785
- if (!existsSync37(resolve22(destDir, "hyperframes.json"))) {
36910
+ if (!existsSync39(resolve22(destDir, "hyperframes.json"))) {
36786
36911
  const { writeProjectConfig: writeProjectConfig2, DEFAULT_PROJECT_CONFIG: DEFAULT_PROJECT_CONFIG2 } = await Promise.resolve().then(() => (init_projectConfig(), projectConfig_exports));
36787
36912
  writeProjectConfig2(destDir, DEFAULT_PROJECT_CONFIG2);
36788
36913
  }
36789
36914
  const sharedDir = getSharedTemplateDir();
36790
- if (existsSync37(sharedDir)) {
36915
+ if (existsSync39(sharedDir)) {
36791
36916
  for (const entry of readdirSync15(sharedDir, { withFileTypes: true })) {
36792
- const src = join39(sharedDir, entry.name);
36917
+ const src = join41(sharedDir, entry.name);
36793
36918
  const dest = resolve22(destDir, entry.name);
36794
36919
  if (entry.isFile() || entry.isSymbolicLink()) {
36795
36920
  copyFileSync3(src, dest);
@@ -36903,11 +37028,11 @@ var init_init = __esm({
36903
37028
  const templateId2 = exampleFlag ?? "blank";
36904
37029
  const name2 = args.name ?? "my-video";
36905
37030
  const destDir2 = resolve22(name2);
36906
- if (existsSync37(destDir2) && readdirSync15(destDir2).length > 0) {
37031
+ if (existsSync39(destDir2) && readdirSync15(destDir2).length > 0) {
36907
37032
  console.error(c.error(`Directory already exists and is not empty: ${name2}`));
36908
37033
  process.exit(1);
36909
37034
  }
36910
- mkdirSync21(destDir2, { recursive: true });
37035
+ mkdirSync23(destDir2, { recursive: true });
36911
37036
  let localVideoName2;
36912
37037
  let videoDuration2;
36913
37038
  let sourceFilePath2;
@@ -36917,7 +37042,7 @@ var init_init = __esm({
36917
37042
  }
36918
37043
  if (videoFlag) {
36919
37044
  const videoPath = resolve22(videoFlag);
36920
- if (!existsSync37(videoPath)) {
37045
+ if (!existsSync39(videoPath)) {
36921
37046
  console.error(c.error(`Video file not found: ${videoFlag}`));
36922
37047
  process.exit(1);
36923
37048
  }
@@ -36931,7 +37056,7 @@ var init_init = __esm({
36931
37056
  }
36932
37057
  if (audioFlag) {
36933
37058
  const audioPath = resolve22(audioFlag);
36934
- if (!existsSync37(audioPath)) {
37059
+ if (!existsSync39(audioPath)) {
36935
37060
  console.error(c.error(`Audio file not found: ${audioFlag}`));
36936
37061
  process.exit(1);
36937
37062
  }
@@ -36977,7 +37102,7 @@ var init_init = __esm({
36977
37102
  }
36978
37103
  trackInitTemplate(templateId2);
36979
37104
  const transcriptFile2 = resolve22(destDir2, "transcript.json");
36980
- if (existsSync37(transcriptFile2)) {
37105
+ if (existsSync39(transcriptFile2)) {
36981
37106
  await patchTranscript(destDir2, transcriptFile2);
36982
37107
  }
36983
37108
  console.log(c.success(`Created ${c.accent(name2 + "/")}`));
@@ -37029,7 +37154,7 @@ var init_init = __esm({
37029
37154
  name = nameResult;
37030
37155
  }
37031
37156
  const destDir = resolve22(name);
37032
- if (existsSync37(destDir) && readdirSync15(destDir).length > 0) {
37157
+ if (existsSync39(destDir) && readdirSync15(destDir).length > 0) {
37033
37158
  const overwrite = await Rt({
37034
37159
  message: `Directory ${c.accent(name)} already exists and is not empty. Overwrite?`,
37035
37160
  initialValue: false
@@ -37044,24 +37169,24 @@ var init_init = __esm({
37044
37169
  let videoDuration;
37045
37170
  if (videoFlag) {
37046
37171
  const videoPath = resolve22(videoFlag);
37047
- if (!existsSync37(videoPath)) {
37172
+ if (!existsSync39(videoPath)) {
37048
37173
  R2.error(`File not found: ${videoFlag}`);
37049
37174
  Nt("Setup cancelled.");
37050
37175
  process.exit(1);
37051
37176
  }
37052
- mkdirSync21(destDir, { recursive: true });
37177
+ mkdirSync23(destDir, { recursive: true });
37053
37178
  sourceFilePath = videoPath;
37054
37179
  const result = await handleVideoFile(videoPath, destDir, true);
37055
37180
  localVideoName = result.localVideoName;
37056
37181
  videoDuration = result.meta.durationSeconds;
37057
37182
  } else if (audioFlag) {
37058
37183
  const audioPath = resolve22(audioFlag);
37059
- if (!existsSync37(audioPath)) {
37184
+ if (!existsSync39(audioPath)) {
37060
37185
  R2.error(`File not found: ${audioFlag}`);
37061
37186
  Nt("Setup cancelled.");
37062
37187
  process.exit(1);
37063
37188
  }
37064
- mkdirSync21(destDir, { recursive: true });
37189
+ mkdirSync23(destDir, { recursive: true });
37065
37190
  sourceFilePath = audioPath;
37066
37191
  copyFileSync3(audioPath, resolve22(destDir, basename5(audioPath)));
37067
37192
  R2.info(`Audio copied to ${c.accent(basename5(audioPath))}`);
@@ -37149,7 +37274,7 @@ ${c.dim("Use --example blank for offline use.")}`
37149
37274
  }
37150
37275
  trackInitTemplate(templateId);
37151
37276
  const transcriptFile = resolve22(destDir, "transcript.json");
37152
- if (existsSync37(transcriptFile)) {
37277
+ if (existsSync39(transcriptFile)) {
37153
37278
  await patchTranscript(destDir, transcriptFile);
37154
37279
  }
37155
37280
  const files = readdirSync15(destDir);
@@ -37235,7 +37360,7 @@ __export(add_exports, {
37235
37360
  remapTarget: () => remapTarget,
37236
37361
  runAdd: () => runAdd
37237
37362
  });
37238
- import { existsSync as existsSync38 } from "fs";
37363
+ import { existsSync as existsSync40 } from "fs";
37239
37364
  import { resolve as resolve23, relative as relative3 } from "path";
37240
37365
  function remapTarget(item, originalTarget, paths) {
37241
37366
  if (item.type === "hyperframes:block") {
@@ -37261,8 +37386,8 @@ function buildSnippet(item, relativeTarget) {
37261
37386
  async function runAdd(opts) {
37262
37387
  const projectDir = resolve23(opts.projectDir);
37263
37388
  let config = loadProjectConfig(projectDir);
37264
- const hasConfig = existsSync38(projectConfigPath(projectDir));
37265
- if (!hasConfig && existsSync38(resolve23(projectDir, "index.html"))) {
37389
+ const hasConfig = existsSync40(projectConfigPath(projectDir));
37390
+ if (!hasConfig && existsSync40(resolve23(projectDir, "index.html"))) {
37266
37391
  writeProjectConfig(projectDir, DEFAULT_PROJECT_CONFIG);
37267
37392
  config = DEFAULT_PROJECT_CONFIG;
37268
37393
  }
@@ -37361,10 +37486,10 @@ var init_add = __esm({
37361
37486
  const projectDir = resolve23(args.dir ?? process.cwd());
37362
37487
  const json = args.json === true;
37363
37488
  const skipClipboard = args["no-clipboard"] === true;
37364
- const hasConfigBefore = existsSync38(projectConfigPath(projectDir));
37489
+ const hasConfigBefore = existsSync40(projectConfigPath(projectDir));
37365
37490
  try {
37366
37491
  const result = await runAdd({ name: args.name, projectDir, skipClipboard });
37367
- const wroteConfig = !hasConfigBefore && existsSync38(projectConfigPath(projectDir));
37492
+ const wroteConfig = !hasConfigBefore && existsSync40(projectConfigPath(projectDir));
37368
37493
  if (json) {
37369
37494
  console.log(JSON.stringify(result));
37370
37495
  return;
@@ -37574,17 +37699,17 @@ var init_format = __esm({
37574
37699
  });
37575
37700
 
37576
37701
  // src/utils/project.ts
37577
- import { existsSync as existsSync39, statSync as statSync15 } from "fs";
37702
+ import { existsSync as existsSync41, statSync as statSync15 } from "fs";
37578
37703
  import { resolve as resolve25, basename as basename6 } from "path";
37579
37704
  function resolveProject(dirArg) {
37580
37705
  const dir = resolve25(dirArg ?? ".");
37581
37706
  const name = basename6(dir);
37582
37707
  const indexPath = resolve25(dir, "index.html");
37583
- if (!existsSync39(dir) || !statSync15(dir).isDirectory()) {
37708
+ if (!existsSync41(dir) || !statSync15(dir).isDirectory()) {
37584
37709
  errorBox("Not a directory: " + dir);
37585
37710
  process.exit(1);
37586
37711
  }
37587
- if (!existsSync39(indexPath)) {
37712
+ if (!existsSync41(indexPath)) {
37588
37713
  errorBox(
37589
37714
  "No composition found in " + dir,
37590
37715
  "No index.html file found.",
@@ -37607,7 +37732,7 @@ __export(play_exports, {
37607
37732
  default: () => play_default,
37608
37733
  examples: () => examples5
37609
37734
  });
37610
- import { existsSync as existsSync40, readFileSync as readFileSync26 } from "fs";
37735
+ import { existsSync as existsSync42, readFileSync as readFileSync27 } from "fs";
37611
37736
  import { resolve as resolve26, dirname as dirname15 } from "path";
37612
37737
  function commandDir() {
37613
37738
  return dirname15(new URL(import.meta.url).pathname);
@@ -37622,7 +37747,7 @@ function resolveRuntimePath2() {
37622
37747
  resolve26(d, "..", "..", "..", "core", "dist", "hyperframe.runtime.iife.js")
37623
37748
  ];
37624
37749
  for (const p of candidates) {
37625
- if (existsSync40(p)) return p;
37750
+ if (existsSync42(p)) return p;
37626
37751
  }
37627
37752
  return null;
37628
37753
  }
@@ -37636,7 +37761,7 @@ function resolvePlayerPath() {
37636
37761
  resolve26(d, "..", "hyperframes-player.global.js")
37637
37762
  ];
37638
37763
  for (const p of candidates) {
37639
- if (existsSync40(p)) return p;
37764
+ if (existsSync42(p)) return p;
37640
37765
  }
37641
37766
  return null;
37642
37767
  }
@@ -37723,13 +37848,13 @@ var init_play = __esm({
37723
37848
  const { createAdaptorServer } = await import("@hono/node-server");
37724
37849
  const app = new Hono6();
37725
37850
  app.get("/player.js", (ctx) => {
37726
- return ctx.body(readFileSync26(playerPath, "utf-8"), 200, {
37851
+ return ctx.body(readFileSync27(playerPath, "utf-8"), 200, {
37727
37852
  "Content-Type": "application/javascript",
37728
37853
  "Cache-Control": "no-cache"
37729
37854
  });
37730
37855
  });
37731
37856
  app.get("/runtime.js", (ctx) => {
37732
- return ctx.body(readFileSync26(runtimePath, "utf-8"), 200, {
37857
+ return ctx.body(readFileSync27(runtimePath, "utf-8"), 200, {
37733
37858
  "Content-Type": "application/javascript",
37734
37859
  "Cache-Control": "no-cache"
37735
37860
  });
@@ -37738,8 +37863,8 @@ var init_play = __esm({
37738
37863
  const reqPath = ctx.req.path.replace("/composition/", "");
37739
37864
  const filePath = resolve26(project.dir, reqPath);
37740
37865
  if (!filePath.startsWith(project.dir)) return ctx.text("Forbidden", 403);
37741
- if (!existsSync40(filePath)) return ctx.text("Not found", 404);
37742
- const content = readFileSync26(filePath, "utf-8");
37866
+ if (!existsSync42(filePath)) return ctx.text("Not found", 404);
37867
+ const content = readFileSync27(filePath, "utf-8");
37743
37868
  if (filePath.endsWith(".html")) {
37744
37869
  const injected = injectRuntime(content);
37745
37870
  return ctx.html(injected);
@@ -37758,7 +37883,7 @@ var init_play = __esm({
37758
37883
  mp3: "audio/mpeg",
37759
37884
  wav: "audio/wav"
37760
37885
  };
37761
- return ctx.body(readFileSync26(filePath), 200, {
37886
+ return ctx.body(readFileSync27(filePath), 200, {
37762
37887
  "Content-Type": types3[ext] ?? "application/octet-stream"
37763
37888
  });
37764
37889
  });
@@ -37814,16 +37939,99 @@ var init_play = __esm({
37814
37939
  });
37815
37940
 
37816
37941
  // src/utils/publishProject.ts
37817
- import { basename as basename7, join as join40, relative as relative4 } from "path";
37818
- import { readdirSync as readdirSync16, readFileSync as readFileSync27, statSync as statSync16 } from "fs";
37942
+ import { basename as basename7, join as join42, relative as relative4 } from "path";
37943
+ import { readdirSync as readdirSync16, readFileSync as readFileSync28, statSync as statSync16 } from "fs";
37819
37944
  import AdmZip from "adm-zip";
37945
+ function isRecord2(value) {
37946
+ return typeof value === "object" && value !== null && !Array.isArray(value);
37947
+ }
37948
+ function dataRecord(payload) {
37949
+ if (!isRecord2(payload) || !isRecord2(payload["data"])) return null;
37950
+ return payload["data"];
37951
+ }
37952
+ function stringField(record, key2) {
37953
+ const value = record[key2];
37954
+ return typeof value === "string" ? value : null;
37955
+ }
37956
+ function parsePublishedProjectResponse(payload) {
37957
+ const data = dataRecord(payload);
37958
+ if (!data) return null;
37959
+ const projectId = stringField(data, "project_id");
37960
+ const title = stringField(data, "title");
37961
+ const url = stringField(data, "url");
37962
+ const claimToken = stringField(data, "claim_token");
37963
+ const fileCount = data["file_count"];
37964
+ if (!projectId || !title || !url || !claimToken || typeof fileCount !== "number") {
37965
+ return null;
37966
+ }
37967
+ return {
37968
+ projectId,
37969
+ title,
37970
+ fileCount,
37971
+ url,
37972
+ claimToken
37973
+ };
37974
+ }
37975
+ function parseStagedUploadResponse(payload, archiveByteLength) {
37976
+ const data = dataRecord(payload);
37977
+ if (!data) return null;
37978
+ const uploadUrl = stringField(data, "upload_url");
37979
+ const uploadKey = stringField(data, "upload_key");
37980
+ const contentType = stringField(data, "content_type") || PUBLISH_CONTENT_TYPE;
37981
+ if (!uploadUrl || !uploadKey) return null;
37982
+ return {
37983
+ uploadUrl,
37984
+ uploadKey,
37985
+ contentType,
37986
+ uploadHeaders: getUploadHeaders(data, uploadUrl, contentType, archiveByteLength)
37987
+ };
37988
+ }
37989
+ function getUploadHeaders(data, uploadUrl, contentType, archiveByteLength) {
37990
+ const headers = {};
37991
+ const uploadHeaders = data["upload_headers"];
37992
+ if (isRecord2(uploadHeaders)) {
37993
+ for (const [key2, value] of Object.entries(uploadHeaders)) {
37994
+ if (typeof value === "string" && key2.trim()) {
37995
+ headers[key2] = value;
37996
+ }
37997
+ }
37998
+ }
37999
+ if (!Object.keys(headers).some((key2) => key2.toLowerCase() === "content-type")) {
38000
+ headers["content-type"] = contentType;
38001
+ }
38002
+ const signedHeaders = new URL(uploadUrl).searchParams.get("X-Amz-SignedHeaders");
38003
+ if (signedHeaders?.split(";").includes("x-amz-server-side-encryption") && !Object.keys(headers).some((key2) => key2.toLowerCase() === "x-amz-server-side-encryption")) {
38004
+ headers["x-amz-server-side-encryption"] = "AES256";
38005
+ }
38006
+ if (signedHeaders?.split(";").includes("content-length") && !Object.keys(headers).some((key2) => key2.toLowerCase() === "content-length")) {
38007
+ headers["content-length"] = String(archiveByteLength);
38008
+ }
38009
+ return headers;
38010
+ }
38011
+ async function readJson(response) {
38012
+ return response.clone().json().catch(() => null);
38013
+ }
38014
+ async function readErrorMessage(response, fallback) {
38015
+ const contentType = response.headers.get("content-type") || "";
38016
+ if (contentType.includes("application/json")) {
38017
+ const payload = await readJson(response);
38018
+ if (isRecord2(payload) && typeof payload["message"] === "string") {
38019
+ return payload["message"];
38020
+ }
38021
+ }
38022
+ if (response.status === 403 && response.headers.get("cf-mitigated") === "challenge") {
38023
+ return "Publish upload was blocked before reaching HyperFrames. Please retry after staged uploads are available.";
38024
+ }
38025
+ const text = await response.text().catch(() => "");
38026
+ return text.trim() ? `${fallback}: ${text.trim().slice(0, 180)}` : fallback;
38027
+ }
37820
38028
  function shouldIgnoreSegment(segment) {
37821
38029
  return segment.startsWith(".") || IGNORED_DIRS.has(segment) || IGNORED_FILES.has(segment);
37822
38030
  }
37823
38031
  function collectProjectFiles(rootDir, currentDir, paths) {
37824
38032
  for (const entry of readdirSync16(currentDir, { withFileTypes: true })) {
37825
38033
  if (shouldIgnoreSegment(entry.name)) continue;
37826
- const absolutePath = join40(currentDir, entry.name);
38034
+ const absolutePath = join42(currentDir, entry.name);
37827
38035
  const relativePath = relative4(rootDir, absolutePath).replaceAll("\\", "/");
37828
38036
  if (!relativePath) continue;
37829
38037
  if (entry.isDirectory()) {
@@ -37842,7 +38050,7 @@ function createPublishArchive(projectDir) {
37842
38050
  }
37843
38051
  const archive = new AdmZip();
37844
38052
  for (const filePath of filePaths) {
37845
- archive.addFile(filePath, readFileSync27(join40(projectDir, filePath)));
38053
+ archive.addFile(filePath, readFileSync28(join42(projectDir, filePath)));
37846
38054
  }
37847
38055
  return {
37848
38056
  buffer: archive.toBuffer(),
@@ -37852,42 +38060,102 @@ function createPublishArchive(projectDir) {
37852
38060
  function getPublishApiBaseUrl() {
37853
38061
  return (process.env["HYPERFRAMES_PUBLISHED_PROJECTS_API_URL"] || process.env["HEYGEN_API_URL"] || "https://api2.heygen.com").replace(/\/$/, "");
37854
38062
  }
37855
- async function publishProjectArchive(projectDir) {
37856
- const title = basename7(projectDir);
37857
- const archive = createPublishArchive(projectDir);
37858
- const archiveBytes = new Uint8Array(archive.buffer.byteLength);
37859
- archiveBytes.set(archive.buffer);
38063
+ function archiveArrayBuffer(archive) {
38064
+ const arrayBuffer = new ArrayBuffer(archive.buffer.byteLength);
38065
+ new Uint8Array(arrayBuffer).set(archive.buffer);
38066
+ return arrayBuffer;
38067
+ }
38068
+ async function publishProjectArchiveDirect(apiBaseUrl, title, archive) {
37860
38069
  const body = new FormData();
37861
38070
  body.set("title", title);
37862
- body.set("file", new File([archiveBytes], `${title}.zip`, { type: "application/zip" }));
38071
+ body.set(
38072
+ "file",
38073
+ new File([archiveArrayBuffer(archive)], `${title}.zip`, { type: PUBLISH_CONTENT_TYPE })
38074
+ );
37863
38075
  const headers = {
37864
38076
  heygen_route: "canary"
37865
38077
  };
37866
- const response = await fetch(`${getPublishApiBaseUrl()}/v1/hyperframes/projects/publish`, {
38078
+ const response = await fetch(`${apiBaseUrl}/v1/hyperframes/projects/publish`, {
37867
38079
  method: "POST",
37868
38080
  body,
37869
38081
  headers,
37870
- signal: AbortSignal.timeout(3e4)
38082
+ signal: AbortSignal.timeout(PUBLISH_REQUEST_TIMEOUT_MS)
37871
38083
  });
37872
- const payload = await response.json().catch(() => null);
37873
- const message = typeof payload?.message === "string" ? payload.message : "Failed to publish project";
37874
- if (!response.ok || !payload?.data) {
37875
- throw new Error(message);
38084
+ const payload = await readJson(response);
38085
+ const publishedProject = parsePublishedProjectResponse(payload);
38086
+ if (!response.ok || !publishedProject) {
38087
+ throw new Error(await readErrorMessage(response, "Failed to publish project"));
37876
38088
  }
37877
- return {
37878
- projectId: String(payload.data.project_id),
37879
- title: String(payload.data.title),
37880
- fileCount: Number(payload.data.file_count),
37881
- url: String(payload.data.url),
37882
- claimToken: String(payload.data.claim_token)
37883
- };
38089
+ return publishedProject;
38090
+ }
38091
+ async function publishProjectArchiveStaged(apiBaseUrl, title, archive) {
38092
+ const fileName = `${title}.zip`;
38093
+ const uploadResponse = await fetch(`${apiBaseUrl}/v1/hyperframes/projects/publish/upload`, {
38094
+ method: "POST",
38095
+ body: JSON.stringify({
38096
+ file_name: fileName,
38097
+ content_type: PUBLISH_CONTENT_TYPE,
38098
+ content_length: archive.buffer.byteLength
38099
+ }),
38100
+ headers: {
38101
+ "content-type": "application/json",
38102
+ heygen_route: "canary"
38103
+ },
38104
+ signal: AbortSignal.timeout(PUBLISH_REQUEST_TIMEOUT_MS)
38105
+ });
38106
+ if (uploadResponse.status === 404 || uploadResponse.status === 405) {
38107
+ return null;
38108
+ }
38109
+ const uploadPayload = await readJson(uploadResponse);
38110
+ const stagedUpload = parseStagedUploadResponse(uploadPayload, archive.buffer.byteLength);
38111
+ if (!uploadResponse.ok || !stagedUpload) {
38112
+ throw new Error(await readErrorMessage(uploadResponse, "Failed to prepare project upload"));
38113
+ }
38114
+ const s3Response = await fetch(stagedUpload.uploadUrl, {
38115
+ method: "PUT",
38116
+ body: new Blob([archiveArrayBuffer(archive)], { type: stagedUpload.contentType }),
38117
+ headers: stagedUpload.uploadHeaders,
38118
+ signal: AbortSignal.timeout(PUBLISH_REQUEST_TIMEOUT_MS)
38119
+ });
38120
+ if (!s3Response.ok) {
38121
+ throw new Error(await readErrorMessage(s3Response, "Failed to upload project archive"));
38122
+ }
38123
+ const completeResponse = await fetch(`${apiBaseUrl}/v1/hyperframes/projects/publish/complete`, {
38124
+ method: "POST",
38125
+ body: JSON.stringify({
38126
+ upload_key: stagedUpload.uploadKey,
38127
+ file_name: fileName,
38128
+ title
38129
+ }),
38130
+ headers: {
38131
+ "content-type": "application/json",
38132
+ heygen_route: "canary"
38133
+ },
38134
+ signal: AbortSignal.timeout(PUBLISH_REQUEST_TIMEOUT_MS)
38135
+ });
38136
+ const completePayload = await readJson(completeResponse);
38137
+ const publishedProject = parsePublishedProjectResponse(completePayload);
38138
+ if (!completeResponse.ok || !publishedProject) {
38139
+ throw new Error(await readErrorMessage(completeResponse, "Failed to publish project"));
38140
+ }
38141
+ return publishedProject;
38142
+ }
38143
+ async function publishProjectArchive(projectDir) {
38144
+ const title = basename7(projectDir);
38145
+ const archive = createPublishArchive(projectDir);
38146
+ const apiBaseUrl = getPublishApiBaseUrl();
38147
+ const stagedResult = await publishProjectArchiveStaged(apiBaseUrl, title, archive);
38148
+ if (stagedResult) return stagedResult;
38149
+ return publishProjectArchiveDirect(apiBaseUrl, title, archive);
37884
38150
  }
37885
- var IGNORED_DIRS, IGNORED_FILES;
38151
+ var IGNORED_DIRS, IGNORED_FILES, PUBLISH_CONTENT_TYPE, PUBLISH_REQUEST_TIMEOUT_MS;
37886
38152
  var init_publishProject = __esm({
37887
38153
  "src/utils/publishProject.ts"() {
37888
38154
  "use strict";
37889
38155
  IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".next", "coverage"]);
37890
38156
  IGNORED_FILES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db"]);
38157
+ PUBLISH_CONTENT_TYPE = "application/zip";
38158
+ PUBLISH_REQUEST_TIMEOUT_MS = 3e4;
37891
38159
  }
37892
38160
  });
37893
38161
 
@@ -37898,8 +38166,8 @@ __export(publish_exports, {
37898
38166
  examples: () => examples6
37899
38167
  });
37900
38168
  import { basename as basename8, resolve as resolve27 } from "path";
37901
- import { existsSync as existsSync41 } from "fs";
37902
- import { join as join41 } from "path";
38169
+ import { existsSync as existsSync43 } from "fs";
38170
+ import { join as join43 } from "path";
37903
38171
  var examples6, publish_default;
37904
38172
  var init_publish = __esm({
37905
38173
  "src/commands/publish.ts"() {
@@ -37934,8 +38202,8 @@ var init_publish = __esm({
37934
38202
  const dir = resolve27(rawArg ?? ".");
37935
38203
  const isImplicitCwd = !rawArg || rawArg === "." || rawArg === "./";
37936
38204
  const projectName = isImplicitCwd ? basename8(process.env["PWD"] ?? dir) : basename8(dir);
37937
- const indexPath = join41(dir, "index.html");
37938
- if (existsSync41(indexPath)) {
38205
+ const indexPath = join43(dir, "index.html");
38206
+ if (existsSync43(indexPath)) {
37939
38207
  const lintResult = lintProject({ dir, name: projectName, indexPath });
37940
38208
  if (lintResult.totalErrors > 0 || lintResult.totalWarnings > 0) {
37941
38209
  console.log();
@@ -38106,10 +38374,10 @@ __export(render_exports, {
38106
38374
  default: () => render_default,
38107
38375
  examples: () => examples7
38108
38376
  });
38109
- import { mkdirSync as mkdirSync22, readFileSync as readFileSync28, statSync as statSync17, writeFileSync as writeFileSync16, rmSync as rmSync9 } from "fs";
38377
+ import { mkdirSync as mkdirSync24, readFileSync as readFileSync29, statSync as statSync17, writeFileSync as writeFileSync18, rmSync as rmSync9 } from "fs";
38110
38378
  import { cpus as cpus3, freemem as freemem3, tmpdir as tmpdir4 } from "os";
38111
- import { resolve as resolve28, dirname as dirname16, join as join42, basename as basename9 } from "path";
38112
- import { execFileSync as execFileSync6, spawn as spawn10 } from "child_process";
38379
+ import { resolve as resolve28, dirname as dirname16, join as join44, basename as basename9 } from "path";
38380
+ import { execFileSync as execFileSync6, spawn as spawn11 } from "child_process";
38113
38381
  function defaultWorkerCount() {
38114
38382
  return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT * 3 / 4), 8));
38115
38383
  }
@@ -38145,9 +38413,9 @@ function ensureDockerImage(version, quiet) {
38145
38413
  }
38146
38414
  if (!quiet) console.log(c.dim(` Building Docker image: ${tag}...`));
38147
38415
  const dockerfilePath = resolveDockerfilePath();
38148
- const tmpDir = join42(tmpdir4(), `hyperframes-docker-${Date.now()}`);
38149
- mkdirSync22(tmpDir, { recursive: true });
38150
- writeFileSync16(join42(tmpDir, "Dockerfile"), readFileSync28(dockerfilePath));
38416
+ const tmpDir = join44(tmpdir4(), `hyperframes-docker-${Date.now()}`);
38417
+ mkdirSync24(tmpDir, { recursive: true });
38418
+ writeFileSync18(join44(tmpDir, "Dockerfile"), readFileSync29(dockerfilePath));
38151
38419
  try {
38152
38420
  execFileSync6(
38153
38421
  "docker",
@@ -38216,7 +38484,7 @@ async function renderDocker(projectDir, outputPath, options) {
38216
38484
  }
38217
38485
  try {
38218
38486
  await new Promise((resolvePromise, reject) => {
38219
- const child = spawn10("docker", dockerArgs, {
38487
+ const child = spawn11("docker", dockerArgs, {
38220
38488
  // When quiet, still show stderr so container errors surface
38221
38489
  stdio: options.quiet ? ["pipe", "pipe", "inherit"] : "inherit"
38222
38490
  });
@@ -38495,8 +38763,8 @@ var init_render2 = __esm({
38495
38763
  const now = /* @__PURE__ */ new Date();
38496
38764
  const datePart = now.toISOString().slice(0, 10);
38497
38765
  const timePart = now.toTimeString().slice(0, 8).replace(/:/g, "-");
38498
- const outputPath = args.output ? resolve28(args.output) : join42(rendersDir, `${project.name}_${datePart}_${timePart}${ext}`);
38499
- mkdirSync22(dirname16(outputPath), { recursive: true });
38766
+ const outputPath = args.output ? resolve28(args.output) : join44(rendersDir, `${project.name}_${datePart}_${timePart}${ext}`);
38767
+ mkdirSync24(dirname16(outputPath), { recursive: true });
38500
38768
  const useDocker = args.docker ?? false;
38501
38769
  const useGpu = args.gpu ?? false;
38502
38770
  const quiet = args.quiet ?? false;
@@ -38971,8 +39239,8 @@ __export(layout_exports, {
38971
39239
  examples: () => examples9
38972
39240
  });
38973
39241
  import { createServer } from "http";
38974
- import { existsSync as existsSync42, readFileSync as readFileSync29 } from "fs";
38975
- import { dirname as dirname17, isAbsolute as isAbsolute6, join as join43, relative as relative5, resolve as resolve29 } from "path";
39242
+ import { existsSync as existsSync44, readFileSync as readFileSync30 } from "fs";
39243
+ import { dirname as dirname17, isAbsolute as isAbsolute6, join as join45, relative as relative5, resolve as resolve29 } from "path";
38976
39244
  import { fileURLToPath as fileURLToPath6 } from "url";
38977
39245
  async function getCompositionDuration2(page) {
38978
39246
  return page.evaluate(() => {
@@ -39042,8 +39310,8 @@ async function bundleProjectHtml(projectDir) {
39042
39310
  "dist",
39043
39311
  "hyperframe.runtime.iife.js"
39044
39312
  );
39045
- if (existsSync42(runtimePath)) {
39046
- const runtimeSource = readFileSync29(runtimePath, "utf-8");
39313
+ if (existsSync44(runtimePath)) {
39314
+ const runtimeSource = readFileSync30(runtimePath, "utf-8");
39047
39315
  html = html.replace(
39048
39316
  /<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
39049
39317
  () => `<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
@@ -39067,9 +39335,9 @@ async function serveProject(projectDir, html) {
39067
39335
  res.end();
39068
39336
  return;
39069
39337
  }
39070
- if (existsSync42(filePath)) {
39338
+ if (existsSync44(filePath)) {
39071
39339
  res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
39072
- res.end(readFileSync29(filePath));
39340
+ res.end(readFileSync30(filePath));
39073
39341
  return;
39074
39342
  }
39075
39343
  res.writeHead(404);
@@ -39171,11 +39439,11 @@ async function runLayoutAudit(projectDir, opts) {
39171
39439
  }
39172
39440
  function loadLayoutAuditScript() {
39173
39441
  const candidates = [
39174
- join43(__dirname2, "layout-audit.browser.js"),
39175
- join43(__dirname2, "commands", "layout-audit.browser.js")
39442
+ join45(__dirname2, "layout-audit.browser.js"),
39443
+ join45(__dirname2, "commands", "layout-audit.browser.js")
39176
39444
  ];
39177
39445
  for (const candidate of candidates) {
39178
- if (existsSync42(candidate)) return readFileSync29(candidate, "utf-8");
39446
+ if (existsSync44(candidate)) return readFileSync30(candidate, "utf-8");
39179
39447
  }
39180
39448
  throw new Error("Missing layout audit browser script");
39181
39449
  }
@@ -39395,12 +39663,12 @@ __export(info_exports, {
39395
39663
  default: () => info_default,
39396
39664
  examples: () => examples11
39397
39665
  });
39398
- import { readFileSync as readFileSync30, readdirSync as readdirSync17, statSync as statSync18 } from "fs";
39399
- import { join as join44 } from "path";
39666
+ import { readFileSync as readFileSync31, readdirSync as readdirSync17, statSync as statSync18 } from "fs";
39667
+ import { join as join46 } from "path";
39400
39668
  function totalSize(dir) {
39401
39669
  let total = 0;
39402
39670
  for (const entry of readdirSync17(dir, { withFileTypes: true })) {
39403
- const path2 = join44(dir, entry.name);
39671
+ const path2 = join46(dir, entry.name);
39404
39672
  if (entry.isDirectory()) {
39405
39673
  total += totalSize(path2);
39406
39674
  } else {
@@ -39432,7 +39700,7 @@ var init_info = __esm({
39432
39700
  },
39433
39701
  async run({ args }) {
39434
39702
  const project = resolveProject(args.dir);
39435
- const html = readFileSync30(project.indexPath, "utf-8");
39703
+ const html = readFileSync31(project.indexPath, "utf-8");
39436
39704
  ensureDOMParser();
39437
39705
  const parsed = parseHtml(html);
39438
39706
  const tracks = new Set(parsed.elements.map((el) => el.zIndex));
@@ -39488,7 +39756,7 @@ __export(compositions_exports, {
39488
39756
  default: () => compositions_default,
39489
39757
  examples: () => examples12
39490
39758
  });
39491
- import { existsSync as existsSync43, readFileSync as readFileSync31 } from "fs";
39759
+ import { existsSync as existsSync45, readFileSync as readFileSync32 } from "fs";
39492
39760
  import { resolve as resolve30, dirname as dirname18 } from "path";
39493
39761
  function parseCompositions(html, baseDir) {
39494
39762
  const parser = new DOMParser();
@@ -39502,8 +39770,8 @@ function parseCompositions(html, baseDir) {
39502
39770
  const compositionSrc = div.getAttribute("data-composition-src");
39503
39771
  if (compositionSrc) {
39504
39772
  const subPath = resolve30(baseDir, compositionSrc);
39505
- if (existsSync43(subPath)) {
39506
- const subHtml = readFileSync31(subPath, "utf-8");
39773
+ if (existsSync45(subPath)) {
39774
+ const subHtml = readFileSync32(subPath, "utf-8");
39507
39775
  const subInfo = parseSubComposition(subHtml, id, width, height);
39508
39776
  compositions.push({ ...subInfo, source: compositionSrc });
39509
39777
  return;
@@ -39597,7 +39865,7 @@ var init_compositions = __esm({
39597
39865
  },
39598
39866
  async run({ args }) {
39599
39867
  const project = resolveProject(args.dir);
39600
- const html = readFileSync31(project.indexPath, "utf-8");
39868
+ const html = readFileSync32(project.indexPath, "utf-8");
39601
39869
  ensureDOMParser();
39602
39870
  const compositions = parseCompositions(html, dirname18(project.indexPath));
39603
39871
  if (compositions.length === 0) {
@@ -39635,8 +39903,8 @@ __export(benchmark_exports, {
39635
39903
  default: () => benchmark_default,
39636
39904
  examples: () => examples13
39637
39905
  });
39638
- import { existsSync as existsSync44, statSync as statSync19 } from "fs";
39639
- import { resolve as resolve31, join as join45 } from "path";
39906
+ import { existsSync as existsSync46, statSync as statSync19 } from "fs";
39907
+ import { resolve as resolve31, join as join47 } from "path";
39640
39908
  var examples13, DEFAULT_CONFIGS, benchmark_default;
39641
39909
  var init_benchmark = __esm({
39642
39910
  "src/commands/benchmark.ts"() {
@@ -39711,7 +39979,7 @@ var init_benchmark = __esm({
39711
39979
  s2?.start(`Benchmarking ${config.label}...`);
39712
39980
  for (let i2 = 0; i2 < runsPerConfig; i2++) {
39713
39981
  s2?.message(`${config.label} \u2014 run ${i2 + 1}/${runsPerConfig}`);
39714
- const outputPath = join45(
39982
+ const outputPath = join47(
39715
39983
  benchDir,
39716
39984
  `${config.label.replace(/[^a-zA-Z0-9]/g, "_")}_run${i2}.mp4`
39717
39985
  );
@@ -39725,7 +39993,7 @@ var init_benchmark = __esm({
39725
39993
  await producer.executeRenderJob(job, project.dir, outputPath);
39726
39994
  const elapsedMs = Date.now() - startTime;
39727
39995
  let fileSize = null;
39728
- if (existsSync44(outputPath)) {
39996
+ if (existsSync46(outputPath)) {
39729
39997
  const stat3 = statSync19(outputPath);
39730
39998
  fileSize = stat3.size;
39731
39999
  }
@@ -39941,8 +40209,8 @@ __export(transcribe_exports2, {
39941
40209
  default: () => transcribe_default,
39942
40210
  examples: () => examples15
39943
40211
  });
39944
- import { existsSync as existsSync45, writeFileSync as writeFileSync17 } from "fs";
39945
- import { resolve as resolve32, join as join46, extname as extname8 } from "path";
40212
+ import { existsSync as existsSync47, writeFileSync as writeFileSync19 } from "fs";
40213
+ import { resolve as resolve32, join as join48, extname as extname8 } from "path";
39946
40214
  async function importTranscript(inputPath, dir, json) {
39947
40215
  const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
39948
40216
  const { words, format } = loadTranscript2(inputPath);
@@ -39950,8 +40218,8 @@ async function importTranscript(inputPath, dir, json) {
39950
40218
  console.error(c.error("No words found in transcript."));
39951
40219
  process.exit(1);
39952
40220
  }
39953
- const outPath = join46(dir, "transcript.json");
39954
- writeFileSync17(outPath, JSON.stringify(words, null, 2));
40221
+ const outPath = join48(dir, "transcript.json");
40222
+ writeFileSync19(outPath, JSON.stringify(words, null, 2));
39955
40223
  patchCaptionHtml2(dir, words);
39956
40224
  if (json) {
39957
40225
  console.log(
@@ -39986,7 +40254,7 @@ async function transcribeAudio(inputPath, dir, opts) {
39986
40254
  );
39987
40255
  }
39988
40256
  }
39989
- writeFileSync17(result.transcriptPath, JSON.stringify(words, null, 2));
40257
+ writeFileSync19(result.transcriptPath, JSON.stringify(words, null, 2));
39990
40258
  patchCaptionHtml2(dir, words);
39991
40259
  if (opts.json) {
39992
40260
  console.log(
@@ -40067,7 +40335,7 @@ var init_transcribe2 = __esm({
40067
40335
  },
40068
40336
  async run({ args }) {
40069
40337
  const inputPath = resolve32(args.input);
40070
- if (!existsSync45(inputPath)) {
40338
+ if (!existsSync47(inputPath)) {
40071
40339
  console.error(c.error(`File not found: ${args.input}`));
40072
40340
  process.exit(1);
40073
40341
  }
@@ -40088,9 +40356,9 @@ var init_transcribe2 = __esm({
40088
40356
  });
40089
40357
 
40090
40358
  // src/tts/manager.ts
40091
- import { existsSync as existsSync46, mkdirSync as mkdirSync23 } from "fs";
40359
+ import { existsSync as existsSync48, mkdirSync as mkdirSync25 } from "fs";
40092
40360
  import { homedir as homedir9 } from "os";
40093
- import { join as join47 } from "path";
40361
+ import { join as join49 } from "path";
40094
40362
  function inferLangFromVoiceId(voiceId) {
40095
40363
  const first = voiceId.charAt(0).toLowerCase();
40096
40364
  return VOICE_PREFIX_LANG[first] ?? "en-us";
@@ -40099,29 +40367,29 @@ function isSupportedLang(value) {
40099
40367
  return SUPPORTED_LANGS.includes(value);
40100
40368
  }
40101
40369
  async function ensureModel2(model = DEFAULT_MODEL2, options) {
40102
- const modelPath = join47(MODELS_DIR2, `${model}.onnx`);
40103
- if (existsSync46(modelPath)) return modelPath;
40370
+ const modelPath = join49(MODELS_DIR2, `${model}.onnx`);
40371
+ if (existsSync48(modelPath)) return modelPath;
40104
40372
  const url = MODEL_URLS[model];
40105
40373
  if (!url) {
40106
40374
  throw new Error(
40107
40375
  `Unknown TTS model: ${model}. Available: ${Object.keys(MODEL_URLS).join(", ")}`
40108
40376
  );
40109
40377
  }
40110
- mkdirSync23(MODELS_DIR2, { recursive: true });
40378
+ mkdirSync25(MODELS_DIR2, { recursive: true });
40111
40379
  options?.onProgress?.(`Downloading TTS model ${model} (~311 MB)...`);
40112
40380
  await downloadFile(url, modelPath);
40113
- if (!existsSync46(modelPath)) {
40381
+ if (!existsSync48(modelPath)) {
40114
40382
  throw new Error(`Model download failed: ${model}`);
40115
40383
  }
40116
40384
  return modelPath;
40117
40385
  }
40118
40386
  async function ensureVoices(options) {
40119
- const voicesPath = join47(VOICES_DIR, "voices-v1.0.bin");
40120
- if (existsSync46(voicesPath)) return voicesPath;
40121
- mkdirSync23(VOICES_DIR, { recursive: true });
40387
+ const voicesPath = join49(VOICES_DIR, "voices-v1.0.bin");
40388
+ if (existsSync48(voicesPath)) return voicesPath;
40389
+ mkdirSync25(VOICES_DIR, { recursive: true });
40122
40390
  options?.onProgress?.("Downloading voice data (~27 MB)...");
40123
40391
  await downloadFile(VOICES_URL, voicesPath);
40124
- if (!existsSync46(voicesPath)) {
40392
+ if (!existsSync48(voicesPath)) {
40125
40393
  throw new Error("Voice data download failed");
40126
40394
  }
40127
40395
  return voicesPath;
@@ -40131,9 +40399,9 @@ var init_manager3 = __esm({
40131
40399
  "src/tts/manager.ts"() {
40132
40400
  "use strict";
40133
40401
  init_download();
40134
- CACHE_DIR3 = join47(homedir9(), ".cache", "hyperframes", "tts");
40135
- MODELS_DIR2 = join47(CACHE_DIR3, "models");
40136
- VOICES_DIR = join47(CACHE_DIR3, "voices");
40402
+ CACHE_DIR3 = join49(homedir9(), ".cache", "hyperframes", "tts");
40403
+ MODELS_DIR2 = join49(CACHE_DIR3, "models");
40404
+ VOICES_DIR = join49(CACHE_DIR3, "voices");
40137
40405
  DEFAULT_MODEL2 = "kokoro-v1.0";
40138
40406
  MODEL_URLS = {
40139
40407
  "kokoro-v1.0": "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx"
@@ -40194,8 +40462,8 @@ __export(synthesize_exports, {
40194
40462
  synthesize: () => synthesize
40195
40463
  });
40196
40464
  import { execFileSync as execFileSync7 } from "child_process";
40197
- import { existsSync as existsSync47, writeFileSync as writeFileSync18, mkdirSync as mkdirSync24, readdirSync as readdirSync18, unlinkSync as unlinkSync6 } from "fs";
40198
- import { join as join48, dirname as dirname19, basename as basename10 } from "path";
40465
+ import { existsSync as existsSync49, writeFileSync as writeFileSync20, mkdirSync as mkdirSync26, readdirSync as readdirSync18, unlinkSync as unlinkSync6 } from "fs";
40466
+ import { join as join50, dirname as dirname19, basename as basename10 } from "path";
40199
40467
  import { homedir as homedir10 } from "os";
40200
40468
  function findPython() {
40201
40469
  for (const name of ["python3", "python"]) {
@@ -40231,15 +40499,15 @@ function hasPythonPackage(python, pkg) {
40231
40499
  }
40232
40500
  }
40233
40501
  function ensureSynthScript() {
40234
- if (!existsSync47(SCRIPT_PATH)) {
40235
- mkdirSync24(SCRIPT_DIR, { recursive: true });
40236
- writeFileSync18(SCRIPT_PATH, SYNTH_SCRIPT);
40502
+ if (!existsSync49(SCRIPT_PATH)) {
40503
+ mkdirSync26(SCRIPT_DIR, { recursive: true });
40504
+ writeFileSync20(SCRIPT_PATH, SYNTH_SCRIPT);
40237
40505
  const currentName = basename10(SCRIPT_PATH);
40238
40506
  try {
40239
40507
  for (const entry of readdirSync18(SCRIPT_DIR)) {
40240
40508
  if (entry !== currentName && /^synth(-v\d+)?\.py$/.test(entry)) {
40241
40509
  try {
40242
- unlinkSync6(join48(SCRIPT_DIR, entry));
40510
+ unlinkSync6(join50(SCRIPT_DIR, entry));
40243
40511
  } catch {
40244
40512
  }
40245
40513
  }
@@ -40273,7 +40541,7 @@ async function synthesize(text, outputPath, options) {
40273
40541
  ensureVoices({ onProgress: options?.onProgress })
40274
40542
  ]);
40275
40543
  const scriptPath = ensureSynthScript();
40276
- mkdirSync24(dirname19(outputPath), { recursive: true });
40544
+ mkdirSync26(dirname19(outputPath), { recursive: true });
40277
40545
  options?.onProgress?.(`Generating speech with voice ${voice} (${lang})...`);
40278
40546
  try {
40279
40547
  const stdout2 = execFileSync7(
@@ -40285,7 +40553,7 @@ async function synthesize(text, outputPath, options) {
40285
40553
  stdio: ["pipe", "pipe", "pipe"]
40286
40554
  }
40287
40555
  );
40288
- if (!existsSync47(outputPath)) {
40556
+ if (!existsSync49(outputPath)) {
40289
40557
  throw new Error("Synthesis completed but no output file was created");
40290
40558
  }
40291
40559
  const lines = stdout2.trim().split("\n");
@@ -40298,7 +40566,7 @@ async function synthesize(text, outputPath, options) {
40298
40566
  langApplied: result.langApplied
40299
40567
  };
40300
40568
  } catch (err) {
40301
- if (err instanceof SyntaxError && existsSync47(outputPath)) {
40569
+ if (err instanceof SyntaxError && existsSync49(outputPath)) {
40302
40570
  throw new Error(
40303
40571
  "Speech was generated but metadata could not be read. Check the output file manually."
40304
40572
  );
@@ -40349,8 +40617,8 @@ print(json.dumps({
40349
40617
  "langApplied": bool(lang and supports_lang),
40350
40618
  }))
40351
40619
  `;
40352
- SCRIPT_DIR = join48(homedir10(), ".cache", "hyperframes", "tts");
40353
- SCRIPT_PATH = join48(SCRIPT_DIR, "synth-v2.py");
40620
+ SCRIPT_DIR = join50(homedir10(), ".cache", "hyperframes", "tts");
40621
+ SCRIPT_PATH = join50(SCRIPT_DIR, "synth-v2.py");
40354
40622
  }
40355
40623
  });
40356
40624
 
@@ -40360,7 +40628,7 @@ __export(tts_exports, {
40360
40628
  default: () => tts_default,
40361
40629
  examples: () => examples16
40362
40630
  });
40363
- import { existsSync as existsSync48, readFileSync as readFileSync32 } from "fs";
40631
+ import { existsSync as existsSync50, readFileSync as readFileSync33 } from "fs";
40364
40632
  import { resolve as resolve33, extname as extname9 } from "path";
40365
40633
  function listVoices(json) {
40366
40634
  const rows = BUNDLED_VOICES.map((v) => ({ ...v, defaultLang: inferLangFromVoiceId(v.id) }));
@@ -40470,8 +40738,8 @@ var init_tts = __esm({
40470
40738
  }
40471
40739
  let text;
40472
40740
  const maybeFile = resolve33(args.input);
40473
- if (existsSync48(maybeFile) && extname9(maybeFile).toLowerCase() === ".txt") {
40474
- text = readFileSync32(maybeFile, "utf-8").trim();
40741
+ if (existsSync50(maybeFile) && extname9(maybeFile).toLowerCase() === ".txt") {
40742
+ text = readFileSync33(maybeFile, "utf-8").trim();
40475
40743
  if (!text) {
40476
40744
  console.error(c.error("File is empty."));
40477
40745
  process.exit(1);
@@ -40563,15 +40831,15 @@ __export(docs_exports, {
40563
40831
  default: () => docs_default,
40564
40832
  examples: () => examples17
40565
40833
  });
40566
- import { readFileSync as readFileSync33, existsSync as existsSync49 } from "fs";
40567
- import { resolve as resolve34, dirname as dirname20, join as join49 } from "path";
40834
+ import { readFileSync as readFileSync34, existsSync as existsSync51 } from "fs";
40835
+ import { resolve as resolve34, dirname as dirname20, join as join51 } from "path";
40568
40836
  import { fileURLToPath as fileURLToPath7 } from "url";
40569
40837
  function docsDir() {
40570
40838
  const thisFile = fileURLToPath7(import.meta.url);
40571
40839
  const dir = dirname20(thisFile);
40572
40840
  const devPath = resolve34(dir, "..", "docs");
40573
40841
  const builtPath = resolve34(dir, "docs");
40574
- return existsSync49(devPath) ? devPath : builtPath;
40842
+ return existsSync51(devPath) ? devPath : builtPath;
40575
40843
  }
40576
40844
  function formatInlineCode(line) {
40577
40845
  return line.replace(/`([^`]+)`/g, (_match, code) => c.accent(code));
@@ -40668,12 +40936,12 @@ var init_docs = __esm({
40668
40936
  }
40669
40937
  process.exit(1);
40670
40938
  }
40671
- const filePath = join49(docsDir(), entry.file);
40672
- if (!existsSync49(filePath)) {
40939
+ const filePath = join51(docsDir(), entry.file);
40940
+ if (!existsSync51(filePath)) {
40673
40941
  console.error(c.error(`Doc file not found: ${filePath}`));
40674
40942
  process.exit(1);
40675
40943
  }
40676
- const content = readFileSync33(filePath, "utf-8");
40944
+ const content = readFileSync34(filePath, "utf-8");
40677
40945
  console.log();
40678
40946
  renderMarkdown(content);
40679
40947
  }
@@ -41099,8 +41367,8 @@ var validate_exports = {};
41099
41367
  __export(validate_exports, {
41100
41368
  default: () => validate_default
41101
41369
  });
41102
- import { existsSync as existsSync50, readFileSync as readFileSync34 } from "fs";
41103
- import { resolve as resolve35, join as join50, dirname as dirname21 } from "path";
41370
+ import { existsSync as existsSync52, readFileSync as readFileSync35 } from "fs";
41371
+ import { resolve as resolve35, join as join52, dirname as dirname21 } from "path";
41104
41372
  import { fileURLToPath as fileURLToPath8 } from "url";
41105
41373
  async function getCompositionDuration3(page) {
41106
41374
  return page.evaluate(() => {
@@ -41155,8 +41423,8 @@ async function validateInBrowser(projectDir, opts) {
41155
41423
  "dist",
41156
41424
  "hyperframe.runtime.iife.js"
41157
41425
  );
41158
- if (existsSync50(runtimePath)) {
41159
- const runtimeSource = readFileSync34(runtimePath, "utf-8");
41426
+ if (existsSync52(runtimePath)) {
41427
+ const runtimeSource = readFileSync35(runtimePath, "utf-8");
41160
41428
  html = html.replace(
41161
41429
  /<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
41162
41430
  () => `<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
@@ -41171,10 +41439,10 @@ async function validateInBrowser(projectDir, opts) {
41171
41439
  res.end(html);
41172
41440
  return;
41173
41441
  }
41174
- const filePath = join50(projectDir, decodeURIComponent(url));
41175
- if (existsSync50(filePath)) {
41442
+ const filePath = join52(projectDir, decodeURIComponent(url));
41443
+ if (existsSync52(filePath)) {
41176
41444
  res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
41177
- res.end(readFileSync34(filePath));
41445
+ res.end(readFileSync35(filePath));
41178
41446
  return;
41179
41447
  }
41180
41448
  res.writeHead(404);
@@ -41364,14 +41632,14 @@ __export(snapshot_exports, {
41364
41632
  default: () => snapshot_default,
41365
41633
  examples: () => examples21
41366
41634
  });
41367
- import { spawn as spawn11 } from "child_process";
41368
- import { existsSync as existsSync51, mkdtempSync as mkdtempSync3, readFileSync as readFileSync35, mkdirSync as mkdirSync25, rmSync as rmSync10 } from "fs";
41635
+ import { spawn as spawn12 } from "child_process";
41636
+ import { existsSync as existsSync53, mkdtempSync as mkdtempSync3, readFileSync as readFileSync36, mkdirSync as mkdirSync27, rmSync as rmSync10 } from "fs";
41369
41637
  import { tmpdir as tmpdir5 } from "os";
41370
- import { resolve as resolve36, join as join51, dirname as dirname22, relative as relative6, isAbsolute as isAbsolute7 } from "path";
41638
+ import { resolve as resolve36, join as join53, dirname as dirname22, relative as relative6, isAbsolute as isAbsolute7 } from "path";
41371
41639
  import { fileURLToPath as fileURLToPath9 } from "url";
41372
41640
  async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDecoder = false) {
41373
- const tmp = mkdtempSync3(join51(tmpdir5(), "hf-snapshot-frame-"));
41374
- const outPath = join51(tmp, "frame.png");
41641
+ const tmp = mkdtempSync3(join53(tmpdir5(), "hf-snapshot-frame-"));
41642
+ const outPath = join53(tmp, "frame.png");
41375
41643
  try {
41376
41644
  const result = await new Promise(
41377
41645
  (resolvePromise) => {
@@ -41391,7 +41659,7 @@ async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDeco
41391
41659
  "-y",
41392
41660
  outPath
41393
41661
  );
41394
- const ff = spawn11("ffmpeg", args);
41662
+ const ff = spawn12("ffmpeg", args);
41395
41663
  let stderr = "";
41396
41664
  let timedOut = false;
41397
41665
  const timer = setTimeout(() => {
@@ -41411,8 +41679,8 @@ async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDeco
41411
41679
  });
41412
41680
  }
41413
41681
  );
41414
- if (result.code !== 0 || result.timedOut || !existsSync51(outPath)) return null;
41415
- return readFileSync35(outPath);
41682
+ if (result.code !== 0 || result.timedOut || !existsSync53(outPath)) return null;
41683
+ return readFileSync36(outPath);
41416
41684
  } finally {
41417
41685
  try {
41418
41686
  rmSync10(tmp, { recursive: true, force: true });
@@ -41434,8 +41702,8 @@ async function captureSnapshots(projectDir, opts) {
41434
41702
  "dist",
41435
41703
  "hyperframe.runtime.iife.js"
41436
41704
  );
41437
- if (existsSync51(runtimePath)) {
41438
- const runtimeSource = readFileSync35(runtimePath, "utf-8");
41705
+ if (existsSync53(runtimePath)) {
41706
+ const runtimeSource = readFileSync36(runtimePath, "utf-8");
41439
41707
  html = html.replace(
41440
41708
  /<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
41441
41709
  () => `<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
@@ -41457,9 +41725,9 @@ async function captureSnapshots(projectDir, opts) {
41457
41725
  res.end();
41458
41726
  return;
41459
41727
  }
41460
- if (existsSync51(filePath)) {
41728
+ if (existsSync53(filePath)) {
41461
41729
  res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
41462
- res.end(readFileSync35(filePath));
41730
+ res.end(readFileSync36(filePath));
41463
41731
  return;
41464
41732
  }
41465
41733
  res.writeHead(404);
@@ -41532,8 +41800,8 @@ async function captureSnapshots(projectDir, opts) {
41532
41800
  return [];
41533
41801
  }
41534
41802
  const positions = opts.at?.length ? opts.at : numFrames === 1 ? [duration / 2] : Array.from({ length: numFrames }, (_2, i2) => i2 / (numFrames - 1) * duration);
41535
- const snapshotDir = join51(projectDir, "snapshots");
41536
- mkdirSync25(snapshotDir, { recursive: true });
41803
+ const snapshotDir = join53(projectDir, "snapshots");
41804
+ mkdirSync27(snapshotDir, { recursive: true });
41537
41805
  let injectVideoFramesBatch2 = null;
41538
41806
  let syncVideoFrameVisibility2 = null;
41539
41807
  let extractMediaMetadata2 = null;
@@ -41607,7 +41875,7 @@ async function captureSnapshots(projectDir, opts) {
41607
41875
  const decodedPath = decodeURIComponent(url.pathname).replace(/^\//, "");
41608
41876
  const candidate = resolve36(projectDir, decodedPath);
41609
41877
  const rel = relative6(projectDir, candidate);
41610
- if (!rel.startsWith("..") && !isAbsolute7(rel) && existsSync51(candidate)) {
41878
+ if (!rel.startsWith("..") && !isAbsolute7(rel) && existsSync53(candidate)) {
41611
41879
  filePath = candidate;
41612
41880
  }
41613
41881
  } catch {
@@ -41637,7 +41905,7 @@ async function captureSnapshots(projectDir, opts) {
41637
41905
  }
41638
41906
  const timeLabel = opts.at?.length ? `${time.toFixed(1)}s` : `${Math.round(time / duration * 100)}pct`;
41639
41907
  const filename = `frame-${String(i2).padStart(2, "0")}-at-${timeLabel}.png`;
41640
- const framePath = join51(snapshotDir, filename);
41908
+ const framePath = join53(snapshotDir, filename);
41641
41909
  await page.screenshot({ path: framePath, type: "png" });
41642
41910
  savedPaths.push(`snapshots/${filename}`);
41643
41911
  }
@@ -41722,14 +41990,14 @@ ${c.error("\u2717")} Snapshot failed: ${msg}`);
41722
41990
  });
41723
41991
 
41724
41992
  // src/capture/assetDownloader.ts
41725
- import { writeFileSync as writeFileSync19, mkdirSync as mkdirSync26 } from "fs";
41726
- import { join as join52, extname as extname10 } from "path";
41993
+ import { writeFileSync as writeFileSync21, mkdirSync as mkdirSync28 } from "fs";
41994
+ import { join as join54, extname as extname10 } from "path";
41727
41995
  async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks) {
41728
- const assetsDir = join52(outputDir, "assets");
41729
- mkdirSync26(assetsDir, { recursive: true });
41996
+ const assetsDir = join54(outputDir, "assets");
41997
+ mkdirSync28(assetsDir, { recursive: true });
41730
41998
  const assets = [];
41731
41999
  const downloadedUrls = /* @__PURE__ */ new Set();
41732
- mkdirSync26(join52(outputDir, "assets", "svgs"), { recursive: true });
42000
+ mkdirSync28(join54(outputDir, "assets", "svgs"), { recursive: true });
41733
42001
  for (let i2 = 0; i2 < tokens.svgs.length && i2 < 30; i2++) {
41734
42002
  const svg = tokens.svgs[i2];
41735
42003
  if (!svg.outerHTML || svg.outerHTML.length < 50) continue;
@@ -41737,7 +42005,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
41737
42005
  const name = label2 ? slugify(label2) + ".svg" : svg.isLogo ? `logo-${i2}.svg` : `icon-${i2}.svg`;
41738
42006
  const localPath = `assets/svgs/${name}`;
41739
42007
  try {
41740
- writeFileSync19(join52(outputDir, localPath), svg.outerHTML, "utf-8");
42008
+ writeFileSync21(join54(outputDir, localPath), svg.outerHTML, "utf-8");
41741
42009
  assets.push({ url: "", localPath, type: "svg" });
41742
42010
  } catch {
41743
42011
  }
@@ -41750,7 +42018,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
41750
42018
  const localPath = `assets/${name}`;
41751
42019
  const buffer = await fetchBuffer(icon.href);
41752
42020
  if (buffer) {
41753
- writeFileSync19(join52(outputDir, localPath), buffer);
42021
+ writeFileSync21(join54(outputDir, localPath), buffer);
41754
42022
  assets.push({ url: icon.href, localPath, type: "favicon" });
41755
42023
  break;
41756
42024
  }
@@ -41807,7 +42075,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
41807
42075
  const slug = isMeaningful ? slugify(rawName) : `${prefix}-${imgIdx}`;
41808
42076
  const name = `${slug}${ext}`;
41809
42077
  const localPath = `assets/${name}`;
41810
- writeFileSync19(join52(outputDir, localPath), buffer);
42078
+ writeFileSync21(join54(outputDir, localPath), buffer);
41811
42079
  assets.push({ url, localPath, type: "image" });
41812
42080
  imgIdx++;
41813
42081
  } catch {
@@ -41820,7 +42088,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
41820
42088
  const localPath = `assets/og-image${ext}`;
41821
42089
  const buffer = await fetchBuffer(tokens.ogImage);
41822
42090
  if (buffer && buffer.length > 5e3) {
41823
- writeFileSync19(join52(outputDir, localPath), buffer);
42091
+ writeFileSync21(join54(outputDir, localPath), buffer);
41824
42092
  assets.push({ url: tokens.ogImage, localPath, type: "image" });
41825
42093
  }
41826
42094
  } catch {
@@ -41843,8 +42111,8 @@ function normalizeUrl(u) {
41843
42111
  }
41844
42112
  }
41845
42113
  async function downloadAndRewriteFonts(css, outputDir) {
41846
- const assetsDir = join52(outputDir, "assets", "fonts");
41847
- mkdirSync26(assetsDir, { recursive: true });
42114
+ const assetsDir = join54(outputDir, "assets", "fonts");
42115
+ mkdirSync28(assetsDir, { recursive: true });
41848
42116
  const fontUrlRegex = /url\(['"]?(https?:\/\/[^'")\s]+\.(?:woff2?|ttf|otf)[^'")\s]*?)['"]?\)/g;
41849
42117
  const fontUrls = /* @__PURE__ */ new Set();
41850
42118
  let match;
@@ -41879,11 +42147,11 @@ async function downloadAndRewriteFonts(css, outputDir) {
41879
42147
  try {
41880
42148
  const urlObj = new URL(fontUrl);
41881
42149
  const filename = urlObj.pathname.split("/").pop() || `font-${count}.woff2`;
41882
- const localPath = join52(assetsDir, filename);
42150
+ const localPath = join54(assetsDir, filename);
41883
42151
  const relativePath = `assets/fonts/${filename}`;
41884
42152
  const buffer = await fetchBuffer(fontUrl);
41885
42153
  if (buffer) {
41886
- writeFileSync19(localPath, buffer);
42154
+ writeFileSync21(localPath, buffer);
41887
42155
  rewritten = rewritten.split(fontUrl).join(relativePath);
41888
42156
  familyCounts.set(family, familyCount + 1);
41889
42157
  count++;
@@ -42676,8 +42944,8 @@ var init_animationCataloger = __esm({
42676
42944
  });
42677
42945
 
42678
42946
  // src/capture/mediaCapture.ts
42679
- import { mkdirSync as mkdirSync27, writeFileSync as writeFileSync20, readdirSync as readdirSync19, readFileSync as readFileSync36, statSync as statSync20 } from "fs";
42680
- import { join as join53 } from "path";
42947
+ import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync22, readdirSync as readdirSync19, readFileSync as readFileSync37, statSync as statSync20 } from "fs";
42948
+ import { join as join55 } from "path";
42681
42949
  async function saveLottieAnimations(discoveredLotties, lottieDir) {
42682
42950
  let savedCount = 0;
42683
42951
  const savedHashes = /* @__PURE__ */ new Set();
@@ -42710,7 +42978,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
42710
42978
  const hash2 = buf.toString("base64").slice(0, 100);
42711
42979
  if (savedHashes.has(hash2)) continue;
42712
42980
  savedHashes.add(hash2);
42713
- writeFileSync20(join53(lottieDir, `animation-${savedCount}.lottie`), buf);
42981
+ writeFileSync22(join55(lottieDir, `animation-${savedCount}.lottie`), buf);
42714
42982
  savedCount++;
42715
42983
  continue;
42716
42984
  }
@@ -42728,7 +42996,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
42728
42996
  } catch {
42729
42997
  continue;
42730
42998
  }
42731
- writeFileSync20(join53(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
42999
+ writeFileSync22(join55(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
42732
43000
  savedCount++;
42733
43001
  }
42734
43002
  } catch {
@@ -42738,22 +43006,22 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
42738
43006
  }
42739
43007
  async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
42740
43008
  const manifest = [];
42741
- const previewDir = join53(lottieDir, "previews");
42742
- mkdirSync27(previewDir, { recursive: true });
43009
+ const previewDir = join55(lottieDir, "previews");
43010
+ mkdirSync29(previewDir, { recursive: true });
42743
43011
  for (const file of readdirSync19(lottieDir)) {
42744
43012
  if (!file.endsWith(".json")) continue;
42745
43013
  try {
42746
- const raw = JSON.parse(readFileSync36(join53(lottieDir, file), "utf-8"));
43014
+ const raw = JSON.parse(readFileSync37(join55(lottieDir, file), "utf-8"));
42747
43015
  const fr = raw.fr || 30;
42748
43016
  const dur = ((raw.op || 0) - (raw.ip || 0)) / fr;
42749
43017
  const previewName = file.replace(".json", "-preview.png");
42750
- const fileSize = statSync20(join53(lottieDir, file)).size;
43018
+ const fileSize = statSync20(join55(lottieDir, file)).size;
42751
43019
  if (fileSize > 2e6) continue;
42752
43020
  let previewPage;
42753
43021
  try {
42754
43022
  previewPage = await chromeBrowser.newPage();
42755
43023
  await previewPage.setViewport({ width: 400, height: 400 });
42756
- const animData = JSON.parse(readFileSync36(join53(lottieDir, file), "utf-8"));
43024
+ const animData = JSON.parse(readFileSync37(join55(lottieDir, file), "utf-8"));
42757
43025
  const midFrame = Math.floor(((raw.op || 0) - (raw.ip || 0)) * 0.3);
42758
43026
  await previewPage.setContent(
42759
43027
  `<!DOCTYPE html>
@@ -42783,7 +43051,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
42783
43051
  await previewPage.waitForFunction(() => window.__READY === true, { timeout: 5e3 }).catch(() => {
42784
43052
  });
42785
43053
  await previewPage.screenshot({
42786
- path: join53(previewDir, previewName),
43054
+ path: join55(previewDir, previewName),
42787
43055
  type: "png",
42788
43056
  omitBackground: true
42789
43057
  });
@@ -42806,8 +43074,8 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
42806
43074
  }
42807
43075
  }
42808
43076
  if (manifest.length > 0) {
42809
- writeFileSync20(
42810
- join53(outputDir, "extracted", "lottie-manifest.json"),
43077
+ writeFileSync22(
43078
+ join55(outputDir, "extracted", "lottie-manifest.json"),
42811
43079
  JSON.stringify(manifest, null, 2),
42812
43080
  "utf-8"
42813
43081
  );
@@ -42869,15 +43137,15 @@ async function captureVideoManifest(page, outputDir, progress) {
42869
43137
  return true;
42870
43138
  });
42871
43139
  if (uniqueVideos.length > 0) {
42872
- const videoManifestDir = join53(outputDir, "assets", "videos");
42873
- mkdirSync27(videoManifestDir, { recursive: true });
42874
- const previewDir = join53(videoManifestDir, "previews");
42875
- mkdirSync27(previewDir, { recursive: true });
43140
+ const videoManifestDir = join55(outputDir, "assets", "videos");
43141
+ mkdirSync29(videoManifestDir, { recursive: true });
43142
+ const previewDir = join55(videoManifestDir, "previews");
43143
+ mkdirSync29(previewDir, { recursive: true });
42876
43144
  const videoManifest = [];
42877
43145
  for (let vi = 0; vi < uniqueVideos.length && vi < 20; vi++) {
42878
43146
  const v = uniqueVideos[vi];
42879
43147
  const previewName = `video-${vi}-preview.png`;
42880
- const previewPath = join53(previewDir, previewName);
43148
+ const previewPath = join55(previewDir, previewName);
42881
43149
  try {
42882
43150
  await page.evaluate(`window.scrollTo(0, ${Math.max(0, v.top - 100)})`);
42883
43151
  await new Promise((r2) => setTimeout(r2, 300));
@@ -42915,8 +43183,8 @@ async function captureVideoManifest(page, outputDir, progress) {
42915
43183
  });
42916
43184
  }
42917
43185
  if (videoManifest.length > 0) {
42918
- writeFileSync20(
42919
- join53(outputDir, "extracted", "video-manifest.json"),
43186
+ writeFileSync22(
43187
+ join55(outputDir, "extracted", "video-manifest.json"),
42920
43188
  JSON.stringify(videoManifest, null, 2),
42921
43189
  "utf-8"
42922
43190
  );
@@ -65416,19 +65684,19 @@ function _moveValueRecursive(data, sourceKeys, destKeys, keyIdx, excludeKeys) {
65416
65684
  const key2 = sourceKeys[keyIdx];
65417
65685
  if (key2.endsWith("[]")) {
65418
65686
  const keyName = key2.slice(0, -2);
65419
- const dataRecord = data;
65420
- if (keyName in dataRecord && Array.isArray(dataRecord[keyName])) {
65421
- for (const item of dataRecord[keyName]) {
65687
+ const dataRecord2 = data;
65688
+ if (keyName in dataRecord2 && Array.isArray(dataRecord2[keyName])) {
65689
+ for (const item of dataRecord2[keyName]) {
65422
65690
  _moveValueRecursive(item, sourceKeys, destKeys, keyIdx + 1, excludeKeys);
65423
65691
  }
65424
65692
  }
65425
65693
  } else if (key2 === "*") {
65426
65694
  if (typeof data === "object" && data !== null && !Array.isArray(data)) {
65427
- const dataRecord = data;
65428
- const keysToMove = Object.keys(dataRecord).filter((k2) => !k2.startsWith("_") && !excludeKeys.has(k2));
65695
+ const dataRecord2 = data;
65696
+ const keysToMove = Object.keys(dataRecord2).filter((k2) => !k2.startsWith("_") && !excludeKeys.has(k2));
65429
65697
  const valuesToMove = {};
65430
65698
  for (const k2 of keysToMove) {
65431
- valuesToMove[k2] = dataRecord[k2];
65699
+ valuesToMove[k2] = dataRecord2[k2];
65432
65700
  }
65433
65701
  for (const [k2, v] of Object.entries(valuesToMove)) {
65434
65702
  const newDestKeys = [];
@@ -65439,16 +65707,16 @@ function _moveValueRecursive(data, sourceKeys, destKeys, keyIdx, excludeKeys) {
65439
65707
  newDestKeys.push(dk);
65440
65708
  }
65441
65709
  }
65442
- setValueByPath(dataRecord, newDestKeys, v);
65710
+ setValueByPath(dataRecord2, newDestKeys, v);
65443
65711
  }
65444
65712
  for (const k2 of keysToMove) {
65445
- delete dataRecord[k2];
65713
+ delete dataRecord2[k2];
65446
65714
  }
65447
65715
  }
65448
65716
  } else {
65449
- const dataRecord = data;
65450
- if (key2 in dataRecord) {
65451
- _moveValueRecursive(dataRecord[key2], sourceKeys, destKeys, keyIdx + 1, excludeKeys);
65717
+ const dataRecord2 = data;
65718
+ if (key2 in dataRecord2) {
65719
+ _moveValueRecursive(dataRecord2[key2], sourceKeys, destKeys, keyIdx + 1, excludeKeys);
65452
65720
  }
65453
65721
  }
65454
65722
  }
@@ -83371,8 +83639,8 @@ ${underline2}`);
83371
83639
  });
83372
83640
 
83373
83641
  // src/capture/contentExtractor.ts
83374
- import { readdirSync as readdirSync20, statSync as statSync22, readFileSync as readFileSync37 } from "fs";
83375
- import { join as join54 } from "path";
83642
+ import { readdirSync as readdirSync20, statSync as statSync22, readFileSync as readFileSync38 } from "fs";
83643
+ import { join as join56 } from "path";
83376
83644
  async function detectLibraries(page, capturedShaders) {
83377
83645
  let detectedLibraries = [];
83378
83646
  try {
@@ -83492,7 +83760,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings) {
83492
83760
  try {
83493
83761
  const { GoogleGenAI: GoogleGenAI2 } = await Promise.resolve().then(() => (init_node4(), node_exports));
83494
83762
  const ai = new GoogleGenAI2({ apiKey: geminiKey });
83495
- const imageFiles = readdirSync20(join54(outputDir, "assets")).filter(
83763
+ const imageFiles = readdirSync20(join56(outputDir, "assets")).filter(
83496
83764
  (f3) => /\.(png|jpg|jpeg|webp|gif)$/i.test(f3)
83497
83765
  );
83498
83766
  const model = process.env.HYPERFRAMES_GEMINI_MODEL || "gemini-3.1-flash-lite-preview";
@@ -83501,10 +83769,10 @@ async function captionImagesWithGemini(outputDir, progress, warnings) {
83501
83769
  const batch = imageFiles.slice(i2, i2 + BATCH_SIZE);
83502
83770
  const results = await Promise.allSettled(
83503
83771
  batch.map(async (file) => {
83504
- const filePath = join54(outputDir, "assets", file);
83772
+ const filePath = join56(outputDir, "assets", file);
83505
83773
  const stat3 = statSync22(filePath);
83506
83774
  if (stat3.size > 4e6) return { file, caption: "" };
83507
- const buffer = readFileSync37(filePath);
83775
+ const buffer = readFileSync38(filePath);
83508
83776
  const base64 = buffer.toString("base64");
83509
83777
  const ext = file.split(".").pop()?.toLowerCase() || "png";
83510
83778
  const mimeType = ext === "jpg" ? "image/jpeg" : `image/${ext}`;
@@ -83550,11 +83818,11 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
83550
83818
  const uncaptionedLines = [];
83551
83819
  const svgLines = [];
83552
83820
  const fontLines = [];
83553
- const assetsPath = join54(outputDir, "assets");
83821
+ const assetsPath = join56(outputDir, "assets");
83554
83822
  try {
83555
83823
  for (const file of readdirSync20(assetsPath)) {
83556
83824
  if (file === "svgs" || file === "fonts" || file === "lottie" || file === "videos") continue;
83557
- const filePath = join54(assetsPath, file);
83825
+ const filePath = join56(assetsPath, file);
83558
83826
  const stat3 = statSync22(filePath);
83559
83827
  if (!stat3.isFile()) continue;
83560
83828
  const sizeKb = Math.round(stat3.size / 1024);
@@ -83583,7 +83851,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
83583
83851
  } catch {
83584
83852
  }
83585
83853
  try {
83586
- const svgsPath = join54(assetsPath, "svgs");
83854
+ const svgsPath = join56(assetsPath, "svgs");
83587
83855
  for (const file of readdirSync20(svgsPath)) {
83588
83856
  if (!file.endsWith(".svg")) continue;
83589
83857
  const svgMatch = tokens.svgs.find(
@@ -83598,7 +83866,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
83598
83866
  } catch {
83599
83867
  }
83600
83868
  try {
83601
- const fontsPath = join54(assetsPath, "fonts");
83869
+ const fontsPath = join56(assetsPath, "fonts");
83602
83870
  for (const file of readdirSync20(fontsPath)) {
83603
83871
  fontLines.push(`fonts/${file} \u2014 font file`);
83604
83872
  }
@@ -83617,13 +83885,13 @@ var agentPromptGenerator_exports = {};
83617
83885
  __export(agentPromptGenerator_exports, {
83618
83886
  generateAgentPrompt: () => generateAgentPrompt
83619
83887
  });
83620
- import { writeFileSync as writeFileSync21 } from "fs";
83621
- import { join as join55 } from "path";
83888
+ import { writeFileSync as writeFileSync23 } from "fs";
83889
+ import { join as join57 } from "path";
83622
83890
  function generateAgentPrompt(outputDir, url, tokens, _animations, hasScreenshot, hasLottie, hasShaders, _catalogedAssets, detectedLibraries) {
83623
83891
  const prompt = buildPrompt(url, tokens, hasScreenshot, hasLottie, hasShaders, detectedLibraries);
83624
- writeFileSync21(join55(outputDir, "AGENTS.md"), prompt, "utf-8");
83625
- writeFileSync21(join55(outputDir, "CLAUDE.md"), prompt, "utf-8");
83626
- writeFileSync21(join55(outputDir, ".cursorrules"), prompt, "utf-8");
83892
+ writeFileSync23(join57(outputDir, "AGENTS.md"), prompt, "utf-8");
83893
+ writeFileSync23(join57(outputDir, "CLAUDE.md"), prompt, "utf-8");
83894
+ writeFileSync23(join57(outputDir, ".cursorrules"), prompt, "utf-8");
83627
83895
  }
83628
83896
  function buildPrompt(url, tokens, hasScreenshot, hasLottie, hasShaders, detectedLibraries) {
83629
83897
  const title = tokens.title || new URL(url).hostname.replace(/^www\./, "");
@@ -83690,15 +83958,15 @@ var init_agentPromptGenerator = __esm({
83690
83958
  });
83691
83959
 
83692
83960
  // src/capture/scaffolding.ts
83693
- import { existsSync as existsSync52, writeFileSync as writeFileSync22, readFileSync as readFileSync38 } from "fs";
83694
- import { join as join56, resolve as resolve37 } from "path";
83961
+ import { existsSync as existsSync54, writeFileSync as writeFileSync24, readFileSync as readFileSync39 } from "fs";
83962
+ import { join as join58, resolve as resolve37 } from "path";
83695
83963
  function loadEnvFile(startDir) {
83696
83964
  try {
83697
83965
  let dir = resolve37(startDir);
83698
83966
  for (let i2 = 0; i2 < 5; i2++) {
83699
83967
  const envPath = resolve37(dir, ".env");
83700
83968
  try {
83701
- const envContent = readFileSync38(envPath, "utf-8");
83969
+ const envContent = readFileSync39(envPath, "utf-8");
83702
83970
  for (const line of envContent.split("\n")) {
83703
83971
  const trimmed = line.trim();
83704
83972
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -83717,10 +83985,10 @@ function loadEnvFile(startDir) {
83717
83985
  }
83718
83986
  }
83719
83987
  async function generateProjectScaffold(outputDir, url, tokens, animationCatalog, hasScreenshots, hasLotties, hasShaders, catalogedAssets, progress, warnings, detectedLibraries) {
83720
- const metaPath = join56(outputDir, "meta.json");
83721
- if (!existsSync52(metaPath)) {
83988
+ const metaPath = join58(outputDir, "meta.json");
83989
+ if (!existsSync54(metaPath)) {
83722
83990
  const hostname = new URL(url).hostname.replace(/^www\./, "");
83723
- writeFileSync22(
83991
+ writeFileSync24(
83724
83992
  metaPath,
83725
83993
  JSON.stringify({ id: hostname + "-video", name: tokens.title || hostname }, null, 2),
83726
83994
  "utf-8"
@@ -83755,11 +84023,11 @@ var screenshotCapture_exports = {};
83755
84023
  __export(screenshotCapture_exports, {
83756
84024
  captureScrollScreenshots: () => captureScrollScreenshots
83757
84025
  });
83758
- import { writeFileSync as writeFileSync23, mkdirSync as mkdirSync28 } from "fs";
83759
- import { join as join57 } from "path";
84026
+ import { writeFileSync as writeFileSync25, mkdirSync as mkdirSync30 } from "fs";
84027
+ import { join as join59 } from "path";
83760
84028
  async function captureScrollScreenshots(page, outputDir) {
83761
- const screenshotsDir = join57(outputDir, "screenshots");
83762
- mkdirSync28(screenshotsDir, { recursive: true });
84029
+ const screenshotsDir = join59(outputDir, "screenshots");
84030
+ mkdirSync30(screenshotsDir, { recursive: true });
83763
84031
  const MAX_SCREENSHOTS = 20;
83764
84032
  const filePaths = [];
83765
84033
  try {
@@ -83792,9 +84060,9 @@ async function captureScrollScreenshots(page, outputDir) {
83792
84060
  finalPositions[i2] / Math.max(1, scrollHeight - viewportHeight) * 100
83793
84061
  );
83794
84062
  const filename = `scroll-${String(Math.min(pct, 100)).padStart(3, "0")}.png`;
83795
- const filePath = join57(screenshotsDir, filename);
84063
+ const filePath = join59(screenshotsDir, filename);
83796
84064
  const buffer = await page.screenshot({ type: "png" });
83797
- writeFileSync23(filePath, buffer);
84065
+ writeFileSync25(filePath, buffer);
83798
84066
  filePaths.push(`screenshots/${filename}`);
83799
84067
  }
83800
84068
  await page.evaluate(`window.scrollTo(0, 0)`);
@@ -84105,8 +84373,8 @@ var capture_exports = {};
84105
84373
  __export(capture_exports, {
84106
84374
  captureWebsite: () => captureWebsite
84107
84375
  });
84108
- import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync24, existsSync as existsSync53 } from "fs";
84109
- import { join as join58 } from "path";
84376
+ import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync26, existsSync as existsSync55 } from "fs";
84377
+ import { join as join60 } from "path";
84110
84378
  async function captureWebsite(opts, onProgress) {
84111
84379
  const {
84112
84380
  url,
@@ -84123,9 +84391,9 @@ async function captureWebsite(opts, onProgress) {
84123
84391
  onProgress?.(stage, detail);
84124
84392
  };
84125
84393
  loadEnvFile(outputDir);
84126
- mkdirSync29(join58(outputDir, "extracted"), { recursive: true });
84127
- mkdirSync29(join58(outputDir, "screenshots"), { recursive: true });
84128
- mkdirSync29(join58(outputDir, "assets"), { recursive: true });
84394
+ mkdirSync31(join60(outputDir, "extracted"), { recursive: true });
84395
+ mkdirSync31(join60(outputDir, "screenshots"), { recursive: true });
84396
+ mkdirSync31(join60(outputDir, "assets"), { recursive: true });
84129
84397
  progress("browser", "Launching headless Chrome...");
84130
84398
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
84131
84399
  const browser = await ensureBrowser2();
@@ -84281,8 +84549,8 @@ async function captureWebsite(opts, onProgress) {
84281
84549
  } catch {
84282
84550
  }
84283
84551
  if (discoveredLotties.length > 0) {
84284
- const lottieDir = join58(outputDir, "assets", "lottie");
84285
- mkdirSync29(lottieDir, { recursive: true });
84552
+ const lottieDir = join60(outputDir, "assets", "lottie");
84553
+ mkdirSync31(lottieDir, { recursive: true });
84286
84554
  const savedCount = await saveLottieAnimations(discoveredLotties, lottieDir);
84287
84555
  if (savedCount > 0) {
84288
84556
  await renderLottiePreviews(chromeBrowser, lottieDir, outputDir);
@@ -84300,8 +84568,8 @@ async function captureWebsite(opts, onProgress) {
84300
84568
  return true;
84301
84569
  });
84302
84570
  capturedShaders = unique;
84303
- writeFileSync24(
84304
- join58(outputDir, "extracted", "shaders.json"),
84571
+ writeFileSync26(
84572
+ join60(outputDir, "extracted", "shaders.json"),
84305
84573
  JSON.stringify(unique, null, 2),
84306
84574
  "utf-8"
84307
84575
  );
@@ -84311,8 +84579,8 @@ async function captureWebsite(opts, onProgress) {
84311
84579
  }
84312
84580
  progress("tokens", "Extracting design tokens...");
84313
84581
  const tokens = await extractTokens(page1);
84314
- writeFileSync24(
84315
- join58(outputDir, "extracted", "tokens.json"),
84582
+ writeFileSync26(
84583
+ join60(outputDir, "extracted", "tokens.json"),
84316
84584
  JSON.stringify(tokens, null, 2),
84317
84585
  "utf-8"
84318
84586
  );
@@ -84385,8 +84653,8 @@ async function captureWebsite(opts, onProgress) {
84385
84653
  scrollTriggeredElements: (animationCatalog.scrollTargets || []).length,
84386
84654
  representativeAnimations: representativeAnims
84387
84655
  };
84388
- writeFileSync24(
84389
- join58(outputDir, "extracted", "animations.json"),
84656
+ writeFileSync26(
84657
+ join60(outputDir, "extracted", "animations.json"),
84390
84658
  JSON.stringify(leanCatalog, null, 2),
84391
84659
  "utf-8"
84392
84660
  );
@@ -84397,18 +84665,18 @@ async function captureWebsite(opts, onProgress) {
84397
84665
  assets = await downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks);
84398
84666
  }
84399
84667
  if (visibleTextContent) {
84400
- writeFileSync24(join58(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
84668
+ writeFileSync26(join60(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
84401
84669
  }
84402
84670
  if (catalogedAssets.length > 0) {
84403
- writeFileSync24(
84404
- join58(outputDir, "extracted", "assets-catalog.json"),
84671
+ writeFileSync26(
84672
+ join60(outputDir, "extracted", "assets-catalog.json"),
84405
84673
  JSON.stringify(catalogedAssets, null, 2),
84406
84674
  "utf-8"
84407
84675
  );
84408
84676
  }
84409
84677
  if (detectedLibraries.length > 0) {
84410
- writeFileSync24(
84411
- join58(outputDir, "extracted", "detected-libraries.json"),
84678
+ writeFileSync26(
84679
+ join60(outputDir, "extracted", "detected-libraries.json"),
84412
84680
  JSON.stringify(detectedLibraries, null, 2),
84413
84681
  "utf-8"
84414
84682
  );
@@ -84418,8 +84686,8 @@ async function captureWebsite(opts, onProgress) {
84418
84686
  try {
84419
84687
  const lines = generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCaptions);
84420
84688
  if (lines.length > 0) {
84421
- writeFileSync24(
84422
- join58(outputDir, "extracted", "asset-descriptions.md"),
84689
+ writeFileSync26(
84690
+ join60(outputDir, "extracted", "asset-descriptions.md"),
84423
84691
  "# Asset Descriptions\n\nOne line per file. Read this instead of opening every image individually.\n\n" + lines.map((l) => "- " + l).join("\n") + "\n",
84424
84692
  "utf-8"
84425
84693
  );
@@ -84435,7 +84703,7 @@ async function captureWebsite(opts, onProgress) {
84435
84703
  animationCatalog,
84436
84704
  screenshots.length > 0,
84437
84705
  discoveredLotties.length > 0,
84438
- existsSync53(join58(outputDir, "extracted", "shaders.json")),
84706
+ existsSync55(join60(outputDir, "extracted", "shaders.json")),
84439
84707
  catalogedAssets,
84440
84708
  progress,
84441
84709
  warnings,
@@ -84614,11 +84882,11 @@ var init_capture2 = __esm({
84614
84882
  } catch (err) {
84615
84883
  const errMsg = err instanceof Error ? err.message : String(err);
84616
84884
  try {
84617
- const { mkdirSync: mkdirSync31, writeFileSync: writeFileSync25 } = await import("fs");
84618
- mkdirSync31(outputDir, { recursive: true });
84885
+ const { mkdirSync: mkdirSync33, writeFileSync: writeFileSync27 } = await import("fs");
84886
+ mkdirSync33(outputDir, { recursive: true });
84619
84887
  const isTimeout = /timeout|timed out/i.test(errMsg);
84620
84888
  const reason = isTimeout ? "Page navigation timed out \u2014 the site may be blocking headless browsers or requires authentication." : `Capture failed: ${errMsg}`;
84621
- writeFileSync25(
84889
+ writeFileSync27(
84622
84890
  `${outputDir}/BLOCKED.md`,
84623
84891
  `# Capture Failed
84624
84892
 
@@ -84781,10 +85049,10 @@ __export(autoUpdate_exports, {
84781
85049
  reportCompletedUpdate: () => reportCompletedUpdate,
84782
85050
  scheduleBackgroundInstall: () => scheduleBackgroundInstall
84783
85051
  });
84784
- import { spawn as spawn12 } from "child_process";
84785
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync30, openSync } from "fs";
85052
+ import { spawn as spawn13 } from "child_process";
85053
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync32, openSync } from "fs";
84786
85054
  import { homedir as homedir11 } from "os";
84787
- import { join as join59 } from "path";
85055
+ import { join as join61 } from "path";
84788
85056
  import { compareVersions as compareVersions2 } from "compare-versions";
84789
85057
  function isAutoInstallDisabled() {
84790
85058
  if (isDevMode()) return true;
@@ -84799,15 +85067,15 @@ function majorOf(version) {
84799
85067
  }
84800
85068
  function log(line) {
84801
85069
  try {
84802
- mkdirSync30(CONFIG_DIR2, { recursive: true, mode: 448 });
85070
+ mkdirSync32(CONFIG_DIR2, { recursive: true, mode: 448 });
84803
85071
  appendFileSync2(LOG_FILE, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
84804
85072
  `, { mode: 384 });
84805
85073
  } catch {
84806
85074
  }
84807
85075
  }
84808
85076
  function launchDetachedInstall(installCommand, version) {
84809
- mkdirSync30(CONFIG_DIR2, { recursive: true, mode: 448 });
84810
- const configFile = join59(CONFIG_DIR2, "config.json");
85077
+ mkdirSync32(CONFIG_DIR2, { recursive: true, mode: 448 });
85078
+ const configFile = join61(CONFIG_DIR2, "config.json");
84811
85079
  const nodeScript = `
84812
85080
  const { exec } = require("node:child_process");
84813
85081
  const { readFileSync, renameSync, writeFileSync } = require("node:fs");
@@ -84832,7 +85100,7 @@ function launchDetachedInstall(installCommand, version) {
84832
85100
  });
84833
85101
  `;
84834
85102
  const out = openSync(LOG_FILE, "a", 384);
84835
- const child = spawn12(process.execPath, ["-e", nodeScript], {
85103
+ const child = spawn13(process.execPath, ["-e", nodeScript], {
84836
85104
  detached: true,
84837
85105
  stdio: ["ignore", out, out],
84838
85106
  windowsHide: true,
@@ -84926,8 +85194,8 @@ var init_autoUpdate = __esm({
84926
85194
  init_config();
84927
85195
  init_env();
84928
85196
  init_installerDetection();
84929
- CONFIG_DIR2 = join59(homedir11(), ".hyperframes");
84930
- LOG_FILE = join59(CONFIG_DIR2, "auto-update.log");
85197
+ CONFIG_DIR2 = join61(homedir11(), ".hyperframes");
85198
+ LOG_FILE = join61(CONFIG_DIR2, "auto-update.log");
84931
85199
  PENDING_TIMEOUT_MS = 10 * 60 * 1e3;
84932
85200
  }
84933
85201
  });