sveltelean Query
Version v0.2.0

Sizes

Each row is a consumer entry built for production. Marginal rows subtract the row they are measured over. Read from packages/query/artifacts/size.json.

EntryOvergzipbrotliBudget (gzip)Target (gzip)
part: poll (lazy)–1039 B963 B1100 B–
part: connect (lazy)–931 B828 B1100 B–
part: access.svelte (lazy)–947 B876 B1100 B–
part: gc (lazy)–741 B671 B800 B–
part: limiter (lazy)–796 B742 B900 B–
part: share (lazy)–536 B503 B600 B–
part: index-part (lazy)–470 B430 B600 B–
query–11787 B10839 B11800 B5000 B missed
query+mutation–15409 B14167 B15500 B6500 B missed
root (every export)–16134 B14844 B16200 B–
query + every part (static)–15667 B14383 B15700 B–
every part (static, marginal)query3880 B3544 B4300 B–
http (marginal)query2239 B2079 B2400 B1500 B missed
laravel (marginal)http (marginal)1681 B1548 B1900 B600 B missed
forms (marginal)query+mutation1406 B1226 B1600 B700 B missed
persist (marginal)query3428 B3201 B3700 B1500 B missed
live (marginal)query2777 B2565 B3100 B900 B missed
devtools (production stub)–92 B68 B100 B60 B missed
paged (marginal)query1234 B1109 B1400 B800 B missed
infinite (marginal)query1648 B1502 B1800 B900 B missed
ssr (marginal)query568 B514 B700 B400 B missed
ssr every export (marginal)query1287 B1178 B1500 B–
tabs (marginal)query3104 B2874 B3500 B1000 B missed
testing (not shipped)–18878 B17235 B19300 B–

Consumer entries bundled with Vite in production mode (Rollup tree-shaking, esbuild minify, ESM, es2022, production condition, svelte external, .svelte.js compiled with compileModule). "query" = defineQuery + createQuery + client (first-load code; optional parts are their own "part:" rows, loaded on first use); "query+mutation" adds defineMutation, createMutation, patch; "query + every part (static)" adds `allParts` from `./parts` (every optional part in the first load, no lazy chunk). Vite's preload helper is the application's and not counted. Marginal rows subtract the row named in "over". gzip level 9 / brotli quality 11 (node:zlib). Budgets are gzip; "target" is the 04 §2 target.

Engine benchmarks

The engine in Node under the production condition, with a virtual clock and an in-memory transport; median of fresh runs, every sample in the file. Node v22.23.3, AMD EPYC 9555P 64-Core Processor, 2026-10-06.

