@framv/headless 0.1.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/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +114 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/runner.d.ts +63 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +107 -0
- package/dist/runner.js.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mens Reversa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# HEADLESS\n\nPart of the Framv framework.\n
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from "util";
|
|
3
|
+
import { resolve } from "path";
|
|
4
|
+
import { render } from "./runner.js";
|
|
5
|
+
const VALID_FORMATS = ["svg", "png", "jpg", "webp", "mp4", "webm", "m4a", "ogg", "pdf"];
|
|
6
|
+
const HELP = `
|
|
7
|
+
framv — headless renderer powered by @framv/headless
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
framv --url <url> --output <file> --format <fmt> [options]
|
|
11
|
+
|
|
12
|
+
Required:
|
|
13
|
+
--url URL of the page to render
|
|
14
|
+
--output Output file path
|
|
15
|
+
--format Export format: svg | png | jpg | webp | mp4 | webm | m4a | ogg | pdf
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--selector CSS selector for the element to render [default: #framv-root]
|
|
19
|
+
--fps Frames per second for video export [default: 30]
|
|
20
|
+
--quality Quality 0–1 for lossy formats [default: 1]
|
|
21
|
+
--start Start time in seconds [default: 0]
|
|
22
|
+
--end End time in seconds [default: 5]
|
|
23
|
+
--width Viewport / output width in pixels [default: 1920]
|
|
24
|
+
--height Viewport / output height in pixels [default: 1080]
|
|
25
|
+
--time Seek time (seconds) for static exports [default: 0]
|
|
26
|
+
--executable Path to a Chromium/Chrome binary
|
|
27
|
+
--help Show this help message
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
framv --url http://localhost:3000 --output out.mp4 --format mp4 --end 10
|
|
31
|
+
|
|
32
|
+
framv \\
|
|
33
|
+
--url http://localhost:3000/intro/ --output intro.mp4 --format mp4 \\
|
|
34
|
+
--selector "#framv-canvas" --fps 30 --end 13 --width 1920 --height 1080
|
|
35
|
+
|
|
36
|
+
framv --url http://localhost:3000 --output frame.png --format png --time 2.5
|
|
37
|
+
`.trim();
|
|
38
|
+
async function main() {
|
|
39
|
+
const { values } = parseArgs({
|
|
40
|
+
args: process.argv.slice(2),
|
|
41
|
+
options: {
|
|
42
|
+
url: { type: "string" },
|
|
43
|
+
output: { type: "string" },
|
|
44
|
+
format: { type: "string" },
|
|
45
|
+
selector: { type: "string" },
|
|
46
|
+
fps: { type: "string" },
|
|
47
|
+
quality: { type: "string" },
|
|
48
|
+
start: { type: "string" },
|
|
49
|
+
end: { type: "string" },
|
|
50
|
+
width: { type: "string" },
|
|
51
|
+
height: { type: "string" },
|
|
52
|
+
time: { type: "string" },
|
|
53
|
+
executable: { type: "string" },
|
|
54
|
+
help: { type: "boolean", short: "h" },
|
|
55
|
+
},
|
|
56
|
+
strict: false,
|
|
57
|
+
});
|
|
58
|
+
if (values.help) {
|
|
59
|
+
console.log(HELP);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
const str = (v) => (typeof v === "string" ? v : undefined);
|
|
63
|
+
const url = str(values.url);
|
|
64
|
+
const output = str(values.output);
|
|
65
|
+
const format = str(values.format);
|
|
66
|
+
if (!url || !output || !format) {
|
|
67
|
+
console.error("Error: --url, --output, and --format are required.\n");
|
|
68
|
+
console.error(HELP);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
if (!VALID_FORMATS.includes(format)) {
|
|
72
|
+
console.error(`Error: invalid --format "${format}". Must be one of: ${VALID_FORMATS.join(", ")}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
const opts = {
|
|
76
|
+
url,
|
|
77
|
+
output: resolve(output),
|
|
78
|
+
format: format,
|
|
79
|
+
selector: str(values.selector),
|
|
80
|
+
fps: str(values.fps) ? parseFloat(str(values.fps)) : undefined,
|
|
81
|
+
quality: str(values.quality) ? parseFloat(str(values.quality)) : undefined,
|
|
82
|
+
start: str(values.start) ? parseFloat(str(values.start)) : undefined,
|
|
83
|
+
end: str(values.end) ? parseFloat(str(values.end)) : undefined,
|
|
84
|
+
width: str(values.width) ? parseInt(str(values.width)) : undefined,
|
|
85
|
+
height: str(values.height) ? parseInt(str(values.height)) : undefined,
|
|
86
|
+
time: str(values.time) ? parseFloat(str(values.time)) : undefined,
|
|
87
|
+
executablePath: str(values.executable),
|
|
88
|
+
onProgress: (p) => process.stdout.write(`\r ${(p * 100).toFixed(1)}%`),
|
|
89
|
+
};
|
|
90
|
+
const isVideo = ["mp4", "webm", "m4a", "ogg"].includes(format);
|
|
91
|
+
const start = opts.start ?? 0;
|
|
92
|
+
const end = opts.end ?? 5;
|
|
93
|
+
const info = isVideo ? `${start}s–${end}s (${end - start}s @ ${opts.fps ?? 30}fps)` : `t=${opts.time ?? 0}s`;
|
|
94
|
+
console.log(`\nframv`);
|
|
95
|
+
console.log(` url: ${url}`);
|
|
96
|
+
console.log(` output: ${opts.output}`);
|
|
97
|
+
console.log(` format: ${format} ${info}`);
|
|
98
|
+
if (opts.width || opts.height)
|
|
99
|
+
console.log(` size: ${opts.width ?? "auto"} × ${opts.height ?? "auto"}`);
|
|
100
|
+
console.log();
|
|
101
|
+
const t0 = Date.now();
|
|
102
|
+
try {
|
|
103
|
+
await render(opts);
|
|
104
|
+
process.stdout.write(`\r 100.0%\n`);
|
|
105
|
+
console.log(`\n ✓ rendered in ${((Date.now() - t0) / 1000).toFixed(1)}s → ${opts.output}\n`);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
process.stdout.write("\n");
|
|
109
|
+
console.error(`\n ✗ render failed: ${err.message}\n`);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
main();
|
|
114
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ExportSettings } from "@framv/core";
|
|
2
|
+
/**
|
|
3
|
+
* Options for the headless renderer.
|
|
4
|
+
*
|
|
5
|
+
* The runner opens `url` in a headless Chromium browser, injects
|
|
6
|
+
* `@framv/core` into the page, then calls `exportElement` and writes
|
|
7
|
+
* the result to `output`.
|
|
8
|
+
*/
|
|
9
|
+
export interface RunnerOptions {
|
|
10
|
+
/** URL of the page to render. */
|
|
11
|
+
url: string;
|
|
12
|
+
/** Absolute or relative path to write the output file. */
|
|
13
|
+
output: string;
|
|
14
|
+
/** CSS selector for the element to render. Defaults to `#framv-root`. */
|
|
15
|
+
selector?: string;
|
|
16
|
+
/** Explicit path to a Chromium/Chrome executable. */
|
|
17
|
+
executablePath?: string;
|
|
18
|
+
/** Export format. */
|
|
19
|
+
format: ExportSettings["format"];
|
|
20
|
+
/** Frames per second for video export. Defaults to 30. */
|
|
21
|
+
fps?: number;
|
|
22
|
+
/** Quality 0–1 for lossy formats. Defaults to 1. */
|
|
23
|
+
quality?: number;
|
|
24
|
+
/** Start time in seconds for video/audio export. Defaults to 0. */
|
|
25
|
+
start?: number;
|
|
26
|
+
/** End time in seconds for video/audio export. Defaults to 5. */
|
|
27
|
+
end?: number;
|
|
28
|
+
/** Width override in pixels. */
|
|
29
|
+
width?: number;
|
|
30
|
+
/** Height override in pixels. */
|
|
31
|
+
height?: number;
|
|
32
|
+
/** Time in seconds to seek to for static image export. Defaults to 0. */
|
|
33
|
+
time?: number;
|
|
34
|
+
/** Progress callback called with a 0–1 value as frames are rendered. */
|
|
35
|
+
onProgress?: (progress: number) => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Launch a headless browser, navigate to `options.url`, inject `@framv/core`,
|
|
39
|
+
* render the target element, and write the result to disk.
|
|
40
|
+
*
|
|
41
|
+
* The page may optionally expose `window.setFramvFrame(frameNumber)` which
|
|
42
|
+
* the runner will call before capturing each frame — useful for custom
|
|
43
|
+
* frame-driven animations (e.g. the framv video compositions).
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { render } from '@framv/runner';
|
|
48
|
+
*
|
|
49
|
+
* await render({
|
|
50
|
+
* url: 'http://localhost:3000/intro/',
|
|
51
|
+
* output: './out/intro.mp4',
|
|
52
|
+
* selector: '#framv-canvas',
|
|
53
|
+
* format: 'mp4',
|
|
54
|
+
* fps: 30,
|
|
55
|
+
* start: 0,
|
|
56
|
+
* end: 13,
|
|
57
|
+
* width: 1920,
|
|
58
|
+
* height: 1080,
|
|
59
|
+
* });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function render(options: RunnerOptions): Promise<void>;
|
|
63
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAclD;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qBAAqB;IACrB,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACjC,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA+GlE"}
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createRequire } from "module";
|
|
2
|
+
import { dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
const _require = createRequire(import.meta.url);
|
|
5
|
+
const _dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
/** Resolve the absolute path to @framv/core's dist directory. */
|
|
7
|
+
function resolveCoreDistDir() {
|
|
8
|
+
const coreMain = _require.resolve("@framv/core");
|
|
9
|
+
return dirname(coreMain);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Launch a headless browser, navigate to `options.url`, inject `@framv/core`,
|
|
13
|
+
* render the target element, and write the result to disk.
|
|
14
|
+
*
|
|
15
|
+
* The page may optionally expose `window.setFramvFrame(frameNumber)` which
|
|
16
|
+
* the runner will call before capturing each frame — useful for custom
|
|
17
|
+
* frame-driven animations (e.g. the framv video compositions).
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { render } from '@framv/runner';
|
|
22
|
+
*
|
|
23
|
+
* await render({
|
|
24
|
+
* url: 'http://localhost:3000/intro/',
|
|
25
|
+
* output: './out/intro.mp4',
|
|
26
|
+
* selector: '#framv-canvas',
|
|
27
|
+
* format: 'mp4',
|
|
28
|
+
* fps: 30,
|
|
29
|
+
* start: 0,
|
|
30
|
+
* end: 13,
|
|
31
|
+
* width: 1920,
|
|
32
|
+
* height: 1080,
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export async function render(options) {
|
|
37
|
+
const { url, output, selector = "#framv-root", executablePath, format, fps = 30, quality = 1, start = 0, end = 5, time = 0, width, height, onProgress } = options;
|
|
38
|
+
const puppeteer = await import("puppeteer");
|
|
39
|
+
const fs = await import("fs/promises");
|
|
40
|
+
const viewportWidth = width ?? 1920;
|
|
41
|
+
const viewportHeight = height ?? 1080;
|
|
42
|
+
const totalFrames = Math.ceil((end - start) * fps);
|
|
43
|
+
const coreDistDir = resolveCoreDistDir();
|
|
44
|
+
const browser = await puppeteer.default.launch({
|
|
45
|
+
headless: true,
|
|
46
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox", "--enable-webgl", "--use-gl=angle", "--allow-file-access-from-files", "--disable-web-security"],
|
|
47
|
+
executablePath,
|
|
48
|
+
});
|
|
49
|
+
try {
|
|
50
|
+
const page = await browser.newPage();
|
|
51
|
+
await page.setViewport({ width: viewportWidth, height: viewportHeight });
|
|
52
|
+
page.on("console", (msg) => console.log("PAGE LOG:", msg.text()));
|
|
53
|
+
page.on("pageerror", (err) => console.error("PAGE ERROR:", String(err)));
|
|
54
|
+
await page.goto(url, { waitUntil: "networkidle0" });
|
|
55
|
+
// Inject @framv/core into the page. Read the IIFE bundle and inject it
|
|
56
|
+
// via addScriptTag with inline content.
|
|
57
|
+
const { readFile } = await import("fs/promises");
|
|
58
|
+
const bundleContent = await readFile(`${coreDistDir}/bundle.iife.js`, "utf-8");
|
|
59
|
+
await page.evaluate((code) => {
|
|
60
|
+
const script = document.createElement("script");
|
|
61
|
+
script.textContent = code;
|
|
62
|
+
document.head.appendChild(script);
|
|
63
|
+
}, bundleContent);
|
|
64
|
+
// Wait for the IIFE to set window.Framv, then alias it as __framvCore.
|
|
65
|
+
await page.waitForFunction(() => typeof window.Framv !== "undefined", { timeout: 10000 });
|
|
66
|
+
await page.evaluate(() => {
|
|
67
|
+
window.__framvCore = window.Framv;
|
|
68
|
+
});
|
|
69
|
+
if (onProgress) {
|
|
70
|
+
await page.exposeFunction("__framvOnProgress", onProgress);
|
|
71
|
+
}
|
|
72
|
+
const result = await page.evaluate(async (sel, cfg, frames, hasProgressCb) => {
|
|
73
|
+
const win = window;
|
|
74
|
+
const { exportElement, settings: createSettings } = win.__framvCore;
|
|
75
|
+
const element = document.querySelector(sel);
|
|
76
|
+
if (!element)
|
|
77
|
+
throw new Error(`Element not found: ${sel}`);
|
|
78
|
+
const blob = await exportElement({
|
|
79
|
+
element,
|
|
80
|
+
settings: createSettings(cfg.format, cfg),
|
|
81
|
+
onProgress: async (progress) => {
|
|
82
|
+
if (hasProgressCb && win.__framvOnProgress) {
|
|
83
|
+
win.__framvOnProgress(progress);
|
|
84
|
+
}
|
|
85
|
+
// If the page exposes setFramvFrame, call it so custom frame-driven
|
|
86
|
+
// animations (e.g. the framv video compositions) can seek to the
|
|
87
|
+
// correct frame before this frame is captured.
|
|
88
|
+
if (win.setFramvFrame) {
|
|
89
|
+
const currentFrame = Math.floor(progress * frames);
|
|
90
|
+
await win.setFramvFrame(currentFrame);
|
|
91
|
+
// Let the browser paint the seeked frame before capture.
|
|
92
|
+
await new Promise((r) => requestAnimationFrame(() => r()));
|
|
93
|
+
await new Promise((r) => requestAnimationFrame(() => r()));
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
const buffer = await blob.arrayBuffer();
|
|
99
|
+
return Array.from(new Uint8Array(buffer));
|
|
100
|
+
}, selector, { format, fps, quality, start, end, time, width, height }, totalFrames, Boolean(onProgress));
|
|
101
|
+
await fs.writeFile(output, Buffer.from(result));
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
await browser.close();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@framv/headless",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"framv": "./dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"description": "Headless renderer for framv — render HTML/SVG elements to images or video via Puppeteer",
|
|
22
|
+
"keywords": [
|
|
23
|
+
"puppeteer",
|
|
24
|
+
"headless",
|
|
25
|
+
"renderer",
|
|
26
|
+
"video",
|
|
27
|
+
"png",
|
|
28
|
+
"svg",
|
|
29
|
+
"mp4",
|
|
30
|
+
"framv",
|
|
31
|
+
"cli"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"scripts": {
|
|
35
|
+
"prepublishOnly": "npm run build",
|
|
36
|
+
"build": "tsc -b",
|
|
37
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
38
|
+
"lint": "eslint \"src/**/*.ts\" --fix",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"test:watch": "vitest"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@framv/core": "*",
|
|
44
|
+
"puppeteer": "^24.37.5"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^25.3.2",
|
|
48
|
+
"@vitest/ui": "^4.0.18",
|
|
49
|
+
"eslint": "^10.0.2",
|
|
50
|
+
"eslint-config-prettier": "^10.1.8",
|
|
51
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
52
|
+
"prettier": "^3.8.1",
|
|
53
|
+
"typescript": "^5.9.3",
|
|
54
|
+
"vitest": "^4.0.18"
|
|
55
|
+
},
|
|
56
|
+
"author": "Mens Reversa",
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "https://github.com/mensreversa/framv.git",
|
|
60
|
+
"directory": "packages/headless"
|
|
61
|
+
},
|
|
62
|
+
"bugs": {
|
|
63
|
+
"url": "https://github.com/mensreversa/framv/issues"
|
|
64
|
+
}
|
|
65
|
+
}
|