@glissade/cli 0.12.1 → 0.13.0-pre.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,22 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import { c as render, o as parseFrameRange } from "./render.js";
3
3
  import { n as parseCaptionsMode } from "./captions.js";
4
- //#region src/cli.ts
4
+ //#region src/args.ts
5
5
  /**
6
- * gs the glissade CLI (DESIGN.md §5.7).
7
- * gs render <scene-module> [--out <dir|file.mp4|file.webm>] [--fps N] [--range a..b]
6
+ * Tiny positional/flag argv parser for the `gs` CLI. Extracted from cli.ts so it
7
+ * is unit-testable without importing the CLI entry point (which runs main() on
8
+ * import).
8
9
  */
9
- function fail(msg) {
10
- console.error(`gs: ${msg}`);
11
- process.exit(1);
12
- }
13
- function parseCaptionsModeOrFail(raw) {
14
- try {
15
- return parseCaptionsMode(raw);
16
- } catch (err) {
17
- fail(err instanceof Error ? err.message : String(err));
18
- }
19
- }
10
+ const KNOWN_BOOLEAN_FLAGS = new Set([
11
+ "record",
12
+ "force",
13
+ "strict",
14
+ "cache",
15
+ "json",
16
+ "fix",
17
+ "no-warnings",
18
+ "lossless-intermediate",
19
+ "allow-gpu-shards",
20
+ "verbose",
21
+ "allow-degraded",
22
+ "bisect",
23
+ "watch"
24
+ ]);
20
25
  function parseArgs(argv) {
21
26
  const positional = [];
22
27
  const flags = /* @__PURE__ */ new Map();
@@ -26,11 +31,12 @@ function parseArgs(argv) {
26
31
  const eq = a.indexOf("=");
27
32
  if (eq >= 0) flags.set(a.slice(2, eq), a.slice(eq + 1));
28
33
  else {
34
+ const name = a.slice(2);
29
35
  const next = argv[i + 1];
30
- if (next !== void 0 && !next.startsWith("--")) {
31
- flags.set(a.slice(2), next);
36
+ if (!KNOWN_BOOLEAN_FLAGS.has(name) && next !== void 0 && !next.startsWith("--")) {
37
+ flags.set(name, next);
32
38
  i++;
33
- } else flags.set(a.slice(2), "");
39
+ } else flags.set(name, "");
34
40
  }
35
41
  } else positional.push(a);
36
42
  }
@@ -39,6 +45,23 @@ function parseArgs(argv) {
39
45
  flags
40
46
  };
41
47
  }
