From 8e5cd744a38d5f44cf4326e40f81b52925efa312 Mon Sep 17 00:00:00 2001 From: Karakatiza666 Date: Thu, 11 Jun 2026 22:16:39 +0000 Subject: [PATCH 1/2] web-console: anchor performance graph time axis to server time The throughput, memory, and storage graphs computed their x-axis domain from the client clock (`Date.now()`), but every sample is stamped with server time. Any client/server clock skew shifted the plotted line relative to the axis, so the graphs under-filled the width and the line appeared to shrink over the first ~30s. Anchor the axis to the newest sample's timestamp (falling back to the server-time estimate via `dateNow()` before data arrives) so the newest point always sits at the right edge and the window spans the full keep interval, regardless of clock skew. Signed-off-by: Karakatiza666 --- .../layout/pipelines/PipelineMemoryGraph.svelte | 15 +++++++++++---- .../layout/pipelines/PipelineStorageGraph.svelte | 15 +++++++++++---- .../pipelines/PipelineThroughputGraph.svelte | 15 +++++++++++---- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineMemoryGraph.svelte b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineMemoryGraph.svelte index 538b641c69f..d6e47e379b8 100644 --- a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineMemoryGraph.svelte +++ b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineMemoryGraph.svelte @@ -11,6 +11,7 @@ import { CanvasRenderer } from 'echarts/renderers' import type { ECMouseEvent } from 'svelte-echarts' import { Chart } from 'svelte-echarts' + import { dateNow } from '$lib/compositions/serverTime' import { getThemeColor } from '$lib/functions/common/color' import { humanSize } from '$lib/functions/common/string' import { tuple } from '$lib/functions/common/tuple' @@ -40,6 +41,10 @@ const pipelineName = $derived(pipeline.current.name) + // Anchor the time axis to the newest sample's timestamp rather than to the + // client clock. + const xAxisMax = $derived(metrics.at(-1)?.t.toNumber() ?? dateNow()) + const valueMax = $derived(metrics.length ? Math.max(...metrics.map((v) => v.m.toNumber())) : 0) const yMaxStep = $derived(2 ** Math.ceil(Math.log2(valueMax * 1.25))) const yMax = $derived(valueMax !== 0 ? yMaxStep : 1024 * 2048) @@ -67,8 +72,8 @@ } ], xAxis: { - min: Date.now() - keepMs, - max: Date.now() + min: xAxisMax - keepMs, + max: xAxisMax }, yAxis: { interval: (yMax - yMin) / 2, @@ -111,8 +116,10 @@ animationDuration: 0, animationDurationUpdate: refetchMs, type: 'time' as const, - min: Date.now() - keepMs - refetchMs, - max: Date.now() - refetchMs, + // svelte-ignore state_referenced_locally + min: dateNow() - keepMs - refetchMs, + // svelte-ignore state_referenced_locally + max: dateNow() - refetchMs, minInterval: 25000, maxInterval: 25000, axisLabel: { diff --git a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineStorageGraph.svelte b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineStorageGraph.svelte index e45638165c4..fdcced26f39 100644 --- a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineStorageGraph.svelte +++ b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineStorageGraph.svelte @@ -11,6 +11,7 @@ import { CanvasRenderer } from 'echarts/renderers' import type { ECMouseEvent } from 'svelte-echarts' import { Chart } from 'svelte-echarts' + import { dateNow } from '$lib/compositions/serverTime' import { getThemeColor } from '$lib/functions/common/color' import { humanSize } from '$lib/functions/common/string' import { tuple } from '$lib/functions/common/tuple' @@ -43,6 +44,10 @@ const pipelineName = $derived(pipeline.current.name) + // Anchor the time axis to the newest sample's timestamp rather than to the + // client clock. + const xAxisMax = $derived(metrics.at(-1)?.t.toNumber() ?? dateNow()) + const valueMax = $derived(metrics.length ? Math.max(...metrics.map((v) => v.s.toNumber())) : 0) const yMaxStep = $derived(2 ** Math.ceil(Math.log2(valueMax * 1.25))) const yMax = $derived(valueMax !== 0 ? yMaxStep : 1024 * 2048) @@ -70,8 +75,8 @@ } ], xAxis: { - min: Date.now() - keepMs, - max: Date.now() + min: xAxisMax - keepMs, + max: xAxisMax }, yAxis: { interval: (yMax - yMin) / 2, @@ -114,8 +119,10 @@ animationDuration: 0, animationDurationUpdate: refetchMs, type: 'time' as const, - min: Date.now() - keepMs - refetchMs, - max: Date.now() - refetchMs, + // svelte-ignore state_referenced_locally + min: dateNow() - keepMs - refetchMs, + // svelte-ignore state_referenced_locally + max: dateNow() - refetchMs, minInterval: 25000, maxInterval: 25000, axisLabel: { diff --git a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineThroughputGraph.svelte b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineThroughputGraph.svelte index 3c396207874..e0eb7791115 100644 --- a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineThroughputGraph.svelte +++ b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineThroughputGraph.svelte @@ -5,6 +5,7 @@ import { type EChartsType, init, use } from 'echarts/core' import { CanvasRenderer } from 'echarts/renderers' import { Chart } from 'svelte-echarts' + import { dateNow } from '$lib/compositions/serverTime' import { getThemeColor } from '$lib/functions/common/color' import { formatQty } from '$lib/functions/format' import { calcPipelineThroughput, type PipelineMetrics } from '$lib/functions/pipelineMetrics' @@ -27,6 +28,10 @@ const pipelineName = $derived(pipeline.current.name) const throughput = $derived(calcPipelineThroughput(metrics)) + // Anchor the time axis to the newest sample's timestamp rather than to the + // client clock. + const xAxisMax = $derived(metrics.at(-1)?.t.toNumber() ?? dateNow()) + const primaryColor = getThemeColor('--color-primary-500').format('hex') let ref: EChartsType | undefined = $state() @@ -43,8 +48,8 @@ } ], xAxis: { - min: Date.now() - keepMs, - max: Date.now() + min: xAxisMax - keepMs, + max: xAxisMax }, yAxis: { interval: (throughput.yMax - throughput.yMin) / 2, @@ -69,8 +74,10 @@ animationDuration: 0, animationDurationUpdate: refetchMs, type: 'time' as const, - min: Date.now() - keepMs - refetchMs, - max: Date.now() - refetchMs, + // svelte-ignore state_referenced_locally + min: dateNow() - keepMs - refetchMs, + // svelte-ignore state_referenced_locally + max: dateNow() - refetchMs, minInterval: 25000, maxInterval: 25000, axisLabel: { From 10de392dd89ea65f1561239f105b6b41f5b6183a Mon Sep 17 00:00:00 2001 From: Karakatiza666 Date: Fri, 12 Jun 2026 07:16:54 +0000 Subject: [PATCH 2/2] [web-console] Add a test for deriving time scope for metrics time series Refactor server time utilities into a single ServerDate class Signed-off-by: Karakatiza666 --- .../pipelines/PipelineMemoryGraph.svelte | 10 ++-- .../pipelines/PipelineStorageGraph.svelte | 10 ++-- .../pipelines/PipelineThroughputGraph.svelte | 14 ++++-- .../pipelines/editor/TabHealth.svelte | 4 +- .../src/lib/compositions/serverTime.spec.ts | 27 +++++++++++ .../src/lib/compositions/serverTime.ts | 47 ++++++++++++++----- .../src/lib/functions/pipelineMetrics.spec.ts | 23 +++++++++ .../src/lib/functions/pipelineMetrics.ts | 15 ++++++ .../(authenticated)/health/+page.svelte | 6 ++- .../web-console/src/routes/+layout.svelte | 4 +- js-packages/web-console/src/routes/+layout.ts | 8 ++-- 11 files changed, 131 insertions(+), 37 deletions(-) create mode 100644 js-packages/web-console/src/lib/compositions/serverTime.spec.ts create mode 100644 js-packages/web-console/src/lib/functions/pipelineMetrics.spec.ts diff --git a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineMemoryGraph.svelte b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineMemoryGraph.svelte index d6e47e379b8..5b21d34dbdf 100644 --- a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineMemoryGraph.svelte +++ b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineMemoryGraph.svelte @@ -11,11 +11,11 @@ import { CanvasRenderer } from 'echarts/renderers' import type { ECMouseEvent } from 'svelte-echarts' import { Chart } from 'svelte-echarts' - import { dateNow } from '$lib/compositions/serverTime' + import { ServerDate } from '$lib/compositions/serverTime' import { getThemeColor } from '$lib/functions/common/color' import { humanSize } from '$lib/functions/common/string' import { tuple } from '$lib/functions/common/tuple' - import type { PipelineMetrics } from '$lib/functions/pipelineMetrics' + import { timeSeriesAxisMax } from '$lib/functions/pipelineMetrics' import type { Pipeline } from '$lib/services/pipelineManager' import type { TimeSeriesEntry } from '$lib/types/pipelineManager' @@ -43,7 +43,7 @@ // Anchor the time axis to the newest sample's timestamp rather than to the // client clock. - const xAxisMax = $derived(metrics.at(-1)?.t.toNumber() ?? dateNow()) + const xAxisMax = $derived(timeSeriesAxisMax(metrics)) const valueMax = $derived(metrics.length ? Math.max(...metrics.map((v) => v.m.toNumber())) : 0) const yMaxStep = $derived(2 ** Math.ceil(Math.log2(valueMax * 1.25))) @@ -117,9 +117,9 @@ animationDurationUpdate: refetchMs, type: 'time' as const, // svelte-ignore state_referenced_locally - min: dateNow() - keepMs - refetchMs, + min: ServerDate.now() - keepMs - refetchMs, // svelte-ignore state_referenced_locally - max: dateNow() - refetchMs, + max: ServerDate.now() - refetchMs, minInterval: 25000, maxInterval: 25000, axisLabel: { diff --git a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineStorageGraph.svelte b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineStorageGraph.svelte index fdcced26f39..946334e2d8e 100644 --- a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineStorageGraph.svelte +++ b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineStorageGraph.svelte @@ -11,11 +11,11 @@ import { CanvasRenderer } from 'echarts/renderers' import type { ECMouseEvent } from 'svelte-echarts' import { Chart } from 'svelte-echarts' - import { dateNow } from '$lib/compositions/serverTime' + import { ServerDate } from '$lib/compositions/serverTime' import { getThemeColor } from '$lib/functions/common/color' import { humanSize } from '$lib/functions/common/string' import { tuple } from '$lib/functions/common/tuple' - import type { PipelineMetrics } from '$lib/functions/pipelineMetrics' + import { timeSeriesAxisMax } from '$lib/functions/pipelineMetrics' import type { Pipeline } from '$lib/services/pipelineManager' import type { TimeSeriesEntry } from '$lib/types/pipelineManager' import type { Snippet } from '$lib/types/svelte' @@ -46,7 +46,7 @@ // Anchor the time axis to the newest sample's timestamp rather than to the // client clock. - const xAxisMax = $derived(metrics.at(-1)?.t.toNumber() ?? dateNow()) + const xAxisMax = $derived(timeSeriesAxisMax(metrics)) const valueMax = $derived(metrics.length ? Math.max(...metrics.map((v) => v.s.toNumber())) : 0) const yMaxStep = $derived(2 ** Math.ceil(Math.log2(valueMax * 1.25))) @@ -120,9 +120,9 @@ animationDurationUpdate: refetchMs, type: 'time' as const, // svelte-ignore state_referenced_locally - min: dateNow() - keepMs - refetchMs, + min: ServerDate.now() - keepMs - refetchMs, // svelte-ignore state_referenced_locally - max: dateNow() - refetchMs, + max: ServerDate.now() - refetchMs, minInterval: 25000, maxInterval: 25000, axisLabel: { diff --git a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineThroughputGraph.svelte b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineThroughputGraph.svelte index e0eb7791115..3c151f585f4 100644 --- a/js-packages/web-console/src/lib/components/layout/pipelines/PipelineThroughputGraph.svelte +++ b/js-packages/web-console/src/lib/components/layout/pipelines/PipelineThroughputGraph.svelte @@ -5,10 +5,14 @@ import { type EChartsType, init, use } from 'echarts/core' import { CanvasRenderer } from 'echarts/renderers' import { Chart } from 'svelte-echarts' - import { dateNow } from '$lib/compositions/serverTime' + import { ServerDate } from '$lib/compositions/serverTime' import { getThemeColor } from '$lib/functions/common/color' import { formatQty } from '$lib/functions/format' - import { calcPipelineThroughput, type PipelineMetrics } from '$lib/functions/pipelineMetrics' + import { + calcPipelineThroughput, + timeSeriesAxisMax, + type PipelineMetrics + } from '$lib/functions/pipelineMetrics' import type { Pipeline } from '$lib/services/pipelineManager' import type { TimeSeriesEntry } from '$lib/types/pipelineManager' @@ -30,7 +34,7 @@ // Anchor the time axis to the newest sample's timestamp rather than to the // client clock. - const xAxisMax = $derived(metrics.at(-1)?.t.toNumber() ?? dateNow()) + const xAxisMax = $derived(timeSeriesAxisMax(metrics)) const primaryColor = getThemeColor('--color-primary-500').format('hex') @@ -75,9 +79,9 @@ animationDurationUpdate: refetchMs, type: 'time' as const, // svelte-ignore state_referenced_locally - min: dateNow() - keepMs - refetchMs, + min: ServerDate.now() - keepMs - refetchMs, // svelte-ignore state_referenced_locally - max: dateNow() - refetchMs, + max: ServerDate.now() - refetchMs, minInterval: 25000, maxInterval: 25000, axisLabel: { diff --git a/js-packages/web-console/src/lib/components/pipelines/editor/TabHealth.svelte b/js-packages/web-console/src/lib/components/pipelines/editor/TabHealth.svelte index 18962c67c92..20479678eb4 100644 --- a/js-packages/web-console/src/lib/components/pipelines/editor/TabHealth.svelte +++ b/js-packages/web-console/src/lib/components/pipelines/editor/TabHealth.svelte @@ -9,7 +9,7 @@ } from '$lib/components/health/StatusTimeline.svelte' import Drawer from '$lib/components/layout/Drawer.svelte' import { useInterval } from '$lib/compositions/common/useInterval.svelte' - import { newDate } from '$lib/compositions/serverTime' + import { ServerDate } from '$lib/compositions/serverTime' import { usePipelineManager } from '$lib/compositions/usePipelineManager.svelte' import { partition } from '$lib/functions/common/array' import { ceilToHour, dateMax } from '$lib/functions/common/date' @@ -121,7 +121,7 @@ const healthWindowHours = 72 const lastTimestamp = (es: PipelineMonitorEventSelectedInfo[] | null) => - ceilToHour(es?.length ? dateMax(new Date(es[0].recorded_at), newDate()) : newDate()) + ceilToHour(es?.length ? dateMax(new Date(es[0].recorded_at), new ServerDate()) : new ServerDate()) const firstTimestamp = (es: PipelineMonitorEventSelectedInfo[] | null) => new Date(lastTimestamp(es).getTime() - healthWindowHours * 60 * 60 * 1000) diff --git a/js-packages/web-console/src/lib/compositions/serverTime.spec.ts b/js-packages/web-console/src/lib/compositions/serverTime.spec.ts new file mode 100644 index 00000000000..a29a30f7a45 --- /dev/null +++ b/js-packages/web-console/src/lib/compositions/serverTime.spec.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from 'vitest' +import { ServerDate } from './serverTime' + +describe('ServerDate', () => { + it('behaves like Date when constructed with an explicit value', () => { + expect(new ServerDate(0).getTime()).toBe(0) + expect(new ServerDate('2020-01-01T00:00:00Z').getTime()).toBe( + Date.parse('2020-01-01T00:00:00Z') + ) + expect(new ServerDate() instanceof Date).toBe(true) + }) + + it('applies the synced server/client offset to now()', () => { + // Pretend the server clock is one minute ahead of this client. + ServerDate.sync(Date.now() + 60_000) + const skew = ServerDate.now() - Date.now() + expect(skew).toBeGreaterThanOrEqual(59_000) + expect(skew).toBeLessThanOrEqual(61_000) + }) + + it('reflects the synced offset in a zero-argument instance', () => { + ServerDate.sync(Date.now() + 60_000) + const skew = new ServerDate().getTime() - Date.now() + expect(skew).toBeGreaterThanOrEqual(59_000) + expect(skew).toBeLessThanOrEqual(61_000) + }) +}) diff --git a/js-packages/web-console/src/lib/compositions/serverTime.ts b/js-packages/web-console/src/lib/compositions/serverTime.ts index 133440bbcaa..9a440011ddb 100644 --- a/js-packages/web-console/src/lib/compositions/serverTime.ts +++ b/js-packages/web-console/src/lib/compositions/serverTime.ts @@ -1,16 +1,39 @@ -let offsetMs = 0 - /** - * Sets the global time offset based on the provided server time. - * @param newTime - A server-provided time (ISO string or Date object) - * @returns Server time + * A drop-in replacement for `Date` that reads the current time from the + * server's clock rather than the client's. + * + * The pipeline manager reports its own wall-clock time (e.g. in the license + * payload). The browser clock may be skewed relative to the server, so any UI + * that compares server-provided timestamps against "now" must use the server's + * notion of now. `ServerDate` measures the offset once via {@link ServerDate.sync} + * and applies it to every `new ServerDate()` and {@link ServerDate.now}. + * + * Instances are ordinary `Date` objects, so a `ServerDate` can be used anywhere + * a `Date` is expected. Constructing one with an explicit argument behaves + * exactly like `Date`; only the zero-argument form (the current time) is + * shifted onto the server clock. */ -export const setCurrentTime = (newTime: string | Date) => { - const serverMs = new Date(newTime).getTime() - const clientMs = Date.now() - offsetMs = serverMs - clientMs -} +export class ServerDate extends Date { + /** Server clock minus client clock, in milliseconds. */ + private static offsetMs = 0 -export const newDate = () => new Date(Date.now() + offsetMs) + /** + * Records the client/server clock offset from a known server timestamp. + * @param serverTime - The server's current time (ISO string, epoch ms, or `Date`). + */ + static sync(serverTime: string | number | Date) { + ServerDate.offsetMs = new Date(serverTime).getTime() - Date.now() + } -export const dateNow = () => Date.now() + offsetMs + /** Current server time, in milliseconds since the Unix epoch. */ + static now(): number { + return Date.now() + ServerDate.offsetMs + } + + /** + * @param value - As `Date`'s constructor; omit to use the current server time. + */ + constructor(value?: number | string | Date) { + super(value === undefined ? ServerDate.now() : value) + } +} diff --git a/js-packages/web-console/src/lib/functions/pipelineMetrics.spec.ts b/js-packages/web-console/src/lib/functions/pipelineMetrics.spec.ts new file mode 100644 index 00000000000..9e7a322811f --- /dev/null +++ b/js-packages/web-console/src/lib/functions/pipelineMetrics.spec.ts @@ -0,0 +1,23 @@ +import { BigNumber } from 'bignumber.js' +import { describe, expect, it } from 'vitest' +import { timeSeriesAxisMax } from './pipelineMetrics' +import type { TimeSeriesEntry } from '$lib/types/pipelineManager' + +const sampleAt = (timeMs: number): TimeSeriesEntry => ({ + t: new BigNumber(timeMs), + r: new BigNumber(0), + m: new BigNumber(0), + s: new BigNumber(0) +}) + +describe('timeSeriesAxisMax', () => { + it('anchors to the newest sample regardless of the client clock', () => { + const metrics = [sampleAt(1000), sampleAt(2000), sampleAt(3000)] + // A client clock that disagrees with the server must not influence the result. + expect(timeSeriesAxisMax(metrics, () => 9999)).toBe(3000) + }) + + it('falls back to the supplied time source when there are no samples', () => { + expect(timeSeriesAxisMax([], () => 4242)).toBe(4242) + }) +}) diff --git a/js-packages/web-console/src/lib/functions/pipelineMetrics.ts b/js-packages/web-console/src/lib/functions/pipelineMetrics.ts index e0748477e94..86641e619eb 100644 --- a/js-packages/web-console/src/lib/functions/pipelineMetrics.ts +++ b/js-packages/web-console/src/lib/functions/pipelineMetrics.ts @@ -1,3 +1,4 @@ +import { ServerDate } from '$lib/compositions/serverTime' import { groupBy } from '$lib/functions/common/array' import { nonNull } from '$lib/functions/common/function' import { discreteDerivative } from '$lib/functions/common/math' @@ -199,6 +200,20 @@ export const accumulatePipelineMetrics = } } +/** + * Right edge (newest time) of a performance graph's time axis. + * + * Samples carry server-side timestamps, so the axis must be anchored to the + * newest sample rather than to the client clock: any client/server clock skew + * would otherwise shift the plotted line relative to the axis and leave the + * graph under-filling its width. Before any sample has arrived, fall back to + * the server-time estimate so the empty window is still in the right time base. + * + * @param now - Source of the fallback time; injectable for testing. + */ +export const timeSeriesAxisMax = (metrics: TimeSeriesEntry[], now: () => number = ServerDate.now) => + metrics.at(-1)?.t.toNumber() ?? now() + /** * @returns Time series of throughput with smoothing window over 3 data intervals */ diff --git a/js-packages/web-console/src/routes/(system)/(authenticated)/health/+page.svelte b/js-packages/web-console/src/routes/(system)/(authenticated)/health/+page.svelte index 9336c98855b..23c769c2ecf 100644 --- a/js-packages/web-console/src/routes/(system)/(authenticated)/health/+page.svelte +++ b/js-packages/web-console/src/routes/(system)/(authenticated)/health/+page.svelte @@ -11,7 +11,7 @@ import BookADemo from '$lib/components/other/BookADemo.svelte' import CreatePipelineButton from '$lib/components/pipelines/CreatePipelineButton.svelte' import { useAdaptiveDrawer } from '$lib/compositions/layout/useAdaptiveDrawer.svelte' - import { newDate } from '$lib/compositions/serverTime' + import { ServerDate } from '$lib/compositions/serverTime' import { usePipelineManager } from '$lib/compositions/usePipelineManager.svelte' import { partition } from '$lib/functions/common/array' import { ceilToHour, dateMax } from '$lib/functions/common/date' @@ -59,7 +59,9 @@ const healthWindowHours = 72 const lastTimestamp = (events: ClusterMonitorEventSelectedInfo[] | null) => - ceilToHour(events?.length ? dateMax(new Date(events.at(0)!.recorded_at), newDate()) : newDate()) + ceilToHour( + events?.length ? dateMax(new Date(events.at(0)!.recorded_at), new ServerDate()) : new ServerDate() + ) const firstTimestamp = (events: ClusterMonitorEventSelectedInfo[] | null) => new Date(lastTimestamp(events).getTime() - healthWindowHours * 60 * 60 * 1000) diff --git a/js-packages/web-console/src/routes/+layout.svelte b/js-packages/web-console/src/routes/+layout.svelte index db48fca18ce..05963ff3d72 100755 --- a/js-packages/web-console/src/routes/+layout.svelte +++ b/js-packages/web-console/src/routes/+layout.svelte @@ -14,7 +14,7 @@ import { page } from '$app/state' import { useInterval } from '$lib/compositions/common/useInterval.svelte' - import { newDate } from '$lib/compositions/serverTime' + import { ServerDate } from '$lib/compositions/serverTime' import { useSystemMessages } from '$lib/compositions/useSystemMessages.svelte' import { getLicenseMessage } from '$lib/functions/license' import { _markRouterReady } from './+layout' @@ -45,7 +45,7 @@ if (!page.data.feldera) { return } - upsert(/^license_/, getLicenseMessage(page.data.feldera.config, newDate())) + upsert(/^license_/, getLicenseMessage(page.data.feldera.config, new ServerDate())) }, 60000) diff --git a/js-packages/web-console/src/routes/+layout.ts b/js-packages/web-console/src/routes/+layout.ts index 461d7b78280..30b1c4e4c54 100644 --- a/js-packages/web-console/src/routes/+layout.ts +++ b/js-packages/web-console/src/routes/+layout.ts @@ -15,7 +15,7 @@ import { getSessionConfigFromCache } from '$lib/compositions/configCache' import { initSystemMessages, type SystemMessage } from '$lib/compositions/initSystemMessages' -import { newDate, setCurrentTime } from '$lib/compositions/serverTime' +import { ServerDate } from '$lib/compositions/serverTime' import { displayScheduleToDismissable, getLicenseMessage } from '$lib/functions/license' import { resolve } from '$lib/functions/svelte' import { @@ -168,7 +168,7 @@ const syncServerTimeFromConfig = (config: Configuration) => { ? config.license_validity.Exists : undefined if (license) { - setCurrentTime(license.current) + ServerDate.sync(license.current) } } @@ -435,7 +435,7 @@ function buildLayoutData( * It is expected to be idempotent - calling it the second time on lazy update * when config hasn't changed should not break anything. * - * Does NOT call `setCurrentTime` — that belongs to `syncServerTimeFromConfig`, + * Does NOT call `ServerDate.sync` — that belongs to `syncServerTimeFromConfig`, * which only runs against freshly-fetched data. */ function initializeConfigDependencies(auth: AuthDetails, config: Configuration) { @@ -454,7 +454,7 @@ function initializeConfigDependencies(auth: AuthDetails, config: Configuration) } { - const message = getLicenseMessage(config, newDate()) + const message = getLicenseMessage(config, new ServerDate()) if (message) { pushSystemMessageOnce(message) }