IDBenchmarkTargetResultMeasured
B1exact invalidation, cache of 10k / 50k entries≤ 0.05 ms at 50kmissed0.009 ms / 0.0277 ms (800 endpoints); 0.0805 ms with 50k entries of one endpoint (first call 15.74 ms, then 0.0018 ms)
B2100 tag invalidations (realtime burst), 50k entries≤ 5 msmet1.963 ms at 50k (0.4172 ms at 10k), burst + flush
B3one write at 1k / 5k / 20k / 100k entriesflat ± 20 %missedupdate 2.969 / 2.633 / 3.493 / 4.179 µs (client.set 2.162 / 2.177 / 2.971 / 3.429 µs, warm 1.659 / 1.437 / 1.435 / 1.765 µs); new entry 2.353 / 2.765 / 2.378 / 3.479 µs; fetch 9.594 / 9.85 / 10.057 / 9.884 µs (largest deviation from 1k: 59 %)
B4invalidate one tag provided by 5 000 entries (sync part)≤ 5 msmet1.532 ms sync; 24.805 ms to queue the refetches; 70.781 ms settled
B5mount 5 000 observers with fresh and poll≤ 1 platform timer; ≤ 40 msmissed1 platform timer; 71.044 ms mount (20.677 ms unmount)
B6refetch 100k rows, 0 / 1 / all rows changed (row-wise identity reuse, internal)≤ 10 ms with 1 changedmissed18.564 ms / 18.708 ms / 12.071 ms at 100k (0 / 1 / all changed)
B7refetch 50-row page, identical text≤ 0.1 ms, no signal writemet11.003 µs per refetch cycle with text retained (polled), 16.084 µs without; data effect runs: 0 / 0
B8key serialization (canonical string), realistic table params≤ 2 µs, once per params changemet1.294 µs per key (canonical 1.288 µs)
B9register 5 000 endpoints across 500 moduleslinear, constant stack depthmet0.6951 µs per endpoint at 5 000 vs 0.901 µs at 500 (×0.77); 17 frames for the first and the last module
B101 000 live events/s into tagged entries with 10k cachedno task > 50 ms; ≤ 1 request per observed entry in flightmettag rule: longest task 0.5503 ms, 1 in flight per entry, 8.702 ms busy; endpoint patch rule: longest task 0.3163 ms, 18.414 ms busy; entity patch rule: longest task 0.3578 ms, 19.424 ms busy
B11fetching/mutating counters read 10k times during a stormO(1) per readmet0.0141 / 0.0163 / 0.0226 / 0.0228 µs per read with 100 / 1k / 5k / 50k in flight (×1.62 for ×500 requests)
B12persistence flush of 1 000 dirty entries among 10k persistedno task > 16 msmetlongest task 4.104 ms over 3 tasks (4.157 ms in all); 5.997 ms when the partition is over its byte budget
B13a $derived page state over 100 bindings during a burst of 1 000 fetch starts/settlesO(members) per change, no data reads, ≤ 2 ms per burst stepmet0.0607 ms median step, 1.119 ms slowest; 0 data reads
B14an optimistic entity patch across 20 cached 50-row pages: push + display + remove≤ 0.5 ms (F04 M2)met0.0498 ms per cycle
B15`allowed(input)` read for 500 table rows, then one rights change≤ 1 ms per pass; one re-evaluation per row on the changemet0.2164 ms per read pass, 0.6987 ms for the change pass; 1 re-evaluation per row
B16`send(request)` overhead excluding transport≤ the ep.fetch pipeline cost; 0 entries, 0 index writesmet0.6238 µs per send vs 4.241 µs per ep.fetch; 0 entries, 0 index writes
F17entity patch across cached pages≤ 0.5 ms (40 pages + 1 detail); ≤ 1 ms (a row of a 10 000-row page)met0.02 ms on 40 pages + 1 detail; 96.816 µs on a 10 000-row page

