@vosjs/cli 0.53.4 → 0.53.6
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/{chunk-ZARSNMVY.js → chunk-MJYXP4AA.js} +2 -2
- package/dist/{chunk-PJX3XE7G.js → chunk-WLUWDQQO.js} +16 -2
- package/dist/chunk-WLUWDQQO.js.map +1 -0
- package/dist/{chunk-3OIYR5VL.js → chunk-ZCLGXETO.js} +69 -7
- package/dist/chunk-ZCLGXETO.js.map +1 -0
- package/dist/cli.js +27 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -3
- package/dist/{run-W5UTMIFU.js → run-DYNCGXWO.js} +3 -3
- package/package.json +6 -6
- package/skills/VERSION +1 -1
- package/skills/vos-port/SKILL.md +8 -3
- package/skills/vos-port/references/elements-and-context.md +155 -6
- package/skills/vos-port/references/hyperframes.md +1 -1
- package/dist/chunk-3OIYR5VL.js.map +0 -1
- package/dist/chunk-PJX3XE7G.js.map +0 -1
- /package/dist/{chunk-ZARSNMVY.js.map → chunk-MJYXP4AA.js.map} +0 -0
- /package/dist/{run-W5UTMIFU.js.map → run-DYNCGXWO.js.map} +0 -0
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/output.ts","../src/check.ts","../src/plugin/knobs.ts","../src/programAudio.ts"],"sourcesContent":["import { writeFile } from 'node:fs/promises'\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { basename, dirname, join } from 'node:path'\nimport { createServer } from 'node:http'\nimport { createRequire } from 'node:module'\nimport { fileURLToPath } from 'node:url'\nimport { parseArgs, numFlag, UsageError } from './args'\nimport { helpFlagsFor, unusedFlagsMessage } from './flagUse'\nimport {\n createReporter,\n EXIT_ERROR,\n EXIT_NO_BROWSER,\n EXIT_OK,\n EXIT_USAGE,\n} from './output'\nimport {\n loadVosConfig,\n configDuration,\n directoryKind,\n loadProgramDirectory,\n readSourceText,\n} from './loadConfig'\nimport { launchBrowser, BrowserUnavailableError } from './browser'\nimport { renderVideo, renderStill, reencodeStill, previewPages } from './render'\nimport { runCheck } from './check'\nimport { programAudio } from './programAudio'\nimport {\n applyDataSets,\n parseTimes,\n setFlags,\n stillFormat,\n stillOutFor,\n videoFormat,\n} from './outputs'\nimport { platformOrigin, resolveCredential } from './plugin/platform'\n\nconst BOOLEAN_FLAGS = new Set(['json', 'help', 'version'])\n\nconst HELP_ENGINE = `vos — command line for the vos programmatic video engine (https://vos.so/engine)\n\nEngine verbs (local, no account, no network beyond the render page's CDN deps)\n vos render <config.json|url|take> [out.mp4|out.webm] [--width 1920] [--height 1080] [--fps 30]\n [--duration <s>] [--format webm|mp4] [--set data.<key>=<value>]... [--json]\n the output's name picks the container; a program document's sound is mixed in\n vos still <config.json|url> [out.webp|out.png|out.jpg] [--time 0 | --times 0,1.5,50%]\n [--width] [--height] [--set data.<key>=<value>]... [--json]\n vos info <config.json|url> [--json]\n vos check <config.json|url> [--json]\n migrate → schema → syntax → compile → determinism/dialect lints, all local\n vos preview <config.json|url> [--port 0]\n vos versions [--json]\n`\n\nconst HELP_CONVENTIONS = `\nConventions\n Results go to stdout; logs go to stderr. --json switches stdout to NDJSON\n events ending with {\"event\":\"done\",…}. Exit codes: 0 ok, 1 error, 2 usage,\n 3 no browser available, 4 the recorder met a sign-in instead of the page\n it was asked for (nothing was recorded; it needs a session, not a new\n script). vos <verb> --help prints that verb's flags.\n`\n\n/** An `audio` element in the config: something a render never mixes. */\nfunction hasAudioElements(config: unknown): boolean {\n const elements = (config as { elements?: unknown }).elements\n return (\n Array.isArray(elements) &&\n elements.some(\n (e) =>\n !!e &&\n typeof e === 'object' &&\n (e as { type?: unknown }).type === 'audio',\n )\n )\n}\n\nfunction outName(source: string, ext: string): string {\n const base = basename(source).replace(/\\.[a-z0-9]+$/i, '') || 'vos'\n return `${base}.${ext}`\n}\n\nasync function cmdRender(argv: string[]): Promise<number> {\n // Polymorphic render: a take DIRECTORY (its doc.json is a recording\n // document) renders through the plugin; a program directory (config.json,\n // composed with a program document when one sits beside it) and every\n // other source are engine config renders. A deterministic sniff, never a\n // flag — and the sniff reads the document, because a program directory\n // carries a doc.json too.\n const first = argv.find((a) => !a.startsWith('-'))\n if (first && directoryKind(first) === 'take') {\n return delegate(['render', ...argv])\n }\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos render <config.json|url|take> [out]')\n const r = createReporter(flags.json === true)\n const format = videoFormat(\n flags.format === undefined ? undefined : String(flags.format),\n positionals[1],\n )\n\n const loaded = await loadVosConfig(source)\n for (const w of loaded.warnings) r.log(`note: ${w}`)\n void flags.set\n const config = applyDataSets(\n loaded.config as Record<string, unknown>,\n setFlags(argv),\n )\n const duration = numFlag(flags, 'duration', configDuration(config) ?? 5)\n const width = numFlag(flags, 'width', 1920)\n const height = numFlag(flags, 'height', 1080)\n const fps = numFlag(flags, 'fps', 30)\n const out = positionals[1] ?? outName(source, format)\n\n // A program document's sound rides its studio entry; mix it like a take's.\n const audio = await programAudio(config as Record<string, unknown>, {\n baseDir:\n existsSync(source) && statSync(source).isDirectory()\n ? source\n : dirname(source),\n duration,\n origin: platformOrigin({}),\n key: resolveCredential(),\n log: (line) => r.log(line),\n })\n if (audio) r.log(`mixing ${audio.clips} sound${audio.clips === 1 ? '' : 's'}`)\n if (hasAudioElements(config)) {\n r.log(\n 'note: an audio ELEMENT is not mixed by a render; put the sound on the program document instead (doc.json audio: [{ key, start, in, out, duration, gain }])',\n )\n }\n\n const browser = await launchBrowser()\n try {\n const result = await renderVideo(browser, {\n config,\n width,\n height,\n fps,\n duration,\n format,\n audioProducerCode: audio?.producerCode,\n onPhase: (phase) => {\n r.log(`${phase}…`)\n r.event({ event: 'phase', phase })\n },\n })\n await writeFile(out, result.bytes)\n r.done(\n {\n out,\n bytes: result.bytes.length,\n width,\n height,\n fps,\n duration,\n format,\n audio: audio?.clips ?? 0,\n },\n `Wrote ${out} (${(result.bytes.length / 1024).toFixed(0)} KB, ${width}x${height}@${fps}, ${duration}s)`,\n )\n return EXIT_OK\n } finally {\n await browser.close()\n }\n}\n\nasync function cmdStill(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source)\n throw new UsageError(\n 'vos still <config.json|url> [out.webp|out.png|out.jpg] [--time t | --times a,b,50%]',\n )\n const r = createReporter(flags.json === true)\n\n const loaded = await loadVosConfig(source)\n for (const w of loaded.warnings) r.log(`note: ${w}`)\n void flags.set\n const config = applyDataSets(\n loaded.config as Record<string, unknown>,\n setFlags(argv),\n )\n const width = numFlag(flags, 'width', 1280)\n const height = numFlag(flags, 'height', 720)\n const out = positionals[1] ?? outName(source, 'webp')\n const encoding = stillFormat(out)\n const timesRaw = flags.times === undefined ? undefined : String(flags.times)\n if (timesRaw !== undefined && flags.time !== undefined)\n throw new UsageError('--time and --times: pass one')\n const times =\n timesRaw !== undefined\n ? parseTimes(timesRaw, configDuration(config) ?? 5)\n : [numFlag(flags, 'time', 0)]\n const many = times.length > 1\n\n // One browser for every time: each still is its own capture page.\n const browser = await launchBrowser()\n try {\n const written: { out: string; bytes: number; time: number }[] = []\n for (const time of times) {\n const target = stillOutFor(out, time, many)\n const result = await renderStill(browser, {\n config,\n width,\n height,\n time,\n onPhase: (phase) => {\n r.event({ event: 'phase', phase, time })\n },\n })\n // The engine captures WebP; a .png or .jpg name is re-encoded in the\n // page, so the bytes always match the name.\n const bytes =\n encoding === 'webp'\n ? result.bytes\n : await reencodeStill(browser, result.bytes, encoding)\n await writeFile(target, bytes)\n written.push({ out: target, bytes: bytes.length, time })\n r.log(\n `Wrote ${target} (${(bytes.length / 1024).toFixed(0)} KB @ t=${Number(time.toFixed(2))}s)`,\n )\n }\n const first = written[0]\n r.done(\n many\n ? { stills: written, width, height, format: encoding }\n : { ...first, width, height, format: encoding },\n many\n ? `Wrote ${written.length} stills (${width}x${height})`\n : `Wrote ${first.out} (${(first.bytes / 1024).toFixed(0)} KB, ${width}x${height} @ t=${first.time}s)`,\n )\n return EXIT_OK\n } finally {\n await browser.close()\n }\n}\n\nasync function cmdInfo(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos info <config.json|url>')\n const r = createReporter(flags.json === true)\n\n const { config, warnings } = await loadVosConfig(source)\n const elements = Array.isArray(config.elements) ? config.elements.length : 0\n const data =\n typeof config.data === 'object' && config.data !== null\n ? Object.keys(config.data)\n : []\n const fns = ['setup', 'createContent', 'createTimeline', 'onFrame'].filter(\n (k) => typeof config[k] === 'string',\n )\n const info = {\n version: config.version,\n duration: configDuration(config) ?? null,\n camera:\n (config.camera as Record<string, unknown> | undefined)?.preset ?? null,\n elements,\n dataKeys: data,\n functions: fns,\n warnings,\n }\n if (r.json) r.done(info, '')\n else {\n for (const w of warnings) r.log(`note: ${w}`)\n process.stdout.write(\n `version: v${String(info.version)}\\n` +\n `duration: ${info.duration === null ? '(none)' : `${info.duration}s`}\\n` +\n `camera: ${String(info.camera ?? '(default)')}\\n` +\n `elements: ${elements}\\n` +\n `data keys: ${data.length ? data.join(', ') : '(none)'}\\n` +\n `functions: ${fns.join(', ')}\\n`,\n )\n }\n return EXIT_OK\n}\n\nfunction packageVersion(name: string): string | null {\n const require = createRequire(import.meta.url)\n // Fast path — packages that export ./package.json (e.g. playwright).\n try {\n return (require(`${name}/package.json`) as { version: string }).version\n } catch {\n // Strict `exports` maps (the @vosjs packages) hide package.json — resolve\n // the entry module instead and walk up to the owning package.json.\n }\n let entry: string | null = null\n try {\n entry = fileURLToPath(import.meta.resolve(name))\n } catch {\n try {\n entry = require.resolve(name)\n } catch {\n return null\n }\n }\n let dir = dirname(entry)\n for (let i = 0; i < 6; i++) {\n try {\n const pkg = JSON.parse(\n readFileSync(join(dir, 'package.json'), 'utf8'),\n ) as {\n name?: string\n version?: string\n }\n if (pkg.name === name && pkg.version) return pkg.version\n } catch {\n // keep walking\n }\n const parent = dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n return null\n}\n\nfunction ownVersion(): string {\n try {\n const own = JSON.parse(\n readFileSync(\n fileURLToPath(new URL('../package.json', import.meta.url)),\n 'utf8',\n ),\n ) as { version: string }\n return own.version\n } catch {\n return '(unknown)'\n }\n}\n\nasync function cmdVersions(argv: string[]): Promise<number> {\n const { flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const r = createReporter(flags.json === true)\n const versions: Record<string, string> = {}\n versions['@vosjs/cli'] = ownVersion()\n for (const name of [\n '@vosjs/core',\n '@vosjs/elements',\n '@vosjs/tween',\n '@vosjs/editor',\n '@vosjs/timeline',\n '@vosjs/studio-core',\n '@vosjs/render-core',\n '@vosjs/shared',\n 'mediabunny',\n 'playwright',\n ]) {\n versions[name] = packageVersion(name) ?? '(not found)'\n }\n if (r.json) r.done({ versions }, '')\n else\n for (const [k, v] of Object.entries(versions))\n process.stdout.write(`${k} ${v}\\n`)\n return EXIT_OK\n}\n\nasync function cmdPreview(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos preview <config.json|url> [--port 0]')\n const r = createReporter(false)\n const { config, warnings } = await loadVosConfig(source)\n for (const w of warnings) r.log(`note: ${w}`)\n const { hostHtml, playerHtml } = previewPages(config)\n const server = createServer((req, res) => {\n const path = new URL(req.url ?? '/', 'http://x').pathname\n res.writeHead(200, { 'content-type': 'text/html' })\n res.end(path === '/player' ? playerHtml : hostHtml)\n })\n const port = numFlag(flags, 'port', 0)\n await new Promise<void>((resolve) => server.listen(port, resolve))\n const addr = server.address()\n const url = `http://localhost:${typeof addr === 'object' && addr ? addr.port : port}/`\n process.stdout.write(`${url}\\n`)\n r.log('Serving playback preview — Ctrl-C to stop.')\n await new Promise(() => {}) // keep alive until interrupted\n return EXIT_OK\n}\n\nasync function cmdCheck(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos check <config.json>')\n const r = createReporter(flags.json === true)\n\n // A directory is a take (refused here: its document is `vos validate`'s\n // job) or a program directory (config.json, composed with the program\n // document beside it so the ladder reads the layers' strings too).\n const kind = /^https?:\\/\\//.test(source) ? 'none' : directoryKind(source)\n if (kind === 'take') {\n throw new UsageError(\n `${source} is a take (its doc.json is a recording document). vos validate ${source} lints it; vos render ${source} renders it.`,\n )\n }\n let raw: string\n let parsed: unknown\n try {\n if (kind === 'program') {\n const notes: string[] = []\n parsed = await loadProgramDirectory(source, notes)\n for (const n of notes) r.log(`note: ${n}`)\n raw = ''\n } else {\n raw = await readSourceText(source)\n parsed = JSON.parse(raw)\n }\n } catch (e) {\n if (!(e instanceof SyntaxError)) throw e\n parsed = undefined\n if (!r.json) process.stdout.write(`error [json] ${(e as Error).message}\\n`)\n r.done(\n {\n ok: false,\n errors: 1,\n warnings: 0,\n issues: [\n { level: 'error', source: 'json', message: (e as Error).message },\n ],\n },\n `${source}: 1 error`,\n )\n return EXIT_ERROR\n }\n\n const result = runCheck(parsed)\n if (r.json) {\n r.done(\n {\n ok: result.ok,\n errors: result.errors,\n warnings: result.warnings,\n issues: result.issues,\n },\n '',\n )\n } else {\n for (const i of result.issues) {\n process.stdout.write(`${i.level} [${i.source}] ${i.message}\\n`)\n }\n process.stdout.write(\n result.ok\n ? `${source}: ok (${result.warnings} warning${result.warnings === 1 ? '' : 's'})\\n`\n : `${source}: ${result.errors} error${result.errors === 1 ? '' : 's'}, ${result.warnings} warning${result.warnings === 1 ? '' : 's'}\\n`,\n )\n }\n return result.ok ? EXIT_OK : EXIT_ERROR\n}\n\n// ---------------------------------------------------------------------------\n// Every verb this file does not own is a take-pipeline or vos.so verb from\n// `./plugin` (what used to ship separately as @vosso/vos-plugin). It loads on\n// demand, so the engine verbs never pay for the recorder's imports, and the\n// old names (`@vosso/cli`, `@vosso/voila-cli`, the `vos voila` alias) keep\n// resolving here for scripts that still use them.\n// ---------------------------------------------------------------------------\n\nasync function delegate(argv: string[], viaAlias = false): Promise<number> {\n const { run } = await import('./plugin/run')\n if (viaAlias && argv[0]) {\n process.stderr.write(\n `note: \"vos voila ${argv[0]}\" is now \"vos ${argv[0]}\".\\n`,\n )\n }\n return await run(argv)\n}\n\nasync function printHelp(): Promise<void> {\n process.stdout.write(HELP_ENGINE)\n const { manifest } = await import('./plugin/manifest')\n process.stdout.write('\\nTake pipeline + vos.so verbs\\n')\n for (const v of manifest.verbs) {\n if (v.name === 'render') continue // polymorphic — already listed above\n process.stdout.write(` vos ${v.name.padEnd(8)} ${v.summary}\\n`)\n }\n process.stdout.write(HELP_CONVENTIONS)\n}\n\nconst ENGINE_VERBS = new Set([\n 'render',\n 'still',\n 'info',\n 'versions',\n 'preview',\n 'check',\n])\n\nasync function main(): Promise<number> {\n const [cmd, ...rest] = process.argv.slice(2)\n if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {\n await printHelp()\n return cmd ? EXIT_OK : EXIT_USAGE\n }\n if (cmd === '--version') return cmdVersions(['--json'])\n if (!ENGINE_VERBS.has(cmd) && cmd !== 'voila') return delegate([cmd, ...rest])\n // An engine verb asked for its flags: the engine's usage lines, plus the\n // take pipeline's where the verb is shared (render takes a config OR a take).\n if (rest.includes('--help') || rest.includes('-h')) {\n const engine = HELP_ENGINE.split('\\n').filter((l) =>\n l.startsWith(` vos ${cmd} `),\n )\n const { verbHelp } = await import('./plugin/run')\n const take = verbHelp(cmd)\n process.stdout.write(\n `${engine.join('\\n')}\\n${take.includes('no such verb') ? '' : take}`,\n )\n return EXIT_OK\n }\n switch (cmd) {\n case 'render':\n return cmdRender(rest)\n case 'still':\n return cmdStill(rest)\n case 'info':\n return cmdInfo(rest)\n case 'versions':\n return cmdVersions(rest)\n case 'preview':\n return cmdPreview(rest)\n case 'check':\n return cmdCheck(rest)\n // Hidden alias for existing scripts; not in HELP. Same code path as the\n // promoted verbs, plus a one-line pointer at the new spelling.\n case 'voila':\n return delegate(rest, true)\n default:\n throw new UsageError(`unknown command \"${cmd}\" — run vos help`)\n }\n}\n\nmain()\n .then(async (code) => {\n // A run that succeeded while ignoring a flag it was given did not do\n // what was asked (flagUse.ts): say so, and do not exit 0.\n if (code === EXIT_OK) {\n const verb = process.argv[2] ?? ''\n const { HELP } = await import('./plugin/run')\n const documented = [\n ...helpFlagsFor(HELP_ENGINE, verb),\n ...helpFlagsFor(HELP, verb),\n ]\n const message = unusedFlagsMessage(verb, [...new Set(documented)])\n if (message) {\n process.stderr.write(`usage error: ${message}\\n`)\n process.exit(EXIT_USAGE)\n }\n }\n process.exit(code)\n })\n .catch((e) => {\n if (e instanceof UsageError) {\n process.stderr.write(`usage error: ${e.message}\\n`)\n process.exit(EXIT_USAGE)\n }\n if (e instanceof BrowserUnavailableError) {\n process.stderr.write(`${e.message}\\n`)\n process.exit(EXIT_NO_BROWSER)\n }\n process.stderr.write(\n `error: ${e instanceof Error ? e.message : String(e)}\\n`,\n )\n process.exit(EXIT_ERROR)\n })\n","/**\n * Output conventions (agent-safe by construction):\n * - logs/progress → stderr (human readable)\n * - results → stdout; with --json, NDJSON events (`{\"event\":\"phase\"...}` then\n * `{\"event\":\"done\"...}`) so scripts and agents never parse prose\n * - exit codes: 0 ok · 1 failure · 2 usage · 3 browser unavailable ·\n * 4 the recorder met a sign-in (the take pipeline's wall check)\n */\nexport const EXIT_OK = 0\nexport const EXIT_ERROR = 1\nexport const EXIT_USAGE = 2\nexport const EXIT_NO_BROWSER = 3\n\nexport interface Reporter {\n json: boolean\n /** Human log line (stderr; suppressed in --json mode). */\n log: (msg: string) => void\n /** Machine event (stdout NDJSON in --json mode; no-op otherwise). */\n event: (obj: Record<string, unknown>) => void\n /** Final result: NDJSON `done` event in --json mode, plain line otherwise. */\n done: (obj: Record<string, unknown>, humanLine: string) => void\n}\n\nexport function createReporter(json: boolean): Reporter {\n return {\n json,\n log: (msg) => {\n if (!json) process.stderr.write(`${msg}\\n`)\n },\n event: (obj) => {\n if (json) process.stdout.write(`${JSON.stringify(obj)}\\n`)\n },\n done: (obj, humanLine) => {\n if (json)\n process.stdout.write(`${JSON.stringify({ event: 'done', ...obj })}\\n`)\n else process.stdout.write(`${humanLine}\\n`)\n },\n }\n}\n","/**\n * `vos check` — the local validation pipeline, pure so it can be unit-tested:\n * envelope unwrap → migrate → schema → compile → determinism + dialect lints.\n * Everything runs locally (no network, no browser). The platform runs the\n * same compiler server-side, so a clean check is a push that will compile.\n */\nimport {\n CURRENT_CONFIG_VERSION,\n compileVosConfig,\n migrateConfig,\n vosConfigJsonSchema,\n} from '@vosjs/core'\nimport {\n lintVosConfig,\n lintVosDialect,\n lintVosFonts,\n lintVosPostprocessing,\n} from '@vosjs/core/lint'\nimport { knobWarnings } from './plugin/knobs'\n\nexport interface CheckIssue {\n level: 'error' | 'warn'\n source:\n | 'schema'\n | 'syntax'\n | 'compile'\n | 'determinism'\n | 'dialect'\n | 'shape'\n | 'fonts'\n | 'postprocessing'\n message: string\n}\n\nexport interface CheckResult {\n ok: boolean\n errors: number\n warnings: number\n issues: CheckIssue[]\n /** The migrated config (params/presets untouched) — what a push should send. */\n config: Record<string, unknown> | null\n}\n\n// Top-level keys the schema does not know but the platform preserves on push.\nconst PLATFORM_EXTRA_KEYS = new Set(['params', 'presets'])\n\nexport function runCheck(parsed: unknown): CheckResult {\n const issues: CheckIssue[] = []\n const finish = (config: Record<string, unknown> | null): CheckResult => {\n const errors = issues.filter((i) => i.level === 'error').length\n return {\n ok: errors === 0,\n errors,\n warnings: issues.length - errors,\n issues,\n config,\n }\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n issues.push({\n level: 'error',\n source: 'shape',\n message: 'not a JSON object',\n })\n return finish(null)\n }\n\n // API endpoints wrap the config: { config: {...} } — accept both shapes.\n let obj = parsed as Record<string, unknown>\n if (\n typeof obj.config === 'object' &&\n obj.config !== null &&\n !('createTimeline' in obj)\n ) {\n obj = obj.config as Record<string, unknown>\n }\n\n const version = obj.version\n let migrated: Record<string, unknown>\n try {\n migrated = migrateConfig(obj) as Record<string, unknown>\n } catch (e) {\n issues.push({\n level: 'error',\n source: 'shape',\n message: `migration failed: ${e instanceof Error ? e.message : String(e)}`,\n })\n return finish(null)\n }\n if (version === undefined) {\n // It compiles (the engine reads an absent version as the current one),\n // but a push is refused: a stored config must say which schema era it was\n // written against, and nobody can tell that from the file later.\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `config declares no \"version\". It plays here, but a push is refused without one. Add \"version\": ${CURRENT_CONFIG_VERSION}.`,\n })\n } else if (version !== CURRENT_CONFIG_VERSION) {\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `config is v${String(version)}, migrated to v${CURRENT_CONFIG_VERSION}. Save the migrated form.`,\n })\n }\n\n const schema = vosConfigJsonSchema.safeParse(migrated)\n if (!schema.success) {\n for (const i of schema.error.issues.slice(0, 10)) {\n issues.push({\n level: 'error',\n source: 'schema',\n message: `${i.path.join('.') || '(root)'}: ${i.message}`,\n })\n }\n return finish(null)\n }\n\n // Keys the platform's schema will silently drop on push (params/presets\n // excepted — those are re-attached by contract).\n const known = new Set([\n ...Object.keys(vosConfigJsonSchema.shape),\n ...PLATFORM_EXTRA_KEYS,\n ])\n for (const key of Object.keys(migrated)) {\n if (!known.has(key)) {\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `unknown top-level key \"${key}\" — the platform drops it on push`,\n })\n }\n }\n\n // Knobs and Looks vos.so would drop (the push still saves, without them).\n for (const message of knobWarnings(migrated as Record<string, unknown>)) {\n issues.push({ level: 'warn', source: 'shape', message })\n }\n\n // Sound as an ELEMENT is not a track: no timeline row, no gain or fades,\n // nothing a render mixes, and a `data:` source inflates the config past\n // what a claimable push can carry. The document is where sound goes.\n const elements = Array.isArray(migrated.elements) ? migrated.elements : []\n for (const el of elements) {\n if (!el || typeof el !== 'object') continue\n const e = el as { type?: unknown; id?: unknown; src?: unknown }\n if (e.type !== 'audio') continue\n const inline =\n typeof e.src === 'string' && e.src.startsWith('data:')\n ? ` (embedded, ${Math.round(e.src.length / 1024)} KB of config)`\n : ''\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `audio element \"${String(e.id ?? '?')}\"${inline} is not a track: renders do not mix it and the studio cannot edit it. Put the file beside the config and name it in doc.json (audio: [{ key: \"score.ogg\", start, in, out, duration, gain }]); vos push uploads it`,\n })\n }\n\n // Function strings are pasted into the template as text, so the compiler\n // cannot see a syntax error — parse each one here (construction only,\n // nothing executes).\n let syntaxErrors = 0\n for (const key of [\n 'setup',\n 'createContent',\n 'createTimeline',\n 'onFrame',\n ] as const) {\n const src = migrated[key]\n if (typeof src !== 'string' || !src.length) continue\n try {\n new Function(`\"use strict\"; return (${src}\\n)`)\n } catch (e) {\n syntaxErrors++\n issues.push({\n level: 'error',\n source: 'syntax',\n message: `${key}: ${e instanceof Error ? e.message : String(e)}`,\n })\n }\n }\n if (syntaxErrors) return finish(migrated)\n\n try {\n // Same options as `vos render` — a clean check is a config the CLI's own\n // render path will accept.\n compileVosConfig(migrated as never, { tweenEngine: 'vos' })\n } catch (e) {\n issues.push({\n level: 'error',\n source: 'compile',\n message: e instanceof Error ? e.message : String(e),\n })\n return finish(migrated)\n }\n\n for (const i of lintVosConfig(migrated as never)) {\n issues.push({\n level: i.severity === 'error' ? 'error' : 'warn',\n source: 'determinism',\n message: `${i.fn}:${i.line} [${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosDialect(migrated as never)) {\n issues.push({\n level: i.severity === 'error' ? 'error' : 'warn',\n source: 'dialect',\n message: `${i.fn}:${i.line} [${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosFonts(migrated as never)) {\n issues.push({\n level: 'warn',\n source: 'fonts',\n message: `[${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosPostprocessing(migrated as never)) {\n issues.push({\n level: 'warn',\n source: 'postprocessing',\n message: `[${i.rule}] ${i.message}`,\n })\n }\n\n return finish(migrated)\n}\n","/**\n * What vos.so keeps of a config's knobs (`params`) and Looks (`presets`).\n *\n * A vos.so fact, so it lives in the platform client: the platform keeps at\n * most 12 knobs and 8 Looks, each within its field limits, and drops the\n * rest ONE BY ONE with a warning (it used to drop them all, silently). This\n * mirrors that rule so `vos check` says the same thing before a push does.\n */\nexport const MAX_PARAMS = 12\nexport const MAX_LOOKS = 8\n\nconst KINDS = new Set(['number', 'color', 'select', 'toggle', 'text', 'font'])\n\nconst str = (v: unknown, max: number, min = 0) =>\n typeof v === 'string' && v.length >= min && v.length <= max\n\n/** Why one knob would be dropped, or null when vos.so keeps it. */\nexport function knobProblem(p: unknown): string | null {\n if (!p || typeof p !== 'object') return 'not an object'\n const k = p as Record<string, unknown>\n if (!str(k.key, 40, 1)) return 'key must be 1 to 40 characters'\n if (typeof k.kind !== 'string' || !KINDS.has(k.kind))\n return `kind must be one of ${[...KINDS].join(', ')}`\n if (k.label !== undefined && !str(k.label, 60))\n return 'label: at most 60 characters'\n if (k.hint !== undefined && !str(k.hint, 140))\n return 'hint: at most 140 characters'\n if (k.unit !== undefined && !str(k.unit, 8))\n return 'unit: at most 8 characters'\n if (k.group !== undefined && !str(k.group, 24))\n return 'group: at most 24 characters'\n if (k.options !== undefined) {\n if (!Array.isArray(k.options) || k.options.length > 12)\n return 'options: at most 12'\n if (k.options.some((o) => !str(o, 40, 1)))\n return 'options: each 1 to 40 characters'\n }\n const d = k.default\n if (\n !(\n typeof d === 'number' ||\n typeof d === 'boolean' ||\n (typeof d === 'string' && d.length <= 280)\n )\n )\n return 'default must be a number, a boolean or at most 280 characters'\n return null\n}\n\n/** Why one Look would be dropped, or null when vos.so keeps it. */\nexport function lookProblem(l: unknown): string | null {\n if (!l || typeof l !== 'object') return 'not an object'\n const look = l as Record<string, unknown>\n if (!str(look.name, 24, 1)) return 'name must be 1 to 24 characters'\n if (!look.values || typeof look.values !== 'object') return 'values missing'\n return null\n}\n\n/** Every knob and Look vos.so would drop, in words, in order. */\nexport function knobWarnings(config: Record<string, unknown>): string[] {\n const out: string[] = []\n const sort = (\n raw: unknown,\n noun: 'knob' | 'look',\n field: string,\n max: number,\n problem: (e: unknown) => string | null,\n ) => {\n if (raw === undefined) return\n if (!Array.isArray(raw)) {\n out.push(`${noun === 'knob' ? 'params' : 'presets'} is not a list`)\n return\n }\n let kept = 0\n raw.forEach((entry, i) => {\n const id =\n entry && typeof (entry as Record<string, unknown>)[field] === 'string'\n ? `${noun} \"${String((entry as Record<string, unknown>)[field])}\"`\n : `${noun} #${i + 1}`\n const why = problem(entry)\n if (why) out.push(`${id} would be dropped on vos.so (${why})`)\n else if (kept >= max)\n out.push(\n `${id} would be dropped on vos.so: a vos keeps at most ${max} ${noun}s${noun === 'knob' ? ' (the rest can stay bound data)' : ''}`,\n )\n else kept += 1\n })\n }\n sort(config.params, 'knob', 'key', MAX_PARAMS, knobProblem)\n sort(config.presets, 'look', 'name', MAX_LOOKS, lookProblem)\n return out\n}\n","/**\n * The sound a program document adds, for a LOCAL render of the program.\n *\n * A composed program carries its `doc.audio` clips on the studio stack entry,\n * exactly as a take does, and the capture page mixes them through the same\n * producer a take render uses. The one difference is WHERE the bytes are: a\n * capture page has no origin of its own, so a clip keyed by a file beside the\n * document (`score.ogg`) or by a hosted asset path (`/api/assets/…`) cannot be\n * fetched from inside it. Each is inlined as a `data:` URI here: a local file\n * read from disk, a hosted asset downloaded with the caller's credential.\n * A clip that cannot be reached is left out and said, never a silent track.\n */\nimport { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { audioProducerCode, studioEntryData } from '@vosjs/render-core'\nimport { studioAudioPlan } from '@vosjs/studio-core'\nimport { MEDIA_HEAD_BYTES, resolveMediaType } from './plugin/container'\nimport type { EnvelopePoint, LoweredAudioClip } from '@vosjs/studio-core'\n\nexport interface ProgramAudioOptions {\n /** The directory the document's relative keys are read against. */\n baseDir: string\n /** The output length the mix is cut to, seconds. */\n duration: number\n /** For a clip keyed by a hosted asset: where it lives and who may read it. */\n origin?: string\n key?: string | null\n log: (line: string) => void\n}\n\nexport interface ProgramAudio {\n producerCode: string\n clips: number\n}\n\nfunction dataUri(bytes: Uint8Array, filename: string): string {\n const { type } = resolveMediaType({\n head: bytes.subarray(0, MEDIA_HEAD_BYTES),\n filename,\n })\n return `data:${type};base64,${Buffer.from(bytes).toString('base64')}`\n}\n\nasync function reach(\n clipKey: string,\n opts: ProgramAudioOptions,\n): Promise<string | null> {\n if (/^(data:|https?:)/.test(clipKey)) return clipKey\n if (clipKey.startsWith('/api/')) {\n if (!opts.origin || !opts.key) {\n opts.log(\n `note: sound ${clipKey} is a hosted asset and no credential is set, so the render leaves it out (vos login, or keep the file beside the document)`,\n )\n return null\n }\n const res = await fetch(`${opts.origin}${clipKey}`, {\n headers: { authorization: `Bearer ${opts.key}` },\n })\n if (!res.ok) {\n opts.log(\n `note: sound ${clipKey} could not be read (${res.status}), so the render leaves it out`,\n )\n return null\n }\n return dataUri(new Uint8Array(await res.arrayBuffer()), clipKey)\n }\n const file = join(opts.baseDir, clipKey.replace(/^\\/+/, ''))\n if (!existsSync(file)) {\n opts.log(\n `note: sound ${clipKey} is not beside the document, so the render leaves it out`,\n )\n return null\n }\n return dataUri(new Uint8Array(await readFile(file)), file)\n}\n\n/** The producer for a composed program's sound, or null when it adds none. */\nexport async function programAudio(\n config: Record<string, unknown>,\n opts: ProgramAudioOptions,\n): Promise<ProgramAudio | null> {\n const entry = studioEntryData(config.stack)\n const clips = Array.isArray(entry?.audio)\n ? (entry.audio as LoweredAudioClip[])\n : []\n if (!clips.length) return null\n const reached: LoweredAudioClip[] = []\n for (const clip of clips) {\n const key = await reach(clip.key, opts)\n if (key) reached.push({ ...clip, key })\n }\n if (!reached.length) return null\n const duckEnv = Array.isArray(entry?.duckEnv)\n ? (entry.duckEnv as EnvelopePoint[])\n : []\n const plan = studioAudioPlan(reached, duckEnv, opts.duration)\n return {\n producerCode: audioProducerCode({ plan }),\n clips: reached.length,\n }\n}\n"],"mappings":"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","names":["existsSync","join","existsSync","require","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/output.ts","../src/check.ts","../src/plugin/knobs.ts","../src/programAudio.ts"],"sourcesContent":["import { writeFile } from 'node:fs/promises'\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { basename, dirname, join } from 'node:path'\nimport { createServer } from 'node:http'\nimport { createRequire } from 'node:module'\nimport { fileURLToPath } from 'node:url'\nimport { parseArgs, numFlag, UsageError } from './args'\nimport { helpFlagsFor, unusedFlagsMessage } from './flagUse'\nimport {\n createReporter,\n EXIT_ERROR,\n EXIT_NO_BROWSER,\n EXIT_OK,\n EXIT_USAGE,\n} from './output'\nimport {\n loadVosConfig,\n configDuration,\n directoryKind,\n loadProgramDirectory,\n readSourceText,\n} from './loadConfig'\nimport { launchBrowser, BrowserUnavailableError } from './browser'\nimport {\n inspectStill,\n previewPages,\n reencodeStill,\n renderStill,\n renderVideo,\n stillWarnings,\n} from './render'\nimport { runCheck } from './check'\nimport { programAudio } from './programAudio'\nimport {\n applyDataSets,\n parseTimes,\n setFlags,\n stillFormat,\n stillOutFor,\n videoFormat,\n} from './outputs'\nimport { platformOrigin, resolveCredential } from './plugin/platform'\n\nconst BOOLEAN_FLAGS = new Set(['json', 'help', 'version'])\n\nconst HELP_ENGINE = `vos — command line for the vos programmatic video engine (https://vos.so/engine)\n\nEngine verbs (local, no account, no network beyond the render page's CDN deps)\n vos render <config.json|url|take> [out.mp4|out.webm] [--width 1920] [--height 1080] [--fps 30]\n [--duration <s>] [--format webm|mp4] [--set data.<key>=<value>]... [--json]\n the output's name picks the container; a program document's sound is mixed in\n vos still <config.json|url> [out.webp|out.png|out.jpg] [--time 0 | --times 0,1.5,50%]\n [--width] [--height] [--set data.<key>=<value>]... [--json]\n vos info <config.json|url> [--json]\n vos check <config.json|url> [--json]\n migrate → schema → syntax → compile → determinism/dialect lints, all local\n vos preview <config.json|url> [--port 0]\n vos versions [--json]\n`\n\nconst HELP_CONVENTIONS = `\nConventions\n Results go to stdout; logs go to stderr. --json switches stdout to NDJSON\n events ending with {\"event\":\"done\",…}. Exit codes: 0 ok, 1 error, 2 usage,\n 3 no browser available, 4 the recorder met a sign-in instead of the page\n it was asked for (nothing was recorded; it needs a session, not a new\n script). vos <verb> --help prints that verb's flags.\n`\n\n/** An `audio` element in the config: something a render never mixes. */\nfunction hasAudioElements(config: unknown): boolean {\n const elements = (config as { elements?: unknown }).elements\n return (\n Array.isArray(elements) &&\n elements.some(\n (e) =>\n !!e &&\n typeof e === 'object' &&\n (e as { type?: unknown }).type === 'audio',\n )\n )\n}\n\nfunction outName(source: string, ext: string): string {\n const base = basename(source).replace(/\\.[a-z0-9]+$/i, '') || 'vos'\n return `${base}.${ext}`\n}\n\nasync function cmdRender(argv: string[]): Promise<number> {\n // Polymorphic render: a take DIRECTORY (its doc.json is a recording\n // document) renders through the plugin; a program directory (config.json,\n // composed with a program document when one sits beside it) and every\n // other source are engine config renders. A deterministic sniff, never a\n // flag — and the sniff reads the document, because a program directory\n // carries a doc.json too.\n const first = argv.find((a) => !a.startsWith('-'))\n if (first && directoryKind(first) === 'take') {\n return delegate(['render', ...argv])\n }\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos render <config.json|url|take> [out]')\n const r = createReporter(flags.json === true)\n const format = videoFormat(\n flags.format === undefined ? undefined : String(flags.format),\n positionals[1],\n )\n\n const loaded = await loadVosConfig(source)\n for (const w of loaded.warnings) r.log(`note: ${w}`)\n void flags.set\n const config = applyDataSets(\n loaded.config as Record<string, unknown>,\n setFlags(argv),\n )\n const duration = numFlag(flags, 'duration', configDuration(config) ?? 5)\n const width = numFlag(flags, 'width', 1920)\n const height = numFlag(flags, 'height', 1080)\n const fps = numFlag(flags, 'fps', 30)\n const out = positionals[1] ?? outName(source, format)\n\n // A program document's sound rides its studio entry; mix it like a take's.\n const audio = await programAudio(config as Record<string, unknown>, {\n baseDir:\n existsSync(source) && statSync(source).isDirectory()\n ? source\n : dirname(source),\n duration,\n origin: platformOrigin({}),\n key: resolveCredential(),\n log: (line) => r.log(line),\n })\n if (audio) r.log(`mixing ${audio.clips} sound${audio.clips === 1 ? '' : 's'}`)\n if (hasAudioElements(config)) {\n r.log(\n 'note: an audio ELEMENT is not mixed by a render; put the sound on the program document instead (doc.json audio: [{ key, start, in, out, duration, gain }])',\n )\n }\n\n const browser = await launchBrowser()\n try {\n const result = await renderVideo(browser, {\n config,\n width,\n height,\n fps,\n duration,\n format,\n audioProducerCode: audio?.producerCode,\n onPhase: (phase) => {\n r.log(`${phase}…`)\n r.event({ event: 'phase', phase })\n },\n })\n await writeFile(out, result.bytes)\n const warnings = stillWarnings(\n { transparent: false, flat: false },\n result.pageErrors,\n )\n for (const w of warnings) r.log(`warn: ${w}`)\n r.done(\n {\n out,\n bytes: result.bytes.length,\n width,\n height,\n fps,\n duration,\n format,\n audio: audio?.clips ?? 0,\n ...(warnings.length ? { warnings } : {}),\n },\n `Wrote ${out} (${(result.bytes.length / 1024).toFixed(0)} KB, ${width}x${height}@${fps}, ${duration}s)`,\n )\n return EXIT_OK\n } finally {\n await browser.close()\n }\n}\n\nasync function cmdStill(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source)\n throw new UsageError(\n 'vos still <config.json|url> [out.webp|out.png|out.jpg] [--time t | --times a,b,50%]',\n )\n const r = createReporter(flags.json === true)\n\n const loaded = await loadVosConfig(source)\n for (const w of loaded.warnings) r.log(`note: ${w}`)\n void flags.set\n const config = applyDataSets(\n loaded.config as Record<string, unknown>,\n setFlags(argv),\n )\n const width = numFlag(flags, 'width', 1280)\n const height = numFlag(flags, 'height', 720)\n const out = positionals[1] ?? outName(source, 'webp')\n const encoding = stillFormat(out)\n const timesRaw = flags.times === undefined ? undefined : String(flags.times)\n if (timesRaw !== undefined && flags.time !== undefined)\n throw new UsageError('--time and --times: pass one')\n const times =\n timesRaw !== undefined\n ? parseTimes(timesRaw, configDuration(config) ?? 5)\n : [numFlag(flags, 'time', 0)]\n const many = times.length > 1\n\n // One browser for every time: each still is its own capture page.\n const browser = await launchBrowser()\n try {\n const written: {\n out: string\n bytes: number\n time: number\n warnings?: string[]\n }[] = []\n for (const time of times) {\n const target = stillOutFor(out, time, many)\n const result = await renderStill(browser, {\n config,\n width,\n height,\n time,\n onPhase: (phase) => {\n r.event({ event: 'phase', phase, time })\n },\n })\n // The engine captures WebP; a .png or .jpg name is re-encoded in the\n // page, so the bytes always match the name.\n const bytes =\n encoding === 'webp'\n ? result.bytes\n : await reencodeStill(browser, result.bytes, encoding)\n await writeFile(target, bytes)\n // A still is checked before anyone looks at it: a fully transparent\n // or flat frame is written, and said, never passed off as a picture.\n const warnings = stillWarnings(\n await inspectStill(browser, result.bytes),\n result.pageErrors,\n )\n written.push({\n out: target,\n bytes: bytes.length,\n time,\n ...(warnings.length ? { warnings } : {}),\n })\n r.log(\n `Wrote ${target} (${(bytes.length / 1024).toFixed(0)} KB @ t=${Number(time.toFixed(2))}s)`,\n )\n for (const w of warnings)\n r.log(`warn: t=${Number(time.toFixed(2))}s ${w}`)\n }\n const first = written[0]\n r.done(\n many\n ? { stills: written, width, height, format: encoding }\n : { ...first, width, height, format: encoding },\n many\n ? `Wrote ${written.length} stills (${width}x${height})`\n : `Wrote ${first.out} (${(first.bytes / 1024).toFixed(0)} KB, ${width}x${height} @ t=${first.time}s)`,\n )\n return EXIT_OK\n } finally {\n await browser.close()\n }\n}\n\nasync function cmdInfo(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos info <config.json|url>')\n const r = createReporter(flags.json === true)\n\n const { config, warnings } = await loadVosConfig(source)\n const elements = Array.isArray(config.elements) ? config.elements.length : 0\n const data =\n typeof config.data === 'object' && config.data !== null\n ? Object.keys(config.data)\n : []\n const fns = ['setup', 'createContent', 'createTimeline', 'onFrame'].filter(\n (k) => typeof config[k] === 'string',\n )\n const info = {\n version: config.version,\n duration: configDuration(config) ?? null,\n camera:\n (config.camera as Record<string, unknown> | undefined)?.preset ?? null,\n elements,\n dataKeys: data,\n functions: fns,\n warnings,\n }\n if (r.json) r.done(info, '')\n else {\n for (const w of warnings) r.log(`note: ${w}`)\n process.stdout.write(\n `version: v${String(info.version)}\\n` +\n `duration: ${info.duration === null ? '(none)' : `${info.duration}s`}\\n` +\n `camera: ${String(info.camera ?? '(default)')}\\n` +\n `elements: ${elements}\\n` +\n `data keys: ${data.length ? data.join(', ') : '(none)'}\\n` +\n `functions: ${fns.join(', ')}\\n`,\n )\n }\n return EXIT_OK\n}\n\nfunction packageVersion(name: string): string | null {\n const require = createRequire(import.meta.url)\n // Fast path — packages that export ./package.json (e.g. playwright).\n try {\n return (require(`${name}/package.json`) as { version: string }).version\n } catch {\n // Strict `exports` maps (the @vosjs packages) hide package.json — resolve\n // the entry module instead and walk up to the owning package.json.\n }\n let entry: string | null = null\n try {\n entry = fileURLToPath(import.meta.resolve(name))\n } catch {\n try {\n entry = require.resolve(name)\n } catch {\n return null\n }\n }\n let dir = dirname(entry)\n for (let i = 0; i < 6; i++) {\n try {\n const pkg = JSON.parse(\n readFileSync(join(dir, 'package.json'), 'utf8'),\n ) as {\n name?: string\n version?: string\n }\n if (pkg.name === name && pkg.version) return pkg.version\n } catch {\n // keep walking\n }\n const parent = dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n return null\n}\n\nfunction ownVersion(): string {\n try {\n const own = JSON.parse(\n readFileSync(\n fileURLToPath(new URL('../package.json', import.meta.url)),\n 'utf8',\n ),\n ) as { version: string }\n return own.version\n } catch {\n return '(unknown)'\n }\n}\n\nasync function cmdVersions(argv: string[]): Promise<number> {\n const { flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const r = createReporter(flags.json === true)\n const versions: Record<string, string> = {}\n versions['@vosjs/cli'] = ownVersion()\n for (const name of [\n '@vosjs/core',\n '@vosjs/elements',\n '@vosjs/tween',\n '@vosjs/editor',\n '@vosjs/timeline',\n '@vosjs/studio-core',\n '@vosjs/render-core',\n '@vosjs/shared',\n 'mediabunny',\n 'playwright',\n ]) {\n versions[name] = packageVersion(name) ?? '(not found)'\n }\n if (r.json) r.done({ versions }, '')\n else\n for (const [k, v] of Object.entries(versions))\n process.stdout.write(`${k} ${v}\\n`)\n return EXIT_OK\n}\n\nasync function cmdPreview(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos preview <config.json|url> [--port 0]')\n const r = createReporter(false)\n const { config, warnings } = await loadVosConfig(source)\n for (const w of warnings) r.log(`note: ${w}`)\n const { hostHtml, playerHtml } = previewPages(config)\n const server = createServer((req, res) => {\n const path = new URL(req.url ?? '/', 'http://x').pathname\n res.writeHead(200, { 'content-type': 'text/html' })\n res.end(path === '/player' ? playerHtml : hostHtml)\n })\n const port = numFlag(flags, 'port', 0)\n await new Promise<void>((resolve) => server.listen(port, resolve))\n const addr = server.address()\n const url = `http://localhost:${typeof addr === 'object' && addr ? addr.port : port}/`\n process.stdout.write(`${url}\\n`)\n r.log('Serving playback preview — Ctrl-C to stop.')\n await new Promise(() => {}) // keep alive until interrupted\n return EXIT_OK\n}\n\nasync function cmdCheck(argv: string[]): Promise<number> {\n const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS)\n const source = positionals[0]\n if (!source) throw new UsageError('vos check <config.json>')\n const r = createReporter(flags.json === true)\n\n // A directory is a take (refused here: its document is `vos validate`'s\n // job) or a program directory (config.json, composed with the program\n // document beside it so the ladder reads the layers' strings too).\n const kind = /^https?:\\/\\//.test(source) ? 'none' : directoryKind(source)\n if (kind === 'take') {\n throw new UsageError(\n `${source} is a take (its doc.json is a recording document). vos validate ${source} lints it; vos render ${source} renders it.`,\n )\n }\n let raw: string\n let parsed: unknown\n try {\n if (kind === 'program') {\n const notes: string[] = []\n parsed = await loadProgramDirectory(source, notes)\n for (const n of notes) r.log(`note: ${n}`)\n raw = ''\n } else {\n raw = await readSourceText(source)\n parsed = JSON.parse(raw)\n }\n } catch (e) {\n if (!(e instanceof SyntaxError)) throw e\n parsed = undefined\n if (!r.json) process.stdout.write(`error [json] ${(e as Error).message}\\n`)\n r.done(\n {\n ok: false,\n errors: 1,\n warnings: 0,\n issues: [\n { level: 'error', source: 'json', message: (e as Error).message },\n ],\n },\n `${source}: 1 error`,\n )\n return EXIT_ERROR\n }\n\n const result = runCheck(parsed)\n if (r.json) {\n r.done(\n {\n ok: result.ok,\n errors: result.errors,\n warnings: result.warnings,\n issues: result.issues,\n },\n '',\n )\n } else {\n for (const i of result.issues) {\n process.stdout.write(`${i.level} [${i.source}] ${i.message}\\n`)\n }\n process.stdout.write(\n result.ok\n ? `${source}: ok (${result.warnings} warning${result.warnings === 1 ? '' : 's'})\\n`\n : `${source}: ${result.errors} error${result.errors === 1 ? '' : 's'}, ${result.warnings} warning${result.warnings === 1 ? '' : 's'}\\n`,\n )\n }\n return result.ok ? EXIT_OK : EXIT_ERROR\n}\n\n// ---------------------------------------------------------------------------\n// Every verb this file does not own is a take-pipeline or vos.so verb from\n// `./plugin` (what used to ship separately as @vosso/vos-plugin). It loads on\n// demand, so the engine verbs never pay for the recorder's imports, and the\n// old names (`@vosso/cli`, `@vosso/voila-cli`, the `vos voila` alias) keep\n// resolving here for scripts that still use them.\n// ---------------------------------------------------------------------------\n\nasync function delegate(argv: string[], viaAlias = false): Promise<number> {\n const { run } = await import('./plugin/run')\n if (viaAlias && argv[0]) {\n process.stderr.write(\n `note: \"vos voila ${argv[0]}\" is now \"vos ${argv[0]}\".\\n`,\n )\n }\n return await run(argv)\n}\n\nasync function printHelp(): Promise<void> {\n process.stdout.write(HELP_ENGINE)\n const { manifest } = await import('./plugin/manifest')\n process.stdout.write('\\nTake pipeline + vos.so verbs\\n')\n for (const v of manifest.verbs) {\n if (v.name === 'render') continue // polymorphic — already listed above\n process.stdout.write(` vos ${v.name.padEnd(8)} ${v.summary}\\n`)\n }\n process.stdout.write(HELP_CONVENTIONS)\n}\n\nconst ENGINE_VERBS = new Set([\n 'render',\n 'still',\n 'info',\n 'versions',\n 'preview',\n 'check',\n])\n\nasync function main(): Promise<number> {\n const [cmd, ...rest] = process.argv.slice(2)\n if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {\n await printHelp()\n return cmd ? EXIT_OK : EXIT_USAGE\n }\n if (cmd === '--version') return cmdVersions(['--json'])\n if (!ENGINE_VERBS.has(cmd) && cmd !== 'voila') return delegate([cmd, ...rest])\n // An engine verb asked for its flags: the engine's usage lines, plus the\n // take pipeline's where the verb is shared (render takes a config OR a take).\n if (rest.includes('--help') || rest.includes('-h')) {\n const engine = HELP_ENGINE.split('\\n').filter((l) =>\n l.startsWith(` vos ${cmd} `),\n )\n const { verbHelp } = await import('./plugin/run')\n const take = verbHelp(cmd)\n process.stdout.write(\n `${engine.join('\\n')}\\n${take.includes('no such verb') ? '' : take}`,\n )\n return EXIT_OK\n }\n switch (cmd) {\n case 'render':\n return cmdRender(rest)\n case 'still':\n return cmdStill(rest)\n case 'info':\n return cmdInfo(rest)\n case 'versions':\n return cmdVersions(rest)\n case 'preview':\n return cmdPreview(rest)\n case 'check':\n return cmdCheck(rest)\n // Hidden alias for existing scripts; not in HELP. Same code path as the\n // promoted verbs, plus a one-line pointer at the new spelling.\n case 'voila':\n return delegate(rest, true)\n default:\n throw new UsageError(`unknown command \"${cmd}\" — run vos help`)\n }\n}\n\nmain()\n .then(async (code) => {\n // A run that succeeded while ignoring a flag it was given did not do\n // what was asked (flagUse.ts): say so, and do not exit 0.\n if (code === EXIT_OK) {\n const verb = process.argv[2] ?? ''\n const { HELP } = await import('./plugin/run')\n const documented = [\n ...helpFlagsFor(HELP_ENGINE, verb),\n ...helpFlagsFor(HELP, verb),\n ]\n const message = unusedFlagsMessage(verb, [...new Set(documented)])\n if (message) {\n process.stderr.write(`usage error: ${message}\\n`)\n process.exit(EXIT_USAGE)\n }\n }\n process.exit(code)\n })\n .catch((e) => {\n if (e instanceof UsageError) {\n process.stderr.write(`usage error: ${e.message}\\n`)\n process.exit(EXIT_USAGE)\n }\n if (e instanceof BrowserUnavailableError) {\n process.stderr.write(`${e.message}\\n`)\n process.exit(EXIT_NO_BROWSER)\n }\n process.stderr.write(\n `error: ${e instanceof Error ? e.message : String(e)}\\n`,\n )\n process.exit(EXIT_ERROR)\n })\n","/**\n * Output conventions (agent-safe by construction):\n * - logs/progress → stderr (human readable)\n * - results → stdout; with --json, NDJSON events (`{\"event\":\"phase\"...}` then\n * `{\"event\":\"done\"...}`) so scripts and agents never parse prose\n * - exit codes: 0 ok · 1 failure · 2 usage · 3 browser unavailable ·\n * 4 the recorder met a sign-in (the take pipeline's wall check)\n */\nexport const EXIT_OK = 0\nexport const EXIT_ERROR = 1\nexport const EXIT_USAGE = 2\nexport const EXIT_NO_BROWSER = 3\n\nexport interface Reporter {\n json: boolean\n /** Human log line (stderr; suppressed in --json mode). */\n log: (msg: string) => void\n /** Machine event (stdout NDJSON in --json mode; no-op otherwise). */\n event: (obj: Record<string, unknown>) => void\n /** Final result: NDJSON `done` event in --json mode, plain line otherwise. */\n done: (obj: Record<string, unknown>, humanLine: string) => void\n}\n\nexport function createReporter(json: boolean): Reporter {\n return {\n json,\n log: (msg) => {\n if (!json) process.stderr.write(`${msg}\\n`)\n },\n event: (obj) => {\n if (json) process.stdout.write(`${JSON.stringify(obj)}\\n`)\n },\n done: (obj, humanLine) => {\n if (json)\n process.stdout.write(`${JSON.stringify({ event: 'done', ...obj })}\\n`)\n else process.stdout.write(`${humanLine}\\n`)\n },\n }\n}\n","/**\n * `vos check` — the local validation pipeline, pure so it can be unit-tested:\n * envelope unwrap → migrate → schema → compile → determinism + dialect lints.\n * Everything runs locally (no network, no browser). The platform runs the\n * same compiler server-side, so a clean check is a push that will compile.\n */\nimport {\n CURRENT_CONFIG_VERSION,\n compileVosConfig,\n migrateConfig,\n vosConfigJsonSchema,\n} from '@vosjs/core'\nimport {\n lintVosConfig,\n lintVosDialect,\n lintVosFonts,\n lintVosPostprocessing,\n} from '@vosjs/core/lint'\nimport { knobWarnings } from './plugin/knobs'\n\nexport interface CheckIssue {\n level: 'error' | 'warn'\n source:\n | 'schema'\n | 'syntax'\n | 'compile'\n | 'determinism'\n | 'dialect'\n | 'shape'\n | 'fonts'\n | 'postprocessing'\n message: string\n}\n\nexport interface CheckResult {\n ok: boolean\n errors: number\n warnings: number\n issues: CheckIssue[]\n /** The migrated config (params/presets untouched) — what a push should send. */\n config: Record<string, unknown> | null\n}\n\n// Top-level keys the schema does not know but the platform preserves on push.\nconst PLATFORM_EXTRA_KEYS = new Set(['params', 'presets'])\n\nexport function runCheck(parsed: unknown): CheckResult {\n const issues: CheckIssue[] = []\n const finish = (config: Record<string, unknown> | null): CheckResult => {\n const errors = issues.filter((i) => i.level === 'error').length\n return {\n ok: errors === 0,\n errors,\n warnings: issues.length - errors,\n issues,\n config,\n }\n }\n\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n issues.push({\n level: 'error',\n source: 'shape',\n message: 'not a JSON object',\n })\n return finish(null)\n }\n\n // API endpoints wrap the config: { config: {...} } — accept both shapes.\n let obj = parsed as Record<string, unknown>\n if (\n typeof obj.config === 'object' &&\n obj.config !== null &&\n !('createTimeline' in obj)\n ) {\n obj = obj.config as Record<string, unknown>\n }\n\n const version = obj.version\n let migrated: Record<string, unknown>\n try {\n migrated = migrateConfig(obj) as Record<string, unknown>\n } catch (e) {\n issues.push({\n level: 'error',\n source: 'shape',\n message: `migration failed: ${e instanceof Error ? e.message : String(e)}`,\n })\n return finish(null)\n }\n if (version === undefined) {\n // It compiles (the engine reads an absent version as the current one),\n // but a push is refused: a stored config must say which schema era it was\n // written against, and nobody can tell that from the file later.\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `config declares no \"version\". It plays here, but a push is refused without one. Add \"version\": ${CURRENT_CONFIG_VERSION}.`,\n })\n } else if (version !== CURRENT_CONFIG_VERSION) {\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `config is v${String(version)}, migrated to v${CURRENT_CONFIG_VERSION}. Save the migrated form.`,\n })\n }\n\n const schema = vosConfigJsonSchema.safeParse(migrated)\n if (!schema.success) {\n for (const i of schema.error.issues.slice(0, 10)) {\n issues.push({\n level: 'error',\n source: 'schema',\n message: `${i.path.join('.') || '(root)'}: ${i.message}`,\n })\n }\n return finish(null)\n }\n\n // Keys the platform's schema will silently drop on push (params/presets\n // excepted — those are re-attached by contract).\n const known = new Set([\n ...Object.keys(vosConfigJsonSchema.shape),\n ...PLATFORM_EXTRA_KEYS,\n ])\n for (const key of Object.keys(migrated)) {\n if (!known.has(key)) {\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `unknown top-level key \"${key}\" — the platform drops it on push`,\n })\n }\n }\n\n // Knobs and Looks vos.so would drop (the push still saves, without them).\n for (const message of knobWarnings(migrated as Record<string, unknown>)) {\n issues.push({ level: 'warn', source: 'shape', message })\n }\n\n // Sound as an ELEMENT is not a track: no timeline row, no gain or fades,\n // nothing a render mixes, and a `data:` source inflates the config past\n // what a claimable push can carry. The document is where sound goes.\n const elements = Array.isArray(migrated.elements) ? migrated.elements : []\n for (const el of elements) {\n if (!el || typeof el !== 'object') continue\n const e = el as { type?: unknown; id?: unknown; src?: unknown }\n if (e.type !== 'audio') continue\n const inline =\n typeof e.src === 'string' && e.src.startsWith('data:')\n ? ` (embedded, ${Math.round(e.src.length / 1024)} KB of config)`\n : ''\n issues.push({\n level: 'warn',\n source: 'shape',\n message: `audio element \"${String(e.id ?? '?')}\"${inline} is not a track: renders do not mix it and the studio cannot edit it. Put the file beside the config and name it in doc.json (audio: [{ key: \"score.ogg\", start, in, out, duration, gain }]); vos push uploads it`,\n })\n }\n\n // Function strings are pasted into the template as text, so the compiler\n // cannot see a syntax error — parse each one here (construction only,\n // nothing executes).\n let syntaxErrors = 0\n for (const key of [\n 'setup',\n 'createContent',\n 'createTimeline',\n 'onFrame',\n ] as const) {\n const src = migrated[key]\n if (typeof src !== 'string' || !src.length) continue\n try {\n new Function(`\"use strict\"; return (${src}\\n)`)\n } catch (e) {\n syntaxErrors++\n issues.push({\n level: 'error',\n source: 'syntax',\n message: `${key}: ${e instanceof Error ? e.message : String(e)}`,\n })\n }\n }\n if (syntaxErrors) return finish(migrated)\n\n try {\n // Same options as `vos render` — a clean check is a config the CLI's own\n // render path will accept.\n compileVosConfig(migrated as never, { tweenEngine: 'vos' })\n } catch (e) {\n issues.push({\n level: 'error',\n source: 'compile',\n message: e instanceof Error ? e.message : String(e),\n })\n return finish(migrated)\n }\n\n for (const i of lintVosConfig(migrated as never)) {\n issues.push({\n level: i.severity === 'error' ? 'error' : 'warn',\n source: 'determinism',\n message: `${i.fn}:${i.line} [${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosDialect(migrated as never)) {\n issues.push({\n level: i.severity === 'error' ? 'error' : 'warn',\n source: 'dialect',\n message: `${i.fn}:${i.line} [${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosFonts(migrated as never)) {\n issues.push({\n level: 'warn',\n source: 'fonts',\n message: `[${i.rule}] ${i.message}`,\n })\n }\n for (const i of lintVosPostprocessing(migrated as never)) {\n issues.push({\n level: 'warn',\n source: 'postprocessing',\n message: `[${i.rule}] ${i.message}`,\n })\n }\n\n return finish(migrated)\n}\n","/**\n * What vos.so keeps of a config's knobs (`params`) and Looks (`presets`).\n *\n * A vos.so fact, so it lives in the platform client: the platform keeps at\n * most 12 knobs and 8 Looks, each within its field limits, and drops the\n * rest ONE BY ONE with a warning (it used to drop them all, silently). This\n * mirrors that rule so `vos check` says the same thing before a push does.\n */\nexport const MAX_PARAMS = 12\nexport const MAX_LOOKS = 8\n\nconst KINDS = new Set(['number', 'color', 'select', 'toggle', 'text', 'font'])\n\nconst str = (v: unknown, max: number, min = 0) =>\n typeof v === 'string' && v.length >= min && v.length <= max\n\n/** Why one knob would be dropped, or null when vos.so keeps it. */\nexport function knobProblem(p: unknown): string | null {\n if (!p || typeof p !== 'object') return 'not an object'\n const k = p as Record<string, unknown>\n if (!str(k.key, 40, 1)) return 'key must be 1 to 40 characters'\n if (typeof k.kind !== 'string' || !KINDS.has(k.kind))\n return `kind must be one of ${[...KINDS].join(', ')}`\n if (k.label !== undefined && !str(k.label, 60))\n return 'label: at most 60 characters'\n if (k.hint !== undefined && !str(k.hint, 140))\n return 'hint: at most 140 characters'\n if (k.unit !== undefined && !str(k.unit, 8))\n return 'unit: at most 8 characters'\n if (k.group !== undefined && !str(k.group, 24))\n return 'group: at most 24 characters'\n if (k.options !== undefined) {\n if (!Array.isArray(k.options) || k.options.length > 12)\n return 'options: at most 12'\n if (k.options.some((o) => !str(o, 40, 1)))\n return 'options: each 1 to 40 characters'\n }\n const d = k.default\n if (\n !(\n typeof d === 'number' ||\n typeof d === 'boolean' ||\n (typeof d === 'string' && d.length <= 280)\n )\n )\n return 'default must be a number, a boolean or at most 280 characters'\n return null\n}\n\n/** Why one Look would be dropped, or null when vos.so keeps it. */\nexport function lookProblem(l: unknown): string | null {\n if (!l || typeof l !== 'object') return 'not an object'\n const look = l as Record<string, unknown>\n if (!str(look.name, 24, 1)) return 'name must be 1 to 24 characters'\n if (!look.values || typeof look.values !== 'object') return 'values missing'\n return null\n}\n\n/** Every knob and Look vos.so would drop, in words, in order. */\nexport function knobWarnings(config: Record<string, unknown>): string[] {\n const out: string[] = []\n const sort = (\n raw: unknown,\n noun: 'knob' | 'look',\n field: string,\n max: number,\n problem: (e: unknown) => string | null,\n ) => {\n if (raw === undefined) return\n if (!Array.isArray(raw)) {\n out.push(`${noun === 'knob' ? 'params' : 'presets'} is not a list`)\n return\n }\n let kept = 0\n raw.forEach((entry, i) => {\n const id =\n entry && typeof (entry as Record<string, unknown>)[field] === 'string'\n ? `${noun} \"${String((entry as Record<string, unknown>)[field])}\"`\n : `${noun} #${i + 1}`\n const why = problem(entry)\n if (why) out.push(`${id} would be dropped on vos.so (${why})`)\n else if (kept >= max)\n out.push(\n `${id} would be dropped on vos.so: a vos keeps at most ${max} ${noun}s${noun === 'knob' ? ' (the rest can stay bound data)' : ''}`,\n )\n else kept += 1\n })\n }\n sort(config.params, 'knob', 'key', MAX_PARAMS, knobProblem)\n sort(config.presets, 'look', 'name', MAX_LOOKS, lookProblem)\n return out\n}\n","/**\n * The sound a program document adds, for a LOCAL render of the program.\n *\n * A composed program carries its `doc.audio` clips on the studio stack entry,\n * exactly as a take does, and the capture page mixes them through the same\n * producer a take render uses. The one difference is WHERE the bytes are: a\n * capture page has no origin of its own, so a clip keyed by a file beside the\n * document (`score.ogg`) or by a hosted asset path (`/api/assets/…`) cannot be\n * fetched from inside it. Each is inlined as a `data:` URI here: a local file\n * read from disk, a hosted asset downloaded with the caller's credential.\n * A clip that cannot be reached is left out and said, never a silent track.\n */\nimport { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { audioProducerCode, studioEntryData } from '@vosjs/render-core'\nimport { studioAudioPlan } from '@vosjs/studio-core'\nimport { MEDIA_HEAD_BYTES, resolveMediaType } from './plugin/container'\nimport type { EnvelopePoint, LoweredAudioClip } from '@vosjs/studio-core'\n\nexport interface ProgramAudioOptions {\n /** The directory the document's relative keys are read against. */\n baseDir: string\n /** The output length the mix is cut to, seconds. */\n duration: number\n /** For a clip keyed by a hosted asset: where it lives and who may read it. */\n origin?: string\n key?: string | null\n log: (line: string) => void\n}\n\nexport interface ProgramAudio {\n producerCode: string\n clips: number\n}\n\nfunction dataUri(bytes: Uint8Array, filename: string): string {\n const { type } = resolveMediaType({\n head: bytes.subarray(0, MEDIA_HEAD_BYTES),\n filename,\n })\n return `data:${type};base64,${Buffer.from(bytes).toString('base64')}`\n}\n\nasync function reach(\n clipKey: string,\n opts: ProgramAudioOptions,\n): Promise<string | null> {\n if (/^(data:|https?:)/.test(clipKey)) return clipKey\n if (clipKey.startsWith('/api/')) {\n if (!opts.origin || !opts.key) {\n opts.log(\n `note: sound ${clipKey} is a hosted asset and no credential is set, so the render leaves it out (vos login, or keep the file beside the document)`,\n )\n return null\n }\n const res = await fetch(`${opts.origin}${clipKey}`, {\n headers: { authorization: `Bearer ${opts.key}` },\n })\n if (!res.ok) {\n opts.log(\n `note: sound ${clipKey} could not be read (${res.status}), so the render leaves it out`,\n )\n return null\n }\n return dataUri(new Uint8Array(await res.arrayBuffer()), clipKey)\n }\n const file = join(opts.baseDir, clipKey.replace(/^\\/+/, ''))\n if (!existsSync(file)) {\n opts.log(\n `note: sound ${clipKey} is not beside the document, so the render leaves it out`,\n )\n return null\n }\n return dataUri(new Uint8Array(await readFile(file)), file)\n}\n\n/** The producer for a composed program's sound, or null when it adds none. */\nexport async function programAudio(\n config: Record<string, unknown>,\n opts: ProgramAudioOptions,\n): Promise<ProgramAudio | null> {\n const entry = studioEntryData(config.stack)\n const clips = Array.isArray(entry?.audio)\n ? (entry.audio as LoweredAudioClip[])\n : []\n if (!clips.length) return null\n const reached: LoweredAudioClip[] = []\n for (const clip of clips) {\n const key = await reach(clip.key, opts)\n if (key) reached.push({ ...clip, key })\n }\n if (!reached.length) return null\n const duckEnv = Array.isArray(entry?.duckEnv)\n ? (entry.duckEnv as EnvelopePoint[])\n : []\n const plan = studioAudioPlan(reached, duckEnv, opts.duration)\n return {\n producerCode: audioProducerCode({ plan }),\n clips: reached.length,\n }\n}\n"],"mappings":"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","names":["existsSync","join","existsSync","require","join"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,9 @@ interface RenderStillOptions extends RenderCommonOptions {
|
|
|
27
27
|
interface RenderResult {
|
|
28
28
|
bytes: Uint8Array;
|
|
29
29
|
mimeType: string;
|
|
30
|
+
/** Uncaught exceptions the page threw while it rendered (it may still have
|
|
31
|
+
* written a frame, so a caller says them instead of hiding them). */
|
|
32
|
+
pageErrors: string[];
|
|
30
33
|
}
|
|
31
34
|
/** Render a vos config to a video (WebM/MP4) in a headless browser. */
|
|
32
35
|
declare function renderVideo(browser: Browser, opts: RenderVideoOptions): Promise<RenderResult>;
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
previewPages,
|
|
4
4
|
renderStill,
|
|
5
5
|
renderVideo
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZCLGXETO.js";
|
|
7
7
|
import {
|
|
8
8
|
RECORDING_NAME,
|
|
9
9
|
convertAgentBrowser,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
takePaths,
|
|
24
24
|
validateActions,
|
|
25
25
|
waitForPageDone
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-MJYXP4AA.js";
|
|
27
27
|
import {
|
|
28
28
|
BrowserUnavailableError,
|
|
29
29
|
apiError,
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
readSyncState,
|
|
37
37
|
resolveCredential,
|
|
38
38
|
writeSyncState
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-WLUWDQQO.js";
|
|
40
40
|
import "./chunk-6XPFWQKP.js";
|
|
41
41
|
import {
|
|
42
42
|
manifest
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
takeBackdrop,
|
|
7
7
|
takeBrowserArgs,
|
|
8
8
|
verbHelp
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-MJYXP4AA.js";
|
|
10
|
+
import "./chunk-WLUWDQQO.js";
|
|
11
11
|
import "./chunk-6XPFWQKP.js";
|
|
12
12
|
export {
|
|
13
13
|
HELP,
|
|
@@ -17,4 +17,4 @@ export {
|
|
|
17
17
|
takeBrowserArgs,
|
|
18
18
|
verbHelp
|
|
19
19
|
};
|
|
20
|
-
//# sourceMappingURL=run-
|
|
20
|
+
//# sourceMappingURL=run-DYNCGXWO.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vosjs/cli",
|
|
3
|
-
"version": "0.53.
|
|
3
|
+
"version": "0.53.6",
|
|
4
4
|
"description": "The vos CLI: record the real product from a scripted browser flow, auto-zoom from the cursor track, cut as data in doc.json, render deterministic video and stills, deliver a release's media per destination spec, and sync with vos.so. One binary, every verb, MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,14 +49,14 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"mediabunny": "^1.55.7",
|
|
51
51
|
"playwright": "^1.49.0",
|
|
52
|
-
"@vosjs/core": "^0.25.3",
|
|
53
52
|
"@vosjs/editor": "^1.4.0",
|
|
54
53
|
"@vosjs/elements": "^0.9.0",
|
|
54
|
+
"@vosjs/core": "^0.25.3",
|
|
55
55
|
"@vosjs/render-core": "^0.3.9",
|
|
56
|
-
"@vosjs/
|
|
57
|
-
"@vosjs/
|
|
58
|
-
"@vosjs/
|
|
59
|
-
"@vosjs/
|
|
56
|
+
"@vosjs/studio-core": "^0.33.2",
|
|
57
|
+
"@vosjs/shared": "^0.6.0",
|
|
58
|
+
"@vosjs/tween": "^0.8.2",
|
|
59
|
+
"@vosjs/timeline": "^0.4.1"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/node": "^22",
|
package/skills/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.12.0
|
package/skills/vos-port/SKILL.md
CHANGED
|
@@ -43,10 +43,15 @@ not in `node_modules`.
|
|
|
43
43
|
`font.family` and `font.color` take `{ "$data": "<key>" }` (they
|
|
44
44
|
re-raster live on a data edit). Logos and shapes are `svg` or `image`
|
|
45
45
|
elements, footage a `video` element. Elements are what the studio selects
|
|
46
|
-
and moves.
|
|
46
|
+
and moves. **Every visible word is a bound element**, whatever animates
|
|
47
|
+
it: a word whose letters rise one by one is ONE `split` element bound to
|
|
48
|
+
its key, never a pool of per-letter elements filled from `onFrame` (a
|
|
49
|
+
person can retype the first in the studio and not the second).
|
|
47
50
|
4. **Motion is the timeline.** `createTimeline` holds the tweens, on
|
|
48
51
|
`ctx.elements.get(id).props` (whole) or `.segments` (`split: { type:
|
|
49
|
-
'chars' | 'words' | 'lines' }` units), in the GSAP dialect.
|
|
52
|
+
'chars' | 'words' | 'lines' }` units), in the GSAP dialect. Read
|
|
53
|
+
`el.segments` inside `createTimeline` and nowhere else: a data edit to a
|
|
54
|
+
bound split word rebuilds its units and the timeline over them. Keep curves
|
|
50
55
|
exact: Remotion's `Easing.bezier(a, b, c, d)` is `ease: 'css-bezier(a, b,
|
|
51
56
|
c, d)'`; GSAP eases keep their names.
|
|
52
57
|
5. **Scenes are labels.** One `tl.addLabel('<scene>', t)` per scene or
|
|
@@ -185,7 +190,7 @@ stated approximation:
|
|
|
185
190
|
|
|
186
191
|
| Source | In vos today |
|
|
187
192
|
| --- | --- |
|
|
188
|
-
| `overflow: hidden` masks, `clip-path` reveals | no element masks: approximate with opacity
|
|
193
|
+
| `overflow: hidden` masks, `clip-path` reveals | no element masks: approximate a masked WORD with opacity (it stays a bound element); paint only a shape |
|
|
189
194
|
| a colour per split unit (`charStyle` making one letter red) | segments carry x/y/opacity/scale/rotation only: a separate element, or paint it |
|
|
190
195
|
| `mixBlendMode` | no blend modes on elements: paint it |
|
|
191
196
|
| a shape's colour as a knob | an svg's colours are compiled in (static `colors`): paint the shape if its colour must change live |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Generated by scripts/build-element-reference.mjs from @vosjs/core 0.25.
|
|
1
|
+
<!-- Generated by scripts/build-element-reference.mjs from @vosjs/core 0.25.3, @vosjs/elements 0.9.0, @vosjs/timeline 0.4.1 and @vosjs/shared 0.5.0. Do not edit by hand: re-run the script. -->
|
|
2
2
|
|
|
3
3
|
# Elements, the context and eases: the declarations
|
|
4
4
|
|
|
@@ -117,12 +117,47 @@ interface VosConfigJson {
|
|
|
117
117
|
|
|
118
118
|
`content`, `font.family` and `font.color` of a text element are the three
|
|
119
119
|
fields that take a `{ "$data": "<key>" }` binding, and the only ones that
|
|
120
|
-
re-resolve on a data edit; every other field is read once at build.
|
|
120
|
+
re-resolve on a data edit; every other field is read once at build. The key
|
|
121
|
+
names a TOP-LEVEL key of `config.data` (no dotted path, no array index), so
|
|
122
|
+
a list of words that each need binding is one key per word; `content`
|
|
123
|
+
takes the value as a string. A bound `split` element follows a data edit
|
|
124
|
+
too: its units are rebuilt from the new words and the timeline is rebuilt
|
|
125
|
+
over them (`@vosjs/elements` 0.9.0 with core 0.25.3; before that the
|
|
126
|
+
studio reloaded the program for such a knob and other hosts kept the old
|
|
127
|
+
word). So a per-letter word that is a knob is ONE bound `split` element,
|
|
128
|
+
never a pool of per-letter elements written from `onFrame`: a person can
|
|
129
|
+
retype a bound element in the studio, and cannot retype a pool.
|
|
121
130
|
`font.size`, `letterSpacing`, `transform.translateX/Y`, `stroke.width`
|
|
122
131
|
and `shadow.blur` are design pixels (a 1080-high frame). There is no
|
|
123
132
|
`mask`, `clip`, `blend` or group field on any element: SKILL.md's
|
|
124
133
|
"honest gaps" says what to do instead.
|
|
125
134
|
|
|
135
|
+
**Where an element sits.** `position` places the element's BOX:
|
|
136
|
+
|
|
137
|
+
- a preset (`'center'`, `'top-left'`, … `'bottom-right'`) sets the box
|
|
138
|
+
flush against the frame's edges or centre, with no margin;
|
|
139
|
+
- `{ x: '10%', y: '20%' }` (strings) puts the box's TOP-LEFT corner at
|
|
140
|
+
that fraction of the frame, the same at every output size;
|
|
141
|
+
- `{ x: 120, y: 80 }` (numbers) is RENDER pixels of that corner, never
|
|
142
|
+
scaled, so it moves when the output size changes: use a percentage or a
|
|
143
|
+
preset plus `transform` instead.
|
|
144
|
+
|
|
145
|
+
Then `transform.translateX` / `translateY` (design px, y down) move it.
|
|
146
|
+
`transform.scale`, `translateZ` and `rotation` / `rotateZ` (degrees,
|
|
147
|
+
about the box's centre) apply to a whole element, and a `split` element
|
|
148
|
+
takes only the translate. `anchor`, `transform.origin`, `scaleX`,
|
|
149
|
+
`scaleY`, `rotateX`, `rotateY` and `perspective` are not read: the
|
|
150
|
+
origin is always the box's centre. A config `rotation` is lost the first
|
|
151
|
+
time a tween writes `props.scale*` or `props.rotation*` (the props start at
|
|
152
|
+
rotation 0), so an element that is both tilted and animated takes its tilt
|
|
153
|
+
as `props.rotation` in the timeline (a `tl.set` at 0), never in the config.
|
|
154
|
+
|
|
155
|
+
A `stroke` is drawn UNDER the fill and centred on the outline, so around a
|
|
156
|
+
filled word only its outer half shows. CSS `-webkit-text-stroke` paints
|
|
157
|
+
over the fill, so a CSS stroke of N px is `stroke.width: 2N` around a
|
|
158
|
+
filled word and `stroke.width: N` for an outline-only word
|
|
159
|
+
(`font.color: 'transparent'`).
|
|
160
|
+
|
|
126
161
|
```ts
|
|
127
162
|
/**
|
|
128
163
|
* `{$data: key}` binding — the value resolves from the host's data object at
|
|
@@ -310,6 +345,14 @@ What `createTimeline` and `onFrame` animate: `el.props` (and
|
|
|
310
345
|
from the frame centre with `y` down, and `rotation` is degrees
|
|
311
346
|
counter-clockwise, unlike the config's design-pixel `transform`.
|
|
312
347
|
|
|
348
|
+
A `split` element lays its units out as one block at the element's
|
|
349
|
+
position: `el.segments[i]` starts at the CENTRE of unit i's ink, which
|
|
350
|
+
sits `lineStart + advanceBefore + width / 2` from the block's centre
|
|
351
|
+
(`advanceBefore` measured with `letterSpacing` and without kerning across
|
|
352
|
+
unit boundaries, the line aligned by `font.align`). So a per-letter tween
|
|
353
|
+
moves each letter FROM its laid-out place (`from`, or `s.y + dy`), never
|
|
354
|
+
to a position computed by hand.
|
|
355
|
+
|
|
313
356
|
Writing any of `content`, `fontSize`, `fontFamily`, `fontWeight`, `fontStyle`, `letterSpacing`, `color`, `strokeColor`, `strokeWidth` on a text element's `props`
|
|
314
357
|
re-rasters it (the runtime's own list), so a scramble or a counter writes
|
|
315
358
|
`el.props.content` in `onFrame` and a colour change sets `el.props.color`.
|
|
@@ -318,9 +361,21 @@ The tween dialect animates numbers only: tween `fontSize` and
|
|
|
318
361
|
|
|
319
362
|
Every such write queues a re-raster, even of an unchanged value, so guard
|
|
320
363
|
a per-frame write: `if (el.props.content !== next) el.props.content = next`.
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
364
|
+
These writes do nothing on a `split` element (its units are structure):
|
|
365
|
+
change a split element's words through its binding, never its props.
|
|
366
|
+
|
|
367
|
+
When `onFrame` writes a bound element's text, `onFrame` wins: the write
|
|
368
|
+
becomes the element's content, a data edit re-applies the bound value, and
|
|
369
|
+
the next frame's write replaces it before anything draws (the frame flushes
|
|
370
|
+
once, after `onFrame`). So derive the written string from `ctx.data`
|
|
371
|
+
(a scramble resolves to `ctx.data.subtitle`), and the binding stays the
|
|
372
|
+
words a person edits.
|
|
373
|
+
|
|
374
|
+
A text raster never fails for being long: its density drops until the
|
|
375
|
+
longest side fits the GPU's largest texture (4096 px when the GPU does
|
|
376
|
+
not say), so a line past about 4096 design px at 1080p, or half that at
|
|
377
|
+
2160p, renders softer. Only then is a long marquee worth cutting into
|
|
378
|
+
elements, and each piece stays bound.
|
|
324
379
|
|
|
325
380
|
```ts
|
|
326
381
|
/**
|
|
@@ -381,9 +436,14 @@ interface ElementInstance {
|
|
|
381
436
|
props: ElementProps;
|
|
382
437
|
/**
|
|
383
438
|
* Split text segments (only available when split config is defined).
|
|
384
|
-
* Each segment has its own ElementProps for individual animation.
|
|
439
|
+
* Each segment has its own ElementProps for individual animation. A data
|
|
440
|
+
* edit to a BOUND split text rebuilds them from the new words: the array
|
|
441
|
+
* and its objects are replaced (and the host rebuilds the timeline), so
|
|
442
|
+
* read `el.segments` inside createTimeline, never cache it elsewhere.
|
|
385
443
|
*/
|
|
386
444
|
segments?: ElementProps[];
|
|
445
|
+
/** Set when a data edit rebuilt this element's units; the host clears it. */
|
|
446
|
+
structural?: boolean;
|
|
387
447
|
/** Update element content (text or image src) */
|
|
388
448
|
setContent: (content: string) => void;
|
|
389
449
|
/**
|
|
@@ -409,6 +469,83 @@ interface ElementInstance {
|
|
|
409
469
|
}
|
|
410
470
|
```
|
|
411
471
|
|
|
472
|
+
## Knobs (`config.params` and `config.presets`)
|
|
473
|
+
|
|
474
|
+
A knob is a `ParamSpec` over one top-level `data` key, and a Look a named
|
|
475
|
+
set of values. vos.so keeps at most 12 knobs and 8 Looks, the first valid
|
|
476
|
+
ones, and `vos check` names every one it would drop before a push. Every
|
|
477
|
+
bound word stays a `data` key whether or not it has a knob: the studio's
|
|
478
|
+
element panel retypes a bound element directly, so the 12 knobs go to what
|
|
479
|
+
a person reaches for first (the palette, the headline), not to every word.
|
|
480
|
+
A `text` knob with `multiline: true` suits a list a painter draws line by
|
|
481
|
+
line; a list of elements is one key per element.
|
|
482
|
+
|
|
483
|
+
```ts
|
|
484
|
+
/**
|
|
485
|
+
* Config params: `config.params`
|
|
486
|
+
* declares the `ctx.data` keys a program reads as its creative knobs — key,
|
|
487
|
+
* kind, range, default. THE one params module, shared by the web Remix
|
|
488
|
+
* panel, the API, and scripts (the knob honesty lint): edits commit by
|
|
489
|
+
* baking BOTH `params[i].default` and `data[key]` into the config, so
|
|
490
|
+
* saves/exports/server renders pick the values up through the existing
|
|
491
|
+
* `config.data` machinery with zero new plumbing.
|
|
492
|
+
*
|
|
493
|
+
* The engine schema does not know `params` yet (upstream addition pending) —
|
|
494
|
+
* `vosConfigJsonSchema` STRIPS unknown fields on parse, so the API re-attaches
|
|
495
|
+
* validated params at the storage boundary (the platform's server-side copy;
|
|
496
|
+
* change both together) and
|
|
497
|
+
* this module validates defensively.
|
|
498
|
+
*/
|
|
499
|
+
type ParamValue = number | string | boolean;
|
|
500
|
+
|
|
501
|
+
interface ParamSpec {
|
|
502
|
+
/** The ctx.data key the program reads. */
|
|
503
|
+
key: string;
|
|
504
|
+
/** Human label; falls back to the key. */
|
|
505
|
+
label?: string;
|
|
506
|
+
/** One sentence on what the knob changes (U3b — knobs carry meaning). */
|
|
507
|
+
hint?: string;
|
|
508
|
+
/** Unit shown inside the number field: px, %, s, ×, °. */
|
|
509
|
+
unit?: string;
|
|
510
|
+
/** Optional card grouping; ungrouped knobs land on the Remix card. */
|
|
511
|
+
group?: string;
|
|
512
|
+
/** Sort order within a group (falls back to declaration order). */
|
|
513
|
+
order?: number;
|
|
514
|
+
kind: 'number' | 'color' | 'select' | 'toggle' | 'text' | 'font';
|
|
515
|
+
/** number kind */
|
|
516
|
+
min?: number;
|
|
517
|
+
max?: number;
|
|
518
|
+
step?: number;
|
|
519
|
+
/**
|
|
520
|
+
* select kind: the enumerated choices (REQUIRED, ≥2). font kind: an
|
|
521
|
+
* OPTIONAL curation — the families the knob offers; absent = the whole
|
|
522
|
+
* hosted catalog. Faces travel with the value: `applyParamValue` writes
|
|
523
|
+
* the chosen family's hosted faces into `data.fonts`, which the engine
|
|
524
|
+
* (core ≥0.17) registers at boot and on SET_DATA — no `config.fonts`
|
|
525
|
+
* declaration needed, no recompile.
|
|
526
|
+
*/
|
|
527
|
+
options?: string[];
|
|
528
|
+
/** text kind: render a multiline editor (content knobs, e.g. a headline). */
|
|
529
|
+
multiline?: boolean;
|
|
530
|
+
default: ParamValue;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* A Look (U3b): a named set of param values — the feel-the-range layer.
|
|
535
|
+
* Tap a look, then fine-tune; applying is ONE undoable multi-value commit.
|
|
536
|
+
*/
|
|
537
|
+
interface LookPreset {
|
|
538
|
+
name: string;
|
|
539
|
+
values: Record<string, ParamValue>;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Text params carry URLs (modelUrl knobs) and bound content
|
|
544
|
+
* (headline knobs) — longer than other string kinds.
|
|
545
|
+
*/
|
|
546
|
+
const TEXT_PARAM_MAX = 280;
|
|
547
|
+
```
|
|
548
|
+
|
|
412
549
|
## The context (`ctx`)
|
|
413
550
|
|
|
414
551
|
`setup(ctx)` gets a `SetupContext` and may return assets;
|
|
@@ -517,6 +654,18 @@ Author it as GSAP: `const tl = ctx.gsap.timeline({ paused: true })`, tweens
|
|
|
517
654
|
on `el.props`, `tl.addLabel(name, t)` per scene, return `tl`. This is the
|
|
518
655
|
whole surface the engine calls on it.
|
|
519
656
|
|
|
657
|
+
**Staggers.** An array target (`el.segments`, a list of props) takes a
|
|
658
|
+
`stagger` in two forms: a number, where target i starts `i × n` seconds
|
|
659
|
+
in; or `{ each | amount, from }`, where `from` is an index, `'start'`,
|
|
660
|
+
`'center'`, `'end'` or `'edges'`, distances are normalized so the
|
|
661
|
+
farthest target sits at n − 1 (as GSAP does), and `amount` spreads that
|
|
662
|
+
range over `amount` seconds. Outside the dialect, and not flagged by
|
|
663
|
+
`vos check`: `grid` and `axis` are ignored (a 2-D grid staggers as one
|
|
664
|
+
line in array order), `from: 'random'` and a stagger `ease` are not
|
|
665
|
+
supported, and a function stagger starts every target at once. For a grid,
|
|
666
|
+
compute each cell's delay in the build script (a row-major distance from the
|
|
667
|
+
source's `from` cell) and place one tween per cell at its time.
|
|
668
|
+
|
|
520
669
|
```ts
|
|
521
670
|
/**
|
|
522
671
|
* Structural master-clock interface the engine seeks each frame.
|
|
@@ -24,7 +24,7 @@ units (render pixels, y down, degrees counter-clockwise).
|
|
|
24
24
|
| a CSS `cubic-bezier(a, b, c, d)` timing | `ease: 'css-bezier(a, b, c, d)'` (exact; spelled `cubic-bezier` it plays linear) |
|
|
25
25
|
| CSS `--chrome` set by the timeline | a `data` value read in `onFrame`, or a tween on the element that shows it |
|
|
26
26
|
| faces resolved by the runtime from CSS families | `fonts: [{ family, weight, url }]` from the catalog |
|
|
27
|
-
| `mix-blend-mode`, `clip-path`, `overflow: hidden` reveals | GAPS:
|
|
27
|
+
| `mix-blend-mode`, `clip-path`, `overflow: hidden` reveals | GAPS: a revealed word keeps its bound element with an opacity approximation; the painter only for a shape; said in the push note |
|
|
28
28
|
|
|
29
29
|
Two traps specific to HyperFrames sources:
|
|
30
30
|
|