@editframe/cli 0.31.2-beta.0 → 0.32.0-beta.0
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/VERSION.js +1 -1
- package/dist/VERSION.js.map +1 -1
- package/dist/commands/check.js +3 -3
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/cloud-render.js +131 -0
- package/dist/commands/cloud-render.js.map +1 -0
- package/dist/commands/render.js +61 -111
- package/dist/commands/render.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/detectChrome.js +87 -0
- package/dist/utils/detectChrome.js.map +1 -0
- package/dist/utils/launchBrowserAndWaitForSDK.js +13 -3
- package/dist/utils/launchBrowserAndWaitForSDK.js.map +1 -1
- package/package.json +7 -6
package/dist/VERSION.js
CHANGED
package/dist/VERSION.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VERSION.js","names":[],"sources":["../src/VERSION.ts"],"sourcesContent":["export const VERSION = \"0.
|
|
1
|
+
{"version":3,"file":"VERSION.js","names":[],"sources":["../src/VERSION.ts"],"sourcesContent":["export const VERSION = \"0.32.0-beta.0\";\n"],"mappings":";AAAA,MAAa,UAAU"}
|
package/dist/commands/check.js
CHANGED
|
@@ -8,9 +8,9 @@ import os from "node:os";
|
|
|
8
8
|
const checks = {
|
|
9
9
|
ffmpeg: {
|
|
10
10
|
message: () => {
|
|
11
|
-
const platform = os.platform();
|
|
11
|
+
const platform$1 = os.platform();
|
|
12
12
|
const message = ["Processing assets for <ef-video>, <ef-audio>, <ef-captions>, and <ef-waveform>\n elements requires ffmpeg to be installed."];
|
|
13
|
-
switch (platform) {
|
|
13
|
+
switch (platform$1) {
|
|
14
14
|
case "darwin":
|
|
15
15
|
message.push("On platform=darwin you can install ffmpeg using Homebrew:");
|
|
16
16
|
message.push(" - brew install ffmpeg");
|
|
@@ -27,7 +27,7 @@ const checks = {
|
|
|
27
27
|
message.push(" - winget install ffmpeg");
|
|
28
28
|
break;
|
|
29
29
|
default:
|
|
30
|
-
message.push(`Unrecognized platform ${platform}`);
|
|
30
|
+
message.push(`Unrecognized platform ${platform$1}`);
|
|
31
31
|
message.push("You can download ffmpeg from https://ffmpeg.org/download.html");
|
|
32
32
|
message.push("Or try installing it from your operating system's package manager");
|
|
33
33
|
break;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.js","names":["checks: Record<string, CheckDescriptor>"],"sources":["../../src/commands/check.ts"],"sourcesContent":["import { exec } from \"node:child_process\";\nimport os from \"node:os\";\nimport chalk from \"chalk\";\nimport { program } from \"commander\";\nimport ora from \"ora\";\n\ninterface CheckDescriptor {\n check(): Promise<boolean>;\n message(): string[];\n}\n\nconst checks: Record<string, CheckDescriptor> = {\n ffmpeg: {\n message: () => {\n const platform = os.platform();\n const message = [\n \"Processing assets for <ef-video>, <ef-audio>, <ef-captions>, and <ef-waveform>\\n elements requires ffmpeg to be installed.\",\n ];\n switch (platform) {\n case \"darwin\": {\n message.push(\n \"On platform=darwin you can install ffmpeg using Homebrew:\",\n );\n message.push(\" - brew install ffmpeg\");\n message.push(\n \"Or you can download ffmpeg from https://ffmpeg.org/download.html\",\n );\n break;\n }\n case \"linux\": {\n message.push(\n \"You can install ffmpeg using your distribution's package manager.\",\n );\n break;\n }\n case \"win32\": {\n message.push(\n \"You can download ffmpeg from https://ffmpeg.org/download.html\",\n );\n message.push(\n \"You can use package managers like Chocolatey or Scoop to install ffmpeg.\",\n );\n message.push(\" - choco install ffmpeg-full\");\n message.push(\" - scoop install ffmpeg\");\n message.push(\" - winget install ffmpeg\");\n break;\n }\n default: {\n message.push(`Unrecognized platform ${platform}`);\n message.push(\n \"You can download ffmpeg from https://ffmpeg.org/download.html\",\n );\n message.push(\n \"Or try installing it from your operating system's package manager\",\n );\n break;\n }\n }\n return message;\n },\n check: async () => {\n return new Promise((resolve, reject) => {\n exec(\"ffmpeg -version\", (error: any, stdout: any, _stderr: any) => {\n if (error) {\n reject(error);\n return;\n }\n resolve(stdout);\n });\n });\n },\n },\n\n whisper_timestamped: {\n message: () => {\n const message = [\n \"<ef-captions> Requires whisper_timestamped to be installed.\",\n ];\n\n message.push(\"whisper_timestamped depends on python3\");\n\n message.push(\" - pip3 install whisper_timestamped\");\n\n message.push(\"Alternate installation instructions are availble at:\");\n message.push(\n \"https://github.com/linto-ai/whisper-timestamped#installation\",\n );\n\n return message;\n },\n check: async () => {\n return new Promise((resolve, reject) => {\n exec(\n \"whisper_timestamped --version\",\n (error: any, stdout: any, _stderr: any) => {\n if (error) {\n reject(error);\n return;\n }\n resolve(stdout);\n },\n );\n });\n },\n },\n};\n\nprogram\n .command(\"check\")\n .description(\"Check on dependencies and other requirements\")\n .action(async () => {\n for (const checkName in checks) {\n const check = checks[checkName];\n if (!check) {\n continue;\n }\n const spinner = ora(`Checking ${checkName}`).start();\n try {\n await check.check();\n spinner.succeed(\n chalk.white.bgGreen(` Check for ${checkName} passed `),\n );\n } catch (_error) {\n spinner.fail(chalk.white.bgRed(` Check for ${checkName} failed `));\n process.stderr.write(chalk.red(check.message().join(\"\\n\\n\")));\n process.stderr.write(\"\\n\");\n }\n }\n });\n"],"mappings":";;;;;;;AAWA,MAAMA,SAA0C;CAC9C,QAAQ;EACN,eAAe;GACb,
|
|
1
|
+
{"version":3,"file":"check.js","names":["checks: Record<string, CheckDescriptor>","platform"],"sources":["../../src/commands/check.ts"],"sourcesContent":["import { exec } from \"node:child_process\";\nimport os from \"node:os\";\nimport chalk from \"chalk\";\nimport { program } from \"commander\";\nimport ora from \"ora\";\n\ninterface CheckDescriptor {\n check(): Promise<boolean>;\n message(): string[];\n}\n\nconst checks: Record<string, CheckDescriptor> = {\n ffmpeg: {\n message: () => {\n const platform = os.platform();\n const message = [\n \"Processing assets for <ef-video>, <ef-audio>, <ef-captions>, and <ef-waveform>\\n elements requires ffmpeg to be installed.\",\n ];\n switch (platform) {\n case \"darwin\": {\n message.push(\n \"On platform=darwin you can install ffmpeg using Homebrew:\",\n );\n message.push(\" - brew install ffmpeg\");\n message.push(\n \"Or you can download ffmpeg from https://ffmpeg.org/download.html\",\n );\n break;\n }\n case \"linux\": {\n message.push(\n \"You can install ffmpeg using your distribution's package manager.\",\n );\n break;\n }\n case \"win32\": {\n message.push(\n \"You can download ffmpeg from https://ffmpeg.org/download.html\",\n );\n message.push(\n \"You can use package managers like Chocolatey or Scoop to install ffmpeg.\",\n );\n message.push(\" - choco install ffmpeg-full\");\n message.push(\" - scoop install ffmpeg\");\n message.push(\" - winget install ffmpeg\");\n break;\n }\n default: {\n message.push(`Unrecognized platform ${platform}`);\n message.push(\n \"You can download ffmpeg from https://ffmpeg.org/download.html\",\n );\n message.push(\n \"Or try installing it from your operating system's package manager\",\n );\n break;\n }\n }\n return message;\n },\n check: async () => {\n return new Promise((resolve, reject) => {\n exec(\"ffmpeg -version\", (error: any, stdout: any, _stderr: any) => {\n if (error) {\n reject(error);\n return;\n }\n resolve(stdout);\n });\n });\n },\n },\n\n whisper_timestamped: {\n message: () => {\n const message = [\n \"<ef-captions> Requires whisper_timestamped to be installed.\",\n ];\n\n message.push(\"whisper_timestamped depends on python3\");\n\n message.push(\" - pip3 install whisper_timestamped\");\n\n message.push(\"Alternate installation instructions are availble at:\");\n message.push(\n \"https://github.com/linto-ai/whisper-timestamped#installation\",\n );\n\n return message;\n },\n check: async () => {\n return new Promise((resolve, reject) => {\n exec(\n \"whisper_timestamped --version\",\n (error: any, stdout: any, _stderr: any) => {\n if (error) {\n reject(error);\n return;\n }\n resolve(stdout);\n },\n );\n });\n },\n },\n};\n\nprogram\n .command(\"check\")\n .description(\"Check on dependencies and other requirements\")\n .action(async () => {\n for (const checkName in checks) {\n const check = checks[checkName];\n if (!check) {\n continue;\n }\n const spinner = ora(`Checking ${checkName}`).start();\n try {\n await check.check();\n spinner.succeed(\n chalk.white.bgGreen(` Check for ${checkName} passed `),\n );\n } catch (_error) {\n spinner.fail(chalk.white.bgRed(` Check for ${checkName} failed `));\n process.stderr.write(chalk.red(check.message().join(\"\\n\\n\")));\n process.stderr.write(\"\\n\");\n }\n }\n });\n"],"mappings":";;;;;;;AAWA,MAAMA,SAA0C;CAC9C,QAAQ;EACN,eAAe;GACb,MAAMC,aAAW,GAAG,UAAU;GAC9B,MAAM,UAAU,CACd,6HACD;AACD,WAAQA,YAAR;IACE,KAAK;AACH,aAAQ,KACN,4DACD;AACD,aAAQ,KAAK,yBAAyB;AACtC,aAAQ,KACN,mEACD;AACD;IAEF,KAAK;AACH,aAAQ,KACN,oEACD;AACD;IAEF,KAAK;AACH,aAAQ,KACN,gEACD;AACD,aAAQ,KACN,2EACD;AACD,aAAQ,KAAK,+BAA+B;AAC5C,aAAQ,KAAK,0BAA0B;AACvC,aAAQ,KAAK,2BAA2B;AACxC;IAEF;AACE,aAAQ,KAAK,yBAAyBA,aAAW;AACjD,aAAQ,KACN,gEACD;AACD,aAAQ,KACN,oEACD;AACD;;AAGJ,UAAO;;EAET,OAAO,YAAY;AACjB,UAAO,IAAI,SAAS,SAAS,WAAW;AACtC,SAAK,oBAAoB,OAAY,QAAa,YAAiB;AACjE,SAAI,OAAO;AACT,aAAO,MAAM;AACb;;AAEF,aAAQ,OAAO;MACf;KACF;;EAEL;CAED,qBAAqB;EACnB,eAAe;GACb,MAAM,UAAU,CACd,8DACD;AAED,WAAQ,KAAK,yCAAyC;AAEtD,WAAQ,KAAK,sCAAsC;AAEnD,WAAQ,KAAK,uDAAuD;AACpE,WAAQ,KACN,+DACD;AAED,UAAO;;EAET,OAAO,YAAY;AACjB,UAAO,IAAI,SAAS,SAAS,WAAW;AACtC,SACE,kCACC,OAAY,QAAa,YAAiB;AACzC,SAAI,OAAO;AACT,aAAO,MAAM;AACb;;AAEF,aAAQ,OAAO;MAElB;KACD;;EAEL;CACF;AAED,QACG,QAAQ,QAAQ,CAChB,YAAY,+CAA+C,CAC3D,OAAO,YAAY;AAClB,MAAK,MAAM,aAAa,QAAQ;EAC9B,MAAM,QAAQ,OAAO;AACrB,MAAI,CAAC,MACH;EAEF,MAAM,UAAU,IAAI,YAAY,YAAY,CAAC,OAAO;AACpD,MAAI;AACF,SAAM,MAAM,OAAO;AACnB,WAAQ,QACN,MAAM,MAAM,QAAQ,cAAc,UAAU,WAAW,CACxD;WACM,QAAQ;AACf,WAAQ,KAAK,MAAM,MAAM,MAAM,eAAe,UAAU,WAAW,CAAC;AACpE,WAAQ,OAAO,MAAM,MAAM,IAAI,MAAM,SAAS,CAAC,KAAK,OAAO,CAAC,CAAC;AAC7D,WAAQ,OAAO,MAAM,KAAK;;;EAG9B"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { getClient } from "../utils/index.js";
|
|
2
|
+
import { createReadableStreamFromReadable } from "../utils/createReadableStreamFromReadable.js";
|
|
3
|
+
import { SyncStatus } from "../operations/syncAssetsDirectory/SyncStatus.js";
|
|
4
|
+
import { syncAssetDirectory } from "../operations/syncAssetsDirectory.js";
|
|
5
|
+
import { processRenderInfo } from "../operations/processRenderInfo.js";
|
|
6
|
+
import { withSpinner } from "../utils/withSpinner.js";
|
|
7
|
+
import { launchBrowserAndWaitForSDK } from "../utils/launchBrowserAndWaitForSDK.js";
|
|
8
|
+
import { PreviewServer } from "../utils/startPreviewServer.js";
|
|
9
|
+
import { validateVideoResolution } from "../utils/validateVideoResolution.js";
|
|
10
|
+
import { Option, program } from "commander";
|
|
11
|
+
import debug from "debug";
|
|
12
|
+
import { createRender, uploadRender } from "@editframe/api";
|
|
13
|
+
import path, { basename, join } from "node:path";
|
|
14
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
15
|
+
import { PassThrough } from "node:stream";
|
|
16
|
+
import { md5Directory, md5FilePath } from "@editframe/assets";
|
|
17
|
+
import { spawnSync } from "node:child_process";
|
|
18
|
+
import { inspect } from "node:util";
|
|
19
|
+
import { RenderInfo, getRenderInfo } from "@editframe/elements";
|
|
20
|
+
import { parse } from "node-html-parser";
|
|
21
|
+
import * as tar from "tar";
|
|
22
|
+
|
|
23
|
+
//#region src/commands/cloud-render.ts
|
|
24
|
+
const log = debug("ef:cli:render");
|
|
25
|
+
const buildAssetId = async (srcDir, src, basename$1) => {
|
|
26
|
+
log(`Building image asset id for ${src}\n`);
|
|
27
|
+
const syncStatus = new SyncStatus(join(srcDir, "assets", ".cache", await md5FilePath(path.join(srcDir, src)), basename$1));
|
|
28
|
+
const info = await syncStatus.readInfo();
|
|
29
|
+
if (!info) throw new Error(`SyncStatus info is not found for ${syncStatus.infoPath}`);
|
|
30
|
+
return info.id;
|
|
31
|
+
};
|
|
32
|
+
program.command("cloud-render [directory]").description("Render a directory's index.html file as a video in the editframe cloud").addOption(new Option("-s, --strategy <strategy>", "Render strategy").choices(["v1"]).default("v1")).action(async (directory, options) => {
|
|
33
|
+
directory ??= ".";
|
|
34
|
+
await syncAssetDirectory(join(process.cwd(), directory, "src", "assets", ".cache"));
|
|
35
|
+
const srcDir = path.join(directory, "src");
|
|
36
|
+
const distDir = path.join(directory, "dist");
|
|
37
|
+
await withSpinner("Building\n", async () => {
|
|
38
|
+
try {
|
|
39
|
+
await withSpinner("Building\n", async () => {
|
|
40
|
+
spawnSync("npx", [
|
|
41
|
+
"vite",
|
|
42
|
+
"build",
|
|
43
|
+
directory,
|
|
44
|
+
"--clearScreen",
|
|
45
|
+
"false",
|
|
46
|
+
"--logLevel",
|
|
47
|
+
"debug"
|
|
48
|
+
], { stdio: "inherit" });
|
|
49
|
+
});
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error("Build failed:", error);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
const previewServer = await PreviewServer.start(distDir);
|
|
55
|
+
process.stderr.write("Preview server started at:");
|
|
56
|
+
process.stderr.write(previewServer.url);
|
|
57
|
+
process.stderr.write("\n");
|
|
58
|
+
await launchBrowserAndWaitForSDK({
|
|
59
|
+
url: previewServer.url,
|
|
60
|
+
efInteractive: false,
|
|
61
|
+
interactive: false,
|
|
62
|
+
headless: true
|
|
63
|
+
}, async (page) => {
|
|
64
|
+
const renderInfo = RenderInfo.parse(await page.evaluate(getRenderInfo));
|
|
65
|
+
validateVideoResolution({
|
|
66
|
+
width: renderInfo.width,
|
|
67
|
+
height: renderInfo.height
|
|
68
|
+
});
|
|
69
|
+
await processRenderInfo(renderInfo);
|
|
70
|
+
const doc = parse(await readFile(path.join(distDir, "index.html"), "utf-8"));
|
|
71
|
+
log("Building asset IDs");
|
|
72
|
+
for (const element of doc.querySelectorAll("ef-image, ef-audio, ef-video")) {
|
|
73
|
+
log(`Processing ${element.tagName}`);
|
|
74
|
+
if (element.hasAttribute("asset-id")) {
|
|
75
|
+
log(`Asset ID for ${element.tagName} ${element.getAttribute("src")} is ${element.getAttribute("asset-id")}`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const src = element.getAttribute("src");
|
|
79
|
+
if (!src) {
|
|
80
|
+
log(`No src attribute for ${element.tagName}`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
switch (element.tagName) {
|
|
84
|
+
case "EF-IMAGE":
|
|
85
|
+
element.setAttribute("asset-id", await buildAssetId(srcDir, src, basename(src)));
|
|
86
|
+
break;
|
|
87
|
+
case "EF-AUDIO":
|
|
88
|
+
case "EF-VIDEO":
|
|
89
|
+
element.setAttribute("asset-id", await buildAssetId(srcDir, src, "isobmff"));
|
|
90
|
+
break;
|
|
91
|
+
default: log(`Unknown element type: ${element.tagName}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
await writeFile(path.join(distDir, "index.html"), doc.toString());
|
|
95
|
+
const md5 = await md5Directory(distDir);
|
|
96
|
+
const render = await createRender(getClient(), {
|
|
97
|
+
md5,
|
|
98
|
+
width: renderInfo.width,
|
|
99
|
+
height: renderInfo.height,
|
|
100
|
+
fps: renderInfo.fps,
|
|
101
|
+
duration_ms: renderInfo.durationMs,
|
|
102
|
+
work_slice_ms: 4e3,
|
|
103
|
+
strategy: options.strategy
|
|
104
|
+
});
|
|
105
|
+
if (render?.status !== "created") {
|
|
106
|
+
process.stderr.write(`Render is in '${render?.status}' status. It cannot be recreated while in this status.\n`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* This tar stream is created with the dist directory as the root.
|
|
111
|
+
* This is acheived by setting the cwd option to the dist directory.
|
|
112
|
+
* And the files to be included in the tar stream are all files in the dist directory.
|
|
113
|
+
*
|
|
114
|
+
* The renderer expects to find the index.html file at the root of the tar stream.
|
|
115
|
+
*/
|
|
116
|
+
const tarStream = tar.create({
|
|
117
|
+
gzip: true,
|
|
118
|
+
cwd: distDir
|
|
119
|
+
}, ["."]);
|
|
120
|
+
const readable = new PassThrough();
|
|
121
|
+
tarStream.pipe(readable);
|
|
122
|
+
await uploadRender(getClient(), render.id, createReadableStreamFromReadable(readable));
|
|
123
|
+
process.stderr.write("Render assets uploaded\n");
|
|
124
|
+
process.stderr.write(inspect(render));
|
|
125
|
+
process.stderr.write("\n");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
//#endregion
|
|
130
|
+
export { };
|
|
131
|
+
//# sourceMappingURL=cloud-render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-render.js","names":["basename","parseHTML"],"sources":["../../src/commands/cloud-render.ts"],"sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport path, { basename, join } from \"node:path\";\nimport { PassThrough } from \"node:stream\";\nimport { inspect } from \"node:util\";\nimport { createRender, uploadRender } from \"@editframe/api\";\nimport { md5Directory, md5FilePath } from \"@editframe/assets\";\nimport { getRenderInfo, RenderInfo } from \"@editframe/elements\";\nimport { Option, program } from \"commander\";\nimport debug from \"debug\";\nimport { parse as parseHTML } from \"node-html-parser\";\nimport * as tar from \"tar\";\nimport { processRenderInfo } from \"../operations/processRenderInfo.js\";\nimport { SyncStatus } from \"../operations/syncAssetsDirectory/SyncStatus.js\";\nimport { syncAssetDirectory } from \"../operations/syncAssetsDirectory.js\";\nimport { createReadableStreamFromReadable } from \"../utils/createReadableStreamFromReadable.js\";\nimport { getClient } from \"../utils/index.js\";\nimport { launchBrowserAndWaitForSDK } from \"../utils/launchBrowserAndWaitForSDK.js\";\nimport { PreviewServer } from \"../utils/startPreviewServer.js\";\nimport { validateVideoResolution } from \"../utils/validateVideoResolution.js\";\nimport { withSpinner } from \"../utils/withSpinner.js\";\n\nconst log = debug(\"ef:cli:render\");\n\nexport const buildAssetId = async (\n srcDir: string,\n src: string,\n basename: string,\n) => {\n log(`Building image asset id for ${src}\\n`);\n const assetPath = path.join(srcDir, src);\n const assetMd5 = await md5FilePath(assetPath);\n const syncStatus = new SyncStatus(\n join(srcDir, \"assets\", \".cache\", assetMd5, basename),\n );\n const info = await syncStatus.readInfo();\n if (!info) {\n throw new Error(`SyncStatus info is not found for ${syncStatus.infoPath}`);\n }\n\n return info.id;\n};\n\nprogram\n .command(\"cloud-render [directory]\")\n .description(\n \"Render a directory's index.html file as a video in the editframe cloud\",\n )\n .addOption(\n new Option(\"-s, --strategy <strategy>\", \"Render strategy\")\n .choices([\"v1\"])\n .default(\"v1\"),\n )\n .action(async (directory, options) => {\n directory ??= \".\";\n\n await syncAssetDirectory(\n join(process.cwd(), directory, \"src\", \"assets\", \".cache\"),\n );\n\n const srcDir = path.join(directory, \"src\");\n const distDir = path.join(directory, \"dist\");\n await withSpinner(\"Building\\n\", async () => {\n try {\n await withSpinner(\"Building\\n\", async () => {\n spawnSync(\n \"npx\",\n // biome-ignore format: Grouping CLI arguments\n [\n \"vite\",\n \"build\",\n directory,\n \"--clearScreen\",\n \"false\",\n \"--logLevel\",\n \"debug\",\n ],\n {\n stdio: \"inherit\",\n },\n );\n });\n } catch (error) {\n console.error(\"Build failed:\", error);\n }\n });\n\n const previewServer = await PreviewServer.start(distDir);\n process.stderr.write(\"Preview server started at:\");\n process.stderr.write(previewServer.url);\n process.stderr.write(\"\\n\");\n await launchBrowserAndWaitForSDK(\n {\n url: previewServer.url,\n efInteractive: false,\n interactive: false,\n headless: true,\n },\n async (page) => {\n const renderInfo = RenderInfo.parse(await page.evaluate(getRenderInfo));\n\n validateVideoResolution({\n width: renderInfo.width,\n height: renderInfo.height,\n });\n\n await processRenderInfo(renderInfo);\n\n const doc = parseHTML(\n await readFile(path.join(distDir, \"index.html\"), \"utf-8\"),\n );\n\n log(\"Building asset IDs\");\n for (const element of doc.querySelectorAll(\n \"ef-image, ef-audio, ef-video\",\n )) {\n log(`Processing ${element.tagName}`);\n if (element.hasAttribute(\"asset-id\")) {\n log(\n `Asset ID for ${element.tagName} ${element.getAttribute(\"src\")} is ${element.getAttribute(\"asset-id\")}`,\n );\n continue;\n }\n const src = element.getAttribute(\"src\");\n if (!src) {\n log(`No src attribute for ${element.tagName}`);\n continue;\n }\n\n switch (element.tagName) {\n case \"EF-IMAGE\":\n element.setAttribute(\n \"asset-id\",\n await buildAssetId(srcDir, src, basename(src)),\n );\n break;\n case \"EF-AUDIO\":\n case \"EF-VIDEO\":\n element.setAttribute(\n \"asset-id\",\n await buildAssetId(srcDir, src, \"isobmff\"),\n );\n break;\n default:\n log(`Unknown element type: ${element.tagName}`);\n }\n }\n\n await writeFile(path.join(distDir, \"index.html\"), doc.toString());\n\n const md5 = await md5Directory(distDir);\n const render = await createRender(getClient(), {\n md5,\n width: renderInfo.width,\n height: renderInfo.height,\n fps: renderInfo.fps,\n duration_ms: renderInfo.durationMs,\n work_slice_ms: 4_000,\n strategy: options.strategy,\n });\n if (render?.status !== \"created\") {\n process.stderr.write(\n `Render is in '${render?.status}' status. It cannot be recreated while in this status.\\n`,\n );\n return;\n }\n /**\n * This tar stream is created with the dist directory as the root.\n * This is acheived by setting the cwd option to the dist directory.\n * And the files to be included in the tar stream are all files in the dist directory.\n *\n * The renderer expects to find the index.html file at the root of the tar stream.\n */\n const tarStream = tar.create(\n {\n gzip: true,\n cwd: distDir,\n },\n [\".\"],\n );\n const readable = new PassThrough();\n tarStream.pipe(readable);\n\n await uploadRender(\n getClient(),\n render.id,\n createReadableStreamFromReadable(readable),\n );\n process.stderr.write(\"Render assets uploaded\\n\");\n process.stderr.write(inspect(render));\n process.stderr.write(\"\\n\");\n },\n );\n });\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAM,MAAM,MAAM,gBAAgB;AAElC,MAAa,eAAe,OAC1B,QACA,KACA,eACG;AACH,KAAI,+BAA+B,IAAI,IAAI;CAG3C,MAAM,aAAa,IAAI,WACrB,KAAK,QAAQ,UAAU,UAFR,MAAM,YADL,KAAK,KAAK,QAAQ,IAAI,CACK,EAEAA,WAAS,CACrD;CACD,MAAM,OAAO,MAAM,WAAW,UAAU;AACxC,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,oCAAoC,WAAW,WAAW;AAG5E,QAAO,KAAK;;AAGd,QACG,QAAQ,2BAA2B,CACnC,YACC,yEACD,CACA,UACC,IAAI,OAAO,6BAA6B,kBAAkB,CACvD,QAAQ,CAAC,KAAK,CAAC,CACf,QAAQ,KAAK,CACjB,CACA,OAAO,OAAO,WAAW,YAAY;AACpC,eAAc;AAEd,OAAM,mBACJ,KAAK,QAAQ,KAAK,EAAE,WAAW,OAAO,UAAU,SAAS,CAC1D;CAED,MAAM,SAAS,KAAK,KAAK,WAAW,MAAM;CAC1C,MAAM,UAAU,KAAK,KAAK,WAAW,OAAO;AAC5C,OAAM,YAAY,cAAc,YAAY;AAC1C,MAAI;AACF,SAAM,YAAY,cAAc,YAAY;AAC1C,cACE,OAEA;KACE;KACA;KACA;KACA;KACA;KACA;KACA;KACD,EACD,EACE,OAAO,WACR,CACF;KACD;WACK,OAAO;AACd,WAAQ,MAAM,iBAAiB,MAAM;;GAEvC;CAEF,MAAM,gBAAgB,MAAM,cAAc,MAAM,QAAQ;AACxD,SAAQ,OAAO,MAAM,6BAA6B;AAClD,SAAQ,OAAO,MAAM,cAAc,IAAI;AACvC,SAAQ,OAAO,MAAM,KAAK;AAC1B,OAAM,2BACJ;EACE,KAAK,cAAc;EACnB,eAAe;EACf,aAAa;EACb,UAAU;EACX,EACD,OAAO,SAAS;EACd,MAAM,aAAa,WAAW,MAAM,MAAM,KAAK,SAAS,cAAc,CAAC;AAEvE,0BAAwB;GACtB,OAAO,WAAW;GAClB,QAAQ,WAAW;GACpB,CAAC;AAEF,QAAM,kBAAkB,WAAW;EAEnC,MAAM,MAAMC,MACV,MAAM,SAAS,KAAK,KAAK,SAAS,aAAa,EAAE,QAAQ,CAC1D;AAED,MAAI,qBAAqB;AACzB,OAAK,MAAM,WAAW,IAAI,iBACxB,+BACD,EAAE;AACD,OAAI,cAAc,QAAQ,UAAU;AACpC,OAAI,QAAQ,aAAa,WAAW,EAAE;AACpC,QACE,gBAAgB,QAAQ,QAAQ,GAAG,QAAQ,aAAa,MAAM,CAAC,MAAM,QAAQ,aAAa,WAAW,GACtG;AACD;;GAEF,MAAM,MAAM,QAAQ,aAAa,MAAM;AACvC,OAAI,CAAC,KAAK;AACR,QAAI,wBAAwB,QAAQ,UAAU;AAC9C;;AAGF,WAAQ,QAAQ,SAAhB;IACE,KAAK;AACH,aAAQ,aACN,YACA,MAAM,aAAa,QAAQ,KAAK,SAAS,IAAI,CAAC,CAC/C;AACD;IACF,KAAK;IACL,KAAK;AACH,aAAQ,aACN,YACA,MAAM,aAAa,QAAQ,KAAK,UAAU,CAC3C;AACD;IACF,QACE,KAAI,yBAAyB,QAAQ,UAAU;;;AAIrD,QAAM,UAAU,KAAK,KAAK,SAAS,aAAa,EAAE,IAAI,UAAU,CAAC;EAEjE,MAAM,MAAM,MAAM,aAAa,QAAQ;EACvC,MAAM,SAAS,MAAM,aAAa,WAAW,EAAE;GAC7C;GACA,OAAO,WAAW;GAClB,QAAQ,WAAW;GACnB,KAAK,WAAW;GAChB,aAAa,WAAW;GACxB,eAAe;GACf,UAAU,QAAQ;GACnB,CAAC;AACF,MAAI,QAAQ,WAAW,WAAW;AAChC,WAAQ,OAAO,MACb,iBAAiB,QAAQ,OAAO,0DACjC;AACD;;;;;;;;;EASF,MAAM,YAAY,IAAI,OACpB;GACE,MAAM;GACN,KAAK;GACN,EACD,CAAC,IAAI,CACN;EACD,MAAM,WAAW,IAAI,aAAa;AAClC,YAAU,KAAK,SAAS;AAExB,QAAM,aACJ,WAAW,EACX,OAAO,IACP,iCAAiC,SAAS,CAC3C;AACD,UAAQ,OAAO,MAAM,2BAA2B;AAChD,UAAQ,OAAO,MAAM,QAAQ,OAAO,CAAC;AACrC,UAAQ,OAAO,MAAM,KAAK;GAE7B;EACD"}
|
package/dist/commands/render.js
CHANGED
|
@@ -1,129 +1,79 @@
|
|
|
1
|
-
import { getClient } from "../utils/index.js";
|
|
2
|
-
import { createReadableStreamFromReadable } from "../utils/createReadableStreamFromReadable.js";
|
|
3
|
-
import { SyncStatus } from "../operations/syncAssetsDirectory/SyncStatus.js";
|
|
4
|
-
import { syncAssetDirectory } from "../operations/syncAssetsDirectory.js";
|
|
5
|
-
import { processRenderInfo } from "../operations/processRenderInfo.js";
|
|
6
1
|
import { withSpinner } from "../utils/withSpinner.js";
|
|
7
2
|
import { launchBrowserAndWaitForSDK } from "../utils/launchBrowserAndWaitForSDK.js";
|
|
8
3
|
import { PreviewServer } from "../utils/startPreviewServer.js";
|
|
9
|
-
import {
|
|
10
|
-
import { Option, program } from "commander";
|
|
4
|
+
import { program } from "commander";
|
|
11
5
|
import debug from "debug";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
import { PassThrough } from "node:stream";
|
|
16
|
-
import { md5Directory, md5FilePath } from "@editframe/assets";
|
|
17
|
-
import { spawnSync } from "node:child_process";
|
|
18
|
-
import { inspect } from "node:util";
|
|
19
|
-
import { RenderInfo, getRenderInfo } from "@editframe/elements";
|
|
20
|
-
import { parse } from "node-html-parser";
|
|
21
|
-
import * as tar from "tar";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import { createWriteStream } from "node:fs";
|
|
22
9
|
|
|
23
10
|
//#region src/commands/render.ts
|
|
24
11
|
const log = debug("ef:cli:render");
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
directory,
|
|
44
|
-
"--clearScreen",
|
|
45
|
-
"false",
|
|
46
|
-
"--logLevel",
|
|
47
|
-
"debug"
|
|
48
|
-
], { stdio: "inherit" });
|
|
49
|
-
});
|
|
50
|
-
} catch (error) {
|
|
51
|
-
console.error("Build failed:", error);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
const previewServer = await PreviewServer.start(distDir);
|
|
55
|
-
process.stderr.write("Preview server started at:");
|
|
56
|
-
process.stderr.write(previewServer.url);
|
|
57
|
-
process.stderr.write("\n");
|
|
12
|
+
program.command("render [directory]").description("Render a video composition locally").option("-o, --output <path>", "Output file path", "output.mp4").option("-d, --data <json>", "Custom render data (JSON string)").option("--data-file <path>", "Custom render data from JSON file").option("--fps <number>", "Frame rate", "30").option("--scale <number>", "Resolution scale (0-1)", "1").option("--include-audio", "Include audio track", true).option("--no-include-audio", "Exclude audio track").option("--from-ms <number>", "Start time in milliseconds").option("--to-ms <number>", "End time in milliseconds").option("--experimental-native-render", "Use experimental canvas capture API (faster)").action(async (directory = ".", options) => {
|
|
13
|
+
const srcDir = path.resolve(process.cwd(), directory);
|
|
14
|
+
const outputPath = path.resolve(process.cwd(), options.output);
|
|
15
|
+
let renderData;
|
|
16
|
+
if (options.dataFile) {
|
|
17
|
+
const dataFileContent = await readFile(options.dataFile, "utf-8");
|
|
18
|
+
renderData = JSON.parse(dataFileContent);
|
|
19
|
+
log("Loaded render data from file:", options.dataFile);
|
|
20
|
+
} else if (options.data) {
|
|
21
|
+
renderData = JSON.parse(options.data);
|
|
22
|
+
log("Using render data from --data option");
|
|
23
|
+
}
|
|
24
|
+
const fps = parseInt(options.fps, 10);
|
|
25
|
+
const scale = parseFloat(options.scale);
|
|
26
|
+
const fromMs = options.fromMs ? parseInt(options.fromMs, 10) : void 0;
|
|
27
|
+
const toMs = options.toMs ? parseInt(options.toMs, 10) : void 0;
|
|
28
|
+
const previewServer = await PreviewServer.start(srcDir);
|
|
29
|
+
log("Preview server started at:", previewServer.url);
|
|
58
30
|
await launchBrowserAndWaitForSDK({
|
|
59
31
|
url: previewServer.url,
|
|
60
|
-
|
|
32
|
+
headless: true,
|
|
61
33
|
interactive: false,
|
|
62
|
-
|
|
34
|
+
efInteractive: false,
|
|
35
|
+
nativeRender: options.experimentalNativeRender === true
|
|
63
36
|
}, async (page) => {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
37
|
+
const outputStream = createWriteStream(outputPath);
|
|
38
|
+
let chunkCount = 0;
|
|
39
|
+
let totalBytes = 0;
|
|
40
|
+
await page.exposeFunction("onRenderChunk", (chunkArray) => {
|
|
41
|
+
const chunk = Buffer.from(chunkArray);
|
|
42
|
+
outputStream.write(chunk);
|
|
43
|
+
chunkCount++;
|
|
44
|
+
totalBytes += chunk.length;
|
|
45
|
+
log(`Received chunk ${chunkCount}: ${chunk.length} bytes (total: ${totalBytes} bytes)`);
|
|
68
46
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
log(
|
|
74
|
-
if (element.hasAttribute("asset-id")) {
|
|
75
|
-
log(`Asset ID for ${element.tagName} ${element.getAttribute("src")} is ${element.getAttribute("asset-id")}`);
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
const src = element.getAttribute("src");
|
|
79
|
-
if (!src) {
|
|
80
|
-
log(`No src attribute for ${element.tagName}`);
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
switch (element.tagName) {
|
|
84
|
-
case "EF-IMAGE":
|
|
85
|
-
element.setAttribute("asset-id", await buildAssetId(srcDir, src, basename(src)));
|
|
86
|
-
break;
|
|
87
|
-
case "EF-AUDIO":
|
|
88
|
-
case "EF-VIDEO":
|
|
89
|
-
element.setAttribute("asset-id", await buildAssetId(srcDir, src, "isobmff"));
|
|
90
|
-
break;
|
|
91
|
-
default: log(`Unknown element type: ${element.tagName}`);
|
|
92
|
-
}
|
|
47
|
+
if (renderData) {
|
|
48
|
+
await page.evaluate((data) => {
|
|
49
|
+
window.EF_RENDER_DATA = data;
|
|
50
|
+
}, renderData);
|
|
51
|
+
log("Set EF_RENDER_DATA:", renderData);
|
|
93
52
|
}
|
|
94
|
-
await
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
53
|
+
await page.waitForFunction(() => typeof window.EF_RENDER !== "undefined", { timeout: 1e4 });
|
|
54
|
+
if (!await page.evaluate(() => window.EF_RENDER?.isReady())) throw new Error("Render API is not ready. No ef-timegroup found.");
|
|
55
|
+
await withSpinner("Rendering video...", async () => {
|
|
56
|
+
const renderOptions = {
|
|
57
|
+
fps,
|
|
58
|
+
scale,
|
|
59
|
+
includeAudio: options.includeAudio !== false
|
|
60
|
+
};
|
|
61
|
+
if (fromMs !== void 0) renderOptions.fromMs = fromMs;
|
|
62
|
+
if (toMs !== void 0) renderOptions.toMs = toMs;
|
|
63
|
+
await page.evaluate(async (opts) => {
|
|
64
|
+
await window.EF_RENDER.renderStreaming(opts);
|
|
65
|
+
}, renderOptions);
|
|
66
|
+
});
|
|
67
|
+
outputStream.end();
|
|
68
|
+
await new Promise((resolve, reject) => {
|
|
69
|
+
outputStream.on("finish", () => {
|
|
70
|
+
log(`Render complete: ${chunkCount} chunks, ${totalBytes} bytes written to ${outputPath}`);
|
|
71
|
+
resolve();
|
|
72
|
+
});
|
|
73
|
+
outputStream.on("error", reject);
|
|
104
74
|
});
|
|
105
|
-
if (render?.status !== "created") {
|
|
106
|
-
process.stderr.write(`Render is in '${render?.status}' status. It cannot be recreated while in this status.\n`);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* This tar stream is created with the dist directory as the root.
|
|
111
|
-
* This is acheived by setting the cwd option to the dist directory.
|
|
112
|
-
* And the files to be included in the tar stream are all files in the dist directory.
|
|
113
|
-
*
|
|
114
|
-
* The renderer expects to find the index.html file at the root of the tar stream.
|
|
115
|
-
*/
|
|
116
|
-
const tarStream = tar.create({
|
|
117
|
-
gzip: true,
|
|
118
|
-
cwd: distDir
|
|
119
|
-
}, ["."]);
|
|
120
|
-
const readable = new PassThrough();
|
|
121
|
-
tarStream.pipe(readable);
|
|
122
|
-
await uploadRender(getClient(), render.id, createReadableStreamFromReadable(readable));
|
|
123
|
-
process.stderr.write("Render assets uploaded\n");
|
|
124
|
-
process.stderr.write(inspect(render));
|
|
125
|
-
process.stderr.write("\n");
|
|
126
75
|
});
|
|
76
|
+
process.stderr.write(`\nRender complete: ${outputPath}\n`);
|
|
127
77
|
});
|
|
128
78
|
|
|
129
79
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","names":["
|
|
1
|
+
{"version":3,"file":"render.js","names":["renderData: Record<string, unknown> | undefined","renderOptions: any"],"sources":["../../src/commands/render.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport path from \"node:path\";\nimport { program } from \"commander\";\nimport debug from \"debug\";\nimport { launchBrowserAndWaitForSDK } from \"../utils/launchBrowserAndWaitForSDK.js\";\nimport { PreviewServer } from \"../utils/startPreviewServer.js\";\nimport { withSpinner } from \"../utils/withSpinner.js\";\n\nconst log = debug(\"ef:cli:render\");\n\nprogram\n .command(\"render [directory]\")\n .description(\"Render a video composition locally\")\n .option(\"-o, --output <path>\", \"Output file path\", \"output.mp4\")\n .option(\"-d, --data <json>\", \"Custom render data (JSON string)\")\n .option(\"--data-file <path>\", \"Custom render data from JSON file\")\n .option(\"--fps <number>\", \"Frame rate\", \"30\")\n .option(\"--scale <number>\", \"Resolution scale (0-1)\", \"1\")\n .option(\"--include-audio\", \"Include audio track\", true)\n .option(\"--no-include-audio\", \"Exclude audio track\")\n .option(\"--from-ms <number>\", \"Start time in milliseconds\")\n .option(\"--to-ms <number>\", \"End time in milliseconds\")\n .option(\"--experimental-native-render\", \"Use experimental canvas capture API (faster)\")\n .action(async (directory = \".\", options) => {\n const srcDir = path.resolve(process.cwd(), directory);\n const outputPath = path.resolve(process.cwd(), options.output);\n\n // Parse custom data if provided\n let renderData: Record<string, unknown> | undefined;\n if (options.dataFile) {\n const dataFileContent = await readFile(options.dataFile, \"utf-8\");\n renderData = JSON.parse(dataFileContent);\n log(\"Loaded render data from file:\", options.dataFile);\n } else if (options.data) {\n renderData = JSON.parse(options.data);\n log(\"Using render data from --data option\");\n }\n\n // Parse numeric options\n const fps = parseInt(options.fps, 10);\n const scale = parseFloat(options.scale);\n const fromMs = options.fromMs ? parseInt(options.fromMs, 10) : undefined;\n const toMs = options.toMs ? parseInt(options.toMs, 10) : undefined;\n\n // Start preview server\n const previewServer = await PreviewServer.start(srcDir);\n log(\"Preview server started at:\", previewServer.url);\n\n // Launch browser and render\n await launchBrowserAndWaitForSDK(\n {\n url: previewServer.url,\n headless: true,\n interactive: false,\n efInteractive: false,\n nativeRender: options.experimentalNativeRender === true,\n },\n async (page) => {\n // Open output file for streaming writes\n const outputStream = createWriteStream(outputPath);\n let chunkCount = 0;\n let totalBytes = 0;\n\n // Expose chunk handler - writes directly to file\n await page.exposeFunction(\"onRenderChunk\", (chunkArray: number[]) => {\n const chunk = Buffer.from(chunkArray);\n outputStream.write(chunk);\n chunkCount++;\n totalBytes += chunk.length;\n log(`Received chunk ${chunkCount}: ${chunk.length} bytes (total: ${totalBytes} bytes)`);\n });\n\n // Set custom render data if provided\n if (renderData) {\n await page.evaluate((data) => {\n window.EF_RENDER_DATA = data;\n }, renderData);\n log(\"Set EF_RENDER_DATA:\", renderData);\n }\n\n // Wait for EF_RENDER API to be available\n await page.waitForFunction(\n () => typeof window.EF_RENDER !== \"undefined\",\n { timeout: 10_000 },\n );\n\n // Check if ready\n const isReady = await page.evaluate(() => window.EF_RENDER?.isReady());\n if (!isReady) {\n throw new Error(\"Render API is not ready. No ef-timegroup found.\");\n }\n\n // Render with streaming\n await withSpinner(\"Rendering video...\", async () => {\n const renderOptions: any = {\n fps,\n scale,\n includeAudio: options.includeAudio !== false,\n };\n\n if (fromMs !== undefined) {\n renderOptions.fromMs = fromMs;\n }\n if (toMs !== undefined) {\n renderOptions.toMs = toMs;\n }\n\n await page.evaluate(async (opts) => {\n await window.EF_RENDER!.renderStreaming(opts);\n }, renderOptions);\n });\n\n // Close the output stream\n outputStream.end();\n\n // Wait for stream to finish\n await new Promise<void>((resolve, reject) => {\n outputStream.on(\"finish\", () => {\n log(`Render complete: ${chunkCount} chunks, ${totalBytes} bytes written to ${outputPath}`);\n resolve();\n });\n outputStream.on(\"error\", reject);\n });\n },\n );\n\n process.stderr.write(`\\nRender complete: ${outputPath}\\n`);\n });\n"],"mappings":";;;;;;;;;;AASA,MAAM,MAAM,MAAM,gBAAgB;AAElC,QACG,QAAQ,qBAAqB,CAC7B,YAAY,qCAAqC,CACjD,OAAO,uBAAuB,oBAAoB,aAAa,CAC/D,OAAO,qBAAqB,mCAAmC,CAC/D,OAAO,sBAAsB,oCAAoC,CACjE,OAAO,kBAAkB,cAAc,KAAK,CAC5C,OAAO,oBAAoB,0BAA0B,IAAI,CACzD,OAAO,mBAAmB,uBAAuB,KAAK,CACtD,OAAO,sBAAsB,sBAAsB,CACnD,OAAO,sBAAsB,6BAA6B,CAC1D,OAAO,oBAAoB,2BAA2B,CACtD,OAAO,gCAAgC,+CAA+C,CACtF,OAAO,OAAO,YAAY,KAAK,YAAY;CAC1C,MAAM,SAAS,KAAK,QAAQ,QAAQ,KAAK,EAAE,UAAU;CACrD,MAAM,aAAa,KAAK,QAAQ,QAAQ,KAAK,EAAE,QAAQ,OAAO;CAG9D,IAAIA;AACJ,KAAI,QAAQ,UAAU;EACpB,MAAM,kBAAkB,MAAM,SAAS,QAAQ,UAAU,QAAQ;AACjE,eAAa,KAAK,MAAM,gBAAgB;AACxC,MAAI,iCAAiC,QAAQ,SAAS;YAC7C,QAAQ,MAAM;AACvB,eAAa,KAAK,MAAM,QAAQ,KAAK;AACrC,MAAI,uCAAuC;;CAI7C,MAAM,MAAM,SAAS,QAAQ,KAAK,GAAG;CACrC,MAAM,QAAQ,WAAW,QAAQ,MAAM;CACvC,MAAM,SAAS,QAAQ,SAAS,SAAS,QAAQ,QAAQ,GAAG,GAAG;CAC/D,MAAM,OAAO,QAAQ,OAAO,SAAS,QAAQ,MAAM,GAAG,GAAG;CAGzD,MAAM,gBAAgB,MAAM,cAAc,MAAM,OAAO;AACvD,KAAI,8BAA8B,cAAc,IAAI;AAGpD,OAAM,2BACJ;EACE,KAAK,cAAc;EACnB,UAAU;EACV,aAAa;EACb,eAAe;EACf,cAAc,QAAQ,6BAA6B;EACpD,EACD,OAAO,SAAS;EAEd,MAAM,eAAe,kBAAkB,WAAW;EAClD,IAAI,aAAa;EACjB,IAAI,aAAa;AAGjB,QAAM,KAAK,eAAe,kBAAkB,eAAyB;GACnE,MAAM,QAAQ,OAAO,KAAK,WAAW;AACrC,gBAAa,MAAM,MAAM;AACzB;AACA,iBAAc,MAAM;AACpB,OAAI,kBAAkB,WAAW,IAAI,MAAM,OAAO,iBAAiB,WAAW,SAAS;IACvF;AAGF,MAAI,YAAY;AACd,SAAM,KAAK,UAAU,SAAS;AAC5B,WAAO,iBAAiB;MACvB,WAAW;AACd,OAAI,uBAAuB,WAAW;;AAIxC,QAAM,KAAK,sBACH,OAAO,OAAO,cAAc,aAClC,EAAE,SAAS,KAAQ,CACpB;AAID,MAAI,CADY,MAAM,KAAK,eAAe,OAAO,WAAW,SAAS,CAAC,CAEpE,OAAM,IAAI,MAAM,kDAAkD;AAIpE,QAAM,YAAY,sBAAsB,YAAY;GAClD,MAAMC,gBAAqB;IACzB;IACA;IACA,cAAc,QAAQ,iBAAiB;IACxC;AAED,OAAI,WAAW,OACb,eAAc,SAAS;AAEzB,OAAI,SAAS,OACX,eAAc,OAAO;AAGvB,SAAM,KAAK,SAAS,OAAO,SAAS;AAClC,UAAM,OAAO,UAAW,gBAAgB,KAAK;MAC5C,cAAc;IACjB;AAGF,eAAa,KAAK;AAGlB,QAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,gBAAa,GAAG,gBAAgB;AAC9B,QAAI,oBAAoB,WAAW,WAAW,WAAW,oBAAoB,aAAa;AAC1F,aAAS;KACT;AACF,gBAAa,GAAG,SAAS,OAAO;IAChC;GAEL;AAED,SAAQ,OAAO,MAAM,sBAAsB,WAAW,IAAI;EAC1D"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport { Option, program } from \"commander\";\n\nimport { VERSION } from \"./VERSION.js\";\n\nprogram\n .name(\"editframe\")\n .addOption(new Option(\"-t, --token <token>\", \"API Token\").env(\"EF_TOKEN\"))\n .addOption(\n new Option(\"--ef-host <host>\", \"Editframe Host\")\n .env(\"EF_HOST\")\n .default(\"https://editframe.com\"),\n )\n .addOption(\n new Option(\"--ef-render-host <host>\", \"Editframe Render Host\")\n .env(\"EF_RENDER_HOST\")\n .default(\"https://editframe.com\"),\n )\n .version(VERSION);\n\nimport \"./commands/auth.js\";\nimport \"./commands/sync.js\";\nimport \"./commands/render.js\";\nimport \"./commands/preview.js\";\nimport \"./commands/process.js\";\nimport \"./commands/process-file.js\";\nimport \"./commands/check.js\";\nimport \"./commands/webhook.js\";\n\nprogram.parse(process.argv);\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport { Option, program } from \"commander\";\n\nimport { VERSION } from \"./VERSION.js\";\n\nprogram\n .name(\"editframe\")\n .addOption(new Option(\"-t, --token <token>\", \"API Token\").env(\"EF_TOKEN\"))\n .addOption(\n new Option(\"--ef-host <host>\", \"Editframe Host\")\n .env(\"EF_HOST\")\n .default(\"https://editframe.com\"),\n )\n .addOption(\n new Option(\"--ef-render-host <host>\", \"Editframe Render Host\")\n .env(\"EF_RENDER_HOST\")\n .default(\"https://editframe.com\"),\n )\n .version(VERSION);\n\nimport \"./commands/auth.js\";\nimport \"./commands/sync.js\";\nimport \"./commands/cloud-render.js\";\nimport \"./commands/render.js\";\nimport \"./commands/preview.js\";\nimport \"./commands/process.js\";\nimport \"./commands/process-file.js\";\nimport \"./commands/check.js\";\nimport \"./commands/webhook.js\";\n\nprogram.parse(process.argv);\n"],"mappings":";;;;;;;;;;;;;;;AAMA,QACG,KAAK,YAAY,CACjB,UAAU,IAAI,OAAO,uBAAuB,YAAY,CAAC,IAAI,WAAW,CAAC,CACzE,UACC,IAAI,OAAO,oBAAoB,iBAAiB,CAC7C,IAAI,UAAU,CACd,QAAQ,wBAAwB,CACpC,CACA,UACC,IAAI,OAAO,2BAA2B,wBAAwB,CAC3D,IAAI,iBAAiB,CACrB,QAAQ,wBAAwB,CACpC,CACA,QAAQ,QAAQ;AAYnB,QAAQ,MAAM,QAAQ,KAAK"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { platform } from "node:os";
|
|
5
|
+
|
|
6
|
+
//#region src/utils/detectChrome.ts
|
|
7
|
+
/**
|
|
8
|
+
* Detects if Google Chrome is installed on the system.
|
|
9
|
+
* Returns the path to Chrome executable if found.
|
|
10
|
+
*/
|
|
11
|
+
function detectChrome() {
|
|
12
|
+
const osPlatform = platform();
|
|
13
|
+
if (osPlatform === "darwin") {
|
|
14
|
+
const chromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
15
|
+
if (existsSync(chromePath)) return {
|
|
16
|
+
found: true,
|
|
17
|
+
path: chromePath
|
|
18
|
+
};
|
|
19
|
+
const chromiumPath = "/Applications/Chromium.app/Contents/MacOS/Chromium";
|
|
20
|
+
if (existsSync(chromiumPath)) return {
|
|
21
|
+
found: true,
|
|
22
|
+
path: chromiumPath
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
found: false,
|
|
26
|
+
error: "Chrome not found in /Applications/Google Chrome.app"
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (osPlatform === "linux") {
|
|
30
|
+
try {
|
|
31
|
+
const chromePath = execSync("which google-chrome", { encoding: "utf-8" }).trim();
|
|
32
|
+
if (chromePath && existsSync(chromePath)) return {
|
|
33
|
+
found: true,
|
|
34
|
+
path: chromePath
|
|
35
|
+
};
|
|
36
|
+
} catch {}
|
|
37
|
+
try {
|
|
38
|
+
const chromiumPath = execSync("which chromium", { encoding: "utf-8" }).trim();
|
|
39
|
+
if (chromiumPath && existsSync(chromiumPath)) return {
|
|
40
|
+
found: true,
|
|
41
|
+
path: chromiumPath
|
|
42
|
+
};
|
|
43
|
+
} catch {}
|
|
44
|
+
try {
|
|
45
|
+
const chromiumBrowserPath = execSync("which chromium-browser", { encoding: "utf-8" }).trim();
|
|
46
|
+
if (chromiumBrowserPath && existsSync(chromiumBrowserPath)) return {
|
|
47
|
+
found: true,
|
|
48
|
+
path: chromiumBrowserPath
|
|
49
|
+
};
|
|
50
|
+
} catch {}
|
|
51
|
+
return {
|
|
52
|
+
found: false,
|
|
53
|
+
error: "Chrome not found. Install with: apt-get install google-chrome-stable"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (osPlatform === "win32") {
|
|
57
|
+
const possiblePaths = [
|
|
58
|
+
path.join(process.env.LOCALAPPDATA || "", "Google", "Chrome", "Application", "chrome.exe"),
|
|
59
|
+
path.join(process.env.PROGRAMFILES || "", "Google", "Chrome", "Application", "chrome.exe"),
|
|
60
|
+
path.join(process.env["PROGRAMFILES(X86)"] || "", "Google", "Chrome", "Application", "chrome.exe")
|
|
61
|
+
];
|
|
62
|
+
for (const chromePath of possiblePaths) if (existsSync(chromePath)) return {
|
|
63
|
+
found: true,
|
|
64
|
+
path: chromePath
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
found: false,
|
|
68
|
+
error: "Chrome not found in common Windows installation paths"
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
found: false,
|
|
73
|
+
error: `Unsupported platform: ${osPlatform}`
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Throws a user-friendly error if Chrome is not found.
|
|
78
|
+
*/
|
|
79
|
+
function requireChrome() {
|
|
80
|
+
const result = detectChrome();
|
|
81
|
+
if (!result.found) throw new Error("Chrome browser not found.\n\nThe render command requires Google Chrome to be installed on your system.\n\nInstall Chrome from: https://www.google.com/chrome/\n" + (result.error ? `\nDetails: ${result.error}` : ""));
|
|
82
|
+
return result.path;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
export { requireChrome };
|
|
87
|
+
//# sourceMappingURL=detectChrome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectChrome.js","names":[],"sources":["../../src/utils/detectChrome.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { platform } from \"node:os\";\nimport path from \"node:path\";\n\nexport interface ChromeDetectionResult {\n found: boolean;\n path?: string;\n error?: string;\n}\n\n/**\n * Detects if Google Chrome is installed on the system.\n * Returns the path to Chrome executable if found.\n */\nexport function detectChrome(): ChromeDetectionResult {\n const osPlatform = platform();\n\n if (osPlatform === \"darwin\") {\n // macOS: Check for Google Chrome.app\n const chromePath = \"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\";\n if (existsSync(chromePath)) {\n return { found: true, path: chromePath };\n }\n\n // Also check for Chromium\n const chromiumPath = \"/Applications/Chromium.app/Contents/MacOS/Chromium\";\n if (existsSync(chromiumPath)) {\n return { found: true, path: chromiumPath };\n }\n\n return {\n found: false,\n error: \"Chrome not found in /Applications/Google Chrome.app\",\n };\n }\n\n if (osPlatform === \"linux\") {\n // Linux: Try to find chrome/chromium via which\n try {\n const chromePath = execSync(\"which google-chrome\", { encoding: \"utf-8\" }).trim();\n if (chromePath && existsSync(chromePath)) {\n return { found: true, path: chromePath };\n }\n } catch {\n // google-chrome not found, try chromium\n }\n\n try {\n const chromiumPath = execSync(\"which chromium\", { encoding: \"utf-8\" }).trim();\n if (chromiumPath && existsSync(chromiumPath)) {\n return { found: true, path: chromiumPath };\n }\n } catch {\n // chromium not found\n }\n\n try {\n const chromiumBrowserPath = execSync(\"which chromium-browser\", { encoding: \"utf-8\" }).trim();\n if (chromiumBrowserPath && existsSync(chromiumBrowserPath)) {\n return { found: true, path: chromiumBrowserPath };\n }\n } catch {\n // chromium-browser not found\n }\n\n return {\n found: false,\n error: \"Chrome not found. Install with: apt-get install google-chrome-stable\",\n };\n }\n\n if (osPlatform === \"win32\") {\n // Windows: Check common installation paths\n const possiblePaths = [\n path.join(process.env.LOCALAPPDATA || \"\", \"Google\", \"Chrome\", \"Application\", \"chrome.exe\"),\n path.join(process.env.PROGRAMFILES || \"\", \"Google\", \"Chrome\", \"Application\", \"chrome.exe\"),\n path.join(process.env[\"PROGRAMFILES(X86)\"] || \"\", \"Google\", \"Chrome\", \"Application\", \"chrome.exe\"),\n ];\n\n for (const chromePath of possiblePaths) {\n if (existsSync(chromePath)) {\n return { found: true, path: chromePath };\n }\n }\n\n return {\n found: false,\n error: \"Chrome not found in common Windows installation paths\",\n };\n }\n\n return {\n found: false,\n error: `Unsupported platform: ${osPlatform}`,\n };\n}\n\n/**\n * Throws a user-friendly error if Chrome is not found.\n */\nexport function requireChrome(): string {\n const result = detectChrome();\n if (!result.found) {\n throw new Error(\n `Chrome browser not found.\\n\\n` +\n `The render command requires Google Chrome to be installed on your system.\\n\\n` +\n `Install Chrome from: https://www.google.com/chrome/\\n` +\n (result.error ? `\\nDetails: ${result.error}` : \"\"),\n );\n }\n return result.path!;\n}\n"],"mappings":";;;;;;;;;;AAeA,SAAgB,eAAsC;CACpD,MAAM,aAAa,UAAU;AAE7B,KAAI,eAAe,UAAU;EAE3B,MAAM,aAAa;AACnB,MAAI,WAAW,WAAW,CACxB,QAAO;GAAE,OAAO;GAAM,MAAM;GAAY;EAI1C,MAAM,eAAe;AACrB,MAAI,WAAW,aAAa,CAC1B,QAAO;GAAE,OAAO;GAAM,MAAM;GAAc;AAG5C,SAAO;GACL,OAAO;GACP,OAAO;GACR;;AAGH,KAAI,eAAe,SAAS;AAE1B,MAAI;GACF,MAAM,aAAa,SAAS,uBAAuB,EAAE,UAAU,SAAS,CAAC,CAAC,MAAM;AAChF,OAAI,cAAc,WAAW,WAAW,CACtC,QAAO;IAAE,OAAO;IAAM,MAAM;IAAY;UAEpC;AAIR,MAAI;GACF,MAAM,eAAe,SAAS,kBAAkB,EAAE,UAAU,SAAS,CAAC,CAAC,MAAM;AAC7E,OAAI,gBAAgB,WAAW,aAAa,CAC1C,QAAO;IAAE,OAAO;IAAM,MAAM;IAAc;UAEtC;AAIR,MAAI;GACF,MAAM,sBAAsB,SAAS,0BAA0B,EAAE,UAAU,SAAS,CAAC,CAAC,MAAM;AAC5F,OAAI,uBAAuB,WAAW,oBAAoB,CACxD,QAAO;IAAE,OAAO;IAAM,MAAM;IAAqB;UAE7C;AAIR,SAAO;GACL,OAAO;GACP,OAAO;GACR;;AAGH,KAAI,eAAe,SAAS;EAE1B,MAAM,gBAAgB;GACpB,KAAK,KAAK,QAAQ,IAAI,gBAAgB,IAAI,UAAU,UAAU,eAAe,aAAa;GAC1F,KAAK,KAAK,QAAQ,IAAI,gBAAgB,IAAI,UAAU,UAAU,eAAe,aAAa;GAC1F,KAAK,KAAK,QAAQ,IAAI,wBAAwB,IAAI,UAAU,UAAU,eAAe,aAAa;GACnG;AAED,OAAK,MAAM,cAAc,cACvB,KAAI,WAAW,WAAW,CACxB,QAAO;GAAE,OAAO;GAAM,MAAM;GAAY;AAI5C,SAAO;GACL,OAAO;GACP,OAAO;GACR;;AAGH,QAAO;EACL,OAAO;EACP,OAAO,yBAAyB;EACjC;;;;;AAMH,SAAgB,gBAAwB;CACtC,MAAM,SAAS,cAAc;AAC7B,KAAI,CAAC,OAAO,MACV,OAAM,IAAI,MACR,qKAGG,OAAO,QAAQ,cAAc,OAAO,UAAU,IAClD;AAEH,QAAO,OAAO"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { requireChrome } from "./detectChrome.js";
|
|
1
2
|
import { withSpinner } from "./withSpinner.js";
|
|
2
3
|
import chalk from "chalk";
|
|
3
4
|
import debug from "debug";
|
|
@@ -6,12 +7,18 @@ import { chromium } from "playwright";
|
|
|
6
7
|
//#region src/utils/launchBrowserAndWaitForSDK.ts
|
|
7
8
|
const browserLog = debug("ef:cli::browser");
|
|
8
9
|
async function launchBrowserAndWaitForSDK(options, fn) {
|
|
10
|
+
if (options.interactive !== true && !options.chromePath) requireChrome();
|
|
9
11
|
const browser = await withSpinner("Launching chrome", async () => {
|
|
10
|
-
|
|
12
|
+
const launchOptions = {
|
|
11
13
|
channel: "chrome",
|
|
12
14
|
headless: options.headless ?? true,
|
|
13
15
|
devtools: options.interactive === true
|
|
14
|
-
}
|
|
16
|
+
};
|
|
17
|
+
if (options.chromePath) {
|
|
18
|
+
launchOptions.executablePath = options.chromePath;
|
|
19
|
+
delete launchOptions.channel;
|
|
20
|
+
}
|
|
21
|
+
return chromium.launch(launchOptions);
|
|
15
22
|
});
|
|
16
23
|
await fn(await withSpinner("Loading Editframe SDK", async () => {
|
|
17
24
|
const pageOptions = {};
|
|
@@ -20,7 +27,10 @@ async function launchBrowserAndWaitForSDK(options, fn) {
|
|
|
20
27
|
page.on("console", (msg) => {
|
|
21
28
|
browserLog(chalk.blue(`browser (${msg.type()}) |`), msg.text());
|
|
22
29
|
});
|
|
23
|
-
const
|
|
30
|
+
const urlParams = new URLSearchParams();
|
|
31
|
+
if (!options.efInteractive) urlParams.set("EF_NONINTERACTIVE", "1");
|
|
32
|
+
if (options.nativeRender) urlParams.set("EF_NATIVE_RENDER", "1");
|
|
33
|
+
const url = options.url + (urlParams.toString() ? `?${urlParams.toString()}` : "");
|
|
24
34
|
process.stderr.write("\nLoading url: ");
|
|
25
35
|
process.stderr.write(url);
|
|
26
36
|
process.stderr.write("\n");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launchBrowserAndWaitForSDK.js","names":["pageOptions: Parameters<Browser[\"newPage\"]>[0]"],"sources":["../../src/utils/launchBrowserAndWaitForSDK.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport debug from \"debug\";\nimport { type Browser, chromium, type Page } from \"playwright\";\n\nimport { withSpinner } from \"./withSpinner.js\";\n\nconst browserLog = debug(\"ef:cli::browser\");\n\ninterface LaunchOptions {\n url: string;\n headless?: boolean;\n interactive?: boolean;\n efInteractive?: boolean;\n}\n\nexport async function launchBrowserAndWaitForSDK(\n options: LaunchOptions,\n fn: (page: Page) => Promise<void>,\n) {\n const browser = await withSpinner(\"Launching chrome\", async () => {\n
|
|
1
|
+
{"version":3,"file":"launchBrowserAndWaitForSDK.js","names":["launchOptions: Parameters<typeof chromium.launch>[0]","pageOptions: Parameters<Browser[\"newPage\"]>[0]"],"sources":["../../src/utils/launchBrowserAndWaitForSDK.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport debug from \"debug\";\nimport { type Browser, chromium, type Page } from \"playwright\";\n\nimport { requireChrome } from \"./detectChrome.js\";\nimport { withSpinner } from \"./withSpinner.js\";\n\nconst browserLog = debug(\"ef:cli::browser\");\n\ninterface LaunchOptions {\n url: string;\n headless?: boolean;\n interactive?: boolean;\n efInteractive?: boolean;\n nativeRender?: boolean;\n chromePath?: string;\n}\n\nexport async function launchBrowserAndWaitForSDK(\n options: LaunchOptions,\n fn: (page: Page) => Promise<void>,\n) {\n // Detect Chrome before launching (only for non-interactive renders)\n if (options.interactive !== true && !options.chromePath) {\n requireChrome();\n }\n\n const browser = await withSpinner(\"Launching chrome\", async () => {\n const launchOptions: Parameters<typeof chromium.launch>[0] = {\n channel: \"chrome\",\n headless: options.headless ?? true,\n devtools: options.interactive === true,\n };\n\n // Use custom Chrome path if provided\n if (options.chromePath) {\n launchOptions.executablePath = options.chromePath;\n // Don't use channel when providing explicit path\n delete launchOptions.channel;\n }\n\n return chromium.launch(launchOptions);\n });\n\n const page = await withSpinner(\"Loading Editframe SDK\", async () => {\n const pageOptions: Parameters<Browser[\"newPage\"]>[0] = {};\n if (options.interactive === true) {\n // By default, playwright uses its own viewport, so resizing the browser window\n // doesn't actually change the viewport. And the gui doesn't scale to fit.\n // This is not desirable for interactive mode, so we disable the viewport feature.\n pageOptions.viewport = null;\n }\n const page = await browser.newPage(pageOptions);\n page.on(\"console\", (msg) => {\n browserLog(chalk.blue(`browser (${msg.type()}) |`), msg.text());\n });\n\n // Build URL with query parameters\n const urlParams = new URLSearchParams();\n if (!options.efInteractive) {\n urlParams.set(\"EF_NONINTERACTIVE\", \"1\");\n }\n if (options.nativeRender) {\n urlParams.set(\"EF_NATIVE_RENDER\", \"1\");\n }\n const url = options.url + (urlParams.toString() ? `?${urlParams.toString()}` : \"\");\n\n process.stderr.write(\"\\nLoading url: \");\n process.stderr.write(url);\n process.stderr.write(\"\\n\");\n await page.goto(url);\n await page.waitForFunction(\n () => {\n return (\n // @ts-expect-error\n window.EF_REGISTERED\n );\n },\n [],\n { timeout: 10_000 },\n );\n return page;\n });\n await fn(page);\n if (options.interactive !== true) {\n await browser.close();\n process.exit(0);\n }\n}\n"],"mappings":";;;;;;;AAOA,MAAM,aAAa,MAAM,kBAAkB;AAW3C,eAAsB,2BACpB,SACA,IACA;AAEA,KAAI,QAAQ,gBAAgB,QAAQ,CAAC,QAAQ,WAC3C,gBAAe;CAGjB,MAAM,UAAU,MAAM,YAAY,oBAAoB,YAAY;EAChE,MAAMA,gBAAuD;GAC3D,SAAS;GACT,UAAU,QAAQ,YAAY;GAC9B,UAAU,QAAQ,gBAAgB;GACnC;AAGD,MAAI,QAAQ,YAAY;AACtB,iBAAc,iBAAiB,QAAQ;AAEvC,UAAO,cAAc;;AAGvB,SAAO,SAAS,OAAO,cAAc;GACrC;AAyCF,OAAM,GAvCO,MAAM,YAAY,yBAAyB,YAAY;EAClE,MAAMC,cAAiD,EAAE;AACzD,MAAI,QAAQ,gBAAgB,KAI1B,aAAY,WAAW;EAEzB,MAAM,OAAO,MAAM,QAAQ,QAAQ,YAAY;AAC/C,OAAK,GAAG,YAAY,QAAQ;AAC1B,cAAW,MAAM,KAAK,YAAY,IAAI,MAAM,CAAC,KAAK,EAAE,IAAI,MAAM,CAAC;IAC/D;EAGF,MAAM,YAAY,IAAI,iBAAiB;AACvC,MAAI,CAAC,QAAQ,cACX,WAAU,IAAI,qBAAqB,IAAI;AAEzC,MAAI,QAAQ,aACV,WAAU,IAAI,oBAAoB,IAAI;EAExC,MAAM,MAAM,QAAQ,OAAO,UAAU,UAAU,GAAG,IAAI,UAAU,UAAU,KAAK;AAE/E,UAAQ,OAAO,MAAM,kBAAkB;AACvC,UAAQ,OAAO,MAAM,IAAI;AACzB,UAAQ,OAAO,MAAM,KAAK;AAC1B,QAAM,KAAK,KAAK,IAAI;AACpB,QAAM,KAAK,sBACH;AACJ,UAEE,OAAO;KAGX,EAAE,EACF,EAAE,SAAS,KAAQ,CACpB;AACD,SAAO;GACP,CACY;AACd,KAAI,QAAQ,gBAAgB,MAAM;AAChC,QAAM,QAAQ,OAAO;AACrB,UAAQ,KAAK,EAAE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@editframe/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0-beta.0",
|
|
4
4
|
"description": "Command line interface for EditFrame",
|
|
5
5
|
"bin": {
|
|
6
6
|
"editframe": "./dist/index.js"
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"typecheck": "tsc --noEmit --emitDeclarationOnly false",
|
|
11
11
|
"build": "tsdown",
|
|
12
|
-
"build:watch": "tsdown --watch"
|
|
12
|
+
"build:watch": "tsdown --watch",
|
|
13
|
+
"postinstall": "echo 'Skipping Playwright browser download - uses system Chrome'"
|
|
13
14
|
},
|
|
14
15
|
"author": "",
|
|
15
16
|
"license": "UNLICENSED",
|
|
@@ -21,10 +22,10 @@
|
|
|
21
22
|
"typescript": "^5.5.4"
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
24
|
-
"@editframe/api": "0.
|
|
25
|
-
"@editframe/assets": "0.
|
|
26
|
-
"@editframe/elements": "0.
|
|
27
|
-
"@editframe/vite-plugin": "0.
|
|
25
|
+
"@editframe/api": "0.32.0-beta.0",
|
|
26
|
+
"@editframe/assets": "0.32.0-beta.0",
|
|
27
|
+
"@editframe/elements": "0.32.0-beta.0",
|
|
28
|
+
"@editframe/vite-plugin": "0.32.0-beta.0",
|
|
28
29
|
"@inquirer/prompts": "^5.3.8",
|
|
29
30
|
"chalk": "^5.3.0",
|
|
30
31
|
"commander": "^12.0.0",
|