From 0263d3632d20a9bb5be801136a92b076f2858d86 Mon Sep 17 00:00:00 2001 From: Carlos Alcaraz <193642530+calcarazgre646@users.noreply.github.com> Date: Thu, 4 Jun 2026 19:51:24 -0300 Subject: [PATCH 1/2] fix(core): treat unparsable timing attributes as missing instead of NaN A typo like data-start="abc" flowed through parseFloat unguarded in both the timing compiler and the HTML parser. The compiler serialized it into the compiled output as data-end="NaN"; the parser turned a garbage data-end into a NaN duration via Math.max(0, NaN), which is NaN. Neither lint nor compile surfaced anything to the user. compileTag now normalizes an unparsable data-start to 0 in the output, strips unparsable data-end/data-duration so they take the existing missing-attribute paths (recompute or resolver), and the parser falls back to its own defaults. Same policy extractResolvedMedia already applied to data-duration, and the same validation the composition-level duration already gets in extractCompositionMetadata. --- .../core/src/compiler/timingCompiler.test.ts | 61 +++++++++++++++++++ packages/core/src/compiler/timingCompiler.ts | 54 ++++++++++++---- packages/core/src/parsers/htmlParser.test.ts | 47 ++++++++++++++ packages/core/src/parsers/htmlParser.ts | 18 +++--- 4 files changed, 158 insertions(+), 22 deletions(-) diff --git a/packages/core/src/compiler/timingCompiler.test.ts b/packages/core/src/compiler/timingCompiler.test.ts index b7dcf044c..0907894fe 100644 --- a/packages/core/src/compiler/timingCompiler.test.ts +++ b/packages/core/src/compiler/timingCompiler.test.ts @@ -4,6 +4,7 @@ import { injectDurations, extractResolvedMedia, clampDurations, + parseTimingAttr, } from "./timingCompiler.js"; describe("compileTimingAttrs", () => { @@ -185,3 +186,63 @@ describe("clampDurations", () => { expect(result).toContain('data-end="7"'); }); }); + +// ── Unparsable timing attributes ── +// +// A typo like data-start="abc" must not reach the compiled output: it would +// otherwise be serialized as data-end="NaN" and parsed downstream as a NaN +// duration. Unparsable values are treated as missing (data-start → 0, +// data-duration/data-end → resolver path), mirroring the Number.isFinite +// guard extractResolvedMedia already applies to data-duration. + +describe("unparsable timing attributes", () => { + it("normalizes an unparsable data-start to 0 and computes data-end from it", () => { + const html = '