@qtsurfer/svelte-timeseries
v0.5.0
Published
[](https://www.npmjs.com/package/@qtsurfer/svelte-timeseries) [, you must update your Vite configuration.
Add the following to your vite.config.ts (or vite.config.js):
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()],
ssr: {
// Prevent SvelteKit from externalizing this library during SSR.
// This ensures Vite processes special imports like `...?url`
// which are required for DuckDB worker files.
noExternal: ['@qtsurfer/svelte-timeseries']
},
optimizeDeps: {
// Avoid pre-bundling these packages with esbuild.
// esbuild cannot handle WASM + Web Worker imports used by DuckDB.
exclude: ['@qtsurfer/svelte-timeseries', '@duckdb/duckdb-wasm']
}
});Explanation
Forces Vite to include this library in the SSR build pipeline.
ssr.noExternal
This allows Vite to correctly transform imports like:import worker from '...worker.js?url'
which DuckDB uses for its Web Worker runtime.optimizeDeps.exclude
Prevents Vite from trying to pre-bundle this library and DuckDB-WASM using esbuild. esbuild does not understand WASM and Worker imports, so excluding these packages avoids “Cannot read file ...?url” and similar errors.
Requirements:
- SvelteKit project with TypeScript enabled.
- Ability to provide Parquet files either by URL or directly in memory.
Getting started
<script lang="ts">
import { SvelteTimeSeries } from '@qtsurfer/svelte-timeseries';
const tables = {
temps: {
url: '/temps_gzip.parquet',
mainColumn: 'temp'
}
};
const markers = {
table: 'temps',
targetColumn: '_signal',
targetDimension: 'temp'
};
</script>
<SvelteTimeSeries table={tables} {markers} debug={false} externalManagerLegend={true} />You can also pass a Parquet file directly instead of a URL:
<script lang="ts">
import { SvelteTimeSeries } from '@qtsurfer/svelte-timeseries';
let parquetFile: File | null = null;
$: tables = parquetFile
? {
temps: {
parquet: parquetFile,
mainColumn: 'temp'
}
}
: {};
</script>
<input
type="file"
accept=".parquet"
onchange={(event) => (parquetFile = event.currentTarget.files?.[0] ?? null)}
/>
{#if parquetFile}
<SvelteTimeSeries table={tables} />
{/if}Component API
| Prop | Type | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| table | Record<string, { mainColumn: string; columnsSelect?: string[] } & ({ url: string } \| { parquet: Blob \| File \| ArrayBuffer \| Uint8Array })> | Defines the Parquet sources and their primary column; the object key becomes the DuckDB view name. |
| markers? | MarkersTableOptions | Table and JSON column used to build the markers view (shape, color, position, text). |
| debug? | boolean (default true) | Enables verbose DuckDB/builder logging. |
| externalManagerLegend? | boolean (default true) | Passes externalManagerLegend to TimeSeriesChartBuilder. Disable it to let the chart manage the legend internally. |
| columnsSnippet? | Snippet<[ColumnsProps]> | Overrides the column toggle panel. |
| performanceSnippet? | Snippet<[PerformanceProps]> | Overrides the performance/metrics panel. |
TimeSeriesFacade in practice
TimeSeriesFacade (see src/lib/TimeSeriesFacade.ts) encapsulates the component logic:
- Initialization (
initialize) – downloads the primary column, builds the dataset, and configures legends/icons. - Incremental loading (
addDimension/toggleColumn) – fetches new columns only when requested. - Markers (
loadMarkers) – reads themarkersview and adds annotations to ECharts. - Observable state (
getColumns,describe,getLegendStatus) – provides data for custom panels without touching DuckDB again.
Import the class directly to craft bespoke dashboards while reusing the DuckDB → Arrow → ECharts pipeline.
Advanced APIs
1. DuckDB
Reuse the same instance to run bespoke SQL before/after chart rendering.
import { DuckDB } from '@qtsurfer/svelte-timeseries';
const duck = await DuckDB.create(
{
signal: {
url: '/signals.parquet',
mainColumn: 'price'
}
},
undefined,
true
);
const rows = await duck.getRangeData('signal', '2024-01-01', '2024-01-31', 1000);
await duck.closeConnection();Key implementations (src/lib/duckdb/DuckDB.ts):
DuckDB.createvalidateswindow + Worker, registers Parquet views, and reports load time.getSingleDimensionnormalizes timestamps (ms) and returns Arrow arrays ready forTimeSeriesChartBuilder.buildTablesAndSchemasauto-detects types (casts%columns toDOUBLE, skips helper fields, builds themarkersview).transformTableToMatrixconverts Arrow results into[rows, columns]matrices consumable by any UI.
2. TimeSeriesChartBuilder
Craft fully custom ECharts layouts while reusing legend, marker, and metrics logic.
import { TimeSeriesChartBuilder } from '@qtsurfer/svelte-timeseries';
const builder = new TimeSeriesChartBuilder(echartsInstance, {
externalManagerLegend: true
});
builder.setLegendIcon('circle');
builder.setDataset({ _ts: timestamps, price: prices, ema20: ema20Series }, [
'_ts',
'price',
'ema20'
]);
builder.addMarkerPoint(
{ dimName: 'price', timestamp: timestamps[100], name: 'Breakout' },
{ icon: 'pin', color: '#FF7F50' }
);
builder.build();Reference scenarios
Taken from the demo at packages/svelte-timeseries/src/routes/+page.svelte:
- Minimal data (
temps_gzip_mini.parquet): ideal for embedded dashboards or smoke tests. - 1 million rows (
temps_gzip.parquet): browser stress test without compromising UX. - Partial dataset (1,807,956 values): leverages
columnsSelectto keep the initial payload slim and load indicators on demand. - Full dataset (10,245,084 values): showcases dense quantitative strategies with every column available.
- Synchronized markers: active in the last two scenarios to overlay
_msignals on top ofprice.
Development & testing
pnpm install
pnpm dev --filter svelte-timeseries- Sample Parquet files live in
packages/svelte-timeseries/static. Adjust the demobaseUrlwhen publishing behind a CDN. - Useful debugging helpers in
DuckDB.ts:closeConnection,getRangeData,getMarkers. - Pass
debug={true}to measure real load times per browser.
Support & contributions
- Need another scenario (streaming feeds, intraday aggregations)? Open an issue describing it.
- PRs are welcome—include reproduction steps, sample datasets, and screen captures when UI changes are involved.
- Using the component in production? Share your story so we can showcase it here.