48
+ //#endregion
49
+ //#region src/cli.ts
50
+ /**
51
+ * gs — the glissade CLI (DESIGN.md §5.7).
52
+ * gs render <scene-module> [--out <dir|file.mp4|file.webm>] [--fps N] [--range a..b]
53
+ */
54
+ function fail(msg) {
55
+ console.error(`gs: ${msg}`);
56
+ process.exit(1);
57
+ }
58
+ function parseCaptionsModeOrFail(raw) {
59
+ try {
60
+ return parseCaptionsMode(raw);
61
+ } catch (err) {
62
+ fail(err instanceof Error ? err.message : String(err));
63
+ }
64
+ }
42
65
  const USAGE = `usage:
43
66
  gs render <scene-module> [options]
44
67
  gs diff <scene-module> --at <t> --against <baseline.dl.json|.png>
@@ -66,7 +89,7 @@ render options:
66
89
  --lossless-intermediate render shards as FFV1 + one final encode — the guaranteed byte-correct join
67
90
  (auto-enabled when the encoder can't honor precise boundary keyframes, e.g. mpeg4/openh264)
68
91
  --allow-gpu-shards permit sharding a scene with GPU/shader nodes (output is not reproducible across shards; §3.7)
69
- --cache [<dir>] persistent whole-frame raster cache in <dir> (default .gscache; §3.5). OFF by default — opting in
92
+ --cache[=<dir>] persistent whole-frame raster cache in <dir> (default .gscache; §3.5). OFF by default — opting in
70
93
  never changes output, only speed. A hit serves a stored frame byte-identical to a cold render.
71
94
  WINS: repeated renders + the UNCHANGED-PREFIX of a single-segment edit. Does NOT win a re-narrate —
72
95
  that shifts every frame's timing, so every DisplayList changes and every frame MISSES. Shards share
package/dist/loudness.js CHANGED
@@ -41,6 +41,7 @@ var loudness_exports = /* @__PURE__ */ __exportAll({
41
41
  PUBLISH_PROFILES: () => PUBLISH_PROFILES,
42
42
  computeGainDb: () => computeGainDb,
43
43
  computeMixHash: () => computeMixHash,
44
+ floorGain2: () => floorGain2,
44
45
  loudnessPathFor: () => loudnessPathFor,
45
46
  measureFile: () => measureFile,
46
47
  measureLoudnessCommand: () => measureLoudnessCommand,
@@ -240,7 +241,7 @@ async function measureLoudnessCommand(opts) {
240
241
  inputI: round2(inputI),
241
242
  inputTp: round2(inputTp),
242
243
  inputLra: round2(inputLra),
243
- gain: round2(gain),
244
+ gain: floorGain2(gain),
244
245
  mixHash
245
246
  };
246
247
  const loudnessPath = loudnessPathFor(opts.modulePath);
@@ -260,5 +261,6 @@ async function measureLoudnessCommand(opts) {
260
261
  }
261
262
  }
262
263
  const round2 = (v) => Math.round(v * 100) / 100;
264
+ const floorGain2 = (v) => Math.floor(v * 100) / 100;
263
265
  //#endregion
264
266
  export { computeGainDb as a, loudness_exports as c, parseLoudnormJson as d, peakClampBinds as f, PUBLISH_PROFILES as i, measureFile as l, resolveProfile as m, LOUDNESS_SCHEMA_VERSION as n, computeMixHash as o, readLoudness as p, LoudnessError as r, loudnessPathFor as s, DEFAULT_PROFILE_ID as t, measureLoudnessCommand as u };
@@ -129,6 +129,11 @@ function parseManifest(json) {
129
129
  */
130
130
  function compareManifests(a, b) {
131
131
  if (a.backend !== b.backend) throw new VerifyDeterminismError(`cross-backend byte-compare rejected: '${a.backend}' vs '${b.backend}'. Byte-equality is a Skia↔Skia (cross-machine/shard) guarantee only; browser↔Skia is perceptual (SSIM) parity, never byte-identity (§5.5 item 6).`);
132
+ if (a.fps !== b.fps || a.size.w !== b.size.w || a.size.h !== b.size.h) return {
133
+ ok: false,
134
+ compared: 0,
135
+ reason: `incomparable: baseline fps/size differs (a: ${a.fps}fps ${a.size.w}x${a.size.h} vs b: ${b.fps}fps ${b.size.w}x${b.size.h}). Same frame index = different wall-clock time when fps differs; a different size yields an incomparable RGBA hash. Re-render the baseline at the same fps/size.`
136
+ };
132
137
  const byFrameB = new Map(b.frames.map((e) => [e.frame, e]));
133
138
  let compared = 0;
134
139
  let absent = 0;
@@ -144,6 +149,7 @@ function compareManifests(a, b) {
144
149
  let node;
145
150
  let groupFallback;
146
151
  for (const [id, ha] of Object.entries(ea.nodes)) {
152
+ if (!(id in eb.nodes)) continue;
147
153
  if (eb.nodes[id] === ha) continue;
148
154
  if (groups.has(id)) {
149
155
  groupFallback ??= id;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/cli",
3
- "version": "0.12.1",
3
+ "version": "0.13.0-pre.1",
4
4
  "description": "glissade CLI: headless rendering via backend-skia (+ FFmpeg mux).",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
@@ -23,15 +23,15 @@
23
23
  "@napi-rs/canvas": "^0.1.65",
24
24
  "esbuild": "^0.28.0",
25
25
  "jiti": "^2.4.2",
26
- "@glissade/backend-skia": "0.12.1",
27
- "@glissade/core": "0.12.1",
28
- "@glissade/interact": "0.12.1",
29
- "@glissade/lottie": "0.12.1",
30
- "@glissade/svg": "0.12.1",
31
- "@glissade/narrate": "0.12.1",
32
- "@glissade/player": "0.12.1",
33
- "@glissade/scene": "0.12.1",
34
- "@glissade/sfx": "0.12.1"
26
+ "@glissade/backend-skia": "0.13.0-pre.1",
27
+ "@glissade/core": "0.13.0-pre.1",
28
+ "@glissade/interact": "0.13.0-pre.1",
29
+ "@glissade/lottie": "0.13.0-pre.1",
30
+ "@glissade/svg": "0.13.0-pre.1",
31
+ "@glissade/narrate": "0.13.0-pre.1",
32
+ "@glissade/player": "0.13.0-pre.1",
33
+ "@glissade/scene": "0.13.0-pre.1",
34
+ "@glissade/sfx": "0.13.0-pre.1"
35
35
  },
36
36
  "repository": {
37
37
  "type": "git",