hyperframes 0.2.3 → 0.2.4
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 +674 -249
- package/dist/hyperframe-runtime.js +5 -5
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +5 -5
- package/dist/skills/hyperframes/SKILL.md +76 -5
- package/dist/skills/hyperframes/house-style.md +1 -1
- package/dist/skills/hyperframes/references/captions.md +1 -1
- package/dist/skills/hyperframes/references/examples.md +1 -1
- package/dist/skills/hyperframes/references/fonts.md +134 -0
- package/dist/skills/hyperframes/references/motion-principles.md +69 -0
- package/dist/skills/hyperframes/references/transitions/catalog.md +2 -2
- package/package.json +1 -1
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.2.
|
|
57
|
+
VERSION = true ? "0.2.4" : "0.0.0-dev";
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -2731,7 +2731,7 @@ import { get as httpsGet } from "https";
|
|
|
2731
2731
|
import { pipeline } from "stream/promises";
|
|
2732
2732
|
function downloadFile(url, dest) {
|
|
2733
2733
|
const tmp = `${dest}.tmp`;
|
|
2734
|
-
return new Promise((
|
|
2734
|
+
return new Promise((resolve28, reject) => {
|
|
2735
2735
|
const follow = (u) => {
|
|
2736
2736
|
httpsGet(u, (res) => {
|
|
2737
2737
|
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
@@ -2748,7 +2748,7 @@ function downloadFile(url, dest) {
|
|
|
2748
2748
|
const file = createWriteStream(tmp);
|
|
2749
2749
|
pipeline(res, file).then(() => {
|
|
2750
2750
|
renameSync(tmp, dest);
|
|
2751
|
-
|
|
2751
|
+
resolve28();
|
|
2752
2752
|
}).catch((err) => {
|
|
2753
2753
|
try {
|
|
2754
2754
|
unlinkSync(tmp);
|
|
@@ -3401,13 +3401,13 @@ function hasNpx() {
|
|
|
3401
3401
|
}
|
|
3402
3402
|
}
|
|
3403
3403
|
function runSkillsAdd(repo) {
|
|
3404
|
-
return new Promise((
|
|
3404
|
+
return new Promise((resolve28, reject) => {
|
|
3405
3405
|
const child = spawn("npx", ["skills", "add", repo, "--all"], {
|
|
3406
3406
|
stdio: "inherit",
|
|
3407
3407
|
timeout: 12e4
|
|
3408
3408
|
});
|
|
3409
3409
|
child.on("close", (code, signal) => {
|
|
3410
|
-
if (code === 0)
|
|
3410
|
+
if (code === 0) resolve28();
|
|
3411
3411
|
else if (signal === "SIGINT" || code === 130) process.exit(0);
|
|
3412
3412
|
else reject(new Error(`npx skills add exited with code ${code}`));
|
|
3413
3413
|
});
|
|
@@ -3554,7 +3554,8 @@ var init_utils = __esm({
|
|
|
3554
3554
|
|
|
3555
3555
|
// ../core/src/lint/context.ts
|
|
3556
3556
|
function buildLintContext(html, options = {}) {
|
|
3557
|
-
|
|
3557
|
+
const rawSource = html || "";
|
|
3558
|
+
let source = rawSource;
|
|
3558
3559
|
const templateMatch = source.match(/<template[^>]*>([\s\S]*)<\/template>/i);
|
|
3559
3560
|
if (templateMatch?.[1]) source = templateMatch[1];
|
|
3560
3561
|
const tags = extractOpenTags(source);
|
|
@@ -3565,6 +3566,7 @@ function buildLintContext(html, options = {}) {
|
|
|
3565
3566
|
const rootCompositionId = readAttr(rootTag?.raw || "", "data-composition-id");
|
|
3566
3567
|
return {
|
|
3567
3568
|
source,
|
|
3569
|
+
rawSource,
|
|
3568
3570
|
tags,
|
|
3569
3571
|
styles,
|
|
3570
3572
|
scripts,
|
|
@@ -4971,6 +4973,56 @@ var init_composition = __esm({
|
|
|
4971
4973
|
}
|
|
4972
4974
|
return findings;
|
|
4973
4975
|
},
|
|
4976
|
+
// root_composition_missing_data_start
|
|
4977
|
+
({ rootTag }) => {
|
|
4978
|
+
const findings = [];
|
|
4979
|
+
if (!rootTag) return findings;
|
|
4980
|
+
const compId = readAttr(rootTag.raw, "data-composition-id");
|
|
4981
|
+
if (!compId) return findings;
|
|
4982
|
+
const hasStart = readAttr(rootTag.raw, "data-start") !== null;
|
|
4983
|
+
if (!hasStart) {
|
|
4984
|
+
findings.push({
|
|
4985
|
+
code: "root_composition_missing_data_start",
|
|
4986
|
+
severity: "warning",
|
|
4987
|
+
message: `Root composition "${compId}" is missing data-start. The runtime needs data-start="0" on the root element to begin playback.`,
|
|
4988
|
+
fixHint: 'Add data-start="0" to the root composition element.',
|
|
4989
|
+
snippet: truncateSnippet(rootTag.raw)
|
|
4990
|
+
});
|
|
4991
|
+
}
|
|
4992
|
+
return findings;
|
|
4993
|
+
},
|
|
4994
|
+
// standalone_composition_wrapped_in_template
|
|
4995
|
+
({ rawSource, options }) => {
|
|
4996
|
+
const findings = [];
|
|
4997
|
+
if (options.isSubComposition) return findings;
|
|
4998
|
+
const trimmed = rawSource.trimStart().toLowerCase();
|
|
4999
|
+
if (trimmed.startsWith("<template")) {
|
|
5000
|
+
findings.push({
|
|
5001
|
+
code: "standalone_composition_wrapped_in_template",
|
|
5002
|
+
severity: "warning",
|
|
5003
|
+
message: "Root index.html is wrapped in a <template> tag. Only sub-compositions loaded via data-composition-src should use <template> wrappers. The runtime cannot play a standalone composition inside a template.",
|
|
5004
|
+
fixHint: "Remove the <template> wrapper. Use <!DOCTYPE html><html>...<div data-composition-id>...</div>...</html> instead."
|
|
5005
|
+
});
|
|
5006
|
+
}
|
|
5007
|
+
return findings;
|
|
5008
|
+
},
|
|
5009
|
+
// root_composition_missing_html_wrapper
|
|
5010
|
+
({ rawSource, options }) => {
|
|
5011
|
+
const findings = [];
|
|
5012
|
+
if (options.isSubComposition) return findings;
|
|
5013
|
+
const trimmed = rawSource.trimStart().toLowerCase();
|
|
5014
|
+
const hasDoctype = trimmed.startsWith("<!doctype") || trimmed.startsWith("<html");
|
|
5015
|
+
const hasComposition = rawSource.includes("data-composition-id");
|
|
5016
|
+
if (hasComposition && !hasDoctype) {
|
|
5017
|
+
findings.push({
|
|
5018
|
+
code: "root_composition_missing_html_wrapper",
|
|
5019
|
+
severity: "warning",
|
|
5020
|
+
message: "Composition is missing <!DOCTYPE html> and <html> wrapper. The bundler and preview expect a complete HTML document for index.html files.",
|
|
5021
|
+
fixHint: 'Wrap the composition in <!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>...</body></html>.'
|
|
5022
|
+
});
|
|
5023
|
+
}
|
|
5024
|
+
return findings;
|
|
5025
|
+
},
|
|
4974
5026
|
// requestanimationframe_in_composition
|
|
4975
5027
|
({ scripts }) => {
|
|
4976
5028
|
const findings = [];
|
|
@@ -5192,7 +5244,7 @@ function lintProject(project) {
|
|
|
5192
5244
|
const filePath = join6(compositionsDir, file);
|
|
5193
5245
|
const html = readFileSync6(filePath, "utf-8");
|
|
5194
5246
|
allHtmlSources.push(html);
|
|
5195
|
-
const result = lintHyperframeHtml(html, { filePath });
|
|
5247
|
+
const result = lintHyperframeHtml(html, { filePath, isSubComposition: true });
|
|
5196
5248
|
results.push({ file: `compositions/${file}`, result });
|
|
5197
5249
|
totalErrors += result.errorCount;
|
|
5198
5250
|
totalWarnings += result.warningCount;
|
|
@@ -5328,6 +5380,238 @@ var init_lintFormat = __esm({
|
|
|
5328
5380
|
}
|
|
5329
5381
|
});
|
|
5330
5382
|
|
|
5383
|
+
// src/server/portUtils.ts
|
|
5384
|
+
import net from "net";
|
|
5385
|
+
import http from "http";
|
|
5386
|
+
import { execFile } from "child_process";
|
|
5387
|
+
import { promisify } from "util";
|
|
5388
|
+
import { resolve as resolve2 } from "path";
|
|
5389
|
+
function isPortAvailableOnHost(port, host) {
|
|
5390
|
+
return new Promise((resolve28) => {
|
|
5391
|
+
const server = net.createServer();
|
|
5392
|
+
server.unref();
|
|
5393
|
+
server.on("error", (err) => {
|
|
5394
|
+
resolve28(err.code !== "EADDRINUSE");
|
|
5395
|
+
});
|
|
5396
|
+
server.listen({ port, host }, () => {
|
|
5397
|
+
server.close(() => {
|
|
5398
|
+
resolve28(true);
|
|
5399
|
+
});
|
|
5400
|
+
});
|
|
5401
|
+
});
|
|
5402
|
+
}
|
|
5403
|
+
async function testPortOnAllHosts(port) {
|
|
5404
|
+
const hosts = ["127.0.0.1", "0.0.0.0", "::1", "::"];
|
|
5405
|
+
const results = await Promise.all(hosts.map((h2) => isPortAvailableOnHost(port, h2)));
|
|
5406
|
+
return results.every(Boolean);
|
|
5407
|
+
}
|
|
5408
|
+
function detectHyperframesServer(port, normalizedProjectDir) {
|
|
5409
|
+
return new Promise((resolveResult) => {
|
|
5410
|
+
const req = http.get(
|
|
5411
|
+
{
|
|
5412
|
+
hostname: "127.0.0.1",
|
|
5413
|
+
port,
|
|
5414
|
+
path: "/__hyperframes_config",
|
|
5415
|
+
timeout: PROBE_TIMEOUT_MS
|
|
5416
|
+
},
|
|
5417
|
+
(res) => {
|
|
5418
|
+
if (res.statusCode !== 200) {
|
|
5419
|
+
res.resume();
|
|
5420
|
+
return resolveResult({ type: "not-hyperframes" });
|
|
5421
|
+
}
|
|
5422
|
+
let data = "";
|
|
5423
|
+
let bytes = 0;
|
|
5424
|
+
res.on("data", (chunk) => {
|
|
5425
|
+
bytes += typeof chunk === "string" ? chunk.length : chunk.byteLength;
|
|
5426
|
+
if (bytes > PROBE_MAX_BYTES) {
|
|
5427
|
+
req.destroy();
|
|
5428
|
+
return resolveResult({ type: "not-hyperframes" });
|
|
5429
|
+
}
|
|
5430
|
+
data += chunk;
|
|
5431
|
+
});
|
|
5432
|
+
res.on("error", () => {
|
|
5433
|
+
resolveResult({ type: "not-hyperframes" });
|
|
5434
|
+
});
|
|
5435
|
+
res.on("end", () => {
|
|
5436
|
+
try {
|
|
5437
|
+
const json = JSON.parse(data);
|
|
5438
|
+
if (json.isHyperframes !== true) {
|
|
5439
|
+
return resolveResult({ type: "not-hyperframes" });
|
|
5440
|
+
}
|
|
5441
|
+
const normalize = (p) => resolve2(p).replace(/\\/g, "/").toLowerCase();
|
|
5442
|
+
if (normalize(json.projectDir) === normalizedProjectDir) {
|
|
5443
|
+
return resolveResult({ type: "match" });
|
|
5444
|
+
}
|
|
5445
|
+
return resolveResult({ type: "mismatch", projectName: json.projectName });
|
|
5446
|
+
} catch {
|
|
5447
|
+
resolveResult({ type: "not-hyperframes" });
|
|
5448
|
+
}
|
|
5449
|
+
});
|
|
5450
|
+
}
|
|
5451
|
+
);
|
|
5452
|
+
req.on("error", () => {
|
|
5453
|
+
resolveResult({ type: "not-hyperframes" });
|
|
5454
|
+
});
|
|
5455
|
+
req.on("timeout", () => {
|
|
5456
|
+
req.destroy();
|
|
5457
|
+
resolveResult({ type: "not-hyperframes" });
|
|
5458
|
+
});
|
|
5459
|
+
});
|
|
5460
|
+
}
|
|
5461
|
+
async function getProcessOnPort(port) {
|
|
5462
|
+
if (process.platform === "win32") return null;
|
|
5463
|
+
try {
|
|
5464
|
+
const { stdout: stdout2 } = await execFileAsync("lsof", [`-ti:${port}`, "-sTCP:LISTEN"], {
|
|
5465
|
+
timeout: 2e3
|
|
5466
|
+
});
|
|
5467
|
+
const pid = stdout2.trim().split("\n")[0]?.trim();
|
|
5468
|
+
return pid || null;
|
|
5469
|
+
} catch {
|
|
5470
|
+
return null;
|
|
5471
|
+
}
|
|
5472
|
+
}
|
|
5473
|
+
function probePort(port) {
|
|
5474
|
+
return new Promise((resolveResult) => {
|
|
5475
|
+
const req = http.get(
|
|
5476
|
+
{ hostname: "127.0.0.1", port, path: "/__hyperframes_config", timeout: PROBE_TIMEOUT_MS },
|
|
5477
|
+
(res) => {
|
|
5478
|
+
if (res.statusCode !== 200) {
|
|
5479
|
+
res.resume();
|
|
5480
|
+
return resolveResult(null);
|
|
5481
|
+
}
|
|
5482
|
+
let data = "";
|
|
5483
|
+
let bytes = 0;
|
|
5484
|
+
res.on("data", (chunk) => {
|
|
5485
|
+
bytes += typeof chunk === "string" ? chunk.length : chunk.byteLength;
|
|
5486
|
+
if (bytes > PROBE_MAX_BYTES) {
|
|
5487
|
+
req.destroy();
|
|
5488
|
+
return resolveResult(null);
|
|
5489
|
+
}
|
|
5490
|
+
data += chunk;
|
|
5491
|
+
});
|
|
5492
|
+
res.on("error", () => resolveResult(null));
|
|
5493
|
+
res.on("end", () => {
|
|
5494
|
+
try {
|
|
5495
|
+
const json = JSON.parse(data);
|
|
5496
|
+
resolveResult(json.isHyperframes === true ? json : null);
|
|
5497
|
+
} catch {
|
|
5498
|
+
resolveResult(null);
|
|
5499
|
+
}
|
|
5500
|
+
});
|
|
5501
|
+
}
|
|
5502
|
+
);
|
|
5503
|
+
req.on("error", () => resolveResult(null));
|
|
5504
|
+
req.on("timeout", () => {
|
|
5505
|
+
req.destroy();
|
|
5506
|
+
resolveResult(null);
|
|
5507
|
+
});
|
|
5508
|
+
});
|
|
5509
|
+
}
|
|
5510
|
+
async function scanActiveServers(startPort = 3002) {
|
|
5511
|
+
const endPort = startPort + MAX_PORT_SCAN - 1;
|
|
5512
|
+
const servers = [];
|
|
5513
|
+
const batchSize = 20;
|
|
5514
|
+
for (let batchStart = startPort; batchStart <= endPort; batchStart += batchSize) {
|
|
5515
|
+
const batchEnd = Math.min(batchStart + batchSize - 1, endPort);
|
|
5516
|
+
const ports = Array.from({ length: batchEnd - batchStart + 1 }, (_2, i) => batchStart + i);
|
|
5517
|
+
const results = await Promise.all(
|
|
5518
|
+
ports.map(async (port) => {
|
|
5519
|
+
const config = await probePort(port);
|
|
5520
|
+
if (!config) return null;
|
|
5521
|
+
const pid = await getProcessOnPort(port);
|
|
5522
|
+
return {
|
|
5523
|
+
port,
|
|
5524
|
+
projectName: config.projectName,
|
|
5525
|
+
projectDir: config.projectDir,
|
|
5526
|
+
version: config.version,
|
|
5527
|
+
pid
|
|
5528
|
+
};
|
|
5529
|
+
})
|
|
5530
|
+
);
|
|
5531
|
+
for (const r of results) {
|
|
5532
|
+
if (r) servers.push(r);
|
|
5533
|
+
}
|
|
5534
|
+
}
|
|
5535
|
+
return servers;
|
|
5536
|
+
}
|
|
5537
|
+
async function killActiveServers(startPort = 3002) {
|
|
5538
|
+
const servers = await scanActiveServers(startPort);
|
|
5539
|
+
let killed = 0;
|
|
5540
|
+
for (const server of servers) {
|
|
5541
|
+
if (server.pid) {
|
|
5542
|
+
try {
|
|
5543
|
+
process.kill(parseInt(server.pid, 10), "SIGTERM");
|
|
5544
|
+
killed++;
|
|
5545
|
+
} catch {
|
|
5546
|
+
}
|
|
5547
|
+
}
|
|
5548
|
+
}
|
|
5549
|
+
return killed;
|
|
5550
|
+
}
|
|
5551
|
+
async function findPortAndServe(fetch3, startPort, projectDir, forceNew) {
|
|
5552
|
+
const { createAdaptorServer } = await import("@hono/node-server");
|
|
5553
|
+
const normalizedDir = resolve2(projectDir).replace(/\\/g, "/").toLowerCase();
|
|
5554
|
+
const endPort = startPort + MAX_PORT_SCAN - 1;
|
|
5555
|
+
let server = null;
|
|
5556
|
+
for (let port = startPort; port <= endPort; port++) {
|
|
5557
|
+
const available = await testPortOnAllHosts(port);
|
|
5558
|
+
if (available) {
|
|
5559
|
+
if (!server) server = createAdaptorServer({ fetch: fetch3 });
|
|
5560
|
+
try {
|
|
5561
|
+
await new Promise((resolveListener, rejectListener) => {
|
|
5562
|
+
const onError = (err) => {
|
|
5563
|
+
server.removeListener("listening", onListening);
|
|
5564
|
+
rejectListener(err);
|
|
5565
|
+
};
|
|
5566
|
+
const onListening = () => {
|
|
5567
|
+
server.removeListener("error", onError);
|
|
5568
|
+
resolveListener();
|
|
5569
|
+
};
|
|
5570
|
+
server.once("error", onError);
|
|
5571
|
+
server.once("listening", onListening);
|
|
5572
|
+
server.listen(port);
|
|
5573
|
+
});
|
|
5574
|
+
return { type: "started", server, port };
|
|
5575
|
+
} catch (err) {
|
|
5576
|
+
if (err.code === "EADDRINUSE") {
|
|
5577
|
+
continue;
|
|
5578
|
+
}
|
|
5579
|
+
throw err;
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
if (!forceNew) {
|
|
5583
|
+
const detection = await detectHyperframesServer(port, normalizedDir);
|
|
5584
|
+
if (detection.type === "match") {
|
|
5585
|
+
return { type: "already-running", port };
|
|
5586
|
+
}
|
|
5587
|
+
if (detection.type === "mismatch") {
|
|
5588
|
+
console.log(
|
|
5589
|
+
` ${c.dim(`Port ${port} in use by HyperFrames project "${detection.projectName}" \u2014 skipping`)}`
|
|
5590
|
+
);
|
|
5591
|
+
continue;
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
const pid = await getProcessOnPort(port);
|
|
5595
|
+
if (pid) {
|
|
5596
|
+
console.log(` ${c.dim(`Port ${port} in use by PID ${pid} \u2014 skipping`)}`);
|
|
5597
|
+
}
|
|
5598
|
+
}
|
|
5599
|
+
throw new Error(
|
|
5600
|
+
`Ports ${startPort}\u2013${endPort} are all in use. Use --port to specify a different starting port.`
|
|
5601
|
+
);
|
|
5602
|
+
}
|
|
5603
|
+
var execFileAsync, MAX_PORT_SCAN, PROBE_TIMEOUT_MS, PROBE_MAX_BYTES;
|
|
5604
|
+
var init_portUtils = __esm({
|
|
5605
|
+
"src/server/portUtils.ts"() {
|
|
5606
|
+
"use strict";
|
|
5607
|
+
init_colors();
|
|
5608
|
+
execFileAsync = promisify(execFile);
|
|
5609
|
+
MAX_PORT_SCAN = 100;
|
|
5610
|
+
PROBE_TIMEOUT_MS = 300;
|
|
5611
|
+
PROBE_MAX_BYTES = 4096;
|
|
5612
|
+
}
|
|
5613
|
+
});
|
|
5614
|
+
|
|
5331
5615
|
// src/server/fileWatcher.ts
|
|
5332
5616
|
import { watch } from "fs";
|
|
5333
5617
|
function createProjectWatcher(projectDir) {
|
|
@@ -5372,11 +5656,11 @@ var init_fileWatcher = __esm({
|
|
|
5372
5656
|
});
|
|
5373
5657
|
|
|
5374
5658
|
// ../core/src/studio-api/helpers/safePath.ts
|
|
5375
|
-
import { resolve as
|
|
5659
|
+
import { resolve as resolve3, sep, join as join7 } from "path";
|
|
5376
5660
|
import { readdirSync as readdirSync3 } from "fs";
|
|
5377
5661
|
function isSafePath(base, resolved) {
|
|
5378
|
-
const norm =
|
|
5379
|
-
return resolved.startsWith(norm) || resolved ===
|
|
5662
|
+
const norm = resolve3(base) + sep;
|
|
5663
|
+
return resolved.startsWith(norm) || resolved === resolve3(base);
|
|
5380
5664
|
}
|
|
5381
5665
|
function walkDir(dir, prefix = "") {
|
|
5382
5666
|
const files = [];
|
|
@@ -5441,7 +5725,7 @@ import {
|
|
|
5441
5725
|
renameSync as renameSync2,
|
|
5442
5726
|
readdirSync as readdirSync4
|
|
5443
5727
|
} from "fs";
|
|
5444
|
-
import { resolve as
|
|
5728
|
+
import { resolve as resolve4, dirname, join as join8 } from "path";
|
|
5445
5729
|
async function resolveProjectFile(c2, adapter2, opts) {
|
|
5446
5730
|
const id = c2.req.param("id");
|
|
5447
5731
|
const project = await adapter2.resolveProject(id);
|
|
@@ -5452,7 +5736,7 @@ async function resolveProjectFile(c2, adapter2, opts) {
|
|
|
5452
5736
|
if (filePath.includes("\0")) {
|
|
5453
5737
|
return { error: c2.json({ error: "forbidden" }, 403) };
|
|
5454
5738
|
}
|
|
5455
|
-
const absPath =
|
|
5739
|
+
const absPath = resolve4(project.dir, filePath);
|
|
5456
5740
|
if (!isSafePath(project.dir, absPath)) {
|
|
5457
5741
|
return { error: c2.json({ error: "forbidden" }, 403) };
|
|
5458
5742
|
}
|
|
@@ -5472,7 +5756,7 @@ function generateCopyPath(projectDir, originalPath) {
|
|
|
5472
5756
|
const cleanBase = copyMatch ? base.slice(0, -copyMatch[0].length) : base;
|
|
5473
5757
|
let num = copyMatch ? copyMatch[1] ? parseInt(copyMatch[1]) + 1 : 2 : 1;
|
|
5474
5758
|
let candidate = num === 1 ? `${cleanBase} (copy)${ext}` : `${cleanBase} (copy ${num})${ext}`;
|
|
5475
|
-
while (existsSync7(
|
|
5759
|
+
while (existsSync7(resolve4(projectDir, candidate))) {
|
|
5476
5760
|
num++;
|
|
5477
5761
|
candidate = `${cleanBase} (copy ${num})${ext}`;
|
|
5478
5762
|
}
|
|
@@ -5553,7 +5837,7 @@ function registerFileRoutes(api, adapter2) {
|
|
|
5553
5837
|
if (!body.newPath || body.newPath.includes("\0")) {
|
|
5554
5838
|
return c2.json({ error: "newPath required" }, 400);
|
|
5555
5839
|
}
|
|
5556
|
-
const newAbs =
|
|
5840
|
+
const newAbs = resolve4(res.project.dir, body.newPath);
|
|
5557
5841
|
if (!isSafePath(res.project.dir, newAbs)) {
|
|
5558
5842
|
return c2.json({ error: "forbidden" }, 403);
|
|
5559
5843
|
}
|
|
@@ -5572,12 +5856,12 @@ function registerFileRoutes(api, adapter2) {
|
|
|
5572
5856
|
if (!body.path || body.path.includes("\0")) {
|
|
5573
5857
|
return c2.json({ error: "path required" }, 400);
|
|
5574
5858
|
}
|
|
5575
|
-
const srcAbs =
|
|
5859
|
+
const srcAbs = resolve4(project.dir, body.path);
|
|
5576
5860
|
if (!isSafePath(project.dir, srcAbs) || !existsSync7(srcAbs)) {
|
|
5577
5861
|
return c2.json({ error: "not found" }, 404);
|
|
5578
5862
|
}
|
|
5579
5863
|
const copyPath = generateCopyPath(project.dir, body.path);
|
|
5580
|
-
const destAbs =
|
|
5864
|
+
const destAbs = resolve4(project.dir, copyPath);
|
|
5581
5865
|
if (!isSafePath(project.dir, destAbs)) {
|
|
5582
5866
|
return c2.json({ error: "forbidden" }, 403);
|
|
5583
5867
|
}
|
|
@@ -5596,7 +5880,7 @@ function registerFileRoutes(api, adapter2) {
|
|
|
5596
5880
|
const project = await adapter2.resolveProject(c2.req.param("id"));
|
|
5597
5881
|
if (!project) return c2.json({ error: "not found" }, 404);
|
|
5598
5882
|
const subDir = c2.req.query("dir") ?? "";
|
|
5599
|
-
const targetDir = subDir ?
|
|
5883
|
+
const targetDir = subDir ? resolve4(project.dir, subDir) : project.dir;
|
|
5600
5884
|
if (!isSafePath(project.dir, targetDir)) return c2.json({ error: "forbidden" }, 403);
|
|
5601
5885
|
if (subDir && !existsSync7(targetDir)) mkdirSync5(targetDir, { recursive: true });
|
|
5602
5886
|
const formData = await c2.req.formData();
|
|
@@ -5610,7 +5894,7 @@ function registerFileRoutes(api, adapter2) {
|
|
|
5610
5894
|
skipped.push(name);
|
|
5611
5895
|
continue;
|
|
5612
5896
|
}
|
|
5613
|
-
const destPath =
|
|
5897
|
+
const destPath = resolve4(targetDir, name);
|
|
5614
5898
|
if (!isSafePath(project.dir, destPath)) continue;
|
|
5615
5899
|
let finalPath = destPath;
|
|
5616
5900
|
let finalName = name;
|
|
@@ -5619,13 +5903,13 @@ function registerFileRoutes(api, adapter2) {
|
|
|
5619
5903
|
const ext = dotIdx > 0 ? name.slice(dotIdx) : "";
|
|
5620
5904
|
const base = dotIdx > 0 ? name.slice(0, dotIdx) : name;
|
|
5621
5905
|
let n = 2;
|
|
5622
|
-
while (n < 1e4 && existsSync7(
|
|
5906
|
+
while (n < 1e4 && existsSync7(resolve4(targetDir, `${base} (${n})${ext}`))) n++;
|
|
5623
5907
|
if (n >= 1e4) {
|
|
5624
5908
|
skipped.push(name);
|
|
5625
5909
|
continue;
|
|
5626
5910
|
}
|
|
5627
5911
|
finalName = `${base} (${n})${ext}`;
|
|
5628
|
-
finalPath =
|
|
5912
|
+
finalPath = resolve4(targetDir, finalName);
|
|
5629
5913
|
}
|
|
5630
5914
|
const buffer = Buffer.from(await value.arrayBuffer());
|
|
5631
5915
|
writeFileSync4(finalPath, buffer);
|
|
@@ -9535,8 +9819,8 @@ var init_custom_element_registry = __esm({
|
|
|
9535
9819
|
} : (element) => element.localName === localName;
|
|
9536
9820
|
registry.set(localName, { Class, check });
|
|
9537
9821
|
if (waiting.has(localName)) {
|
|
9538
|
-
for (const
|
|
9539
|
-
|
|
9822
|
+
for (const resolve28 of waiting.get(localName))
|
|
9823
|
+
resolve28(Class);
|
|
9540
9824
|
waiting.delete(localName);
|
|
9541
9825
|
}
|
|
9542
9826
|
ownerDocument.querySelectorAll(
|
|
@@ -9576,13 +9860,13 @@ var init_custom_element_registry = __esm({
|
|
|
9576
9860
|
*/
|
|
9577
9861
|
whenDefined(localName) {
|
|
9578
9862
|
const { registry, waiting } = this;
|
|
9579
|
-
return new Promise((
|
|
9863
|
+
return new Promise((resolve28) => {
|
|
9580
9864
|
if (registry.has(localName))
|
|
9581
|
-
|
|
9865
|
+
resolve28(registry.get(localName).Class);
|
|
9582
9866
|
else {
|
|
9583
9867
|
if (!waiting.has(localName))
|
|
9584
9868
|
waiting.set(localName, []);
|
|
9585
|
-
waiting.get(localName).push(
|
|
9869
|
+
waiting.get(localName).push(resolve28);
|
|
9586
9870
|
}
|
|
9587
9871
|
});
|
|
9588
9872
|
}
|
|
@@ -18283,7 +18567,7 @@ var init_esm10 = __esm({
|
|
|
18283
18567
|
});
|
|
18284
18568
|
|
|
18285
18569
|
// ../core/src/compiler/rewriteSubCompPaths.ts
|
|
18286
|
-
import { join as join9, resolve as
|
|
18570
|
+
import { join as join9, resolve as resolve5, dirname as dirname2 } from "path";
|
|
18287
18571
|
function isAbsoluteOrSpecial(val) {
|
|
18288
18572
|
return !val || val.startsWith("http://") || val.startsWith("https://") || val.startsWith("//") || val.startsWith("data:") || val.startsWith("#");
|
|
18289
18573
|
}
|
|
@@ -18296,7 +18580,7 @@ function rewriteAssetPath(compSrcPath, relativePath) {
|
|
|
18296
18580
|
const compDir = dirname2(compSrcPath);
|
|
18297
18581
|
if (!compDir || compDir === ".") return relativePath;
|
|
18298
18582
|
const resolved = join9(compDir, relativePath);
|
|
18299
|
-
const normalized =
|
|
18583
|
+
const normalized = resolve5("/", resolved).slice(1);
|
|
18300
18584
|
return normalized;
|
|
18301
18585
|
}
|
|
18302
18586
|
function rewriteAssetPaths(elements, compSrcPath, getAttr2, setAttr) {
|
|
@@ -18308,7 +18592,7 @@ function rewriteAssetPaths(elements, compSrcPath, getAttr2, setAttr) {
|
|
|
18308
18592
|
if (isAbsoluteOrSpecial(val)) continue;
|
|
18309
18593
|
if (!needsRewrite(val)) continue;
|
|
18310
18594
|
const rewritten = join9(compDir, val);
|
|
18311
|
-
const normalized =
|
|
18595
|
+
const normalized = resolve5("/", rewritten).slice(1);
|
|
18312
18596
|
if (normalized !== val) {
|
|
18313
18597
|
setAttr(el, attr, normalized);
|
|
18314
18598
|
}
|
|
@@ -18397,7 +18681,7 @@ var init_subComposition = __esm({
|
|
|
18397
18681
|
|
|
18398
18682
|
// ../core/src/studio-api/routes/preview.ts
|
|
18399
18683
|
import { existsSync as existsSync9, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
|
|
18400
|
-
import { resolve as
|
|
18684
|
+
import { resolve as resolve6 } from "path";
|
|
18401
18685
|
function registerPreviewRoutes(api, adapter2) {
|
|
18402
18686
|
api.get("/projects/:id/preview", async (c2) => {
|
|
18403
18687
|
const project = await adapter2.resolveProject(c2.req.param("id"));
|
|
@@ -18405,7 +18689,7 @@ function registerPreviewRoutes(api, adapter2) {
|
|
|
18405
18689
|
try {
|
|
18406
18690
|
let bundled = await adapter2.bundle(project.dir);
|
|
18407
18691
|
if (!bundled) {
|
|
18408
|
-
const indexPath =
|
|
18692
|
+
const indexPath = resolve6(project.dir, "index.html");
|
|
18409
18693
|
if (!existsSync9(indexPath)) return c2.text("not found", 404);
|
|
18410
18694
|
bundled = readFileSync9(indexPath, "utf-8");
|
|
18411
18695
|
}
|
|
@@ -18421,7 +18705,7 @@ ${runtimeTag}`;
|
|
|
18421
18705
|
}
|
|
18422
18706
|
return c2.html(bundled);
|
|
18423
18707
|
} catch {
|
|
18424
|
-
const file =
|
|
18708
|
+
const file = resolve6(project.dir, "index.html");
|
|
18425
18709
|
if (existsSync9(file)) return c2.html(readFileSync9(file, "utf-8"));
|
|
18426
18710
|
return c2.text("not found", 404);
|
|
18427
18711
|
}
|
|
@@ -18432,7 +18716,7 @@ ${runtimeTag}`;
|
|
|
18432
18716
|
const compPath = decodeURIComponent(
|
|
18433
18717
|
c2.req.path.replace(`/projects/${project.id}/preview/comp/`, "").split("?")[0] ?? ""
|
|
18434
18718
|
);
|
|
18435
|
-
const compFile =
|
|
18719
|
+
const compFile = resolve6(project.dir, compPath);
|
|
18436
18720
|
if (!isSafePath(project.dir, compFile) || !existsSync9(compFile) || !statSync2(compFile).isFile()) {
|
|
18437
18721
|
return c2.text("not found", 404);
|
|
18438
18722
|
}
|
|
@@ -18447,7 +18731,7 @@ ${runtimeTag}`;
|
|
|
18447
18731
|
const subPath = decodeURIComponent(
|
|
18448
18732
|
c2.req.path.replace(`/projects/${project.id}/preview/`, "").split("?")[0] ?? ""
|
|
18449
18733
|
);
|
|
18450
|
-
const file =
|
|
18734
|
+
const file = resolve6(project.dir, subPath);
|
|
18451
18735
|
if (!isSafePath(project.dir, file) || !existsSync9(file) || !statSync2(file).isFile()) {
|
|
18452
18736
|
return c2.text("not found", 404);
|
|
18453
18737
|
}
|
|
@@ -19823,7 +20107,7 @@ var init_timingCompiler = __esm({
|
|
|
19823
20107
|
|
|
19824
20108
|
// ../core/src/inline-scripts/hyperframesRuntime.engine.ts
|
|
19825
20109
|
import { buildSync } from "esbuild";
|
|
19826
|
-
import { dirname as dirname3, resolve as
|
|
20110
|
+
import { dirname as dirname3, resolve as resolve7 } from "path";
|
|
19827
20111
|
import { fileURLToPath } from "url";
|
|
19828
20112
|
var init_hyperframesRuntime_engine = __esm({
|
|
19829
20113
|
"../core/src/inline-scripts/hyperframesRuntime.engine.ts"() {
|
|
@@ -19990,18 +20274,35 @@ async function getCdpSession(page) {
|
|
|
19990
20274
|
}
|
|
19991
20275
|
return client;
|
|
19992
20276
|
}
|
|
20277
|
+
async function sendBeginFrame(client, params) {
|
|
20278
|
+
for (let attempt = 0; ; attempt++) {
|
|
20279
|
+
try {
|
|
20280
|
+
return await client.send("HeadlessExperimental.beginFrame", params);
|
|
20281
|
+
} catch (err) {
|
|
20282
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
20283
|
+
const isPending = msg.includes("Another frame is pending");
|
|
20284
|
+
if (isPending && attempt < PENDING_FRAME_RETRIES) {
|
|
20285
|
+
await new Promise((r) => setTimeout(r, 50 * 2 ** attempt));
|
|
20286
|
+
continue;
|
|
20287
|
+
}
|
|
20288
|
+
if (isPending) {
|
|
20289
|
+
throw new Error(
|
|
20290
|
+
`[BeginFrame] Frame still pending after ${PENDING_FRAME_RETRIES} retries \u2014 CPU overloaded by parallel renders. Reduce concurrent renders or use --docker for isolation.`
|
|
20291
|
+
);
|
|
20292
|
+
}
|
|
20293
|
+
throw err;
|
|
20294
|
+
}
|
|
20295
|
+
}
|
|
20296
|
+
}
|
|
19993
20297
|
async function beginFrameCapture(page, options, frameTimeTicks, interval) {
|
|
19994
20298
|
const client = await getCdpSession(page);
|
|
19995
|
-
const
|
|
19996
|
-
const
|
|
19997
|
-
|
|
19998
|
-
|
|
19999
|
-
|
|
20000
|
-
|
|
20001
|
-
|
|
20002
|
-
optimizeForSpeed: true
|
|
20003
|
-
}
|
|
20004
|
-
});
|
|
20299
|
+
const isPng = options.format === "png";
|
|
20300
|
+
const screenshot = {
|
|
20301
|
+
format: isPng ? "png" : "jpeg",
|
|
20302
|
+
quality: isPng ? void 0 : options.quality ?? 80,
|
|
20303
|
+
optimizeForSpeed: true
|
|
20304
|
+
};
|
|
20305
|
+
const result = await sendBeginFrame(client, { frameTimeTicks, interval, screenshot });
|
|
20005
20306
|
let buffer;
|
|
20006
20307
|
if (result.screenshotData) {
|
|
20007
20308
|
buffer = Buffer.from(result.screenshotData, "base64");
|
|
@@ -20011,16 +20312,12 @@ async function beginFrameCapture(page, options, frameTimeTicks, interval) {
|
|
|
20011
20312
|
if (cached2) {
|
|
20012
20313
|
buffer = cached2;
|
|
20013
20314
|
} else {
|
|
20014
|
-
const
|
|
20315
|
+
const fallback = await sendBeginFrame(client, {
|
|
20015
20316
|
frameTimeTicks: frameTimeTicks + 1e-3,
|
|
20016
20317
|
interval,
|
|
20017
|
-
screenshot
|
|
20018
|
-
format,
|
|
20019
|
-
quality: format === "jpeg" ? options.quality ?? 80 : void 0,
|
|
20020
|
-
optimizeForSpeed: true
|
|
20021
|
-
}
|
|
20318
|
+
screenshot
|
|
20022
20319
|
});
|
|
20023
|
-
buffer =
|
|
20320
|
+
buffer = fallback.screenshotData ? Buffer.from(fallback.screenshotData, "base64") : Buffer.alloc(0);
|
|
20024
20321
|
if (buffer.length > 0) lastFrameCache.set(page, buffer);
|
|
20025
20322
|
}
|
|
20026
20323
|
}
|
|
@@ -20134,13 +20431,14 @@ async function syncVideoFrameVisibility(page, activeVideoIds) {
|
|
|
20134
20431
|
}
|
|
20135
20432
|
}, activeVideoIds);
|
|
20136
20433
|
}
|
|
20137
|
-
var cdpSessionCache, lastFrameCache;
|
|
20434
|
+
var cdpSessionCache, lastFrameCache, PENDING_FRAME_RETRIES;
|
|
20138
20435
|
var init_screenshotService = __esm({
|
|
20139
20436
|
"../engine/src/services/screenshotService.ts"() {
|
|
20140
20437
|
"use strict";
|
|
20141
20438
|
init_src();
|
|
20142
20439
|
cdpSessionCache = /* @__PURE__ */ new WeakMap();
|
|
20143
20440
|
lastFrameCache = /* @__PURE__ */ new WeakMap();
|
|
20441
|
+
PENDING_FRAME_RETRIES = 5;
|
|
20144
20442
|
}
|
|
20145
20443
|
});
|
|
20146
20444
|
|
|
@@ -20472,7 +20770,7 @@ var init_frameCapture = __esm({
|
|
|
20472
20770
|
// ../engine/src/utils/gpuEncoder.ts
|
|
20473
20771
|
import { spawn as spawn2 } from "child_process";
|
|
20474
20772
|
async function detectGpuEncoder() {
|
|
20475
|
-
return new Promise((
|
|
20773
|
+
return new Promise((resolve28) => {
|
|
20476
20774
|
const ffmpeg = spawn2("ffmpeg", ["-encoders"], {
|
|
20477
20775
|
stdio: ["pipe", "pipe", "pipe"]
|
|
20478
20776
|
});
|
|
@@ -20481,13 +20779,13 @@ async function detectGpuEncoder() {
|
|
|
20481
20779
|
stdout2 += data.toString();
|
|
20482
20780
|
});
|
|
20483
20781
|
ffmpeg.on("close", () => {
|
|
20484
|
-
if (stdout2.includes("h264_nvenc"))
|
|
20485
|
-
else if (stdout2.includes("h264_videotoolbox"))
|
|
20486
|
-
else if (stdout2.includes("h264_vaapi"))
|
|
20487
|
-
else if (stdout2.includes("h264_qsv"))
|
|
20488
|
-
else
|
|
20782
|
+
if (stdout2.includes("h264_nvenc")) resolve28("nvenc");
|
|
20783
|
+
else if (stdout2.includes("h264_videotoolbox")) resolve28("videotoolbox");
|
|
20784
|
+
else if (stdout2.includes("h264_vaapi")) resolve28("vaapi");
|
|
20785
|
+
else if (stdout2.includes("h264_qsv")) resolve28("qsv");
|
|
20786
|
+
else resolve28(null);
|
|
20489
20787
|
});
|
|
20490
|
-
ffmpeg.on("error", () =>
|
|
20788
|
+
ffmpeg.on("error", () => resolve28(null));
|
|
20491
20789
|
});
|
|
20492
20790
|
}
|
|
20493
20791
|
async function getCachedGpuEncoder() {
|
|
@@ -20526,7 +20824,7 @@ async function runFfmpeg(args, opts) {
|
|
|
20526
20824
|
const signal = opts?.signal;
|
|
20527
20825
|
const timeout = opts?.timeout ?? DEFAULT_TIMEOUT;
|
|
20528
20826
|
const onStderr = opts?.onStderr;
|
|
20529
|
-
return new Promise((
|
|
20827
|
+
return new Promise((resolve28) => {
|
|
20530
20828
|
const ffmpeg = spawn3("ffmpeg", args);
|
|
20531
20829
|
let stderr = "";
|
|
20532
20830
|
const onAbort = () => {
|
|
@@ -20552,7 +20850,7 @@ async function runFfmpeg(args, opts) {
|
|
|
20552
20850
|
ffmpeg.on("close", (code) => {
|
|
20553
20851
|
clearTimeout(timer);
|
|
20554
20852
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
20555
|
-
|
|
20853
|
+
resolve28({
|
|
20556
20854
|
success: !signal?.aborted && code === 0,
|
|
20557
20855
|
exitCode: code,
|
|
20558
20856
|
stderr,
|
|
@@ -20562,7 +20860,7 @@ async function runFfmpeg(args, opts) {
|
|
|
20562
20860
|
ffmpeg.on("error", (err) => {
|
|
20563
20861
|
clearTimeout(timer);
|
|
20564
20862
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
20565
|
-
|
|
20863
|
+
resolve28({
|
|
20566
20864
|
success: false,
|
|
20567
20865
|
exitCode: null,
|
|
20568
20866
|
stderr: err.message,
|
|
@@ -20721,7 +21019,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
20721
21019
|
const inputPath = join17(framesDir, framePattern);
|
|
20722
21020
|
const inputArgs = ["-framerate", String(options.fps), "-i", inputPath];
|
|
20723
21021
|
const args = buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder);
|
|
20724
|
-
return new Promise((
|
|
21022
|
+
return new Promise((resolve28) => {
|
|
20725
21023
|
const ffmpeg = spawn4("ffmpeg", args);
|
|
20726
21024
|
let stderr = "";
|
|
20727
21025
|
const onAbort = () => {
|
|
@@ -20746,7 +21044,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
20746
21044
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
20747
21045
|
const durationMs = Date.now() - startTime;
|
|
20748
21046
|
if (signal?.aborted) {
|
|
20749
|
-
|
|
21047
|
+
resolve28({
|
|
20750
21048
|
success: false,
|
|
20751
21049
|
outputPath,
|
|
20752
21050
|
durationMs,
|
|
@@ -20757,7 +21055,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
20757
21055
|
return;
|
|
20758
21056
|
}
|
|
20759
21057
|
if (code !== 0) {
|
|
20760
|
-
|
|
21058
|
+
resolve28({
|
|
20761
21059
|
success: false,
|
|
20762
21060
|
outputPath,
|
|
20763
21061
|
durationMs,
|
|
@@ -20768,12 +21066,12 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
20768
21066
|
return;
|
|
20769
21067
|
}
|
|
20770
21068
|
const fileSize = existsSync15(outputPath) ? statSync4(outputPath).size : 0;
|
|
20771
|
-
|
|
21069
|
+
resolve28({ success: true, outputPath, durationMs, framesEncoded: frameCount, fileSize });
|
|
20772
21070
|
});
|
|
20773
21071
|
ffmpeg.on("error", (err) => {
|
|
20774
21072
|
clearTimeout(timer);
|
|
20775
21073
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
20776
|
-
|
|
21074
|
+
resolve28({
|
|
20777
21075
|
success: false,
|
|
20778
21076
|
outputPath,
|
|
20779
21077
|
durationMs: Date.now() - startTime,
|
|
@@ -20831,18 +21129,18 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
20831
21129
|
let gpuEncoder = null;
|
|
20832
21130
|
if (options.useGpu) gpuEncoder = await getCachedGpuEncoder();
|
|
20833
21131
|
const args = buildEncoderArgs(options, inputArgs, chunkPath, gpuEncoder);
|
|
20834
|
-
const chunkResult = await new Promise((
|
|
21132
|
+
const chunkResult = await new Promise((resolve28) => {
|
|
20835
21133
|
const ffmpeg = spawn4("ffmpeg", args);
|
|
20836
21134
|
let stderr = "";
|
|
20837
21135
|
ffmpeg.stderr.on("data", (d) => {
|
|
20838
21136
|
stderr += d.toString();
|
|
20839
21137
|
});
|
|
20840
21138
|
ffmpeg.on("close", (code) => {
|
|
20841
|
-
if (code === 0)
|
|
20842
|
-
else
|
|
21139
|
+
if (code === 0) resolve28({ success: true });
|
|
21140
|
+
else resolve28({ success: false, error: `Chunk ${i} encode failed: ${stderr.slice(-400)}` });
|
|
20843
21141
|
});
|
|
20844
21142
|
ffmpeg.on("error", (err) => {
|
|
20845
|
-
|
|
21143
|
+
resolve28({ success: false, error: `Chunk ${i} encode error: ${err.message}` });
|
|
20846
21144
|
});
|
|
20847
21145
|
});
|
|
20848
21146
|
if (!chunkResult.success) {
|
|
@@ -20872,18 +21170,18 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
20872
21170
|
"-y",
|
|
20873
21171
|
outputPath
|
|
20874
21172
|
];
|
|
20875
|
-
const concatResult = await new Promise((
|
|
21173
|
+
const concatResult = await new Promise((resolve28) => {
|
|
20876
21174
|
const ffmpeg = spawn4("ffmpeg", concatArgs);
|
|
20877
21175
|
let stderr = "";
|
|
20878
21176
|
ffmpeg.stderr.on("data", (d) => {
|
|
20879
21177
|
stderr += d.toString();
|
|
20880
21178
|
});
|
|
20881
21179
|
ffmpeg.on("close", (code) => {
|
|
20882
|
-
if (code === 0)
|
|
20883
|
-
else
|
|
21180
|
+
if (code === 0) resolve28({ success: true });
|
|
21181
|
+
else resolve28({ success: false, error: `Chunk concat failed: ${stderr.slice(-400)}` });
|
|
20884
21182
|
});
|
|
20885
21183
|
ffmpeg.on("error", (err) => {
|
|
20886
|
-
|
|
21184
|
+
resolve28({ success: false, error: `Chunk concat error: ${err.message}` });
|
|
20887
21185
|
});
|
|
20888
21186
|
});
|
|
20889
21187
|
if (!concatResult.success) {
|
|
@@ -20991,16 +21289,16 @@ function createFrameReorderBuffer(startFrame, endFrame) {
|
|
|
20991
21289
|
}
|
|
20992
21290
|
};
|
|
20993
21291
|
return {
|
|
20994
|
-
waitForFrame: (frame) => new Promise((
|
|
20995
|
-
waiters.push({ frame, resolve:
|
|
21292
|
+
waitForFrame: (frame) => new Promise((resolve28) => {
|
|
21293
|
+
waiters.push({ frame, resolve: resolve28 });
|
|
20996
21294
|
resolveWaiters();
|
|
20997
21295
|
}),
|
|
20998
21296
|
advanceTo: (frame) => {
|
|
20999
21297
|
nextFrame = frame;
|
|
21000
21298
|
resolveWaiters();
|
|
21001
21299
|
},
|
|
21002
|
-
waitForAllDone: () => new Promise((
|
|
21003
|
-
waiters.push({ frame: endFrame, resolve:
|
|
21300
|
+
waitForAllDone: () => new Promise((resolve28) => {
|
|
21301
|
+
waiters.push({ frame: endFrame, resolve: resolve28 });
|
|
21004
21302
|
resolveWaiters();
|
|
21005
21303
|
})
|
|
21006
21304
|
};
|
|
@@ -21129,7 +21427,7 @@ async function spawnStreamingEncoder(outputPath, options, signal, config) {
|
|
|
21129
21427
|
let stderr = "";
|
|
21130
21428
|
let exitCode = null;
|
|
21131
21429
|
let exitPromiseResolve = null;
|
|
21132
|
-
const exitPromise = new Promise((
|
|
21430
|
+
const exitPromise = new Promise((resolve28) => exitPromiseResolve = resolve28);
|
|
21133
21431
|
ffmpeg.stderr?.on("data", (data) => {
|
|
21134
21432
|
stderr += data.toString();
|
|
21135
21433
|
});
|
|
@@ -21173,8 +21471,8 @@ Process error: ${err.message}`;
|
|
|
21173
21471
|
clearTimeout(timer);
|
|
21174
21472
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
21175
21473
|
if (ffmpeg.stdin && !ffmpeg.stdin.destroyed) {
|
|
21176
|
-
await new Promise((
|
|
21177
|
-
ffmpeg.stdin.end(() =>
|
|
21474
|
+
await new Promise((resolve28) => {
|
|
21475
|
+
ffmpeg.stdin.end(() => resolve28());
|
|
21178
21476
|
});
|
|
21179
21477
|
}
|
|
21180
21478
|
await exitPromise;
|
|
@@ -21213,7 +21511,7 @@ var init_streamingEncoder = __esm({
|
|
|
21213
21511
|
// ../engine/src/utils/ffprobe.ts
|
|
21214
21512
|
import { spawn as spawn6 } from "child_process";
|
|
21215
21513
|
function runFfprobe(args) {
|
|
21216
|
-
return new Promise((
|
|
21514
|
+
return new Promise((resolve28, reject) => {
|
|
21217
21515
|
const proc = spawn6("ffprobe", args);
|
|
21218
21516
|
let stdout2 = "";
|
|
21219
21517
|
let stderr = "";
|
|
@@ -21227,7 +21525,7 @@ function runFfprobe(args) {
|
|
|
21227
21525
|
if (code !== 0) {
|
|
21228
21526
|
reject(new Error(`[FFmpeg] ffprobe exited with code ${code}: ${stderr}`));
|
|
21229
21527
|
} else {
|
|
21230
|
-
|
|
21528
|
+
resolve28(stdout2);
|
|
21231
21529
|
}
|
|
21232
21530
|
});
|
|
21233
21531
|
proc.on("error", (err) => {
|
|
@@ -21524,7 +21822,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
21524
21822
|
];
|
|
21525
21823
|
if (format === "png") args.push("-compression_level", "6");
|
|
21526
21824
|
args.push("-y", outputPattern);
|
|
21527
|
-
return new Promise((
|
|
21825
|
+
return new Promise((resolve28, reject) => {
|
|
21528
21826
|
const ffmpeg = spawn7("ffmpeg", args);
|
|
21529
21827
|
let stderr = "";
|
|
21530
21828
|
const onAbort = () => {
|
|
@@ -21559,7 +21857,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
21559
21857
|
files.forEach((file, index) => {
|
|
21560
21858
|
framePaths.set(index, join19(videoOutputDir, file));
|
|
21561
21859
|
});
|
|
21562
|
-
|
|
21860
|
+
resolve28({
|
|
21563
21861
|
videoId,
|
|
21564
21862
|
srcPath: videoPath,
|
|
21565
21863
|
outputDir: videoOutputDir,
|
|
@@ -21581,7 +21879,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
21581
21879
|
});
|
|
21582
21880
|
});
|
|
21583
21881
|
}
|
|
21584
|
-
async function extractAllVideoFrames(videos, baseDir, options, signal, config) {
|
|
21882
|
+
async function extractAllVideoFrames(videos, baseDir, options, signal, config, compiledDir) {
|
|
21585
21883
|
const startTime = Date.now();
|
|
21586
21884
|
const extracted = [];
|
|
21587
21885
|
const errors = [];
|
|
@@ -21594,7 +21892,8 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config) {
|
|
|
21594
21892
|
try {
|
|
21595
21893
|
let videoPath = video.src;
|
|
21596
21894
|
if (!videoPath.startsWith("/") && !isHttpUrl(videoPath)) {
|
|
21597
|
-
|
|
21895
|
+
const fromCompiled = compiledDir ? join19(compiledDir, videoPath) : null;
|
|
21896
|
+
videoPath = fromCompiled && existsSync18(fromCompiled) ? fromCompiled : join19(baseDir, videoPath);
|
|
21598
21897
|
}
|
|
21599
21898
|
if (isHttpUrl(videoPath)) {
|
|
21600
21899
|
const downloadDir = join19(options.outputDir, "_downloads");
|
|
@@ -22072,7 +22371,7 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config)
|
|
|
22072
22371
|
tracksProcessed: tracks.length
|
|
22073
22372
|
};
|
|
22074
22373
|
}
|
|
22075
|
-
async function processCompositionAudio(elements, baseDir, workDir, outputPath, totalDuration, signal, config) {
|
|
22374
|
+
async function processCompositionAudio(elements, baseDir, workDir, outputPath, totalDuration, signal, config, compiledDir) {
|
|
22076
22375
|
const startMs = Date.now();
|
|
22077
22376
|
const tracks = [];
|
|
22078
22377
|
const errors = [];
|
|
@@ -22086,7 +22385,8 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
22086
22385
|
try {
|
|
22087
22386
|
let srcPath = element.src;
|
|
22088
22387
|
if (!srcPath.startsWith("/") && !isHttpUrl(srcPath)) {
|
|
22089
|
-
|
|
22388
|
+
const fromCompiled = compiledDir ? join20(compiledDir, srcPath) : null;
|
|
22389
|
+
srcPath = fromCompiled && existsSync19(fromCompiled) ? fromCompiled : join20(baseDir, srcPath);
|
|
22090
22390
|
}
|
|
22091
22391
|
if (isHttpUrl(srcPath)) {
|
|
22092
22392
|
try {
|
|
@@ -22099,7 +22399,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
22099
22399
|
}
|
|
22100
22400
|
}
|
|
22101
22401
|
if (!existsSync19(srcPath)) {
|
|
22102
|
-
errors.push(`Source not found: ${element.id}`);
|
|
22402
|
+
errors.push(`Source not found: ${element.id} (${element.src})`);
|
|
22103
22403
|
return;
|
|
22104
22404
|
}
|
|
22105
22405
|
if (element.end - element.start <= 0) {
|
|
@@ -22460,11 +22760,11 @@ function createFileServer(options) {
|
|
|
22460
22760
|
headers: { "Content-Type": contentType }
|
|
22461
22761
|
});
|
|
22462
22762
|
});
|
|
22463
|
-
return new Promise((
|
|
22763
|
+
return new Promise((resolve28) => {
|
|
22464
22764
|
const server = serve({ fetch: app.fetch, port }, (info) => {
|
|
22465
22765
|
const actualPort = info.port;
|
|
22466
22766
|
const url = `http://localhost:${actualPort}`;
|
|
22467
|
-
|
|
22767
|
+
resolve28({
|
|
22468
22768
|
url,
|
|
22469
22769
|
port: actualPort,
|
|
22470
22770
|
close: () => server.close()
|
|
@@ -22578,9 +22878,9 @@ var init_src2 = __esm({
|
|
|
22578
22878
|
});
|
|
22579
22879
|
|
|
22580
22880
|
// ../core/src/compiler/htmlCompiler.ts
|
|
22581
|
-
import { resolve as
|
|
22881
|
+
import { resolve as resolve8 } from "path";
|
|
22582
22882
|
function resolveMediaSrc(src, projectDir) {
|
|
22583
|
-
return src.startsWith("http://") || src.startsWith("https://") ? src :
|
|
22883
|
+
return src.startsWith("http://") || src.startsWith("https://") ? src : resolve8(projectDir, src);
|
|
22584
22884
|
}
|
|
22585
22885
|
async function compileHtml(rawHtml, projectDir, probeMediaDuration) {
|
|
22586
22886
|
const { html: staticCompiled, unresolved } = compileTimingAttrs(rawHtml);
|
|
@@ -22659,7 +22959,7 @@ var init_staticGuard = __esm({
|
|
|
22659
22959
|
|
|
22660
22960
|
// ../core/src/compiler/htmlBundler.ts
|
|
22661
22961
|
import { readFileSync as readFileSync14, existsSync as existsSync22 } from "fs";
|
|
22662
|
-
import { join as join23, resolve as
|
|
22962
|
+
import { join as join23, resolve as resolve9, isAbsolute, sep as sep2 } from "path";
|
|
22663
22963
|
import { transformSync } from "esbuild";
|
|
22664
22964
|
function parseHTMLContent(html) {
|
|
22665
22965
|
const trimmed = html.trimStart().toLowerCase();
|
|
@@ -22669,9 +22969,9 @@ function parseHTMLContent(html) {
|
|
|
22669
22969
|
return parseHTML(`<!DOCTYPE html><html><head></head><body>${html}</body></html>`).document;
|
|
22670
22970
|
}
|
|
22671
22971
|
function safePath(projectDir, relativePath) {
|
|
22672
|
-
const resolved =
|
|
22673
|
-
const normalizedBase =
|
|
22674
|
-
if (!resolved.startsWith(normalizedBase) && resolved !==
|
|
22972
|
+
const resolved = resolve9(projectDir, relativePath);
|
|
22973
|
+
const normalizedBase = resolve9(projectDir) + sep2;
|
|
22974
|
+
if (!resolved.startsWith(normalizedBase) && resolved !== resolve9(projectDir)) return null;
|
|
22675
22975
|
return resolved;
|
|
22676
22976
|
}
|
|
22677
22977
|
function stripEmbeddedRuntimeScripts2(html) {
|
|
@@ -23206,7 +23506,7 @@ var init_compiler = __esm({
|
|
|
23206
23506
|
// ../producer/src/services/hyperframeRuntimeLoader.ts
|
|
23207
23507
|
import { createHash as createHash2 } from "crypto";
|
|
23208
23508
|
import { existsSync as existsSync23, readFileSync as readFileSync15 } from "fs";
|
|
23209
|
-
import { dirname as dirname7, resolve as
|
|
23509
|
+
import { dirname as dirname7, resolve as resolve10 } from "path";
|
|
23210
23510
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
23211
23511
|
function resolveHyperframeManifestPath() {
|
|
23212
23512
|
if (process.env.PRODUCER_HYPERFRAME_MANIFEST_PATH) {
|
|
@@ -23242,7 +23542,7 @@ function resolveVerifiedHyperframeRuntime() {
|
|
|
23242
23542
|
`[HyperframeRuntimeLoader] Invalid manifest at ${manifestPath}; missing iife artifact or sha256.`
|
|
23243
23543
|
);
|
|
23244
23544
|
}
|
|
23245
|
-
const runtimePath =
|
|
23545
|
+
const runtimePath = resolve10(dirname7(manifestPath), runtimeFileName);
|
|
23246
23546
|
if (!existsSync23(runtimePath)) {
|
|
23247
23547
|
throw new Error(`[HyperframeRuntimeLoader] Missing runtime artifact at ${runtimePath}.`);
|
|
23248
23548
|
}
|
|
@@ -23266,18 +23566,18 @@ var init_hyperframeRuntimeLoader = __esm({
|
|
|
23266
23566
|
"../producer/src/services/hyperframeRuntimeLoader.ts"() {
|
|
23267
23567
|
"use strict";
|
|
23268
23568
|
PRODUCER_DIR = dirname7(fileURLToPath2(import.meta.url));
|
|
23269
|
-
SIBLING_MANIFEST_PATH =
|
|
23270
|
-
MODULE_RELATIVE_MANIFEST_PATH =
|
|
23569
|
+
SIBLING_MANIFEST_PATH = resolve10(PRODUCER_DIR, "hyperframe.manifest.json");
|
|
23570
|
+
MODULE_RELATIVE_MANIFEST_PATH = resolve10(
|
|
23271
23571
|
PRODUCER_DIR,
|
|
23272
23572
|
"../../../core/dist/hyperframe.manifest.json"
|
|
23273
23573
|
);
|
|
23274
23574
|
CWD_RELATIVE_MANIFEST_PATHS = [
|
|
23275
23575
|
// When bundled to a single file (dist/public-server.js), the manifest
|
|
23276
23576
|
// is copied as a sibling by build.mjs
|
|
23277
|
-
|
|
23278
|
-
|
|
23279
|
-
|
|
23280
|
-
|
|
23577
|
+
resolve10(PRODUCER_DIR, "hyperframe.manifest.json"),
|
|
23578
|
+
resolve10(process.cwd(), "packages/core/dist/hyperframe.manifest.json"),
|
|
23579
|
+
resolve10(process.cwd(), "../core/dist/hyperframe.manifest.json"),
|
|
23580
|
+
resolve10(process.cwd(), "core/dist/hyperframe.manifest.json")
|
|
23281
23581
|
];
|
|
23282
23582
|
}
|
|
23283
23583
|
});
|
|
@@ -23380,10 +23680,10 @@ function createFileServer2(options) {
|
|
|
23380
23680
|
headers: { "Content-Type": contentType }
|
|
23381
23681
|
});
|
|
23382
23682
|
});
|
|
23383
|
-
return new Promise((
|
|
23683
|
+
return new Promise((resolve28) => {
|
|
23384
23684
|
const connections = /* @__PURE__ */ new Set();
|
|
23385
23685
|
const server = serve2({ fetch: app.fetch, port }, (info) => {
|
|
23386
|
-
|
|
23686
|
+
resolve28({
|
|
23387
23687
|
url: `http://localhost:${info.port}`,
|
|
23388
23688
|
port: info.port,
|
|
23389
23689
|
close: () => {
|
|
@@ -23906,7 +24206,7 @@ var init_deterministicFonts = __esm({
|
|
|
23906
24206
|
|
|
23907
24207
|
// ../producer/src/services/htmlCompiler.ts
|
|
23908
24208
|
import { readFileSync as readFileSync17, existsSync as existsSync25, mkdirSync as mkdirSync15 } from "fs";
|
|
23909
|
-
import { join as join25, dirname as dirname8, resolve as
|
|
24209
|
+
import { join as join25, dirname as dirname8, resolve as resolve11 } from "path";
|
|
23910
24210
|
import postcss from "postcss";
|
|
23911
24211
|
function dedupeElementsById(elements) {
|
|
23912
24212
|
const deduped = /* @__PURE__ */ new Map();
|
|
@@ -23991,7 +24291,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
|
|
|
23991
24291
|
const elEnd = elEndRaw ? parseFloat(elEndRaw) : Infinity;
|
|
23992
24292
|
const absoluteStart = parentOffset + elStart;
|
|
23993
24293
|
const absoluteEnd = Math.min(parentEnd, isFinite(elEnd) ? parentOffset + elEnd : Infinity);
|
|
23994
|
-
const filePath =
|
|
24294
|
+
const filePath = resolve11(projectDir, srcPath);
|
|
23995
24295
|
if (visited.has(filePath)) {
|
|
23996
24296
|
continue;
|
|
23997
24297
|
}
|
|
@@ -24191,7 +24491,7 @@ function inlineSubCompositions(html, subCompositions, projectDir) {
|
|
|
24191
24491
|
if (!srcPath) continue;
|
|
24192
24492
|
let compHtml = subCompositions.get(srcPath) || null;
|
|
24193
24493
|
if (!compHtml) {
|
|
24194
|
-
const filePath =
|
|
24494
|
+
const filePath = resolve11(projectDir, srcPath);
|
|
24195
24495
|
if (existsSync25(filePath)) {
|
|
24196
24496
|
compHtml = readFileSync17(filePath, "utf-8");
|
|
24197
24497
|
}
|
|
@@ -24404,7 +24704,7 @@ ${safeText}
|
|
|
24404
24704
|
return result;
|
|
24405
24705
|
}
|
|
24406
24706
|
function collectExternalAssets(html, projectDir) {
|
|
24407
|
-
const absProjectDir =
|
|
24707
|
+
const absProjectDir = resolve11(projectDir);
|
|
24408
24708
|
const externalAssets = /* @__PURE__ */ new Map();
|
|
24409
24709
|
const CSS_URL_RE2 = /\burl\(\s*(["']?)([^)"']+)\1\s*\)/g;
|
|
24410
24710
|
function processPath(rawPath) {
|
|
@@ -24412,7 +24712,7 @@ function collectExternalAssets(html, projectDir) {
|
|
|
24412
24712
|
if (!trimmed || trimmed.startsWith("/") || trimmed.startsWith("http://") || trimmed.startsWith("https://") || trimmed.startsWith("//") || trimmed.startsWith("data:") || trimmed.startsWith("#")) {
|
|
24413
24713
|
return null;
|
|
24414
24714
|
}
|
|
24415
|
-
const absPath =
|
|
24715
|
+
const absPath = resolve11(absProjectDir, trimmed);
|
|
24416
24716
|
if (absPath.startsWith(absProjectDir + "/") || absPath === absProjectDir) {
|
|
24417
24717
|
return null;
|
|
24418
24718
|
}
|
|
@@ -24489,7 +24789,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
|
24489
24789
|
const audios = dedupeElementsById([...mainAudios, ...subAudios]);
|
|
24490
24790
|
for (const video of videos) {
|
|
24491
24791
|
if (isHttpUrl(video.src)) continue;
|
|
24492
|
-
const videoPath =
|
|
24792
|
+
const videoPath = resolve11(projectDir, video.src);
|
|
24493
24793
|
const reencode = `ffmpeg -i "${video.src}" -c:v libx264 -r 30 -g 30 -keyint_min 30 -movflags +faststart -c:a copy output.mp4`;
|
|
24494
24794
|
Promise.all([analyzeKeyframeIntervals(videoPath), extractVideoMetadata(videoPath)]).then(([analysis, metadata]) => {
|
|
24495
24795
|
if (analysis.isProblematic) {
|
|
@@ -24682,7 +24982,7 @@ import {
|
|
|
24682
24982
|
copyFileSync as copyFileSync2,
|
|
24683
24983
|
appendFileSync
|
|
24684
24984
|
} from "fs";
|
|
24685
|
-
import { join as join26, dirname as dirname9, resolve as
|
|
24985
|
+
import { join as join26, dirname as dirname9, resolve as resolve12 } from "path";
|
|
24686
24986
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
24687
24987
|
import { freemem as freemem2 } from "os";
|
|
24688
24988
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -24747,7 +25047,7 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
|
|
|
24747
25047
|
writeFileSync8(outPath, html, "utf-8");
|
|
24748
25048
|
}
|
|
24749
25049
|
for (const [relativePath, absolutePath] of compiled.externalAssets) {
|
|
24750
|
-
const outPath =
|
|
25050
|
+
const outPath = resolve12(join26(compileDir, relativePath));
|
|
24751
25051
|
if (!outPath.startsWith(compileDir + "/")) {
|
|
24752
25052
|
console.warn(`[Render] Skipping external asset with unsafe path: ${relativePath}`);
|
|
24753
25053
|
continue;
|
|
@@ -24820,7 +25120,7 @@ function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
|
24820
25120
|
}
|
|
24821
25121
|
async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSignal) {
|
|
24822
25122
|
const moduleDir = dirname9(fileURLToPath3(import.meta.url));
|
|
24823
|
-
const producerRoot = process.env.PRODUCER_RENDERS_DIR ?
|
|
25123
|
+
const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve12(process.env.PRODUCER_RENDERS_DIR, "..") : resolve12(moduleDir, "../..");
|
|
24824
25124
|
const debugDir = join26(producerRoot, ".debug");
|
|
24825
25125
|
const workDir = job.config.debug ? join26(debugDir, job.id) : join26(dirname9(outputPath), `work-${job.id}`);
|
|
24826
25126
|
const pipelineStart = Date.now();
|
|
@@ -25097,12 +25397,15 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
25097
25397
|
const stage2Start = Date.now();
|
|
25098
25398
|
updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress);
|
|
25099
25399
|
let frameLookup = null;
|
|
25400
|
+
const compiledDir = join26(workDir, "compiled");
|
|
25100
25401
|
if (composition.videos.length > 0) {
|
|
25101
25402
|
const extractionResult = await extractAllVideoFrames(
|
|
25102
25403
|
composition.videos,
|
|
25103
25404
|
projectDir,
|
|
25104
25405
|
{ fps: job.config.fps, outputDir: join26(workDir, "video-frames") },
|
|
25105
|
-
abortSignal
|
|
25406
|
+
abortSignal,
|
|
25407
|
+
void 0,
|
|
25408
|
+
compiledDir
|
|
25106
25409
|
);
|
|
25107
25410
|
assertNotAborted();
|
|
25108
25411
|
if (extractionResult.extracted.length > 0) {
|
|
@@ -25142,7 +25445,9 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
25142
25445
|
join26(workDir, "audio-work"),
|
|
25143
25446
|
audioOutputPath,
|
|
25144
25447
|
job.duration,
|
|
25145
|
-
abortSignal
|
|
25448
|
+
abortSignal,
|
|
25449
|
+
void 0,
|
|
25450
|
+
compiledDir
|
|
25146
25451
|
);
|
|
25147
25452
|
assertNotAborted();
|
|
25148
25453
|
hasAudio = audioResult.success;
|
|
@@ -25589,7 +25894,7 @@ var init_config3 = __esm({
|
|
|
25589
25894
|
|
|
25590
25895
|
// ../producer/src/services/hyperframeLint.ts
|
|
25591
25896
|
import { existsSync as existsSync27, readFileSync as readFileSync19, statSync as statSync8 } from "fs";
|
|
25592
|
-
import { resolve as
|
|
25897
|
+
import { resolve as resolve13, join as join27 } from "path";
|
|
25593
25898
|
function isStringRecord(value) {
|
|
25594
25899
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
25595
25900
|
return false;
|
|
@@ -25616,7 +25921,7 @@ function pickEntryFile(files, preferredEntryFile) {
|
|
|
25616
25921
|
return null;
|
|
25617
25922
|
}
|
|
25618
25923
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
25619
|
-
const absProjectDir =
|
|
25924
|
+
const absProjectDir = resolve13(projectDir);
|
|
25620
25925
|
if (!existsSync27(absProjectDir) || !statSync8(absProjectDir).isDirectory()) {
|
|
25621
25926
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
25622
25927
|
}
|
|
@@ -25624,7 +25929,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
25624
25929
|
(value) => typeof value === "string" && value.trim().length > 0
|
|
25625
25930
|
);
|
|
25626
25931
|
for (const entryFile of entryCandidates) {
|
|
25627
|
-
const absoluteEntryPath =
|
|
25932
|
+
const absoluteEntryPath = resolve13(absProjectDir, entryFile);
|
|
25628
25933
|
if (!absoluteEntryPath.startsWith(absProjectDir)) {
|
|
25629
25934
|
return { error: `Entry file must stay inside project directory: ${entryFile}` };
|
|
25630
25935
|
}
|
|
@@ -25684,19 +25989,59 @@ var init_hyperframeLint = __esm({
|
|
|
25684
25989
|
});
|
|
25685
25990
|
|
|
25686
25991
|
// ../producer/src/utils/paths.ts
|
|
25687
|
-
import { resolve as
|
|
25992
|
+
import { resolve as resolve14, basename, join as join28 } from "path";
|
|
25688
25993
|
function resolveRenderPaths(projectDir, outputPath, rendersDir = DEFAULT_RENDERS_DIR) {
|
|
25689
|
-
const absoluteProjectDir =
|
|
25994
|
+
const absoluteProjectDir = resolve14(projectDir);
|
|
25690
25995
|
const projectName = basename(absoluteProjectDir);
|
|
25691
25996
|
const resolvedOutputPath = outputPath ?? join28(rendersDir, `${projectName}.mp4`);
|
|
25692
|
-
const absoluteOutputPath =
|
|
25997
|
+
const absoluteOutputPath = resolve14(resolvedOutputPath);
|
|
25693
25998
|
return { absoluteProjectDir, absoluteOutputPath };
|
|
25694
25999
|
}
|
|
25695
26000
|
var DEFAULT_RENDERS_DIR;
|
|
25696
26001
|
var init_paths = __esm({
|
|
25697
26002
|
"../producer/src/utils/paths.ts"() {
|
|
25698
26003
|
"use strict";
|
|
25699
|
-
DEFAULT_RENDERS_DIR = process.env.PRODUCER_RENDERS_DIR ??
|
|
26004
|
+
DEFAULT_RENDERS_DIR = process.env.PRODUCER_RENDERS_DIR ?? resolve14(new URL(import.meta.url).pathname, "../../..", "renders");
|
|
26005
|
+
}
|
|
26006
|
+
});
|
|
26007
|
+
|
|
26008
|
+
// ../producer/src/utils/semaphore.ts
|
|
26009
|
+
var Semaphore;
|
|
26010
|
+
var init_semaphore = __esm({
|
|
26011
|
+
"../producer/src/utils/semaphore.ts"() {
|
|
26012
|
+
"use strict";
|
|
26013
|
+
Semaphore = class {
|
|
26014
|
+
constructor(maxConcurrent) {
|
|
26015
|
+
this.maxConcurrent = maxConcurrent;
|
|
26016
|
+
}
|
|
26017
|
+
queue = [];
|
|
26018
|
+
active = 0;
|
|
26019
|
+
async acquire() {
|
|
26020
|
+
if (this.active < this.maxConcurrent) {
|
|
26021
|
+
this.active++;
|
|
26022
|
+
return () => this.release();
|
|
26023
|
+
}
|
|
26024
|
+
return new Promise((resolve28) => {
|
|
26025
|
+
this.queue.push(() => {
|
|
26026
|
+
this.active++;
|
|
26027
|
+
resolve28(() => this.release());
|
|
26028
|
+
});
|
|
26029
|
+
});
|
|
26030
|
+
}
|
|
26031
|
+
release() {
|
|
26032
|
+
this.active--;
|
|
26033
|
+
const next = this.queue.shift();
|
|
26034
|
+
if (next) next();
|
|
26035
|
+
}
|
|
26036
|
+
/** Current number of active slots. */
|
|
26037
|
+
get activeCount() {
|
|
26038
|
+
return this.active;
|
|
26039
|
+
}
|
|
26040
|
+
/** Number of waiters in the queue. */
|
|
26041
|
+
get waitingCount() {
|
|
26042
|
+
return this.queue.length;
|
|
26043
|
+
}
|
|
26044
|
+
};
|
|
25700
26045
|
}
|
|
25701
26046
|
});
|
|
25702
26047
|
|
|
@@ -25710,7 +26055,7 @@ import {
|
|
|
25710
26055
|
rmSync as rmSync7,
|
|
25711
26056
|
createReadStream
|
|
25712
26057
|
} from "fs";
|
|
25713
|
-
import { resolve as
|
|
26058
|
+
import { resolve as resolve15, dirname as dirname10, join as join29 } from "path";
|
|
25714
26059
|
import { tmpdir as tmpdir2 } from "os";
|
|
25715
26060
|
import { parseArgs as parseArgs2 } from "util";
|
|
25716
26061
|
import crypto from "crypto";
|
|
@@ -25732,12 +26077,12 @@ async function prepareRenderBody(body) {
|
|
|
25732
26077
|
const options = parseRenderOptions(body);
|
|
25733
26078
|
const projectDir = typeof body.projectDir === "string" ? body.projectDir : void 0;
|
|
25734
26079
|
if (projectDir) {
|
|
25735
|
-
const absProjectDir =
|
|
26080
|
+
const absProjectDir = resolve15(projectDir);
|
|
25736
26081
|
if (!existsSync28(absProjectDir) || !statSync9(absProjectDir).isDirectory()) {
|
|
25737
26082
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
25738
26083
|
}
|
|
25739
26084
|
const entry = options.entryFile || "index.html";
|
|
25740
|
-
if (!existsSync28(
|
|
26085
|
+
if (!existsSync28(resolve15(absProjectDir, entry))) {
|
|
25741
26086
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
25742
26087
|
}
|
|
25743
26088
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -25778,7 +26123,7 @@ function resolveOutputPath(projectDir, outputCandidate, rendersDir, log) {
|
|
|
25778
26123
|
try {
|
|
25779
26124
|
return resolveRenderPaths(projectDir, outputCandidate, rendersDir).absoluteOutputPath;
|
|
25780
26125
|
} catch (error) {
|
|
25781
|
-
const fallbackPath =
|
|
26126
|
+
const fallbackPath = resolve15(rendersDir, `producer-fallback-${Date.now()}.mp4`);
|
|
25782
26127
|
log.warn("Failed to resolve output path, using fallback", {
|
|
25783
26128
|
fallback: fallbackPath,
|
|
25784
26129
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -25829,6 +26174,8 @@ function createRenderHandlers(options = {}) {
|
|
|
25829
26174
|
const rendersDir = options.rendersDir ?? process.env.PRODUCER_RENDERS_DIR ?? "/tmp";
|
|
25830
26175
|
const artifactTtlMs = options.artifactTtlMs ?? Number(process.env.PRODUCER_OUTPUT_ARTIFACT_TTL_MS || 15 * 60 * 1e3);
|
|
25831
26176
|
const store = createArtifactStore(artifactTtlMs);
|
|
26177
|
+
const maxConcurrentRenders = options.maxConcurrentRenders ?? Number(process.env.PRODUCER_MAX_CONCURRENT_RENDERS || 2);
|
|
26178
|
+
const renderSemaphore = new Semaphore(maxConcurrentRenders);
|
|
25832
26179
|
const startTime = Date.now();
|
|
25833
26180
|
const health = (c2) => c2.json({
|
|
25834
26181
|
status: "ok",
|
|
@@ -25885,6 +26232,7 @@ function createRenderHandlers(options = {}) {
|
|
|
25885
26232
|
);
|
|
25886
26233
|
const outputDir = dirname10(absoluteOutputPath);
|
|
25887
26234
|
if (!existsSync28(outputDir)) mkdirSync17(outputDir, { recursive: true });
|
|
26235
|
+
const release2 = await renderSemaphore.acquire();
|
|
25888
26236
|
log.info("render started", {
|
|
25889
26237
|
requestId,
|
|
25890
26238
|
projectDir: input.projectDir,
|
|
@@ -25952,6 +26300,7 @@ function createRenderHandlers(options = {}) {
|
|
|
25952
26300
|
500
|
|
25953
26301
|
);
|
|
25954
26302
|
} finally {
|
|
26303
|
+
release2();
|
|
25955
26304
|
cleanupTempDir(cleanupProjectDir, log);
|
|
25956
26305
|
}
|
|
25957
26306
|
};
|
|
@@ -26008,6 +26357,16 @@ function createRenderHandlers(options = {}) {
|
|
|
26008
26357
|
const abortController = new AbortController();
|
|
26009
26358
|
const onRequestAbort = () => abortController.abort(new RenderCancelledError("request_aborted"));
|
|
26010
26359
|
c2.req.raw.signal.addEventListener("abort", onRequestAbort, { once: true });
|
|
26360
|
+
if (renderSemaphore.activeCount >= maxConcurrentRenders) {
|
|
26361
|
+
await stream.writeSSE({
|
|
26362
|
+
data: JSON.stringify({
|
|
26363
|
+
type: "queued",
|
|
26364
|
+
requestId,
|
|
26365
|
+
position: renderSemaphore.waitingCount
|
|
26366
|
+
})
|
|
26367
|
+
});
|
|
26368
|
+
}
|
|
26369
|
+
const release2 = await renderSemaphore.acquire();
|
|
26011
26370
|
try {
|
|
26012
26371
|
await executeRenderJob(
|
|
26013
26372
|
job,
|
|
@@ -26075,6 +26434,7 @@ function createRenderHandlers(options = {}) {
|
|
|
26075
26434
|
})
|
|
26076
26435
|
});
|
|
26077
26436
|
} finally {
|
|
26437
|
+
release2();
|
|
26078
26438
|
c2.req.raw.signal.removeEventListener("abort", onRequestAbort);
|
|
26079
26439
|
cleanupTempDir(cleanupProjectDir, log);
|
|
26080
26440
|
}
|
|
@@ -26099,7 +26459,12 @@ function createRenderHandlers(options = {}) {
|
|
|
26099
26459
|
}
|
|
26100
26460
|
});
|
|
26101
26461
|
};
|
|
26102
|
-
|
|
26462
|
+
const queue = (c2) => c2.json({
|
|
26463
|
+
maxConcurrentRenders,
|
|
26464
|
+
activeRenders: renderSemaphore.activeCount,
|
|
26465
|
+
queuedRenders: renderSemaphore.waitingCount
|
|
26466
|
+
});
|
|
26467
|
+
return { render: render2, renderStream, lint, health, outputs, queue };
|
|
26103
26468
|
}
|
|
26104
26469
|
function createProducerApp(options = {}) {
|
|
26105
26470
|
const app = new Hono4();
|
|
@@ -26107,6 +26472,7 @@ function createProducerApp(options = {}) {
|
|
|
26107
26472
|
app.get("/health", handlers.health);
|
|
26108
26473
|
app.post("/render", handlers.render);
|
|
26109
26474
|
app.post("/render/stream", handlers.renderStream);
|
|
26475
|
+
app.get("/render/queue", handlers.queue);
|
|
26110
26476
|
app.post("/lint", handlers.lint);
|
|
26111
26477
|
app.get("/outputs/:token", handlers.outputs);
|
|
26112
26478
|
return app;
|
|
@@ -26144,7 +26510,8 @@ var init_server = __esm({
|
|
|
26144
26510
|
init_hyperframeLint();
|
|
26145
26511
|
init_paths();
|
|
26146
26512
|
init_logger();
|
|
26147
|
-
|
|
26513
|
+
init_semaphore();
|
|
26514
|
+
entryScript = process.argv[1] ? resolve15(process.argv[1]) : "";
|
|
26148
26515
|
isPublicServerEntry = entryScript.endsWith("/public-server.js") || entryScript.endsWith("/src/server.ts");
|
|
26149
26516
|
if (isPublicServerEntry) {
|
|
26150
26517
|
const { values } = parseArgs2({
|
|
@@ -26217,18 +26584,18 @@ __export(studioServer_exports, {
|
|
|
26217
26584
|
import { Hono as Hono5 } from "hono";
|
|
26218
26585
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
26219
26586
|
import { existsSync as existsSync29, readFileSync as readFileSync20, writeFileSync as writeFileSync10, statSync as statSync10 } from "fs";
|
|
26220
|
-
import { resolve as
|
|
26587
|
+
import { resolve as resolve16, join as join30, basename as basename2 } from "path";
|
|
26221
26588
|
function resolveDistDir() {
|
|
26222
|
-
const builtPath =
|
|
26223
|
-
if (existsSync29(
|
|
26224
|
-
const devPath =
|
|
26225
|
-
if (existsSync29(
|
|
26589
|
+
const builtPath = resolve16(__dirname, "studio");
|
|
26590
|
+
if (existsSync29(resolve16(builtPath, "index.html"))) return builtPath;
|
|
26591
|
+
const devPath = resolve16(__dirname, "..", "..", "..", "studio", "dist");
|
|
26592
|
+
if (existsSync29(resolve16(devPath, "index.html"))) return devPath;
|
|
26226
26593
|
return builtPath;
|
|
26227
26594
|
}
|
|
26228
26595
|
function resolveRuntimePath() {
|
|
26229
|
-
const builtPath =
|
|
26596
|
+
const builtPath = resolve16(__dirname, "hyperframe-runtime.js");
|
|
26230
26597
|
if (existsSync29(builtPath)) return builtPath;
|
|
26231
|
-
const devPath =
|
|
26598
|
+
const devPath = resolve16(
|
|
26232
26599
|
__dirname,
|
|
26233
26600
|
"..",
|
|
26234
26601
|
"..",
|
|
@@ -26379,6 +26746,14 @@ function createStudioServer(options) {
|
|
|
26379
26746
|
}
|
|
26380
26747
|
};
|
|
26381
26748
|
const app = new Hono5();
|
|
26749
|
+
app.get("/__hyperframes_config", (c2) => {
|
|
26750
|
+
return c2.json({
|
|
26751
|
+
isHyperframes: true,
|
|
26752
|
+
projectName: projectId,
|
|
26753
|
+
projectDir,
|
|
26754
|
+
version: VERSION
|
|
26755
|
+
});
|
|
26756
|
+
});
|
|
26382
26757
|
app.get("/api/runtime.js", (c2) => {
|
|
26383
26758
|
if (!existsSync29(runtimePath)) return c2.text("runtime not built", 404);
|
|
26384
26759
|
return c2.body(readFileSync20(runtimePath, "utf-8"), 200, {
|
|
@@ -26412,7 +26787,7 @@ function createStudioServer(options) {
|
|
|
26412
26787
|
return api.fetch(forwardReq);
|
|
26413
26788
|
});
|
|
26414
26789
|
app.get("/assets/*", (c2) => {
|
|
26415
|
-
const filePath =
|
|
26790
|
+
const filePath = resolve16(studioDir, c2.req.path.slice(1));
|
|
26416
26791
|
if (!existsSync29(filePath) || !statSync10(filePath).isFile()) return c2.text("not found", 404);
|
|
26417
26792
|
const content = readFileSync20(filePath);
|
|
26418
26793
|
return new Response(content, {
|
|
@@ -26420,7 +26795,7 @@ function createStudioServer(options) {
|
|
|
26420
26795
|
});
|
|
26421
26796
|
});
|
|
26422
26797
|
app.get("/icons/*", (c2) => {
|
|
26423
|
-
const filePath =
|
|
26798
|
+
const filePath = resolve16(studioDir, c2.req.path.slice(1));
|
|
26424
26799
|
if (!existsSync29(filePath) || !statSync10(filePath).isFile()) return c2.text("not found", 404);
|
|
26425
26800
|
const content = readFileSync20(filePath);
|
|
26426
26801
|
return new Response(content, {
|
|
@@ -26428,7 +26803,7 @@ function createStudioServer(options) {
|
|
|
26428
26803
|
});
|
|
26429
26804
|
});
|
|
26430
26805
|
app.get("*", (c2) => {
|
|
26431
|
-
const indexPath =
|
|
26806
|
+
const indexPath = resolve16(studioDir, "index.html");
|
|
26432
26807
|
if (!existsSync29(indexPath)) {
|
|
26433
26808
|
return c2.text("Studio not found. Rebuild with: pnpm run build", 500);
|
|
26434
26809
|
}
|
|
@@ -26441,6 +26816,7 @@ var init_studioServer = __esm({
|
|
|
26441
26816
|
"src/server/studioServer.ts"() {
|
|
26442
26817
|
"use strict";
|
|
26443
26818
|
init_fileWatcher();
|
|
26819
|
+
init_version();
|
|
26444
26820
|
init_studio_api();
|
|
26445
26821
|
_thumbnailBrowser = null;
|
|
26446
26822
|
_thumbnailBrowserInitializing = null;
|
|
@@ -26455,45 +26831,12 @@ __export(preview_exports, {
|
|
|
26455
26831
|
});
|
|
26456
26832
|
import { spawn as spawn8 } from "child_process";
|
|
26457
26833
|
import { existsSync as existsSync30, lstatSync, symlinkSync, unlinkSync as unlinkSync5, readlinkSync, mkdirSync as mkdirSync18 } from "fs";
|
|
26458
|
-
import { resolve as
|
|
26834
|
+
import { resolve as resolve17, dirname as dirname11, basename as basename3, join as join31 } from "path";
|
|
26459
26835
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
26460
26836
|
import { createRequire } from "module";
|
|
26461
|
-
async function serveWithPortFallback(fetch3, startPort, maxAttempts = 10) {
|
|
26462
|
-
const { createAdaptorServer } = await import("@hono/node-server");
|
|
26463
|
-
const server = createAdaptorServer({ fetch: fetch3 });
|
|
26464
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
26465
|
-
const port = startPort + attempt;
|
|
26466
|
-
try {
|
|
26467
|
-
await new Promise((resolveListener, rejectListener) => {
|
|
26468
|
-
const onError = (err) => {
|
|
26469
|
-
server.removeListener("listening", onListening);
|
|
26470
|
-
rejectListener(err);
|
|
26471
|
-
};
|
|
26472
|
-
const onListening = () => {
|
|
26473
|
-
server.removeListener("error", onError);
|
|
26474
|
-
resolveListener();
|
|
26475
|
-
};
|
|
26476
|
-
server.once("error", onError);
|
|
26477
|
-
server.once("listening", onListening);
|
|
26478
|
-
server.listen(port);
|
|
26479
|
-
});
|
|
26480
|
-
return { server, port };
|
|
26481
|
-
} catch (err) {
|
|
26482
|
-
const code = err.code;
|
|
26483
|
-
if (code === "EADDRINUSE") {
|
|
26484
|
-
continue;
|
|
26485
|
-
}
|
|
26486
|
-
throw err;
|
|
26487
|
-
}
|
|
26488
|
-
}
|
|
26489
|
-
const lastPort = startPort + maxAttempts - 1;
|
|
26490
|
-
throw new Error(
|
|
26491
|
-
`Ports ${startPort}\u2013${lastPort} are all in use. Use --port to specify a different port.`
|
|
26492
|
-
);
|
|
26493
|
-
}
|
|
26494
26837
|
async function runDevMode(dir, projectName) {
|
|
26495
26838
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
26496
|
-
const repoRoot =
|
|
26839
|
+
const repoRoot = resolve17(dirname11(thisFile), "..", "..", "..", "..");
|
|
26497
26840
|
const projectsDir = join31(repoRoot, "packages", "studio", "data", "projects");
|
|
26498
26841
|
const pName = projectName ?? basename3(dir);
|
|
26499
26842
|
const symlinkPath = join31(projectsDir, pName);
|
|
@@ -26505,7 +26848,7 @@ async function runDevMode(dir, projectName) {
|
|
|
26505
26848
|
const stat = lstatSync(symlinkPath);
|
|
26506
26849
|
if (stat.isSymbolicLink()) {
|
|
26507
26850
|
const target = readlinkSync(symlinkPath);
|
|
26508
|
-
if (
|
|
26851
|
+
if (resolve17(target) !== resolve17(dir)) {
|
|
26509
26852
|
unlinkSync5(symlinkPath);
|
|
26510
26853
|
}
|
|
26511
26854
|
}
|
|
@@ -26559,8 +26902,8 @@ async function runDevMode(dir, projectName) {
|
|
|
26559
26902
|
}
|
|
26560
26903
|
});
|
|
26561
26904
|
}
|
|
26562
|
-
return new Promise((
|
|
26563
|
-
child.on("close", () =>
|
|
26905
|
+
return new Promise((resolve28) => {
|
|
26906
|
+
child.on("close", () => resolve28());
|
|
26564
26907
|
});
|
|
26565
26908
|
}
|
|
26566
26909
|
function hasLocalStudio(dir) {
|
|
@@ -26582,7 +26925,7 @@ async function runLocalStudioMode(dir, projectName) {
|
|
|
26582
26925
|
let createdSymlink = false;
|
|
26583
26926
|
if (dir !== symlinkPath) {
|
|
26584
26927
|
if (existsSync30(symlinkPath) && lstatSync(symlinkPath).isSymbolicLink()) {
|
|
26585
|
-
if (
|
|
26928
|
+
if (resolve17(readlinkSync(symlinkPath)) !== resolve17(dir)) {
|
|
26586
26929
|
unlinkSync5(symlinkPath);
|
|
26587
26930
|
}
|
|
26588
26931
|
}
|
|
@@ -26630,20 +26973,20 @@ async function runLocalStudioMode(dir, projectName) {
|
|
|
26630
26973
|
}
|
|
26631
26974
|
});
|
|
26632
26975
|
}
|
|
26633
|
-
return new Promise((
|
|
26634
|
-
child.on("close", () =>
|
|
26976
|
+
return new Promise((resolve28) => {
|
|
26977
|
+
child.on("close", () => resolve28());
|
|
26635
26978
|
});
|
|
26636
26979
|
}
|
|
26637
|
-
async function runEmbeddedMode(dir, startPort, projectName) {
|
|
26980
|
+
async function runEmbeddedMode(dir, startPort, projectName, forceNew = false) {
|
|
26638
26981
|
const { createStudioServer: createStudioServer2 } = await Promise.resolve().then(() => (init_studioServer(), studioServer_exports));
|
|
26639
26982
|
const pName = projectName ?? basename3(dir);
|
|
26640
26983
|
const { app } = createStudioServer2({ projectDir: dir, projectName: pName });
|
|
26641
26984
|
Wt2(c.bold("hyperframes preview"));
|
|
26642
26985
|
const s = be();
|
|
26643
26986
|
s.start("Starting studio...");
|
|
26644
|
-
let
|
|
26987
|
+
let result;
|
|
26645
26988
|
try {
|
|
26646
|
-
|
|
26989
|
+
result = await findPortAndServe(app.fetch, startPort, dir, forceNew);
|
|
26647
26990
|
} catch (err) {
|
|
26648
26991
|
s.stop(c.error("Failed to start studio"));
|
|
26649
26992
|
console.error();
|
|
@@ -26652,11 +26995,26 @@ async function runEmbeddedMode(dir, startPort, projectName) {
|
|
|
26652
26995
|
process.exitCode = 1;
|
|
26653
26996
|
return;
|
|
26654
26997
|
}
|
|
26655
|
-
|
|
26998
|
+
if (result.type === "already-running") {
|
|
26999
|
+
const url2 = `http://localhost:${result.port}`;
|
|
27000
|
+
s.stop(c.success("Already running"));
|
|
27001
|
+
console.log();
|
|
27002
|
+
console.log(` ${c.dim("Project")} ${c.accent(pName)}`);
|
|
27003
|
+
console.log(` ${c.dim("Studio")} ${c.accent(url2)}`);
|
|
27004
|
+
console.log();
|
|
27005
|
+
console.log(
|
|
27006
|
+
` ${c.dim("Reusing existing server. Use --force-new to start a fresh instance.")}`
|
|
27007
|
+
);
|
|
27008
|
+
console.log();
|
|
27009
|
+
import("open").then((mod) => mod.default(`${url2}#project/${pName}`)).catch(() => {
|
|
27010
|
+
});
|
|
27011
|
+
return;
|
|
27012
|
+
}
|
|
27013
|
+
const url = `http://localhost:${result.port}`;
|
|
26656
27014
|
s.stop(c.success("Studio running"));
|
|
26657
27015
|
console.log();
|
|
26658
|
-
if (
|
|
26659
|
-
console.log(` ${c.warn(`Port ${startPort} is in use, using ${
|
|
27016
|
+
if (result.port !== startPort) {
|
|
27017
|
+
console.log(` ${c.warn(`Port ${startPort} is in use, using ${result.port} instead`)}`);
|
|
26660
27018
|
console.log();
|
|
26661
27019
|
}
|
|
26662
27020
|
console.log(` ${c.dim("Project")} ${c.accent(pName)}`);
|
|
@@ -26682,21 +27040,72 @@ var init_preview2 = __esm({
|
|
|
26682
27040
|
init_env();
|
|
26683
27041
|
init_lintProject();
|
|
26684
27042
|
init_lintFormat();
|
|
27043
|
+
init_portUtils();
|
|
26685
27044
|
examples = [
|
|
26686
27045
|
["Preview the current project", "hyperframes preview"],
|
|
26687
27046
|
["Preview a specific project directory", "hyperframes preview ./my-video"],
|
|
26688
|
-
["Use a custom port", "hyperframes preview --port 8080"]
|
|
27047
|
+
["Use a custom port", "hyperframes preview --port 8080"],
|
|
27048
|
+
["Force a new server even if one is already running", "hyperframes preview --force-new"],
|
|
27049
|
+
["List all active preview servers", "hyperframes preview --list"],
|
|
27050
|
+
["Kill all active preview servers", "hyperframes preview --kill-all"]
|
|
26689
27051
|
];
|
|
26690
27052
|
preview_default = defineCommand({
|
|
26691
27053
|
meta: { name: "preview", description: "Start the studio for previewing compositions" },
|
|
26692
27054
|
args: {
|
|
26693
27055
|
dir: { type: "positional", description: "Project directory", required: false },
|
|
26694
|
-
port: { type: "string", description: "Port to run the preview server on", default: "3002" }
|
|
27056
|
+
port: { type: "string", description: "Port to run the preview server on", default: "3002" },
|
|
27057
|
+
"force-new": {
|
|
27058
|
+
type: "boolean",
|
|
27059
|
+
description: "Start a new server even if one is already running for this project",
|
|
27060
|
+
default: false
|
|
27061
|
+
},
|
|
27062
|
+
list: {
|
|
27063
|
+
type: "boolean",
|
|
27064
|
+
description: "List all active preview servers and exit",
|
|
27065
|
+
default: false
|
|
27066
|
+
},
|
|
27067
|
+
"kill-all": {
|
|
27068
|
+
type: "boolean",
|
|
27069
|
+
description: "Kill all active preview servers and exit",
|
|
27070
|
+
default: false
|
|
27071
|
+
}
|
|
26695
27072
|
},
|
|
26696
27073
|
async run({ args }) {
|
|
26697
|
-
const rawArg = args.dir;
|
|
26698
|
-
const dir = resolve16(rawArg ?? ".");
|
|
26699
27074
|
const startPort = parseInt(args.port ?? "3002", 10);
|
|
27075
|
+
if (args.list) {
|
|
27076
|
+
const servers = await scanActiveServers(startPort);
|
|
27077
|
+
if (servers.length === 0) {
|
|
27078
|
+
console.log("\n No active preview servers found.\n");
|
|
27079
|
+
return;
|
|
27080
|
+
}
|
|
27081
|
+
console.log(`
|
|
27082
|
+
${c.bold("Active preview servers:")}
|
|
27083
|
+
`);
|
|
27084
|
+
for (const s of servers) {
|
|
27085
|
+
const pidStr = s.pid ? c.dim(` (PID ${s.pid})`) : "";
|
|
27086
|
+
console.log(
|
|
27087
|
+
` ${c.accent(`Port ${s.port}`)} ${s.projectName} ${c.dim(s.projectDir)}${pidStr}`
|
|
27088
|
+
);
|
|
27089
|
+
}
|
|
27090
|
+
console.log(`
|
|
27091
|
+
${servers.length} server${servers.length === 1 ? "" : "s"} running.
|
|
27092
|
+
`);
|
|
27093
|
+
return;
|
|
27094
|
+
}
|
|
27095
|
+
if (args["kill-all"]) {
|
|
27096
|
+
const servers = await scanActiveServers(startPort);
|
|
27097
|
+
if (servers.length === 0) {
|
|
27098
|
+
console.log("\n No active preview servers to kill.\n");
|
|
27099
|
+
return;
|
|
27100
|
+
}
|
|
27101
|
+
const killed = await killActiveServers(startPort);
|
|
27102
|
+
console.log(`
|
|
27103
|
+
Killed ${killed} preview server${killed === 1 ? "" : "s"}.
|
|
27104
|
+
`);
|
|
27105
|
+
return;
|
|
27106
|
+
}
|
|
27107
|
+
const rawArg = args.dir;
|
|
27108
|
+
const dir = resolve17(rawArg ?? ".");
|
|
26700
27109
|
const isImplicitCwd = !rawArg || rawArg === "." || rawArg === "./";
|
|
26701
27110
|
const projectName = isImplicitCwd ? basename3(process.env.PWD ?? dir) : basename3(dir);
|
|
26702
27111
|
const indexPath = join31(dir, "index.html");
|
|
@@ -26715,7 +27124,8 @@ var init_preview2 = __esm({
|
|
|
26715
27124
|
if (hasLocalStudio(dir)) {
|
|
26716
27125
|
return runLocalStudioMode(dir, projectName);
|
|
26717
27126
|
}
|
|
26718
|
-
|
|
27127
|
+
const forceNew = !!args["force-new"];
|
|
27128
|
+
return runEmbeddedMode(dir, startPort, projectName, forceNew);
|
|
26719
27129
|
}
|
|
26720
27130
|
});
|
|
26721
27131
|
}
|
|
@@ -26736,7 +27146,7 @@ import {
|
|
|
26736
27146
|
readFileSync as readFileSync21,
|
|
26737
27147
|
readdirSync as readdirSync10
|
|
26738
27148
|
} from "fs";
|
|
26739
|
-
import { resolve as
|
|
27149
|
+
import { resolve as resolve18, basename as basename4, join as join32, dirname as dirname12 } from "path";
|
|
26740
27150
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
26741
27151
|
import { execFileSync as execFileSync4, spawn as spawn9 } from "child_process";
|
|
26742
27152
|
function probeVideo(filePath) {
|
|
@@ -26806,8 +27216,8 @@ function transcodeToMp4(inputPath, outputPath) {
|
|
|
26806
27216
|
}
|
|
26807
27217
|
function resolveAssetDir(devSegments, builtSegments) {
|
|
26808
27218
|
const base = dirname12(fileURLToPath5(import.meta.url));
|
|
26809
|
-
const devPath =
|
|
26810
|
-
const builtPath =
|
|
27219
|
+
const devPath = resolve18(base, ...devSegments);
|
|
27220
|
+
const builtPath = resolve18(base, ...builtSegments);
|
|
26811
27221
|
return existsSync31(devPath) ? devPath : builtPath;
|
|
26812
27222
|
}
|
|
26813
27223
|
function getStaticTemplateDir(templateId) {
|
|
@@ -26892,7 +27302,7 @@ async function handleVideoFile(videoPath, destDir, interactive) {
|
|
|
26892
27302
|
}
|
|
26893
27303
|
if (shouldTranscode) {
|
|
26894
27304
|
const mp4Name = localVideoName.replace(/\.[^.]+$/, ".mp4");
|
|
26895
|
-
const mp4Path =
|
|
27305
|
+
const mp4Path = resolve18(destDir, mp4Name);
|
|
26896
27306
|
const spin = be();
|
|
26897
27307
|
spin.start("Transcoding to H.264 MP4...");
|
|
26898
27308
|
const ok = await transcodeToMp4(videoPath, mp4Path);
|
|
@@ -26901,10 +27311,10 @@ async function handleVideoFile(videoPath, destDir, interactive) {
|
|
|
26901
27311
|
localVideoName = mp4Name;
|
|
26902
27312
|
} else {
|
|
26903
27313
|
spin.stop(c.warn("Transcode failed \u2014 copying original file"));
|
|
26904
|
-
copyFileSync3(videoPath,
|
|
27314
|
+
copyFileSync3(videoPath, resolve18(destDir, localVideoName));
|
|
26905
27315
|
}
|
|
26906
27316
|
} else {
|
|
26907
|
-
copyFileSync3(videoPath,
|
|
27317
|
+
copyFileSync3(videoPath, resolve18(destDir, localVideoName));
|
|
26908
27318
|
}
|
|
26909
27319
|
} else {
|
|
26910
27320
|
if (interactive) {
|
|
@@ -26914,10 +27324,10 @@ async function handleVideoFile(videoPath, destDir, interactive) {
|
|
|
26914
27324
|
console.log(c.warn("ffmpeg not installed \u2014 cannot transcode. Copying original."));
|
|
26915
27325
|
console.log(c.dim("Install: ") + c.accent("brew install ffmpeg"));
|
|
26916
27326
|
}
|
|
26917
|
-
copyFileSync3(videoPath,
|
|
27327
|
+
copyFileSync3(videoPath, resolve18(destDir, localVideoName));
|
|
26918
27328
|
}
|
|
26919
27329
|
} else {
|
|
26920
|
-
copyFileSync3(videoPath,
|
|
27330
|
+
copyFileSync3(videoPath, resolve18(destDir, localVideoName));
|
|
26921
27331
|
}
|
|
26922
27332
|
return { meta, localVideoName };
|
|
26923
27333
|
}
|
|
@@ -26931,7 +27341,7 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
|
|
|
26931
27341
|
}
|
|
26932
27342
|
patchVideoSrc(destDir, localVideoName, durationSeconds);
|
|
26933
27343
|
writeFileSync11(
|
|
26934
|
-
|
|
27344
|
+
resolve18(destDir, "meta.json"),
|
|
26935
27345
|
JSON.stringify(
|
|
26936
27346
|
{
|
|
26937
27347
|
id: name,
|
|
@@ -26947,7 +27357,7 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
|
|
|
26947
27357
|
if (existsSync31(sharedDir)) {
|
|
26948
27358
|
for (const entry of readdirSync10(sharedDir, { withFileTypes: true })) {
|
|
26949
27359
|
const src = join32(sharedDir, entry.name);
|
|
26950
|
-
const dest =
|
|
27360
|
+
const dest = resolve18(destDir, entry.name);
|
|
26951
27361
|
if (entry.isFile() || entry.isSymbolicLink()) {
|
|
26952
27362
|
copyFileSync3(src, dest);
|
|
26953
27363
|
}
|
|
@@ -27039,7 +27449,7 @@ var init_init = __esm({
|
|
|
27039
27449
|
if (!interactive) {
|
|
27040
27450
|
const templateId2 = templateFlag ?? "blank";
|
|
27041
27451
|
const name2 = args.name ?? "my-video";
|
|
27042
|
-
const destDir2 =
|
|
27452
|
+
const destDir2 = resolve18(name2);
|
|
27043
27453
|
if (existsSync31(destDir2) && readdirSync10(destDir2).length > 0) {
|
|
27044
27454
|
console.error(c.error(`Directory already exists and is not empty: ${name2}`));
|
|
27045
27455
|
process.exit(1);
|
|
@@ -27053,7 +27463,7 @@ var init_init = __esm({
|
|
|
27053
27463
|
process.exit(1);
|
|
27054
27464
|
}
|
|
27055
27465
|
if (videoFlag) {
|
|
27056
|
-
const videoPath =
|
|
27466
|
+
const videoPath = resolve18(videoFlag);
|
|
27057
27467
|
if (!existsSync31(videoPath)) {
|
|
27058
27468
|
console.error(c.error(`Video file not found: ${videoFlag}`));
|
|
27059
27469
|
process.exit(1);
|
|
@@ -27067,13 +27477,13 @@ var init_init = __esm({
|
|
|
27067
27477
|
);
|
|
27068
27478
|
}
|
|
27069
27479
|
if (audioFlag) {
|
|
27070
|
-
const audioPath =
|
|
27480
|
+
const audioPath = resolve18(audioFlag);
|
|
27071
27481
|
if (!existsSync31(audioPath)) {
|
|
27072
27482
|
console.error(c.error(`Audio file not found: ${audioFlag}`));
|
|
27073
27483
|
process.exit(1);
|
|
27074
27484
|
}
|
|
27075
27485
|
sourceFilePath2 = audioPath;
|
|
27076
|
-
copyFileSync3(audioPath,
|
|
27486
|
+
copyFileSync3(audioPath, resolve18(destDir2, basename4(audioPath)));
|
|
27077
27487
|
console.log(`Audio: ${basename4(audioPath)}`);
|
|
27078
27488
|
}
|
|
27079
27489
|
if (sourceFilePath2 && !skipTranscribe) {
|
|
@@ -27113,7 +27523,7 @@ var init_init = __esm({
|
|
|
27113
27523
|
process.exit(1);
|
|
27114
27524
|
}
|
|
27115
27525
|
trackInitTemplate(templateId2);
|
|
27116
|
-
const transcriptFile2 =
|
|
27526
|
+
const transcriptFile2 = resolve18(destDir2, "transcript.json");
|
|
27117
27527
|
if (existsSync31(transcriptFile2)) {
|
|
27118
27528
|
await patchTranscript(destDir2, transcriptFile2);
|
|
27119
27529
|
}
|
|
@@ -27159,7 +27569,7 @@ var init_init = __esm({
|
|
|
27159
27569
|
}
|
|
27160
27570
|
name = nameResult;
|
|
27161
27571
|
}
|
|
27162
|
-
const destDir =
|
|
27572
|
+
const destDir = resolve18(name);
|
|
27163
27573
|
if (existsSync31(destDir) && readdirSync10(destDir).length > 0) {
|
|
27164
27574
|
const overwrite = await Rt({
|
|
27165
27575
|
message: `Directory ${c.accent(name)} already exists and is not empty. Overwrite?`,
|
|
@@ -27174,7 +27584,7 @@ var init_init = __esm({
|
|
|
27174
27584
|
let sourceFilePath;
|
|
27175
27585
|
let videoDuration;
|
|
27176
27586
|
if (videoFlag) {
|
|
27177
|
-
const videoPath =
|
|
27587
|
+
const videoPath = resolve18(videoFlag);
|
|
27178
27588
|
if (!existsSync31(videoPath)) {
|
|
27179
27589
|
R2.error(`File not found: ${videoFlag}`);
|
|
27180
27590
|
Nt("Setup cancelled.");
|
|
@@ -27186,7 +27596,7 @@ var init_init = __esm({
|
|
|
27186
27596
|
localVideoName = result.localVideoName;
|
|
27187
27597
|
videoDuration = result.meta.durationSeconds;
|
|
27188
27598
|
} else if (audioFlag) {
|
|
27189
|
-
const audioPath =
|
|
27599
|
+
const audioPath = resolve18(audioFlag);
|
|
27190
27600
|
if (!existsSync31(audioPath)) {
|
|
27191
27601
|
R2.error(`File not found: ${audioFlag}`);
|
|
27192
27602
|
Nt("Setup cancelled.");
|
|
@@ -27194,7 +27604,7 @@ var init_init = __esm({
|
|
|
27194
27604
|
}
|
|
27195
27605
|
mkdirSync19(destDir, { recursive: true });
|
|
27196
27606
|
sourceFilePath = audioPath;
|
|
27197
|
-
copyFileSync3(audioPath,
|
|
27607
|
+
copyFileSync3(audioPath, resolve18(destDir, basename4(audioPath)));
|
|
27198
27608
|
R2.info(`Audio copied to ${c.accent(basename4(audioPath))}`);
|
|
27199
27609
|
}
|
|
27200
27610
|
if (sourceFilePath) {
|
|
@@ -27279,7 +27689,7 @@ ${c.dim("Use --template blank for offline use.")}`
|
|
|
27279
27689
|
process.exit(1);
|
|
27280
27690
|
}
|
|
27281
27691
|
trackInitTemplate(templateId);
|
|
27282
|
-
const transcriptFile =
|
|
27692
|
+
const transcriptFile = resolve18(destDir, "transcript.json");
|
|
27283
27693
|
if (existsSync31(transcriptFile)) {
|
|
27284
27694
|
await patchTranscript(destDir, transcriptFile);
|
|
27285
27695
|
}
|
|
@@ -27344,11 +27754,11 @@ var init_format = __esm({
|
|
|
27344
27754
|
|
|
27345
27755
|
// src/utils/project.ts
|
|
27346
27756
|
import { existsSync as existsSync32, statSync as statSync11 } from "fs";
|
|
27347
|
-
import { resolve as
|
|
27757
|
+
import { resolve as resolve19, basename as basename5 } from "path";
|
|
27348
27758
|
function resolveProject(dirArg) {
|
|
27349
|
-
const dir =
|
|
27759
|
+
const dir = resolve19(dirArg ?? ".");
|
|
27350
27760
|
const name = basename5(dir);
|
|
27351
|
-
const indexPath =
|
|
27761
|
+
const indexPath = resolve19(dir, "index.html");
|
|
27352
27762
|
if (!existsSync32(dir) || !statSync11(dir).isDirectory()) {
|
|
27353
27763
|
errorBox("Not a directory: " + dir);
|
|
27354
27764
|
process.exit(1);
|
|
@@ -27377,7 +27787,7 @@ __export(play_exports, {
|
|
|
27377
27787
|
examples: () => examples3
|
|
27378
27788
|
});
|
|
27379
27789
|
import { existsSync as existsSync33, readFileSync as readFileSync22 } from "fs";
|
|
27380
|
-
import { resolve as
|
|
27790
|
+
import { resolve as resolve20, dirname as dirname13 } from "path";
|
|
27381
27791
|
function commandDir() {
|
|
27382
27792
|
return dirname13(new URL(import.meta.url).pathname);
|
|
27383
27793
|
}
|
|
@@ -27385,10 +27795,10 @@ function resolveRuntimePath2() {
|
|
|
27385
27795
|
const d = commandDir();
|
|
27386
27796
|
const candidates = [
|
|
27387
27797
|
// Bundled with CLI dist
|
|
27388
|
-
|
|
27389
|
-
|
|
27798
|
+
resolve20(d, "hyperframe-runtime.js"),
|
|
27799
|
+
resolve20(d, "..", "hyperframe-runtime.js"),
|
|
27390
27800
|
// Monorepo dev: commands/ → src/ → cli/ → packages/ then into core/dist/
|
|
27391
|
-
|
|
27801
|
+
resolve20(d, "..", "..", "..", "core", "dist", "hyperframe.runtime.iife.js")
|
|
27392
27802
|
];
|
|
27393
27803
|
for (const p of candidates) {
|
|
27394
27804
|
if (existsSync33(p)) return p;
|
|
@@ -27399,10 +27809,10 @@ function resolvePlayerPath() {
|
|
|
27399
27809
|
const d = commandDir();
|
|
27400
27810
|
const candidates = [
|
|
27401
27811
|
// Monorepo dev: commands/ → src/ → cli/ → packages/ then into player/dist/
|
|
27402
|
-
|
|
27812
|
+
resolve20(d, "..", "..", "..", "player", "dist", "hyperframes-player.global.js"),
|
|
27403
27813
|
// Bundled with CLI dist
|
|
27404
|
-
|
|
27405
|
-
|
|
27814
|
+
resolve20(d, "hyperframes-player.global.js"),
|
|
27815
|
+
resolve20(d, "..", "hyperframes-player.global.js")
|
|
27406
27816
|
];
|
|
27407
27817
|
for (const p of candidates) {
|
|
27408
27818
|
if (existsSync33(p)) return p;
|
|
@@ -27505,7 +27915,7 @@ var init_play = __esm({
|
|
|
27505
27915
|
});
|
|
27506
27916
|
app.get("/composition/*", async (ctx) => {
|
|
27507
27917
|
const reqPath = ctx.req.path.replace("/composition/", "");
|
|
27508
|
-
const filePath =
|
|
27918
|
+
const filePath = resolve20(project.dir, reqPath);
|
|
27509
27919
|
if (!filePath.startsWith(project.dir)) return ctx.text("Forbidden", 403);
|
|
27510
27920
|
if (!existsSync33(filePath)) return ctx.text("Not found", 404);
|
|
27511
27921
|
const content = readFileSync22(filePath, "utf-8");
|
|
@@ -27657,7 +28067,7 @@ __export(render_exports, {
|
|
|
27657
28067
|
});
|
|
27658
28068
|
import { mkdirSync as mkdirSync20, readFileSync as readFileSync23, statSync as statSync12, writeFileSync as writeFileSync12, rmSync as rmSync8 } from "fs";
|
|
27659
28069
|
import { cpus as cpus3, freemem as freemem3, tmpdir as tmpdir3 } from "os";
|
|
27660
|
-
import { resolve as
|
|
28070
|
+
import { resolve as resolve21, dirname as dirname14, join as join33, basename as basename6 } from "path";
|
|
27661
28071
|
import { execFileSync as execFileSync5, spawn as spawn10 } from "child_process";
|
|
27662
28072
|
function defaultWorkerCount() {
|
|
27663
28073
|
return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT * 3 / 4), 8));
|
|
@@ -27666,8 +28076,8 @@ function dockerImageTag(version) {
|
|
|
27666
28076
|
return `${DOCKER_IMAGE_PREFIX}:${version}`;
|
|
27667
28077
|
}
|
|
27668
28078
|
function resolveDockerfilePath() {
|
|
27669
|
-
const builtPath =
|
|
27670
|
-
const devPath =
|
|
28079
|
+
const builtPath = resolve21(__dirname, "docker", "Dockerfile.render");
|
|
28080
|
+
const devPath = resolve21(__dirname, "..", "src", "docker", "Dockerfile.render");
|
|
27671
28081
|
for (const p of [builtPath, devPath]) {
|
|
27672
28082
|
try {
|
|
27673
28083
|
statSync12(p);
|
|
@@ -27751,9 +28161,9 @@ async function renderDocker(projectDir, outputPath, options) {
|
|
|
27751
28161
|
// GPU encoding requires host GPU passthrough
|
|
27752
28162
|
...options.gpu ? ["--gpus", "all"] : [],
|
|
27753
28163
|
"-v",
|
|
27754
|
-
`${
|
|
28164
|
+
`${resolve21(projectDir)}:/project:ro`,
|
|
27755
28165
|
"-v",
|
|
27756
|
-
`${
|
|
28166
|
+
`${resolve21(outputDir)}:/output`,
|
|
27757
28167
|
imageTag,
|
|
27758
28168
|
"/project",
|
|
27759
28169
|
"--output",
|
|
@@ -27962,6 +28372,10 @@ var init_render2 = __esm({
|
|
|
27962
28372
|
type: "boolean",
|
|
27963
28373
|
description: "Fail render on lint errors AND warnings",
|
|
27964
28374
|
default: false
|
|
28375
|
+
},
|
|
28376
|
+
"max-concurrent-renders": {
|
|
28377
|
+
type: "string",
|
|
28378
|
+
description: "Max concurrent renders when using the producer server (1-10). Default: 2."
|
|
27965
28379
|
}
|
|
27966
28380
|
},
|
|
27967
28381
|
async run({ args }) {
|
|
@@ -27993,12 +28407,23 @@ var init_render2 = __esm({
|
|
|
27993
28407
|
}
|
|
27994
28408
|
workers = parsed;
|
|
27995
28409
|
}
|
|
27996
|
-
|
|
28410
|
+
if (args["max-concurrent-renders"] != null) {
|
|
28411
|
+
const parsed = parseInt(args["max-concurrent-renders"], 10);
|
|
28412
|
+
if (isNaN(parsed) || parsed < 1 || parsed > 10) {
|
|
28413
|
+
errorBox(
|
|
28414
|
+
"Invalid max-concurrent-renders",
|
|
28415
|
+
`Got "${args["max-concurrent-renders"]}". Must be a number between 1 and 10.`
|
|
28416
|
+
);
|
|
28417
|
+
process.exit(1);
|
|
28418
|
+
}
|
|
28419
|
+
process.env.PRODUCER_MAX_CONCURRENT_RENDERS = String(parsed);
|
|
28420
|
+
}
|
|
28421
|
+
const rendersDir = resolve21("renders");
|
|
27997
28422
|
const ext = FORMAT_EXT[format] ?? ".mp4";
|
|
27998
28423
|
const now = /* @__PURE__ */ new Date();
|
|
27999
28424
|
const datePart = now.toISOString().slice(0, 10);
|
|
28000
28425
|
const timePart = now.toTimeString().slice(0, 8).replace(/:/g, "-");
|
|
28001
|
-
const outputPath = args.output ?
|
|
28426
|
+
const outputPath = args.output ? resolve21(args.output) : join33(rendersDir, `${project.name}_${datePart}_${timePart}${ext}`);
|
|
28002
28427
|
mkdirSync20(dirname14(outputPath), { recursive: true });
|
|
28003
28428
|
const useDocker = args.docker ?? false;
|
|
28004
28429
|
const useGpu = args.gpu ?? false;
|
|
@@ -28412,7 +28837,7 @@ __export(compositions_exports, {
|
|
|
28412
28837
|
examples: () => examples7
|
|
28413
28838
|
});
|
|
28414
28839
|
import { existsSync as existsSync34, readFileSync as readFileSync25 } from "fs";
|
|
28415
|
-
import { resolve as
|
|
28840
|
+
import { resolve as resolve22, dirname as dirname15 } from "path";
|
|
28416
28841
|
function parseCompositions(html, baseDir) {
|
|
28417
28842
|
const parser = new DOMParser();
|
|
28418
28843
|
const doc = parser.parseFromString(html, "text/html");
|
|
@@ -28424,7 +28849,7 @@ function parseCompositions(html, baseDir) {
|
|
|
28424
28849
|
const height = parseInt(div.getAttribute("data-height") ?? "1080", 10);
|
|
28425
28850
|
const compositionSrc = div.getAttribute("data-composition-src");
|
|
28426
28851
|
if (compositionSrc) {
|
|
28427
|
-
const subPath =
|
|
28852
|
+
const subPath = resolve22(baseDir, compositionSrc);
|
|
28428
28853
|
if (existsSync34(subPath)) {
|
|
28429
28854
|
const subHtml = readFileSync25(subPath, "utf-8");
|
|
28430
28855
|
const subInfo = parseSubComposition(subHtml, id, width, height);
|
|
@@ -28559,7 +28984,7 @@ __export(benchmark_exports, {
|
|
|
28559
28984
|
examples: () => examples8
|
|
28560
28985
|
});
|
|
28561
28986
|
import { existsSync as existsSync35, statSync as statSync14 } from "fs";
|
|
28562
|
-
import { resolve as
|
|
28987
|
+
import { resolve as resolve23, join as join35 } from "path";
|
|
28563
28988
|
var examples8, DEFAULT_CONFIGS, benchmark_default;
|
|
28564
28989
|
var init_benchmark = __esm({
|
|
28565
28990
|
"src/commands/benchmark.ts"() {
|
|
@@ -28601,7 +29026,7 @@ var init_benchmark = __esm({
|
|
|
28601
29026
|
process.exit(1);
|
|
28602
29027
|
}
|
|
28603
29028
|
const jsonOutput = args.json ?? false;
|
|
28604
|
-
const benchDir =
|
|
29029
|
+
const benchDir = resolve23("renders", ".benchmark");
|
|
28605
29030
|
let producer = null;
|
|
28606
29031
|
try {
|
|
28607
29032
|
producer = await loadProducer();
|
|
@@ -28865,7 +29290,7 @@ __export(transcribe_exports2, {
|
|
|
28865
29290
|
examples: () => examples10
|
|
28866
29291
|
});
|
|
28867
29292
|
import { existsSync as existsSync36, writeFileSync as writeFileSync13 } from "fs";
|
|
28868
|
-
import { resolve as
|
|
29293
|
+
import { resolve as resolve24, join as join36, extname as extname7 } from "path";
|
|
28869
29294
|
async function importTranscript(inputPath, dir, json) {
|
|
28870
29295
|
const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
|
|
28871
29296
|
const { words, format } = loadTranscript2(inputPath);
|
|
@@ -28989,12 +29414,12 @@ var init_transcribe2 = __esm({
|
|
|
28989
29414
|
}
|
|
28990
29415
|
},
|
|
28991
29416
|
async run({ args }) {
|
|
28992
|
-
const inputPath =
|
|
29417
|
+
const inputPath = resolve24(args.input);
|
|
28993
29418
|
if (!existsSync36(inputPath)) {
|
|
28994
29419
|
console.error(c.error(`File not found: ${args.input}`));
|
|
28995
29420
|
process.exit(1);
|
|
28996
29421
|
}
|
|
28997
|
-
const dir =
|
|
29422
|
+
const dir = resolve24(args.dir ?? ".");
|
|
28998
29423
|
const ext = extname7(inputPath).toLowerCase();
|
|
28999
29424
|
const isImport = ext === ".json" || ext === ".srt" || ext === ".vtt";
|
|
29000
29425
|
if (isImport) {
|
|
@@ -29217,7 +29642,7 @@ __export(tts_exports, {
|
|
|
29217
29642
|
examples: () => examples11
|
|
29218
29643
|
});
|
|
29219
29644
|
import { existsSync as existsSync39, readFileSync as readFileSync26 } from "fs";
|
|
29220
|
-
import { resolve as
|
|
29645
|
+
import { resolve as resolve25, extname as extname8 } from "path";
|
|
29221
29646
|
function listVoices(json) {
|
|
29222
29647
|
if (json) {
|
|
29223
29648
|
console.log(JSON.stringify(BUNDLED_VOICES));
|
|
@@ -29305,7 +29730,7 @@ var init_tts = __esm({
|
|
|
29305
29730
|
process.exit(1);
|
|
29306
29731
|
}
|
|
29307
29732
|
let text;
|
|
29308
|
-
const maybeFile =
|
|
29733
|
+
const maybeFile = resolve25(args.input);
|
|
29309
29734
|
if (existsSync39(maybeFile) && extname8(maybeFile).toLowerCase() === ".txt") {
|
|
29310
29735
|
text = readFileSync26(maybeFile, "utf-8").trim();
|
|
29311
29736
|
if (!text) {
|
|
@@ -29319,7 +29744,7 @@ var init_tts = __esm({
|
|
|
29319
29744
|
console.error(c.error("No text provided."));
|
|
29320
29745
|
process.exit(1);
|
|
29321
29746
|
}
|
|
29322
|
-
const output =
|
|
29747
|
+
const output = resolve25(args.output ?? "speech.wav");
|
|
29323
29748
|
const voice = args.voice ?? DEFAULT_VOICE;
|
|
29324
29749
|
const speed = args.speed ? parseFloat(args.speed) : 1;
|
|
29325
29750
|
if (isNaN(speed) || speed <= 0 || speed > 3) {
|
|
@@ -29373,13 +29798,13 @@ __export(docs_exports, {
|
|
|
29373
29798
|
examples: () => examples12
|
|
29374
29799
|
});
|
|
29375
29800
|
import { readFileSync as readFileSync27, existsSync as existsSync40 } from "fs";
|
|
29376
|
-
import { resolve as
|
|
29801
|
+
import { resolve as resolve26, dirname as dirname17, join as join39 } from "path";
|
|
29377
29802
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
29378
29803
|
function docsDir() {
|
|
29379
29804
|
const thisFile = fileURLToPath6(import.meta.url);
|
|
29380
29805
|
const dir = dirname17(thisFile);
|
|
29381
|
-
const devPath =
|
|
29382
|
-
const builtPath =
|
|
29806
|
+
const devPath = resolve26(dir, "..", "docs");
|
|
29807
|
+
const builtPath = resolve26(dir, "docs");
|
|
29383
29808
|
return existsSync40(devPath) ? devPath : builtPath;
|
|
29384
29809
|
}
|
|
29385
29810
|
function formatInlineCode(line) {
|
|
@@ -29897,13 +30322,13 @@ __export(validate_exports, {
|
|
|
29897
30322
|
default: () => validate_default
|
|
29898
30323
|
});
|
|
29899
30324
|
import { existsSync as existsSync41, readFileSync as readFileSync28 } from "fs";
|
|
29900
|
-
import { resolve as
|
|
30325
|
+
import { resolve as resolve27, join as join40, dirname as dirname18 } from "path";
|
|
29901
30326
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
29902
30327
|
async function validateInBrowser(projectDir, opts) {
|
|
29903
30328
|
const { bundleToSingleHtml: bundleToSingleHtml2 } = await Promise.resolve().then(() => (init_compiler(), compiler_exports));
|
|
29904
30329
|
const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
|
|
29905
30330
|
let html = await bundleToSingleHtml2(projectDir);
|
|
29906
|
-
const runtimePath =
|
|
30331
|
+
const runtimePath = resolve27(
|
|
29907
30332
|
__dirname2,
|
|
29908
30333
|
"..",
|
|
29909
30334
|
"..",
|
|
@@ -29916,7 +30341,7 @@ async function validateInBrowser(projectDir, opts) {
|
|
|
29916
30341
|
const runtimeSource = readFileSync28(runtimePath, "utf-8");
|
|
29917
30342
|
html = html.replace(
|
|
29918
30343
|
/<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
|
|
29919
|
-
`<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
|
|
30344
|
+
() => `<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
|
|
29920
30345
|
);
|
|
29921
30346
|
}
|
|
29922
30347
|
const { createServer } = await import("http");
|