Commit 29fae38
feat(wasm-viewer): Save as Report works offline via shared report-save crate
The static-site WASM viewer's Save as Report button was POSTing to
`/api/v1/save_with_selection`. On rezolus.com (a static deployment
with no backend) the CDN returned 405 Method Not Allowed. Port the
trim + tarball-repack flow into a shared workspace crate so both the
server viewer and the WASM viewer run the same projection logic.
## Shared crate: `crates/report-save/`
Single source of truth for the trim/embed/tarball flow:
- `ReportPayload` / `ReportEntry` / `Side` payload types
- `resolve_kept_columns` (generic over `T: Deref<Target = Tsdb>`)
- `save_single_parquet(Bytes, …) -> Vec<u8>`
- `save_combined_ab_tarball(Bytes, Bytes, manifest_bytes: &[u8], …) -> Vec<u8>`
- All operating on `metriken_query::Bytes` — the unified primitive
the server reads paths into and the WASM viewer already holds.
- 10 unit tests covering kept-column resolution + the full round-trip
for both trim/no-trim and single/AB paths.
The shared crate is manifest-agnostic: it takes pre-serialized
manifest bytes for the AB tar entry, so it doesn't need to know about
either crate's `AbContainers` struct.
## Server-side shim: `src/viewer/report_save.rs`
Reads paths into `Bytes`, serializes the binary crate's `AbContainers`,
delegates to `report-save`. ~70 LOC (was ~250 LOC of trim logic + tests).
## WASM-side shim: `crates/viewer/src/report_save.rs`
Synthesizes an `AbContainers` manifest from the two attached viewers
(compare mode loads two separate parquets — there's no pre-existing
tar manifest), serializes it, delegates to `report-save`. The WASM
crate keeps its own `AbContainers` definition since it doesn't depend
on the rezolus binary crate.
`Viewer` now keeps the original parquet `Bytes` alongside the Tsdb so
the trim writer can project columns from the source schema (the Tsdb
has lost Arrow field metadata like the `metric` key that
`parquet filter`'s keep-field predicate needs).
`WasmCaptureRegistry::save_with_selection(payload_json)` dispatches
based on slot attachment.
## Unified JS surface
Both `site/viewer/lib/viewer_api.js` (WASM) and
`src/viewer/assets/lib/viewer_api.js` (server) expose
`saveWithSelection(payload) -> { bytes, mime, extension }`.
`saveToParquet` in `selection.js` calls through `ViewerApi` instead of
raw XHR — swaps the filename's `.parquet` suffix for the reported
extension so AB tarballs land as `*.parquet.ab.tar`.
## Cost
WASM bundle: 3.6 MB → 4.3 MB (parquet writer + tar). The `no_asm`
zstd-sys feature is target-scoped — resolver 2 doesn't propagate it
to `cargo build --bin rezolus`, so the standalone binary's zstd
keeps its x86 asm path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 5e0cc7b commit 29fae38
11 files changed
Lines changed: 910 additions & 661 deletions
File tree
- crates
- report-save
- src
- viewer
- src
- site/viewer/lib
- src/viewer
- assets/lib
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
30 | | - | |
| 31 | + | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| |||
121 | 122 | | |
122 | 123 | | |
123 | 124 | | |
| 125 | + | |
124 | 126 | | |
125 | 127 | | |
126 | 128 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
0 commit comments