@sequio/cli 0.1.0 → 0.1.2
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/args.d.ts +25 -2
- package/dist/audio.d.ts +15 -0
- package/dist/check.d.ts +46 -0
- package/dist/cli.js +16 -12
- package/dist/index.d.ts +9 -2
- package/dist/index.js +13 -8
- package/dist/version-C7OkFI_V.js +760 -0
- package/package.json +4 -4
- package/dist/version-B2TKvZen.js +0 -338
package/dist/args.d.ts
CHANGED
|
@@ -27,6 +27,21 @@ export interface PreviewCommand {
|
|
|
27
27
|
/** Bind to `0.0.0.0` (expose on the network) instead of localhost. */
|
|
28
28
|
host: boolean;
|
|
29
29
|
}
|
|
30
|
+
/** Audio-only container formats `sequio audio` can write. */
|
|
31
|
+
export declare const AUDIO_FORMATS: readonly ["m4a", "mp3", "wav", "ogg", "webm"];
|
|
32
|
+
export type AudioFormat = (typeof AUDIO_FORMATS)[number];
|
|
33
|
+
/** `sequio audio <file> [-o out.m4a] [--format m4a] [--bitrate N]`. */
|
|
34
|
+
export interface AudioCommand {
|
|
35
|
+
kind: 'audio';
|
|
36
|
+
/** Path to the entry composition file. */
|
|
37
|
+
file: string;
|
|
38
|
+
/** Output audio path. `undefined` → `out.<format>`. */
|
|
39
|
+
out?: string;
|
|
40
|
+
/** Audio container format. `undefined` → inferred from `out`'s extension, else `mp3`. */
|
|
41
|
+
format?: AudioFormat;
|
|
42
|
+
/** Target audio bitrate, bits/sec. `undefined` → the engine default (128 kbps). */
|
|
43
|
+
bitrate?: number;
|
|
44
|
+
}
|
|
30
45
|
/** `sequio frame <file> [-t sec] [-o out.png] [--scale N]`. */
|
|
31
46
|
export interface FrameCommand {
|
|
32
47
|
kind: 'frame';
|
|
@@ -39,15 +54,23 @@ export interface FrameCommand {
|
|
|
39
54
|
/** Output resolution multiplier (N× the composition size). @default 1 */
|
|
40
55
|
scale: number;
|
|
41
56
|
}
|
|
57
|
+
/** `sequio check <file> [--json]`. */
|
|
58
|
+
export interface CheckCommand {
|
|
59
|
+
kind: 'check';
|
|
60
|
+
/** Path to the entry composition file. */
|
|
61
|
+
file: string;
|
|
62
|
+
/** Emit machine-readable `Diagnostic[]` JSON instead of human-readable lines. */
|
|
63
|
+
json: boolean;
|
|
64
|
+
}
|
|
42
65
|
/** Show usage / version / a parse error and exit. */
|
|
43
66
|
export interface MetaCommand {
|
|
44
67
|
kind: 'help' | 'version' | 'error';
|
|
45
68
|
/** For `error`: the message to print (before usage) and exit non-zero. */
|
|
46
69
|
message?: string;
|
|
47
70
|
}
|
|
48
|
-
export type CliCommand = RenderCommand | PreviewCommand | FrameCommand | MetaCommand;
|
|
71
|
+
export type CliCommand = RenderCommand | PreviewCommand | FrameCommand | AudioCommand | CheckCommand | MetaCommand;
|
|
49
72
|
export declare const DEFAULT_PREVIEW_PORT = 6180;
|
|
50
|
-
export declare const USAGE
|
|
73
|
+
export declare const USAGE: string;
|
|
51
74
|
/** Parse `process.argv.slice(2)` into a {@link CliCommand}. Never throws. */
|
|
52
75
|
export declare function parseArgs(argv: string[]): CliCommand;
|
|
53
76
|
//# sourceMappingURL=args.d.ts.map
|
package/dist/audio.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AudioFormat } from './args';
|
|
2
|
+
export interface AudioOptions {
|
|
3
|
+
/** Output audio path. `undefined` → `out.<format>`. */
|
|
4
|
+
out?: string;
|
|
5
|
+
/** Audio container format. `undefined` → inferred from `out`'s extension, else `mp3`. */
|
|
6
|
+
format?: AudioFormat;
|
|
7
|
+
/** Target audio bitrate, bits/sec. `undefined` → the engine default (128 kbps). */
|
|
8
|
+
bitrate?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Export the audio of `entryFile` to a file, returning the process exit code
|
|
12
|
+
* (0 = success).
|
|
13
|
+
*/
|
|
14
|
+
export declare function runAudio(entryFile: string, options?: AudioOptions): Promise<number>;
|
|
15
|
+
//# sourceMappingURL=audio.d.ts.map
|
package/dist/check.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Renderer } from '@sequio/engine';
|
|
2
|
+
import { Externals, RuntimeBundle } from '@sequio/runtime';
|
|
3
|
+
/** A single validation finding. */
|
|
4
|
+
export interface Diagnostic {
|
|
5
|
+
/** `error` fails the check (non-zero exit); `warn` is advisory. */
|
|
6
|
+
severity: 'error' | 'warn';
|
|
7
|
+
/** Stable code (`A1`…`C9`) identifying the check that fired. */
|
|
8
|
+
code: string;
|
|
9
|
+
/** Human-readable explanation. */
|
|
10
|
+
message: string;
|
|
11
|
+
/** Where in the object graph it was found (e.g. `track 0 · clip 2 (TextClip)`). */
|
|
12
|
+
at?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface CheckBundleOptions {
|
|
15
|
+
/** Extra bare modules the composition may import (e.g. `{ gsap }`). */
|
|
16
|
+
externals?: Externals;
|
|
17
|
+
/**
|
|
18
|
+
* Existence predicate for `loadAsset('./x.mp4')` paths (normalized,
|
|
19
|
+
* root-relative). Returns `false` → a C7 "asset not found" diagnostic. Omit to
|
|
20
|
+
* skip asset-existence checking (in-memory tests have no disk).
|
|
21
|
+
*/
|
|
22
|
+
assetExists?: (path: string) => boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A headless no-op {@link Renderer}: `init()` resolves without a GPU and nothing
|
|
26
|
+
* ever draws (check reconciles no frames). Injected via
|
|
27
|
+
* `CompositorOptions.createRenderer`, so `await compositor.init()` in a builder
|
|
28
|
+
* completes on a machine with no Vulkan at all.
|
|
29
|
+
*/
|
|
30
|
+
export declare function nullRenderer(): Promise<Renderer>;
|
|
31
|
+
/**
|
|
32
|
+
* Validate a {@link RuntimeBundle} and return every {@link Diagnostic} found.
|
|
33
|
+
* Pure and GPU-free — the unit-testable core behind `runCheck`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function checkBundle(bundle: RuntimeBundle, options?: CheckBundleOptions): Promise<Diagnostic[]>;
|
|
36
|
+
export interface CheckOptions {
|
|
37
|
+
/** Emit machine-readable `Diagnostic[]` JSON instead of human lines. */
|
|
38
|
+
json?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Statically check `entryFile` and return the process exit code (0 = no errors,
|
|
42
|
+
* 1 = at least one `error`). `warn`-only results still exit 0. Prints a
|
|
43
|
+
* human-readable summary, or a `Diagnostic[]` JSON array with `--json`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function runCheck(entryFile: string, options?: CheckOptions): Promise<number>;
|
|
46
|
+
//# sourceMappingURL=check.d.ts.map
|
package/dist/cli.js
CHANGED
|
@@ -1,34 +1,38 @@
|
|
|
1
|
-
import { p as
|
|
2
|
-
async function
|
|
3
|
-
const e =
|
|
1
|
+
import { p as c, s as n, a as i, d as l, e as u, b as f, U as t, v as p } from "./version-C7OkFI_V.js";
|
|
2
|
+
async function m(r) {
|
|
3
|
+
const e = c(r);
|
|
4
4
|
switch (e.kind) {
|
|
5
5
|
case "help":
|
|
6
|
-
return console.log(
|
|
6
|
+
return console.log(t), 0;
|
|
7
7
|
case "version":
|
|
8
8
|
return console.log(p), 0;
|
|
9
9
|
case "error":
|
|
10
10
|
return console.error(`✖ ${e.message}
|
|
11
|
-
`), console.error(
|
|
11
|
+
`), console.error(t), 2;
|
|
12
|
+
case "check":
|
|
13
|
+
return f(e.file, { json: e.json });
|
|
12
14
|
case "render":
|
|
13
|
-
return
|
|
15
|
+
return u(e.file, { out: e.out, scale: e.scale, verify: e.verify });
|
|
14
16
|
case "frame":
|
|
15
|
-
return
|
|
17
|
+
return l(e.file, { out: e.out, time: e.time, scale: e.scale });
|
|
18
|
+
case "audio":
|
|
19
|
+
return i(e.file, { out: e.out, format: e.format, bitrate: e.bitrate });
|
|
16
20
|
case "preview": {
|
|
17
|
-
const
|
|
21
|
+
const s = await n(e.file, {
|
|
18
22
|
port: e.port,
|
|
19
23
|
host: e.host,
|
|
20
24
|
watch: e.watch
|
|
21
25
|
});
|
|
22
|
-
return console.log(`▸ sequio preview → ${
|
|
26
|
+
return console.log(`▸ sequio preview → ${s.url}`), console.log(` serving ${e.file}${e.watch ? " (watching for changes)" : ""}`), console.log(" press Ctrl-C to stop."), await new Promise((a) => {
|
|
23
27
|
const o = () => {
|
|
24
|
-
|
|
28
|
+
s.close().finally(() => a());
|
|
25
29
|
};
|
|
26
30
|
process.once("SIGINT", o), process.once("SIGTERM", o);
|
|
27
31
|
}), 0;
|
|
28
32
|
}
|
|
29
33
|
}
|
|
30
34
|
}
|
|
31
|
-
|
|
32
|
-
console.error("✖",
|
|
35
|
+
m(process.argv.slice(2)).then((r) => process.exit(r)).catch((r) => {
|
|
36
|
+
console.error("✖", r instanceof Error ? r.message : r), process.exit(1);
|
|
33
37
|
});
|
|
34
38
|
//# sourceMappingURL=cli.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@sequio/cli` — the `sequio` command line.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Five commands, all thin front-ends over infrastructure the other packages
|
|
5
5
|
* already own:
|
|
6
|
+
* - `check <file>` → compile + link + build the object graph with a null
|
|
7
|
+
* renderer and statically validate it — no GPU, no network (the fast
|
|
8
|
+
* pre-flight in the `check → frame → render` loop).
|
|
6
9
|
* - `render <file>` → snapshot the composition into a {@link RuntimeBundle} and
|
|
7
10
|
* hand it to the server's Route B (pure Node, WebGPU) to encode a video.
|
|
8
11
|
* - `frame <file>` → the same Route B path, but seek to one time and write a
|
|
9
12
|
* single PNG — a fast visual check without a full render.
|
|
13
|
+
* - `audio <file>` → the same Route B path, but export only the audio mix to an
|
|
14
|
+
* audio-only file (m4a / mp3 / wav / ogg / webm).
|
|
10
15
|
* - `preview <file>` → boot a Vite dev server whose page runs the same
|
|
11
16
|
* `Runtime` → `Composer` → `preview()` path in-browser; `--watch` live-reloads.
|
|
12
17
|
*
|
|
13
18
|
* This barrel is the programmatic surface (used by tests and embedders); the
|
|
14
19
|
* `sequio` binary is `bin/sequio.js`, which runs `src/cli.ts`.
|
|
15
20
|
*/
|
|
16
|
-
export { parseArgs, USAGE, DEFAULT_PREVIEW_PORT, type CliCommand, type RenderCommand, type FrameCommand, type PreviewCommand, type MetaCommand, } from './args';
|
|
21
|
+
export { parseArgs, USAGE, DEFAULT_PREVIEW_PORT, AUDIO_FORMATS, type AudioFormat, type CliCommand, type RenderCommand, type FrameCommand, type AudioCommand, type CheckCommand, type PreviewCommand, type MetaCommand, } from './args';
|
|
17
22
|
export { readBundle } from './bundle';
|
|
23
|
+
export { runCheck, checkBundle, nullRenderer, type Diagnostic, type CheckOptions, type CheckBundleOptions, } from './check';
|
|
18
24
|
export { runRender, type RenderOptions } from './render';
|
|
19
25
|
export { runFrame, type FrameOptions } from './frame';
|
|
26
|
+
export { runAudio, type AudioOptions } from './audio';
|
|
20
27
|
export { startPreviewServer, type PreviewServer, type PreviewServerOptions, } from './preview';
|
|
21
28
|
export { version } from './version';
|
|
22
29
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { D as s, U as
|
|
1
|
+
import { A as a, D as s, U as n, c as u, n as d, p as A, r as R, a as c, b as l, d as o, e as v, s as E, v as U } from "./version-C7OkFI_V.js";
|
|
2
2
|
export {
|
|
3
|
+
a as AUDIO_FORMATS,
|
|
3
4
|
s as DEFAULT_PREVIEW_PORT,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
n as USAGE,
|
|
6
|
+
u as checkBundle,
|
|
7
|
+
d as nullRenderer,
|
|
8
|
+
A as parseArgs,
|
|
9
|
+
R as readBundle,
|
|
10
|
+
c as runAudio,
|
|
11
|
+
l as runCheck,
|
|
12
|
+
o as runFrame,
|
|
13
|
+
v as runRender,
|
|
14
|
+
E as startPreviewServer,
|
|
15
|
+
U as version
|
|
11
16
|
};
|
|
12
17
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
import { existsSync as $, readFileSync as A, statSync as R, createReadStream as U } from "node:fs";
|
|
2
|
+
import { resolve as m, dirname as h, basename as P } from "node:path";
|
|
3
|
+
import { Compositor as N, FontManager as B, fonts as I, VisualTrack as L, VisualClip as j, GroupClip as O, TextClip as b } from "@sequio/engine";
|
|
4
|
+
import { Runtime as F, ModuleResolutionError as G, resolveAssetPath as _ } from "@sequio/runtime";
|
|
5
|
+
import { cliExternals as w } from "./externals.js";
|
|
6
|
+
import { fileURLToPath as D } from "node:url";
|
|
7
|
+
import { NodeFileSystem as W } from "@sequio/runtime/node-fs";
|
|
8
|
+
import { createRequire as q } from "node:module";
|
|
9
|
+
const v = ["m4a", "mp3", "wav", "ogg", "webm"], x = 6180, be = `sequio — programmable-timeline CLI
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
sequio check <file> [options] Statically validate a composition (no GPU, offline)
|
|
13
|
+
sequio render <file> [options] Encode a composition to a video file
|
|
14
|
+
sequio frame <file> [options] Export a single frame at a time as a PNG
|
|
15
|
+
sequio audio <file> [options] Export just the audio track to an audio file
|
|
16
|
+
sequio preview <file> [options] Serve a live in-browser preview
|
|
17
|
+
|
|
18
|
+
Check options (no GPU, no network — the fast pre-flight before frame/render):
|
|
19
|
+
--json Emit machine-readable Diagnostic[] JSON
|
|
20
|
+
|
|
21
|
+
Render options (pure Node, PixiJS WebGPU — needs a GPU or Mesa lavapipe):
|
|
22
|
+
-o, --out <path> Output path (default: out.mp4)
|
|
23
|
+
-s, --scale <n> Render at n× the composition resolution (default: 1)
|
|
24
|
+
--verify Assert a valid video container came out
|
|
25
|
+
|
|
26
|
+
Frame options (pure Node, PixiJS WebGPU — needs a GPU or Mesa lavapipe):
|
|
27
|
+
-t, --time <sec> Timeline time to sample, in seconds (default: 0)
|
|
28
|
+
-o, --out <path> Output PNG path (default: frame.png)
|
|
29
|
+
-s, --scale <n> Render at n× the composition resolution (default: 1)
|
|
30
|
+
|
|
31
|
+
Audio options:
|
|
32
|
+
-o, --out <path> Output path (default: out.<format>)
|
|
33
|
+
-f, --format <fmt> Container: ${v.join(" | ")} (default: mp3, or inferred from --out)
|
|
34
|
+
-b, --bitrate <bps> Target audio bitrate in bits/sec (default: 128000; ignored for wav)
|
|
35
|
+
|
|
36
|
+
Preview options:
|
|
37
|
+
-w, --watch Re-run on any project-file change (live reload)
|
|
38
|
+
-p, --port <n> Dev-server port (default: ${x})
|
|
39
|
+
--host Expose the server on the local network (0.0.0.0)
|
|
40
|
+
|
|
41
|
+
Global:
|
|
42
|
+
-h, --help Show this help
|
|
43
|
+
-v, --version Print the version
|
|
44
|
+
|
|
45
|
+
<file> is a TS/JS module whose default export is a
|
|
46
|
+
defineComposition(builder) (or a bare builder). Its sibling files are
|
|
47
|
+
bundled with it, so relative imports (./scene, ./title) resolve.`;
|
|
48
|
+
function ke(e) {
|
|
49
|
+
if (e[0] === "--" && (e = e.slice(1)), e.length === 0) return { kind: "help" };
|
|
50
|
+
const t = e[0];
|
|
51
|
+
return t === "-h" || t === "--help" ? { kind: "help" } : t === "-v" || t === "--version" ? { kind: "version" } : t === "check" ? M(e.slice(1)) : t === "render" ? H(e.slice(1)) : t === "frame" ? z(e.slice(1)) : t === "audio" ? J(e.slice(1)) : t === "preview" ? V(e.slice(1)) : { kind: "error", message: `Unknown command: ${t}` };
|
|
52
|
+
}
|
|
53
|
+
function M(e) {
|
|
54
|
+
let t, n = !1;
|
|
55
|
+
for (let c = 0; c < e.length; c++) {
|
|
56
|
+
const i = e[c];
|
|
57
|
+
if (i === "-h" || i === "--help") return { kind: "help" };
|
|
58
|
+
if (i === "--json") n = !0;
|
|
59
|
+
else {
|
|
60
|
+
if (i.startsWith("-")) return { kind: "error", message: `Unknown option: ${i}` };
|
|
61
|
+
if (t === void 0) t = i;
|
|
62
|
+
else return { kind: "error", message: `Unexpected argument: ${i}` };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return t === void 0 ? { kind: "error", message: "check needs a <file>" } : { kind: "check", file: t, json: n };
|
|
66
|
+
}
|
|
67
|
+
function H(e) {
|
|
68
|
+
let t, n, c = 1, i = !1;
|
|
69
|
+
for (let o = 0; o < e.length; o++) {
|
|
70
|
+
const r = e[o];
|
|
71
|
+
if (r === "-h" || r === "--help") return { kind: "help" };
|
|
72
|
+
if (r === "-o" || r === "--out") {
|
|
73
|
+
if (n = e[++o], n === void 0) return { kind: "error", message: `${r} needs a path` };
|
|
74
|
+
} else if (r === "-s" || r === "--scale") {
|
|
75
|
+
const s = e[++o], a = Number(s);
|
|
76
|
+
if (!(a >= 1) || !Number.isFinite(a)) return { kind: "error", message: `${r} needs a number ≥ 1, got: ${s}` };
|
|
77
|
+
c = a;
|
|
78
|
+
} else if (r === "--verify") i = !0;
|
|
79
|
+
else {
|
|
80
|
+
if (r.startsWith("-")) return { kind: "error", message: `Unknown option: ${r}` };
|
|
81
|
+
if (t === void 0) t = r;
|
|
82
|
+
else return { kind: "error", message: `Unexpected argument: ${r}` };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return t === void 0 ? { kind: "error", message: "render needs a <file>" } : { kind: "render", file: t, out: n, scale: c, verify: i };
|
|
86
|
+
}
|
|
87
|
+
function z(e) {
|
|
88
|
+
let t, n, c = 0, i = 1;
|
|
89
|
+
for (let o = 0; o < e.length; o++) {
|
|
90
|
+
const r = e[o];
|
|
91
|
+
if (r === "-h" || r === "--help") return { kind: "help" };
|
|
92
|
+
if (r === "-o" || r === "--out") {
|
|
93
|
+
if (n = e[++o], n === void 0) return { kind: "error", message: `${r} needs a path` };
|
|
94
|
+
} else if (r === "-t" || r === "--time") {
|
|
95
|
+
const s = e[++o], a = Number(s);
|
|
96
|
+
if (!Number.isFinite(a) || a < 0) return { kind: "error", message: `${r} needs a number ≥ 0, got: ${s}` };
|
|
97
|
+
c = a;
|
|
98
|
+
} else if (r === "-s" || r === "--scale") {
|
|
99
|
+
const s = e[++o], a = Number(s);
|
|
100
|
+
if (!(a >= 1) || !Number.isFinite(a)) return { kind: "error", message: `${r} needs a number ≥ 1, got: ${s}` };
|
|
101
|
+
i = a;
|
|
102
|
+
} else {
|
|
103
|
+
if (r.startsWith("-")) return { kind: "error", message: `Unknown option: ${r}` };
|
|
104
|
+
if (t === void 0) t = r;
|
|
105
|
+
else return { kind: "error", message: `Unexpected argument: ${r}` };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return t === void 0 ? { kind: "error", message: "frame needs a <file>" } : { kind: "frame", file: t, time: c, out: n, scale: i };
|
|
109
|
+
}
|
|
110
|
+
function J(e) {
|
|
111
|
+
let t, n, c, i;
|
|
112
|
+
for (let o = 0; o < e.length; o++) {
|
|
113
|
+
const r = e[o];
|
|
114
|
+
if (r === "-h" || r === "--help") return { kind: "help" };
|
|
115
|
+
if (r === "-o" || r === "--out") {
|
|
116
|
+
if (n = e[++o], n === void 0) return { kind: "error", message: `${r} needs a path` };
|
|
117
|
+
} else if (r === "-f" || r === "--format") {
|
|
118
|
+
const s = e[++o];
|
|
119
|
+
if (!v.includes(s))
|
|
120
|
+
return { kind: "error", message: `${r} needs one of ${v.join(", ")}, got: ${s}` };
|
|
121
|
+
c = s;
|
|
122
|
+
} else if (r === "-b" || r === "--bitrate") {
|
|
123
|
+
const s = e[++o], a = Number(s);
|
|
124
|
+
if (!Number.isFinite(a) || a <= 0) return { kind: "error", message: `${r} needs a number > 0, got: ${s}` };
|
|
125
|
+
i = a;
|
|
126
|
+
} else {
|
|
127
|
+
if (r.startsWith("-")) return { kind: "error", message: `Unknown option: ${r}` };
|
|
128
|
+
if (t === void 0) t = r;
|
|
129
|
+
else return { kind: "error", message: `Unexpected argument: ${r}` };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return t === void 0 ? { kind: "error", message: "audio needs a <file>" } : { kind: "audio", file: t, out: n, format: c, bitrate: i };
|
|
133
|
+
}
|
|
134
|
+
function V(e) {
|
|
135
|
+
let t, n = !1, c = !1, i = x;
|
|
136
|
+
for (let o = 0; o < e.length; o++) {
|
|
137
|
+
const r = e[o];
|
|
138
|
+
if (r === "-h" || r === "--help") return { kind: "help" };
|
|
139
|
+
if (r === "-w" || r === "--watch") n = !0;
|
|
140
|
+
else if (r === "--host") c = !0;
|
|
141
|
+
else if (r === "-p" || r === "--port") {
|
|
142
|
+
const s = e[++o], a = Number(s);
|
|
143
|
+
if (!Number.isInteger(a) || a <= 0 || a > 65535)
|
|
144
|
+
return { kind: "error", message: `${r} needs a valid port, got: ${s}` };
|
|
145
|
+
i = a;
|
|
146
|
+
} else {
|
|
147
|
+
if (r.startsWith("-")) return { kind: "error", message: `Unknown option: ${r}` };
|
|
148
|
+
if (t === void 0) t = r;
|
|
149
|
+
else return { kind: "error", message: `Unexpected argument: ${r}` };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return t === void 0 ? { kind: "error", message: "preview needs a <file>" } : { kind: "preview", file: t, watch: n, port: i, host: c };
|
|
153
|
+
}
|
|
154
|
+
const K = /* @__PURE__ */ new Set([
|
|
155
|
+
// images
|
|
156
|
+
"png",
|
|
157
|
+
"jpg",
|
|
158
|
+
"jpeg",
|
|
159
|
+
"gif",
|
|
160
|
+
"webp",
|
|
161
|
+
"avif",
|
|
162
|
+
"bmp",
|
|
163
|
+
"ico",
|
|
164
|
+
// video
|
|
165
|
+
"mp4",
|
|
166
|
+
"webm",
|
|
167
|
+
"mov",
|
|
168
|
+
"mkv",
|
|
169
|
+
"avi",
|
|
170
|
+
"m4v",
|
|
171
|
+
// audio
|
|
172
|
+
"mp3",
|
|
173
|
+
"wav",
|
|
174
|
+
"m4a",
|
|
175
|
+
"aac",
|
|
176
|
+
"ogg",
|
|
177
|
+
"oga",
|
|
178
|
+
"flac",
|
|
179
|
+
"opus",
|
|
180
|
+
// fonts
|
|
181
|
+
"ttf",
|
|
182
|
+
"otf",
|
|
183
|
+
"woff",
|
|
184
|
+
"woff2"
|
|
185
|
+
]);
|
|
186
|
+
function C(e) {
|
|
187
|
+
const t = e.lastIndexOf(".");
|
|
188
|
+
return t < 0 ? "" : e.slice(t + 1).toLowerCase();
|
|
189
|
+
}
|
|
190
|
+
function X(e) {
|
|
191
|
+
return K.has(C(e));
|
|
192
|
+
}
|
|
193
|
+
function Y(e) {
|
|
194
|
+
return {
|
|
195
|
+
png: "image/png",
|
|
196
|
+
jpg: "image/jpeg",
|
|
197
|
+
jpeg: "image/jpeg",
|
|
198
|
+
gif: "image/gif",
|
|
199
|
+
webp: "image/webp",
|
|
200
|
+
avif: "image/avif",
|
|
201
|
+
bmp: "image/bmp",
|
|
202
|
+
ico: "image/x-icon",
|
|
203
|
+
mp4: "video/mp4",
|
|
204
|
+
webm: "video/webm",
|
|
205
|
+
mov: "video/quicktime",
|
|
206
|
+
mkv: "video/x-matroska",
|
|
207
|
+
avi: "video/x-msvideo",
|
|
208
|
+
m4v: "video/x-m4v",
|
|
209
|
+
mp3: "audio/mpeg",
|
|
210
|
+
wav: "audio/wav",
|
|
211
|
+
m4a: "audio/mp4",
|
|
212
|
+
aac: "audio/aac",
|
|
213
|
+
ogg: "audio/ogg",
|
|
214
|
+
oga: "audio/ogg",
|
|
215
|
+
flac: "audio/flac",
|
|
216
|
+
opus: "audio/opus",
|
|
217
|
+
ttf: "font/ttf",
|
|
218
|
+
otf: "font/otf",
|
|
219
|
+
woff: "font/woff",
|
|
220
|
+
woff2: "font/woff2"
|
|
221
|
+
}[C(e)] ?? "application/octet-stream";
|
|
222
|
+
}
|
|
223
|
+
function g(e) {
|
|
224
|
+
const t = m(e), n = h(t), c = "/" + P(t), i = new W(n), o = {};
|
|
225
|
+
for (const r of i.listFiles()) {
|
|
226
|
+
if (X(r)) continue;
|
|
227
|
+
const s = i.readFile(r);
|
|
228
|
+
s !== null && (o[r] = s);
|
|
229
|
+
}
|
|
230
|
+
if (o[c] === void 0)
|
|
231
|
+
throw new Error(`Entry file not found: ${e}`);
|
|
232
|
+
return { files: o, entry: c };
|
|
233
|
+
}
|
|
234
|
+
const Q = /* @__PURE__ */ new Set([
|
|
235
|
+
"sans-serif",
|
|
236
|
+
"serif",
|
|
237
|
+
"monospace",
|
|
238
|
+
"cursive",
|
|
239
|
+
"fantasy",
|
|
240
|
+
"system-ui",
|
|
241
|
+
"ui-sans-serif",
|
|
242
|
+
"ui-serif",
|
|
243
|
+
"ui-monospace",
|
|
244
|
+
"ui-rounded",
|
|
245
|
+
"math",
|
|
246
|
+
"emoji",
|
|
247
|
+
"inherit",
|
|
248
|
+
"initial",
|
|
249
|
+
"unset"
|
|
250
|
+
]);
|
|
251
|
+
function Z() {
|
|
252
|
+
const e = () => {
|
|
253
|
+
};
|
|
254
|
+
return Promise.resolve({ render: e, resize: e, destroy: e });
|
|
255
|
+
}
|
|
256
|
+
function ee() {
|
|
257
|
+
const e = globalThis;
|
|
258
|
+
e.createImageBitmap ??= async () => ({ width: 1, height: 1, close() {
|
|
259
|
+
} });
|
|
260
|
+
class t {
|
|
261
|
+
}
|
|
262
|
+
class n {
|
|
263
|
+
}
|
|
264
|
+
class c {
|
|
265
|
+
}
|
|
266
|
+
e.CanvasRenderingContext2D ??= t, e.HTMLCanvasElement ??= n, e.HTMLImageElement ??= c;
|
|
267
|
+
const i = e.CanvasRenderingContext2D, o = e.HTMLCanvasElement, r = (a) => Object.assign(new i(), {
|
|
268
|
+
canvas: a,
|
|
269
|
+
font: "10px sans-serif",
|
|
270
|
+
textBaseline: "alphabetic",
|
|
271
|
+
fillStyle: "#000",
|
|
272
|
+
strokeStyle: "#000",
|
|
273
|
+
lineWidth: 1,
|
|
274
|
+
globalAlpha: 1,
|
|
275
|
+
// A monospace-ish advance is enough for split/layout math; check never paints.
|
|
276
|
+
measureText: (l) => {
|
|
277
|
+
const d = (l?.length ?? 0) * 8;
|
|
278
|
+
return {
|
|
279
|
+
width: d,
|
|
280
|
+
actualBoundingBoxAscent: 8,
|
|
281
|
+
actualBoundingBoxDescent: 2,
|
|
282
|
+
actualBoundingBoxLeft: 0,
|
|
283
|
+
actualBoundingBoxRight: d,
|
|
284
|
+
fontBoundingBoxAscent: 8,
|
|
285
|
+
fontBoundingBoxDescent: 2
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
getImageData: (l, d, f, u) => ({
|
|
289
|
+
data: new Uint8ClampedArray(Math.max(1, f) * Math.max(1, u) * 4),
|
|
290
|
+
width: f,
|
|
291
|
+
height: u
|
|
292
|
+
}),
|
|
293
|
+
createLinearGradient: () => ({ addColorStop() {
|
|
294
|
+
} }),
|
|
295
|
+
createPattern: () => null,
|
|
296
|
+
fillText() {
|
|
297
|
+
},
|
|
298
|
+
strokeText() {
|
|
299
|
+
},
|
|
300
|
+
clearRect() {
|
|
301
|
+
},
|
|
302
|
+
fillRect() {
|
|
303
|
+
},
|
|
304
|
+
save() {
|
|
305
|
+
},
|
|
306
|
+
restore() {
|
|
307
|
+
},
|
|
308
|
+
scale() {
|
|
309
|
+
},
|
|
310
|
+
translate() {
|
|
311
|
+
},
|
|
312
|
+
rotate() {
|
|
313
|
+
},
|
|
314
|
+
beginPath() {
|
|
315
|
+
},
|
|
316
|
+
closePath() {
|
|
317
|
+
},
|
|
318
|
+
moveTo() {
|
|
319
|
+
},
|
|
320
|
+
lineTo() {
|
|
321
|
+
},
|
|
322
|
+
arc() {
|
|
323
|
+
},
|
|
324
|
+
fill() {
|
|
325
|
+
},
|
|
326
|
+
stroke() {
|
|
327
|
+
},
|
|
328
|
+
putImageData() {
|
|
329
|
+
},
|
|
330
|
+
drawImage() {
|
|
331
|
+
},
|
|
332
|
+
setTransform() {
|
|
333
|
+
},
|
|
334
|
+
transform() {
|
|
335
|
+
},
|
|
336
|
+
rect() {
|
|
337
|
+
},
|
|
338
|
+
clip() {
|
|
339
|
+
}
|
|
340
|
+
}), s = () => {
|
|
341
|
+
const a = new o();
|
|
342
|
+
let l;
|
|
343
|
+
return Object.assign(a, {
|
|
344
|
+
width: 1,
|
|
345
|
+
height: 1,
|
|
346
|
+
style: {},
|
|
347
|
+
getContext: (d) => d === "2d" ? l ??= r(a) : null,
|
|
348
|
+
addEventListener() {
|
|
349
|
+
},
|
|
350
|
+
removeEventListener() {
|
|
351
|
+
},
|
|
352
|
+
toDataURL: () => "data:,"
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
e.document === void 0 && (e.document = {
|
|
356
|
+
createElement: (a) => a === "canvas" ? s() : { style: {}, appendChild() {
|
|
357
|
+
}, addEventListener() {
|
|
358
|
+
}, removeEventListener() {
|
|
359
|
+
} },
|
|
360
|
+
createElementNS: () => s(),
|
|
361
|
+
documentElement: { style: {} },
|
|
362
|
+
body: { appendChild() {
|
|
363
|
+
}, style: {} },
|
|
364
|
+
head: { appendChild() {
|
|
365
|
+
} },
|
|
366
|
+
addEventListener() {
|
|
367
|
+
},
|
|
368
|
+
removeEventListener() {
|
|
369
|
+
},
|
|
370
|
+
fonts: { add() {
|
|
371
|
+
}, load: async () => [], ready: Promise.resolve() }
|
|
372
|
+
}), e.OffscreenCanvas ??= class {
|
|
373
|
+
width;
|
|
374
|
+
height;
|
|
375
|
+
constructor(a, l) {
|
|
376
|
+
this.width = a ?? 1, this.height = l ?? 1;
|
|
377
|
+
}
|
|
378
|
+
getContext(a) {
|
|
379
|
+
return a === "2d" ? r(this) : null;
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function te() {
|
|
384
|
+
const e = B.prototype;
|
|
385
|
+
e.loadFace = () => Promise.resolve(), e.loadGoogle = () => Promise.resolve();
|
|
386
|
+
}
|
|
387
|
+
function p(e) {
|
|
388
|
+
return typeof e == "number" && Number.isFinite(e);
|
|
389
|
+
}
|
|
390
|
+
const re = /createImageBitmap|source type for resource|ImageBitmap|VideoFrame|GPU|WebGL|texture|decode|fetch|ENOTFOUND|ECONNREFUSED|EAI_AGAIN|Forbidden|\b4\d\d\b|\b5\d\d\b|network/i;
|
|
391
|
+
function k(e, t) {
|
|
392
|
+
const n = e instanceof Error ? e.message : String(e);
|
|
393
|
+
return e instanceof G ? !/^[./]/.test(e.specifier) ? {
|
|
394
|
+
severity: "error",
|
|
395
|
+
code: "A3",
|
|
396
|
+
message: `external module '${e.specifier}' is not injected — provide it via externals (the CLI ships gsap): ${n}`
|
|
397
|
+
} : { severity: "error", code: "A2", message: `relative import does not resolve: ${n}` } : /must export|defineComposition|builder function/i.test(n) ? { severity: "error", code: "A4", message: n } : /not implemented/i.test(n) ? {
|
|
398
|
+
severity: "error",
|
|
399
|
+
code: "B2",
|
|
400
|
+
message: `hit an unimplemented path (throws instead of rendering a black frame): ${n}`
|
|
401
|
+
} : t === "build" && re.test(n) ? {
|
|
402
|
+
severity: "warn",
|
|
403
|
+
code: "B4",
|
|
404
|
+
message: `builder needs a capability check runs without (media decode / network / GPU texture) — the code compiled and linked; verify the picture with \`sequio frame\`: ${n}`
|
|
405
|
+
} : t === "build" ? { severity: "error", code: "B1", message: `builder threw: ${n}` } : { severity: "error", code: "A1", message: n };
|
|
406
|
+
}
|
|
407
|
+
async function oe(e, t = {}) {
|
|
408
|
+
const n = [];
|
|
409
|
+
ee(), te();
|
|
410
|
+
const c = [], i = async (d) => (t.assetExists && !t.assetExists(d) && c.push(d), new Blob([new Uint8Array([0])])), o = new F({
|
|
411
|
+
files: e.files,
|
|
412
|
+
entry: e.entry,
|
|
413
|
+
externals: t.externals,
|
|
414
|
+
loadAsset: i
|
|
415
|
+
}), r = {
|
|
416
|
+
compositorOptions: { createRenderer: Z },
|
|
417
|
+
target: "server"
|
|
418
|
+
};
|
|
419
|
+
let s;
|
|
420
|
+
try {
|
|
421
|
+
s = o.link(r);
|
|
422
|
+
} catch (d) {
|
|
423
|
+
return n.push(k(d, "link")), n;
|
|
424
|
+
}
|
|
425
|
+
let a;
|
|
426
|
+
try {
|
|
427
|
+
a = await s.build(r);
|
|
428
|
+
} catch (d) {
|
|
429
|
+
return n.push(k(d, "build")), n;
|
|
430
|
+
}
|
|
431
|
+
if (!a || !(a.compositor instanceof N))
|
|
432
|
+
return n.push({
|
|
433
|
+
severity: "error",
|
|
434
|
+
code: "B1",
|
|
435
|
+
message: "builder did not return { compositor } (an initialized Compositor)"
|
|
436
|
+
}), n;
|
|
437
|
+
const l = a.compositor;
|
|
438
|
+
l.isInitialized || n.push({
|
|
439
|
+
severity: "error",
|
|
440
|
+
code: "B3",
|
|
441
|
+
message: "compositor was never initialized — add `await compositor.init()` in the builder"
|
|
442
|
+
});
|
|
443
|
+
for (const d of c)
|
|
444
|
+
n.push({ severity: "error", code: "C7", message: `local asset not found: ${d}` });
|
|
445
|
+
ne(l, a.duration, n);
|
|
446
|
+
try {
|
|
447
|
+
l.dispose();
|
|
448
|
+
} catch {
|
|
449
|
+
}
|
|
450
|
+
return n;
|
|
451
|
+
}
|
|
452
|
+
function ne(e, t, n) {
|
|
453
|
+
const c = new Set(I.families());
|
|
454
|
+
e.getTracks().forEach((o, r) => {
|
|
455
|
+
o.clips.forEach((s, a) => {
|
|
456
|
+
E(s, `track ${r} · clip ${a}`, t, c, n);
|
|
457
|
+
}), o instanceof L && o.transitions.forEach((s, a) => {
|
|
458
|
+
ie(s, o.clips, `track ${r} · transition ${a}`, n);
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
function E(e, t, n, c, i) {
|
|
463
|
+
const o = `${t} (${e.constructor.name})`;
|
|
464
|
+
!p(e.start) || !p(e.end) ? i.push({ severity: "error", code: "C1", message: "clip start/end is not a finite number", at: o }) : (e.start < 0 && i.push({ severity: "error", code: "C1", message: `clip start < 0 (${e.start})`, at: o }), e.end <= e.start && i.push({
|
|
465
|
+
severity: "error",
|
|
466
|
+
code: "C1",
|
|
467
|
+
message: `clip end ≤ start (start=${e.start}, end=${e.end})`,
|
|
468
|
+
at: o
|
|
469
|
+
}), n !== void 0 && p(n) && e.end > n && i.push({
|
|
470
|
+
severity: "error",
|
|
471
|
+
code: "C3",
|
|
472
|
+
message: `clip end (${e.end}) exceeds the declared duration (${n})`,
|
|
473
|
+
at: o
|
|
474
|
+
}), p(e.sourceIn) && p(e.sourceOut) && e.sourceOut > 0 && e.sourceOut < e.sourceIn && i.push({
|
|
475
|
+
severity: "warn",
|
|
476
|
+
code: "C9",
|
|
477
|
+
message: `sourceOut (${e.sourceOut}) is before sourceIn (${e.sourceIn})`,
|
|
478
|
+
at: o
|
|
479
|
+
})), e instanceof j && se(e, o, c, i), e instanceof O && e.children.forEach((r, s) => {
|
|
480
|
+
E(r, `${t} · child ${s}`, void 0, c, i);
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
function se(e, t, n, c) {
|
|
484
|
+
const i = e.start, o = e.end, r = p(i) && p(o) && o > i, s = [
|
|
485
|
+
{ name: "position", times: e.transform.position.keyframeTimes },
|
|
486
|
+
{ name: "scale", times: e.transform.scale.keyframeTimes },
|
|
487
|
+
{ name: "rotation", times: e.transform.rotation.keyframeTimes },
|
|
488
|
+
{ name: "anchor", times: e.transform.anchor.keyframeTimes },
|
|
489
|
+
{ name: "opacity", times: e.opacity.keyframeTimes }
|
|
490
|
+
];
|
|
491
|
+
if (e instanceof b && s.push({ name: "fontSize", times: e.fontSize.keyframeTimes }), r)
|
|
492
|
+
for (const { name: d, times: f } of s)
|
|
493
|
+
for (const u of f)
|
|
494
|
+
(u < i || u > o) && c.push({
|
|
495
|
+
severity: "error",
|
|
496
|
+
code: "C2",
|
|
497
|
+
message: `${d} keyframe at t=${u} falls outside the clip's [${i}, ${o}] — dead keyframe`,
|
|
498
|
+
at: t
|
|
499
|
+
});
|
|
500
|
+
const a = e.transform.anchor, l = a.keyframeTimes.length ? a.keyframeTimes : [r ? i : 0];
|
|
501
|
+
for (const d of l) {
|
|
502
|
+
const [f, u] = a.valueAt(d);
|
|
503
|
+
if (f < -1e-6 || f > 1 + 1e-6 || u < -1e-6 || u > 1 + 1e-6) {
|
|
504
|
+
c.push({
|
|
505
|
+
severity: "error",
|
|
506
|
+
code: "C8",
|
|
507
|
+
message: `anchor [${f}, ${u}] is outside 0..1 (anchor is normalized, not pixels)`,
|
|
508
|
+
at: t
|
|
509
|
+
});
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (e instanceof b) {
|
|
514
|
+
const d = e.fontFamily.split(",").map((u) => u.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
|
|
515
|
+
!d.some((u) => Q.has(u.toLowerCase()) || n.has(u)) && d.length > 0 && c.push({
|
|
516
|
+
severity: "warn",
|
|
517
|
+
code: "C4",
|
|
518
|
+
message: `font-family "${e.fontFamily}" is not registered via fonts.load(...) — will fall back to a system font (preview may differ from render)`,
|
|
519
|
+
at: t
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function ie(e, t, n, c) {
|
|
524
|
+
const { from: i, to: o } = e;
|
|
525
|
+
if (!i || !o) {
|
|
526
|
+
c.push({ severity: "error", code: "C5", message: "transition is not bound to two clips (call .between(a, b))", at: n });
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (!t.includes(i) || !t.includes(o)) {
|
|
530
|
+
c.push({
|
|
531
|
+
severity: "error",
|
|
532
|
+
code: "C5",
|
|
533
|
+
message: "transition binds clips that are not both on this track",
|
|
534
|
+
at: n
|
|
535
|
+
});
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
e.windowAt() == null && c.push({
|
|
539
|
+
severity: "error",
|
|
540
|
+
code: "C5",
|
|
541
|
+
message: `transition clips do not overlap — the transition window is empty (overlap [${i.start}, ${i.end}] with [${o.start}, ${o.end}])`,
|
|
542
|
+
at: n
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
function ae(e) {
|
|
546
|
+
return e instanceof Error ? e.message : String(e);
|
|
547
|
+
}
|
|
548
|
+
async function xe(e, t = {}) {
|
|
549
|
+
let n;
|
|
550
|
+
try {
|
|
551
|
+
const o = g(e), r = h(m(e));
|
|
552
|
+
n = await oe(o, {
|
|
553
|
+
externals: w(),
|
|
554
|
+
assetExists: (s) => $(m(r, s))
|
|
555
|
+
});
|
|
556
|
+
} catch (o) {
|
|
557
|
+
n = [{ severity: "error", code: "A0", message: ae(o) }];
|
|
558
|
+
}
|
|
559
|
+
const c = n.filter((o) => o.severity === "error"), i = n.filter((o) => o.severity === "warn");
|
|
560
|
+
if (t.json)
|
|
561
|
+
return console.log(JSON.stringify(n, null, 2)), c.length > 0 ? 1 : 0;
|
|
562
|
+
for (const o of n) {
|
|
563
|
+
const r = o.severity === "error" ? "✖" : "⚠", s = o.at ? ` [${o.at}]` : "";
|
|
564
|
+
console.error(`${r} ${o.code}${s}: ${o.message}`);
|
|
565
|
+
}
|
|
566
|
+
if (c.length === 0) {
|
|
567
|
+
const o = i.length ? ` (${i.length} warning${i.length === 1 ? "" : "s"})` : "";
|
|
568
|
+
return console.log(`✅ check passed: ${e}${o}`), 0;
|
|
569
|
+
}
|
|
570
|
+
return console.error(
|
|
571
|
+
`
|
|
572
|
+
✖ check failed: ${c.length} error${c.length === 1 ? "" : "s"}${i.length ? `, ${i.length} warning${i.length === 1 ? "" : "s"}` : ""}.`
|
|
573
|
+
), 1;
|
|
574
|
+
}
|
|
575
|
+
function y(e) {
|
|
576
|
+
const t = m(e);
|
|
577
|
+
return async (n) => {
|
|
578
|
+
const c = m(t, n);
|
|
579
|
+
if (c !== t && !c.startsWith(t + "/"))
|
|
580
|
+
throw new Error(`asset path escapes the project root: ${n}`);
|
|
581
|
+
let i;
|
|
582
|
+
try {
|
|
583
|
+
i = A(c);
|
|
584
|
+
} catch {
|
|
585
|
+
throw new Error(
|
|
586
|
+
`local asset not found: ${n} (looked in ${t}). Drop the file there, or reference a network URL instead.`
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
return new Blob([new Uint8Array(i)]);
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
function ce(e) {
|
|
593
|
+
return e.length >= 12 && Buffer.from(e.subarray(4, 8)).toString("latin1") === "ftyp" ? "mp4" : e.length >= 4 && e[0] === 26 && e[1] === 69 && e[2] === 223 && e[3] === 163 ? "webm" : null;
|
|
594
|
+
}
|
|
595
|
+
async function Ce(e, t = {}) {
|
|
596
|
+
const n = g(e), c = h(m(e)), { renderBundleToFile: i } = await import("@sequio/server/route-b");
|
|
597
|
+
try {
|
|
598
|
+
const o = t.out ?? "out.mp4";
|
|
599
|
+
console.log(`Rendering ${e} (pure Node, WebGPU)${t.scale && t.scale !== 1 ? ` @ ${t.scale}×` : ""} …`);
|
|
600
|
+
let r = -1;
|
|
601
|
+
const s = await i(n, {
|
|
602
|
+
out: o,
|
|
603
|
+
scale: t.scale,
|
|
604
|
+
// Make gsap (and any other CLI-provided lib) resolvable to the composition
|
|
605
|
+
// in the Node render, same as the browser preview does.
|
|
606
|
+
externals: w(),
|
|
607
|
+
// Resolve `loadAsset('./clip.mp4')` against the project directory on disk,
|
|
608
|
+
// so a local image/video renders the same as it previews (contract #3).
|
|
609
|
+
loadAsset: y(c),
|
|
610
|
+
onProgress: (a) => {
|
|
611
|
+
const l = Math.round(a * 100);
|
|
612
|
+
l !== r && l % 10 === 0 && (r = l, process.stdout.write(`\r ${l}%`));
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
if (process.stdout.write("\r \r"), console.log(
|
|
616
|
+
`✅ wrote ${s.out} (${s.frames} frames, ${s.container}/${s.videoCodec}, ${s.bytes} bytes${s.audio ? ", +audio" : ""})`
|
|
617
|
+
), t.verify) {
|
|
618
|
+
const l = (await import("node:fs")).readFileSync(s.out), d = ce(l);
|
|
619
|
+
if (!d || l.length < 500)
|
|
620
|
+
throw new Error(`--verify failed: not a valid container (detected=${d}, size=${l.length})`);
|
|
621
|
+
console.log(`✅ verified: valid ${d} container.`);
|
|
622
|
+
}
|
|
623
|
+
return 0;
|
|
624
|
+
} catch (o) {
|
|
625
|
+
return console.error("✖", o instanceof Error ? o.message : String(o)), 1;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
async function Ee(e, t = {}) {
|
|
629
|
+
const n = g(e), c = h(m(e)), { renderBundleFrameToFile: i } = await import("@sequio/server/route-b");
|
|
630
|
+
try {
|
|
631
|
+
const o = t.out ?? "frame.png", r = t.time ?? 0;
|
|
632
|
+
console.log(
|
|
633
|
+
`Rendering ${e} frame @ t=${r}s (pure Node, WebGPU)${t.scale && t.scale !== 1 ? ` @ ${t.scale}×` : ""} …`
|
|
634
|
+
);
|
|
635
|
+
const s = await i(n, {
|
|
636
|
+
out: o,
|
|
637
|
+
time: r,
|
|
638
|
+
scale: t.scale,
|
|
639
|
+
// Make gsap (and any other CLI-provided lib) resolvable to the composition
|
|
640
|
+
// in the Node render, same as the browser preview does.
|
|
641
|
+
externals: w(),
|
|
642
|
+
// Resolve `loadAsset('./clip.mp4')` against the project directory on disk,
|
|
643
|
+
// so a local image/video renders the same as it previews (contract #3).
|
|
644
|
+
loadAsset: y(c)
|
|
645
|
+
}), a = s.time !== r ? ` (clamped from ${r}s)` : "";
|
|
646
|
+
return console.log(
|
|
647
|
+
`✅ wrote ${s.out} (${s.width}×${s.height}, t=${s.time}s${a}, ${s.bytes} bytes)`
|
|
648
|
+
), 0;
|
|
649
|
+
} catch (o) {
|
|
650
|
+
return console.error("✖", o instanceof Error ? o.message : String(o)), 1;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
function le(e) {
|
|
654
|
+
const t = e?.toLowerCase().match(/\.([^./\\]+)$/), n = t?.[1] === "oga" ? "ogg" : t?.[1];
|
|
655
|
+
return v.includes(n) ? n : void 0;
|
|
656
|
+
}
|
|
657
|
+
async function Se(e, t = {}) {
|
|
658
|
+
const n = g(e), c = h(m(e)), i = t.format ?? le(t.out) ?? "mp3", { exportBundleAudioToFile: o } = await import("@sequio/server/route-b");
|
|
659
|
+
try {
|
|
660
|
+
const r = t.out ?? `out.${i}`;
|
|
661
|
+
console.log(`Exporting audio from ${e} (pure Node) → ${i} …`);
|
|
662
|
+
const s = await o(n, {
|
|
663
|
+
out: r,
|
|
664
|
+
format: i,
|
|
665
|
+
bitrate: t.bitrate,
|
|
666
|
+
// Make gsap (and any other CLI-provided lib) resolvable to the composition
|
|
667
|
+
// in the Node run, same as the browser preview does.
|
|
668
|
+
externals: w(),
|
|
669
|
+
// Resolve `loadAsset('./song.mp3')` against the project directory on disk,
|
|
670
|
+
// so a local audio source mixes the same as it previews (contract #3).
|
|
671
|
+
loadAsset: y(c)
|
|
672
|
+
});
|
|
673
|
+
return console.log(
|
|
674
|
+
`✅ wrote ${s.out} (${s.format}/${s.codec}, ${s.duration.toFixed(2)}s, ${s.bytes} bytes)`
|
|
675
|
+
), 0;
|
|
676
|
+
} catch (r) {
|
|
677
|
+
return console.error("✖", r instanceof Error ? r.message : String(r)), 1;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
const S = h(D(import.meta.url)), de = m(S, "../preview"), T = {};
|
|
681
|
+
for (const [e, t] of [
|
|
682
|
+
["@sequio/engine", "../../engine/src/index.ts"],
|
|
683
|
+
["@sequio/runtime", "../../runtime/src/index.ts"],
|
|
684
|
+
["@sequio/cli/externals", "./externals.ts"]
|
|
685
|
+
]) {
|
|
686
|
+
const n = m(S, t);
|
|
687
|
+
$(n) && (T[e] = n);
|
|
688
|
+
}
|
|
689
|
+
async function Te(e, t = {}) {
|
|
690
|
+
const n = m(e), c = h(n);
|
|
691
|
+
g(n);
|
|
692
|
+
const { createServer: i } = await import("vite"), o = await i({
|
|
693
|
+
configFile: !1,
|
|
694
|
+
root: de,
|
|
695
|
+
logLevel: "warn",
|
|
696
|
+
server: {
|
|
697
|
+
port: t.port ?? 6180,
|
|
698
|
+
host: t.host ? !0 : "localhost",
|
|
699
|
+
strictPort: !0
|
|
700
|
+
},
|
|
701
|
+
resolve: {
|
|
702
|
+
alias: T
|
|
703
|
+
},
|
|
704
|
+
plugins: [
|
|
705
|
+
{
|
|
706
|
+
name: "sequio-preview-bundle",
|
|
707
|
+
configureServer(s) {
|
|
708
|
+
if (s.middlewares.use("/__bundle", (a, l) => {
|
|
709
|
+
try {
|
|
710
|
+
const d = g(n);
|
|
711
|
+
l.setHeader("Content-Type", "application/json"), l.setHeader("Cache-Control", "no-store"), l.end(JSON.stringify(d));
|
|
712
|
+
} catch (d) {
|
|
713
|
+
l.statusCode = 500, l.setHeader("Content-Type", "application/json"), l.end(JSON.stringify({ error: d instanceof Error ? d.message : String(d) }));
|
|
714
|
+
}
|
|
715
|
+
}), s.middlewares.use("/__asset", (a, l) => {
|
|
716
|
+
try {
|
|
717
|
+
const d = (a.url ?? "").split("?")[0], f = _(decodeURIComponent(d)), u = m(c, f);
|
|
718
|
+
if (u !== c && !u.startsWith(c + "/"))
|
|
719
|
+
return l.statusCode = 403, l.end("forbidden");
|
|
720
|
+
if (!$(u) || !R(u).isFile())
|
|
721
|
+
return l.statusCode = 404, l.end(`asset not found: ${f}`);
|
|
722
|
+
l.setHeader("Content-Type", Y(f)), l.setHeader("Cache-Control", "no-store"), U(u).pipe(l);
|
|
723
|
+
} catch (d) {
|
|
724
|
+
l.statusCode = 400, l.end(d instanceof Error ? d.message : String(d));
|
|
725
|
+
}
|
|
726
|
+
}), t.watch) {
|
|
727
|
+
s.watcher.add(c);
|
|
728
|
+
const a = (l) => {
|
|
729
|
+
m(l).startsWith(c) && s.ws.send({ type: "full-reload", path: "*" });
|
|
730
|
+
};
|
|
731
|
+
s.watcher.on("change", a), s.watcher.on("add", a), s.watcher.on("unlink", a);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
]
|
|
736
|
+
});
|
|
737
|
+
return await o.listen(), {
|
|
738
|
+
url: o.resolvedUrls?.local[0] ?? `http://localhost:${t.port ?? 6180}/`,
|
|
739
|
+
async close() {
|
|
740
|
+
await o.close();
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
const ue = q(import.meta.url), fe = ue("../package.json"), Ae = fe.version;
|
|
745
|
+
export {
|
|
746
|
+
v as A,
|
|
747
|
+
x as D,
|
|
748
|
+
be as U,
|
|
749
|
+
Se as a,
|
|
750
|
+
xe as b,
|
|
751
|
+
oe as c,
|
|
752
|
+
Ee as d,
|
|
753
|
+
Ce as e,
|
|
754
|
+
Z as n,
|
|
755
|
+
ke as p,
|
|
756
|
+
g as r,
|
|
757
|
+
Te as s,
|
|
758
|
+
Ae as v
|
|
759
|
+
};
|
|
760
|
+
//# sourceMappingURL=version-C7OkFI_V.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sequio/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "The sequio command-line: `sequio render <file>` encodes a composition to video (pure Node, PixiJS WebGPU), `sequio frame <file>` writes a single-frame PNG, and `sequio preview <file>` serves a live in-browser preview (with --watch).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"gsap": "^3.12.0",
|
|
54
54
|
"vite": "^6.0.0",
|
|
55
|
-
"@sequio/engine": "^0.1.
|
|
56
|
-
"@sequio/server": "^0.1.
|
|
57
|
-
"@sequio/runtime": "^0.1.
|
|
55
|
+
"@sequio/engine": "^0.1.2",
|
|
56
|
+
"@sequio/server": "^0.1.2",
|
|
57
|
+
"@sequio/runtime": "^0.1.2"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"dev": "tsx src/cli.ts",
|
package/dist/version-B2TKvZen.js
DELETED
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
import { resolve as f, dirname as m, basename as S } from "node:path";
|
|
2
|
-
import { cliExternals as h } from "./externals.js";
|
|
3
|
-
import { readFileSync as E, existsSync as w, statSync as P, createReadStream as x } from "node:fs";
|
|
4
|
-
import { fileURLToPath as U } from "node:url";
|
|
5
|
-
import { resolveAssetPath as R } from "@sequio/runtime";
|
|
6
|
-
import { NodeFileSystem as N } from "@sequio/runtime/node-fs";
|
|
7
|
-
import { createRequire as C } from "node:module";
|
|
8
|
-
const v = 6180, z = `sequio — programmable-timeline CLI
|
|
9
|
-
|
|
10
|
-
Usage:
|
|
11
|
-
sequio render <file> [options] Encode a composition to a video file
|
|
12
|
-
sequio frame <file> [options] Export a single frame at a time as a PNG
|
|
13
|
-
sequio preview <file> [options] Serve a live in-browser preview
|
|
14
|
-
|
|
15
|
-
Render options (pure Node, PixiJS WebGPU — needs a GPU or Mesa lavapipe):
|
|
16
|
-
-o, --out <path> Output path (default: out.mp4)
|
|
17
|
-
-s, --scale <n> Render at n× the composition resolution (default: 1)
|
|
18
|
-
--verify Assert a valid video container came out
|
|
19
|
-
|
|
20
|
-
Frame options (pure Node, PixiJS WebGPU — needs a GPU or Mesa lavapipe):
|
|
21
|
-
-t, --time <sec> Timeline time to sample, in seconds (default: 0)
|
|
22
|
-
-o, --out <path> Output PNG path (default: frame.png)
|
|
23
|
-
-s, --scale <n> Render at n× the composition resolution (default: 1)
|
|
24
|
-
|
|
25
|
-
Preview options:
|
|
26
|
-
-w, --watch Re-run on any project-file change (live reload)
|
|
27
|
-
-p, --port <n> Dev-server port (default: ${v})
|
|
28
|
-
--host Expose the server on the local network (0.0.0.0)
|
|
29
|
-
|
|
30
|
-
Global:
|
|
31
|
-
-h, --help Show this help
|
|
32
|
-
-v, --version Print the version
|
|
33
|
-
|
|
34
|
-
<file> is a TS/JS module whose default export is a
|
|
35
|
-
defineComposition(builder) (or a bare builder). Its sibling files are
|
|
36
|
-
bundled with it, so relative imports (./scene, ./title) resolve.`;
|
|
37
|
-
function X(t) {
|
|
38
|
-
if (t[0] === "--" && (t = t.slice(1)), t.length === 0) return { kind: "help" };
|
|
39
|
-
const e = t[0];
|
|
40
|
-
return e === "-h" || e === "--help" ? { kind: "help" } : e === "-v" || e === "--version" ? { kind: "version" } : e === "render" ? j(t.slice(1)) : e === "frame" ? A(t.slice(1)) : e === "preview" ? F(t.slice(1)) : { kind: "error", message: `Unknown command: ${e}` };
|
|
41
|
-
}
|
|
42
|
-
function j(t) {
|
|
43
|
-
let e, i, l = 1, c = !1;
|
|
44
|
-
for (let o = 0; o < t.length; o++) {
|
|
45
|
-
const r = t[o];
|
|
46
|
-
if (r === "-h" || r === "--help") return { kind: "help" };
|
|
47
|
-
if (r === "-o" || r === "--out") {
|
|
48
|
-
if (i = t[++o], i === void 0) return { kind: "error", message: `${r} needs a path` };
|
|
49
|
-
} else if (r === "-s" || r === "--scale") {
|
|
50
|
-
const n = t[++o], s = Number(n);
|
|
51
|
-
if (!(s >= 1) || !Number.isFinite(s)) return { kind: "error", message: `${r} needs a number ≥ 1, got: ${n}` };
|
|
52
|
-
l = s;
|
|
53
|
-
} else if (r === "--verify") c = !0;
|
|
54
|
-
else {
|
|
55
|
-
if (r.startsWith("-")) return { kind: "error", message: `Unknown option: ${r}` };
|
|
56
|
-
if (e === void 0) e = r;
|
|
57
|
-
else return { kind: "error", message: `Unexpected argument: ${r}` };
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return e === void 0 ? { kind: "error", message: "render needs a <file>" } : { kind: "render", file: e, out: i, scale: l, verify: c };
|
|
61
|
-
}
|
|
62
|
-
function A(t) {
|
|
63
|
-
let e, i, l = 0, c = 1;
|
|
64
|
-
for (let o = 0; o < t.length; o++) {
|
|
65
|
-
const r = t[o];
|
|
66
|
-
if (r === "-h" || r === "--help") return { kind: "help" };
|
|
67
|
-
if (r === "-o" || r === "--out") {
|
|
68
|
-
if (i = t[++o], i === void 0) return { kind: "error", message: `${r} needs a path` };
|
|
69
|
-
} else if (r === "-t" || r === "--time") {
|
|
70
|
-
const n = t[++o], s = Number(n);
|
|
71
|
-
if (!Number.isFinite(s) || s < 0) return { kind: "error", message: `${r} needs a number ≥ 0, got: ${n}` };
|
|
72
|
-
l = s;
|
|
73
|
-
} else if (r === "-s" || r === "--scale") {
|
|
74
|
-
const n = t[++o], s = Number(n);
|
|
75
|
-
if (!(s >= 1) || !Number.isFinite(s)) return { kind: "error", message: `${r} needs a number ≥ 1, got: ${n}` };
|
|
76
|
-
c = s;
|
|
77
|
-
} else {
|
|
78
|
-
if (r.startsWith("-")) return { kind: "error", message: `Unknown option: ${r}` };
|
|
79
|
-
if (e === void 0) e = r;
|
|
80
|
-
else return { kind: "error", message: `Unexpected argument: ${r}` };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return e === void 0 ? { kind: "error", message: "frame needs a <file>" } : { kind: "frame", file: e, time: l, out: i, scale: c };
|
|
84
|
-
}
|
|
85
|
-
function F(t) {
|
|
86
|
-
let e, i = !1, l = !1, c = v;
|
|
87
|
-
for (let o = 0; o < t.length; o++) {
|
|
88
|
-
const r = t[o];
|
|
89
|
-
if (r === "-h" || r === "--help") return { kind: "help" };
|
|
90
|
-
if (r === "-w" || r === "--watch") i = !0;
|
|
91
|
-
else if (r === "--host") l = !0;
|
|
92
|
-
else if (r === "-p" || r === "--port") {
|
|
93
|
-
const n = t[++o], s = Number(n);
|
|
94
|
-
if (!Number.isInteger(s) || s <= 0 || s > 65535)
|
|
95
|
-
return { kind: "error", message: `${r} needs a valid port, got: ${n}` };
|
|
96
|
-
c = s;
|
|
97
|
-
} else {
|
|
98
|
-
if (r.startsWith("-")) return { kind: "error", message: `Unknown option: ${r}` };
|
|
99
|
-
if (e === void 0) e = r;
|
|
100
|
-
else return { kind: "error", message: `Unexpected argument: ${r}` };
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return e === void 0 ? { kind: "error", message: "preview needs a <file>" } : { kind: "preview", file: e, watch: i, port: c, host: l };
|
|
104
|
-
}
|
|
105
|
-
const T = /* @__PURE__ */ new Set([
|
|
106
|
-
// images
|
|
107
|
-
"png",
|
|
108
|
-
"jpg",
|
|
109
|
-
"jpeg",
|
|
110
|
-
"gif",
|
|
111
|
-
"webp",
|
|
112
|
-
"avif",
|
|
113
|
-
"bmp",
|
|
114
|
-
"ico",
|
|
115
|
-
// video
|
|
116
|
-
"mp4",
|
|
117
|
-
"webm",
|
|
118
|
-
"mov",
|
|
119
|
-
"mkv",
|
|
120
|
-
"avi",
|
|
121
|
-
"m4v",
|
|
122
|
-
// audio
|
|
123
|
-
"mp3",
|
|
124
|
-
"wav",
|
|
125
|
-
"m4a",
|
|
126
|
-
"aac",
|
|
127
|
-
"ogg",
|
|
128
|
-
"oga",
|
|
129
|
-
"flac",
|
|
130
|
-
"opus",
|
|
131
|
-
// fonts
|
|
132
|
-
"ttf",
|
|
133
|
-
"otf",
|
|
134
|
-
"woff",
|
|
135
|
-
"woff2"
|
|
136
|
-
]);
|
|
137
|
-
function $(t) {
|
|
138
|
-
const e = t.lastIndexOf(".");
|
|
139
|
-
return e < 0 ? "" : t.slice(e + 1).toLowerCase();
|
|
140
|
-
}
|
|
141
|
-
function q(t) {
|
|
142
|
-
return T.has($(t));
|
|
143
|
-
}
|
|
144
|
-
function W(t) {
|
|
145
|
-
return {
|
|
146
|
-
png: "image/png",
|
|
147
|
-
jpg: "image/jpeg",
|
|
148
|
-
jpeg: "image/jpeg",
|
|
149
|
-
gif: "image/gif",
|
|
150
|
-
webp: "image/webp",
|
|
151
|
-
avif: "image/avif",
|
|
152
|
-
bmp: "image/bmp",
|
|
153
|
-
ico: "image/x-icon",
|
|
154
|
-
mp4: "video/mp4",
|
|
155
|
-
webm: "video/webm",
|
|
156
|
-
mov: "video/quicktime",
|
|
157
|
-
mkv: "video/x-matroska",
|
|
158
|
-
avi: "video/x-msvideo",
|
|
159
|
-
m4v: "video/x-m4v",
|
|
160
|
-
mp3: "audio/mpeg",
|
|
161
|
-
wav: "audio/wav",
|
|
162
|
-
m4a: "audio/mp4",
|
|
163
|
-
aac: "audio/aac",
|
|
164
|
-
ogg: "audio/ogg",
|
|
165
|
-
oga: "audio/ogg",
|
|
166
|
-
flac: "audio/flac",
|
|
167
|
-
opus: "audio/opus",
|
|
168
|
-
ttf: "font/ttf",
|
|
169
|
-
otf: "font/otf",
|
|
170
|
-
woff: "font/woff",
|
|
171
|
-
woff2: "font/woff2"
|
|
172
|
-
}[$(t)] ?? "application/octet-stream";
|
|
173
|
-
}
|
|
174
|
-
function p(t) {
|
|
175
|
-
const e = f(t), i = m(e), l = "/" + S(e), c = new N(i), o = {};
|
|
176
|
-
for (const r of c.listFiles()) {
|
|
177
|
-
if (q(r)) continue;
|
|
178
|
-
const n = c.readFile(r);
|
|
179
|
-
n !== null && (o[r] = n);
|
|
180
|
-
}
|
|
181
|
-
if (o[l] === void 0)
|
|
182
|
-
throw new Error(`Entry file not found: ${t}`);
|
|
183
|
-
return { files: o, entry: l };
|
|
184
|
-
}
|
|
185
|
-
function b(t) {
|
|
186
|
-
const e = f(t);
|
|
187
|
-
return async (i) => {
|
|
188
|
-
const l = f(e, i);
|
|
189
|
-
if (l !== e && !l.startsWith(e + "/"))
|
|
190
|
-
throw new Error(`asset path escapes the project root: ${i}`);
|
|
191
|
-
let c;
|
|
192
|
-
try {
|
|
193
|
-
c = E(l);
|
|
194
|
-
} catch {
|
|
195
|
-
throw new Error(
|
|
196
|
-
`local asset not found: ${i} (looked in ${e}). Drop the file there, or reference a network URL instead.`
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
return new Blob([new Uint8Array(c)]);
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
function O(t) {
|
|
203
|
-
return t.length >= 12 && Buffer.from(t.subarray(4, 8)).toString("latin1") === "ftyp" ? "mp4" : t.length >= 4 && t[0] === 26 && t[1] === 69 && t[2] === 223 && t[3] === 163 ? "webm" : null;
|
|
204
|
-
}
|
|
205
|
-
async function Y(t, e = {}) {
|
|
206
|
-
const i = p(t), l = m(f(t)), { renderBundleToFile: c } = await import("@sequio/server/route-b");
|
|
207
|
-
try {
|
|
208
|
-
const o = e.out ?? "out.mp4";
|
|
209
|
-
console.log(`Rendering ${t} (pure Node, WebGPU)${e.scale && e.scale !== 1 ? ` @ ${e.scale}×` : ""} …`);
|
|
210
|
-
let r = -1;
|
|
211
|
-
const n = await c(i, {
|
|
212
|
-
out: o,
|
|
213
|
-
scale: e.scale,
|
|
214
|
-
// Make gsap (and any other CLI-provided lib) resolvable to the composition
|
|
215
|
-
// in the Node render, same as the browser preview does.
|
|
216
|
-
externals: h(),
|
|
217
|
-
// Resolve `loadAsset('./clip.mp4')` against the project directory on disk,
|
|
218
|
-
// so a local image/video renders the same as it previews (contract #3).
|
|
219
|
-
loadAsset: b(l),
|
|
220
|
-
onProgress: (s) => {
|
|
221
|
-
const a = Math.round(s * 100);
|
|
222
|
-
a !== r && a % 10 === 0 && (r = a, process.stdout.write(`\r ${a}%`));
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
if (process.stdout.write("\r \r"), console.log(
|
|
226
|
-
`✅ wrote ${n.out} (${n.frames} frames, ${n.container}/${n.videoCodec}, ${n.bytes} bytes${n.audio ? ", +audio" : ""})`
|
|
227
|
-
), e.verify) {
|
|
228
|
-
const a = (await import("node:fs")).readFileSync(n.out), d = O(a);
|
|
229
|
-
if (!d || a.length < 500)
|
|
230
|
-
throw new Error(`--verify failed: not a valid container (detected=${d}, size=${a.length})`);
|
|
231
|
-
console.log(`✅ verified: valid ${d} container.`);
|
|
232
|
-
}
|
|
233
|
-
return 0;
|
|
234
|
-
} catch (o) {
|
|
235
|
-
return console.error("✖", o instanceof Error ? o.message : String(o)), 1;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
async function K(t, e = {}) {
|
|
239
|
-
const i = p(t), l = m(f(t)), { renderBundleFrameToFile: c } = await import("@sequio/server/route-b");
|
|
240
|
-
try {
|
|
241
|
-
const o = e.out ?? "frame.png", r = e.time ?? 0;
|
|
242
|
-
console.log(
|
|
243
|
-
`Rendering ${t} frame @ t=${r}s (pure Node, WebGPU)${e.scale && e.scale !== 1 ? ` @ ${e.scale}×` : ""} …`
|
|
244
|
-
);
|
|
245
|
-
const n = await c(i, {
|
|
246
|
-
out: o,
|
|
247
|
-
time: r,
|
|
248
|
-
scale: e.scale,
|
|
249
|
-
// Make gsap (and any other CLI-provided lib) resolvable to the composition
|
|
250
|
-
// in the Node render, same as the browser preview does.
|
|
251
|
-
externals: h(),
|
|
252
|
-
// Resolve `loadAsset('./clip.mp4')` against the project directory on disk,
|
|
253
|
-
// so a local image/video renders the same as it previews (contract #3).
|
|
254
|
-
loadAsset: b(l)
|
|
255
|
-
}), s = n.time !== r ? ` (clamped from ${r}s)` : "";
|
|
256
|
-
return console.log(
|
|
257
|
-
`✅ wrote ${n.out} (${n.width}×${n.height}, t=${n.time}s${s}, ${n.bytes} bytes)`
|
|
258
|
-
), 0;
|
|
259
|
-
} catch (o) {
|
|
260
|
-
return console.error("✖", o instanceof Error ? o.message : String(o)), 1;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const k = m(U(import.meta.url)), _ = f(k, "../preview"), y = {};
|
|
264
|
-
for (const [t, e] of [
|
|
265
|
-
["@sequio/engine", "../../engine/src/index.ts"],
|
|
266
|
-
["@sequio/runtime", "../../runtime/src/index.ts"],
|
|
267
|
-
["@sequio/cli/externals", "./externals.ts"]
|
|
268
|
-
]) {
|
|
269
|
-
const i = f(k, e);
|
|
270
|
-
w(i) && (y[t] = i);
|
|
271
|
-
}
|
|
272
|
-
async function Q(t, e = {}) {
|
|
273
|
-
const i = f(t), l = m(i);
|
|
274
|
-
p(i);
|
|
275
|
-
const { createServer: c } = await import("vite"), o = await c({
|
|
276
|
-
configFile: !1,
|
|
277
|
-
root: _,
|
|
278
|
-
logLevel: "warn",
|
|
279
|
-
server: {
|
|
280
|
-
port: e.port ?? 6180,
|
|
281
|
-
host: e.host ? !0 : "localhost",
|
|
282
|
-
strictPort: !0
|
|
283
|
-
},
|
|
284
|
-
resolve: {
|
|
285
|
-
alias: y
|
|
286
|
-
},
|
|
287
|
-
plugins: [
|
|
288
|
-
{
|
|
289
|
-
name: "sequio-preview-bundle",
|
|
290
|
-
configureServer(n) {
|
|
291
|
-
if (n.middlewares.use("/__bundle", (s, a) => {
|
|
292
|
-
try {
|
|
293
|
-
const d = p(i);
|
|
294
|
-
a.setHeader("Content-Type", "application/json"), a.setHeader("Cache-Control", "no-store"), a.end(JSON.stringify(d));
|
|
295
|
-
} catch (d) {
|
|
296
|
-
a.statusCode = 500, a.setHeader("Content-Type", "application/json"), a.end(JSON.stringify({ error: d instanceof Error ? d.message : String(d) }));
|
|
297
|
-
}
|
|
298
|
-
}), n.middlewares.use("/__asset", (s, a) => {
|
|
299
|
-
try {
|
|
300
|
-
const d = (s.url ?? "").split("?")[0], g = R(decodeURIComponent(d)), u = f(l, g);
|
|
301
|
-
if (u !== l && !u.startsWith(l + "/"))
|
|
302
|
-
return a.statusCode = 403, a.end("forbidden");
|
|
303
|
-
if (!w(u) || !P(u).isFile())
|
|
304
|
-
return a.statusCode = 404, a.end(`asset not found: ${g}`);
|
|
305
|
-
a.setHeader("Content-Type", W(g)), a.setHeader("Cache-Control", "no-store"), x(u).pipe(a);
|
|
306
|
-
} catch (d) {
|
|
307
|
-
a.statusCode = 400, a.end(d instanceof Error ? d.message : String(d));
|
|
308
|
-
}
|
|
309
|
-
}), e.watch) {
|
|
310
|
-
n.watcher.add(l);
|
|
311
|
-
const s = (a) => {
|
|
312
|
-
f(a).startsWith(l) && n.ws.send({ type: "full-reload", path: "*" });
|
|
313
|
-
};
|
|
314
|
-
n.watcher.on("change", s), n.watcher.on("add", s), n.watcher.on("unlink", s);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
]
|
|
319
|
-
});
|
|
320
|
-
return await o.listen(), {
|
|
321
|
-
url: o.resolvedUrls?.local[0] ?? `http://localhost:${e.port ?? 6180}/`,
|
|
322
|
-
async close() {
|
|
323
|
-
await o.close();
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
const G = C(import.meta.url), I = G("../package.json"), Z = I.version;
|
|
328
|
-
export {
|
|
329
|
-
v as D,
|
|
330
|
-
z as U,
|
|
331
|
-
K as a,
|
|
332
|
-
Y as b,
|
|
333
|
-
X as p,
|
|
334
|
-
p as r,
|
|
335
|
-
Q as s,
|
|
336
|
-
Z as v
|
|
337
|
-
};
|
|
338
|
-
//# sourceMappingURL=version-B2TKvZen.js.map
|