Engine benchmarks of docs/query/04 §3, produced by bench/run.mjs. Node, the engine from src under the production condition (#env DEV = false, Svelte browser + production build, compiled with dev: false), a virtual clock and an in-memory transport that answers with an already resolved promise unless a latency is stated. Each entry: WARMUP discarded runs, then RUNS fresh runs (state rebuilt, heap collected), median reported with every sample. A partial run (file filters) replaces only its own ids; the environment is that of the latest run.

Browser benchmarks

Pages of the playground in chromium 153.0.8010.12, production build, CPU throttled 4 times, against a fake server with a fixed latency and six connections; AMD EPYC 9555P 64-Core Processor, 2026-10-04.

P1: Paging quickly through a server-paged DataTable

QuantityMedianp95
19 "Next page" clicks 50 ms apart, first click to the frame where page 20 is shown and nothing fetches; CPU 4×, fake server 150 ms; median of 51118.1 ms1133.2 ms
last of the 19 clicks to the frame where page 20 is shown and nothing fetches; CPU 4×; median of 5183.8 ms189.4 ms
requests that reached the fake server during the 19 clicks; median of 51919
requests aborted after reaching the fake server (pages left before their answer); median of 51818
longtask entries from the first click to the settled frame; median of 501
summed duration of those long tasks; median of 50 ms68 ms

P2: A polled table that receives identical answers

QuantityMedianp95
poll requests answered in 60 s at 2 s; median of 53030
MutationObserver records inside the table body (rows and cells) during 60 s of identical poll answers; median of 500
MutationObserver records in the whole table, including the data-refreshing indicator on its root; median of 5180180
runs of a page $effect that reads list.data during the 60 s; median of 500
main-thread task time (CDP Performance.getMetrics TaskDuration) over the 60 s run, CPU 4×; median of 51341.5 ms1499.58 ms
longtask entries during the 60 s; median of 500

P3: A user click during an invalidation storm on a dashboard

QuantityMedianp95
click "Open details" 500 ms into the storm to the frame showing the details (one 200 ms request); CPU 4×; median of 5213.1 ms218.9 ms
time the details request waited for a fake-server connection (6, FIFO); median of 50 ms0 ms
storm start to the frame where all 40 widgets show the last server version; median of 53523.3 ms3524.5 ms
widget requests that reached the fake server during the run; median of 59090
widget requests aborted after reaching the fake server; median of 500
most requests the fake server held at once; median of 566
longtask entries during the run; median of 500

P4: A large page refetched with one changed row

QuantityMedianp95
fake server answer handed over (JSON text) to the $effect.pre that sees the new value (0 = no commit); CPU 4×; median of 538.9 ms135.8 ms
fake server answer handed over to the $effect after the DOM update of the same flush (0 = no commit); CPU 4×; median of 555.6 ms149.3 ms
fake server answer handed over to the animation frame after the changed cell shows its new text; CPU 4×; median of 51145.7 ms1393.4 ms
control without the library: one direct textContent write into a cell of the same 10 000-row document to the next animation frame; CPU 4×; median of 5264.2 ms296.8 ms
row objects whose identity changed; median of 511
runs of an $effect.pre reading the data; median of 511
fake server answer handed over (JSON text) to the $effect.pre that sees the new value (0 = no commit); CPU 4×; median of 50 ms0 ms
fake server answer handed over to the $effect after the DOM update of the same flush (0 = no commit); CPU 4×; median of 50 ms0 ms
fake server answer handed over to the animation frame after fetching ended; CPU 4×; median of 5805.4 ms1069.5 ms
control without the library: one direct textContent write into a cell of the same 10 000-row document to the next animation frame; CPU 4×; median of 5319.9 ms379.8 ms
row objects whose identity changed; median of 500
runs of an $effect.pre reading the data; median of 500

P5: An optimistic toggle clicked rapidly while the list polls

QuantityMedianp95
aria-pressed changes to a value other than the one the user last chose, during 20 clicks 40 ms apart until two polls after the last write; median of 500
1 when the shown state, the server state and the last choice agree at the end; median of 511
aria-pressed changes observed (20 = one per click); median of 52020
first click to two poll answers after the last write; CPU 4×; median of 58276.3 ms8276.6 ms
list requests (polls and invalidation refetches) during the run; median of 533

P6: The same polled list open in several tabs, with ./tabs

QuantityMedianp95
list requests per minute at the network (30 s window × 2), list polled every 2 s, 1 page(s) of one context with ./tabs; CPU 4×; median of 53030
live connections (EventSource requests) opened, 1 page(s) of one context with ./tabs; median of 511
list requests per minute at the network (30 s window × 2), list polled every 2 s, 2 page(s) of one context with ./tabs; CPU 4×; median of 53030
live connections (EventSource requests) opened, 2 page(s) of one context with ./tabs; median of 511
list requests per minute at the network (30 s window × 2), list polled every 2 s, 5 page(s) of one context with ./tabs; CPU 4×; median of 53030
live connections (EventSource requests) opened, 5 page(s) of one context with ./tabs; median of 511
list requests per minute at the network (30 s window × 2), list polled every 2 s, 5 pages of one context without ./tabs (control); CPU 4×; median of 5150150
live connections (EventSource requests) opened, 5 pages of one context without ./tabs (control); median of 555

P7: A reload with persisted reference data

QuantityMedianp95
reload with a persisted entry: navigation start to the animation frame after the restored 250 rows were first rendered; CPU 4×; median of 5115.2 ms178.7 ms
time covered by IndexedDB store reads (list, get; overlaps once) before that frame; median of 540 ms43.1 ms
store reads before that frame; median of 522
baseline without the persist module: navigation start to the frame after the network answer (800 ms) was rendered; CPU 4×; median of 5870 ms877.4 ms

Browser benchmarks P1–P7 of docs/query/04-performance.md §5 from apps/playground/tests/bench/query-*.bench.spec.ts (Playwright project bench, production build, CPU throttled 4× over the DevTools protocol, the fake server of @svelte-lean/test-utils): each entry is the median of its samples with min, max, p95 and the method in one sentence. Published by `yarn bench:publish` (apps/playground/scripts/publish-results.mjs), which copies test-results/query-bench.json here unchanged with publishedAt set to environment.date. Method: docs/proof/methodology.md. The query.P6.* entries (tests/bench/query-tabs.bench.spec.ts) were recorded on 2026-10-04T14:23:22.864Z in the same environment (machine, browser, mode, CPU throttling) and appended to this file.

Long session

One session of client-side navigations with polled lists, a live source, invalidation storms, hidden periods and freezes; chromium 153.0.8010.12, 2026-10-04.

QuantityMedianMax
JSHeapUsedSize after two forced GCs on the empty route /query/soak, after 50 warm-up navigations and keep (20 s) + 3 s; CPU 4×5816040 B5816040 B
JSHeapUsedSize after two forced GCs every 100 navigations during the session (one sample per batch, on a session page with its entries)25009304 B30814436 B
JSHeapUsedSize after two forced GCs on the empty route after 2000 navigations and keep + 3 s4528280 B4528280 B
heap-end minus heap-baseline; target within 10 % of the baseline (581604 B)-1287760 B-1287760 B
Performance.getMetrics Nodes at the baseline and at the end297297
client.stats() entries minus observed on the empty route after keep + 3 s (target 0)00
client.stats().observed on the empty route at the end: the three polled shell lists33
client.stats().entries sampled every 1 s; the largest sample of each 100-navigation batch178233
client.stats().inactiveBytes sampled every 1 s; the largest sample of each batch (budget 8388608 B)8385278.5 B8388297 B
pending platform timers armed by the package (a counting ClientInit.clock) sampled every 1 s; the largest sample of each batch (target ≤ 1 per client)11
the most platform timers the package had pending at any moment of the session (counted at every setTimer/clearTimer)11
client.stats().timers (items in the scheduler heap behind the one platform timer), largest 1 s sample per batch12.515
listeners on window and document (DOMDebugger.getEventListeners): baseline, every 100 navigations, end (target constant)2828
Performance.getMetrics JSEventListeners after forced GC: baseline, every batch, end3438
long animation frames over 50 ms during the session (PerformanceObserver long-animation-frame; the observer is checked first with an 80 ms timer task)44
of those, frames with a script entry attributed to the package chunk00
script entries attributed to the package chunk whose own duration was over 50 ms (target 0)00
self time in the package chunk per navigation, CPU profile (0.5 ms sampling) over navigations 501–600, 1001–1100, 1501–1600, 1901–2000; one sample per profiled batch; CPU 4×; ./live is inlined in the layout chunk and not counted0.63 ms0.9 ms
the package chunk self time as a percentage of all sampled time in the same profiled batches0.350.5
goto() to the page reporting its data (or 2 s), median of each 100-navigation batch; CPU 4×170.75 ms197 ms
requests answered and aborted by the fake server over the whole run (warm-up and end included)46659036
storms, tag invalidations and live events sent during the session1200019013

Soak benchmark of docs/query/features/F14-memory-long-sessions.md §7 from apps/playground/tests/stress/query-soak.spec.ts (Playwright project stress, SLEAN_STRESS=1, production build, CPU throttled 4×): one long session; each entry lists its samples over the session (per 100-navigation batch, or baseline and end) with median, min, max, p95 and the method in one sentence. Published by `yarn bench:publish` (apps/playground/scripts/publish-results.mjs), which copies test-results/query-soak.json here unchanged with publishedAt set to environment.date. Method: docs/proof/methodology.md.

Type cost

What endpoint definitions cost the type checker as a program grows. M2: instantiations a consumer adds when it imports two endpoints, relative to the smallest program. M3: the whole-program slope, instantiations per endpoint. TypeScript 5.9.3.

ProgramM2 ratioM2 gateM3M3 gate
contract11.161.965
aggregate (control)33.082–66.9–
generated11.161.465

tsc --extendedDiagnostics over generated programs (types-bench/generate.mjs): feature modules of 10 endpoints in the 03a shape, each augmenting Tags and Permissions, plus a consumer importing two endpoints. M2 = instantiations with the consumer minus without it. M3 = whole-program instantiation slope from N=100 to N=3000. "aggregate" is the control shape (one object of every endpoint with generated hook names). "generated" is the same feature mix written by @svelte-lean/query-codegen from an OpenAPI document (types-bench/openapi.mjs: openapi-typescript output, generated modules, one satisfies-typed policy module per feature); it is held to the same gates. Programs read the built declarations (dist/*.d.ts), as a consumer does.

Comparison

A harness runs the browser scenarios and the engine benchmarks against the same pages written with TanStack Query and RTK Query (benchmarks/compare/query). Its results are committed as evidence and are not quoted here: the repository's methodology admits a comparison with another library on the site only when every condition of a fair comparison holds, which these runs do not yet meet (methodology).

Reproduce

from the repository root
yarn build:packages
yarn workspace @svelte-lean/query size     # packages/query/artifacts/size.json and types.json
yarn workspace @svelte-lean/query bench    # packages/query/artifacts/bench.json
yarn bench:publish                         # apps/playground/results/*.json (browser, soak)