// Headless guard for the trace-chart viewer (chart-viewer.js) gap-handling. // Property protected: when taps fire at DIFFERENT cadences, the CLI's union-spine // alignment (spec 0056 ยง6) null-fills the timestamps a tap did NOT fire at, and // buildCharts must hand those nulls to uPlot VERBATIM as the data array โ€” a null is // a rendered gap, never dropped, never coerced to 0. This is the observable end of // the "no tap's points dropped, no privileged spine" contract: a positional shift or // a `null -> 0` coercion would silently misalign every series past the first gap. // // It loads the real viewer module (a regression there is observed here) and drives // the exported pure `buildCharts` over a fixture whose two series have nulls at // DIFFERENT positions; uPlot itself (which needs `document`) is never instantiated. import { createRequire } from "node:module"; import { readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { dirname, join } from "node:path"; const require = createRequire(import.meta.url); const here = dirname(fileURLToPath(import.meta.url)); const traces = JSON.parse(readFileSync(join(here, "fixtures", "sparse-traces.json"), "utf8")); const fail = (msg) => { console.error("FAIL: " + msg); process.exit(1); }; const sameArr = (a, b, msg) => { // strict identity per slot, so a null that became 0 (or a shifted point) fails. if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length || a.some((v, i) => v !== b[i])) fail(msg); }; // No `document` global -> the browser mount is skipped; we get only the pure export. global.window = { AURA_TRACES: traces }; const { buildCharts } = require(join(here, "..", "assets", "chart-viewer.js")); const eqPoints = traces.series[0].points; // [0.0, 0.001, null, 0.004] const exPoints = traces.series[1].points; // [1.0, null, null, -1.0] const idx = Array.from({ length: traces.xs.length }, (_, i) => i); // overlay: the data array is [ordinal spine, ...each series' points], nulls preserved. const overlay = buildCharts(traces, "overlay")[0]; sameArr(overlay.data[0], idx, "overlay x is the ordinal index spine (gaps collapsed)"); sameArr(overlay.data[1], eqPoints, "overlay equity points carry their null gap verbatim"); sameArr(overlay.data[2], exPoints, "overlay exposure points carry their null gaps verbatim"); // panels: each panel is [ordinal spine, its own series' points] โ€” same nulls. const panels = buildCharts(traces, "panels"); sameArr(panels[0].data[0], idx, "panel 0 shares the ordinal index spine"); sameArr(panels[0].data[1], eqPoints, "panel 0 carries equity's null gap verbatim"); sameArr(panels[1].data[1], exPoints, "panel 1 carries exposure's null gaps verbatim"); console.log("OK โ€” null gaps preserved verbatim across overlay + panels, shared union xs."); process.exit(0);