Contributor/agent notes. User-facing docs live in README.md; this is the
"how it's built and where the sharp edges are" companion.
A VGI worker (Rust, compiled binary) that decodes
JavaScript Source Map v3 (.map) files and resolves minified (line, col)
positions back to their original source:line:col:name, exposed to DuckDB/SQL
over Arrow IPC (ATTACH 'sourcemap' (TYPE vgi, LOCATION '…')). Functions live
under catalog sourcemap, schema main. Built on the published VGI Rust SDK
(vgi = "0.18.0" from crates.io), arrow 59; modeled on ../vgi-units and
../vgi-fixedformat. Builds standalone — no local SDK checkout.
Deliberately thin (committee scope ≈ 1 week): the Sentry sourcemap crate
(BSD-3-Clause) does all the real work — Base64-VLQ decode, index maps
(sections), inline sourcesContent, the nearest-preceding lookup_token. We
never reimplement any of it; the worker is row-mapping + the lookup_all
table-in-out plumbing.
Cargo.toml workspace; pins vgi 0.18, arrow 59, sourcemap 9
crates/sourcemap-worker/
src/main.rs Worker::new(); registers scalars/tables/table-in-out + catalog metadata
src/lib.rs lib target re-exporting `smap` for integration tests
src/smap.rs PURE engine (no Arrow): decode_slice → flatten → TokenRow/Resolved/SourceRow/Meta + unit tests
src/maparg.rs overloaded `map` arg (path | JSON text | BLOB) → bytes
src/cache.rs last-value decode cache (parse one map once across a column of rows)
src/arrow_io.rs int/pos cell reads, the resolved STRUCT fields, commented-Field helper, test harness
src/meta.rs vgi-lint metadata-tag helpers (keywords_json / object_tags / agent_test_tasks_json)
src/scalar/{lookup,source_content,version,mod}.rs thin Arrow scalar adapters
src/table/{tokens,sources,map_meta,mod}.rs thin Arrow table-producer adapters
src/table_in_out/{lookup_all,mod}.rs the bulk frame-resolution exchange function
tests/golden.rs fixture-driven engine tests (data/*.map)
tests/fuzz.rs proptest: the decoder NEVER panics on arbitrary/truncated input
data/*.map golden fixtures: basic, index (sections), inline sourcesContent, webpack-style
test/sql/*.test haybarn sqllogictest E2E (authoritative)
ci/ run-integration.sh + preprocess-require.awk + check-version.sh
Pattern: keep decoding in smap.rs (pure, unit-tested), keep Arrow marshalling
in arrow_io.rs + maparg.rs + the scalar//table//table_in_out/ adapters
(thin, harness-tested). The bin (main.rs) and lib (lib.rs) both compile
smap.rs — main.rs has mod smap;, lib.rs re-exports pub mod smap; for
tests/ (the vgi-units idiom).
Smap::parse(bytes) calls sourcemap::decode_slice (which strips the )]}'
XSSI guard and handles regular / index / Hermes maps), then normalizes to a
single flattened SourceMap: index maps flatten via SourceMapIndex::flatten,
Hermes maps reduce to their embedded regular map. Token/lookup/source access is
then uniform. meta() reports version (always 3), file, source_root, and
counts (n_sources/n_names/n_mappings over the flattened token list).
get_source already joins the map's sourceRoot onto each path (so a
sourceRoot of /src yields /src/index.js) — that's the resolved path you
want for de-minification, and meta.source_root still surfaces the root
separately.
-
All coordinates are 0-indexed (Source Map v3 / the crate). Both the generated
(line, col)you pass tolookup/lookup_alland every*_line/*_coloutput. Document this loudly; browser stack frames are usually 1-indexed. -
Nearest-preceding, not exact.
lookup_tokenreturns the greatest token<= (line, col). A genuine no-match → NULL therefore only happens when the position precedes the first mapping; a position after all mappings resolves to the last token (that's why the basic fixture's first token starts at generated column 2, so(0,0)is a real no-match in tests). -
NULL-vs-error policy. Per-row resolution (
lookup,lookup_all) treats a NULL/malformed/empty/unreadable map, a NULL position, or a no-match as NULL resolution — never a query error — so a column of dirty frames doesn't abort a scan. The whole-map table functions (tokens/sources/meta) take a constant map and DO error on a bad path/parse (the call is about that one map). The split lives inmaparg.rs(per-cell, tolerant) vstable::load_const_map(strict). -
The overloaded
maparg.maparg::map_bytes_celldecides path-vs-text by a leading{(or)]}'); everything else is read as a file path.BLOBcolumns are used verbatim. There is no HTTP fetch ofsourceMappingURL— the caller supplies the map. -
lookup_allis table-in-out. Input relation(SELECT id, line, col FROM …)+ namedmap :=/id :=/line :=/col :=(defaultsid/line/col).on_bindclones the inputidfield verbatim (passthrough keeps its name AND type) and validates the line/col columns exist;processdecodes the single map once per batch and emits[id, source, src_line, src_col, name]. A bad map → all rows NULL resolution but the id still passes through. -
read_textis a TABLE function in DuckDB, not a scalar — so pass the map path directly (lookup('app.min.js.map', …)), which the worker reads, or inline JSON text. The E2Efiles.testuses path mode for exactly this reason. -
Decode cache.
cache::MapCachememoizes the last-decoded map keyed by a hash of its bytes, so a column of rows referencing the same deployed map parses it once. Hash-only key (negligible collision risk, accepted).
cargo test --workspace --all-features # smap unit + golden fixtures + proptest fuzz + arrow-boundary
cargo clippy --all-targets --all-features -- -D warnings && cargo fmt --all -- --check
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --workspace
# E2E (subprocess|http|unix), needs haybarn-unittest + community vgi extension:
HAYBARN_UNITTEST="$(command -v haybarn-unittest)" \
WORKER_BIN="$PWD/target/release/sourcemap-worker" TRANSPORT=subprocess ci/run-integration.shtest/sql/*.test use LOAD vgi; (not require vgi) + require-env VGI_SOURCEMAP_WORKER. Scalar/table tests SET search_path='sourcemap.main';
lookup_all.test does NOT (it CREATEs a temp table, so it fully-qualifies the
sourcemap.main.lookup_all call instead of pointing search_path at the
read-only worker catalog). Fixtures in data/ are staged next to the runner so
path-mode tests resolve.
Per-object discovery tags come from meta::object_tags; the catalog/schema
metadata + source_url + agent-test tasks live in main.rs. Every example query,
executable example, and agent task runs against inline map text, so they bind
AND execute offline — the worker passes clean (0 findings at info, score 100) at vgi-lint --fail-on info
(see vgi-lint.toml). Watch out for VGI313 (don't name a data type in an
argument description) and VGI310 (don't type every argument ANY — the position
args are BIGINT, only the genuinely-overloaded map is ANY).
Rule notes worth keeping in mind:
- VGI515 — a described example (
{description, sql}) is required per object. AFunctionExample's description is dropped byduckdb_functions().examples, so each function ALSO emits a byte-identicalvgi.example_queriestag (meta::example_queries_json); the schema'svgi.example_queriesis likewise a described-JSON list, not a newline-joined SQL blob. - VGI328 — no parameterless
*_version()scalar. The build version is the catalog'simplementation_version(set incatalog_metadata, read fromvgi_catalogs());version()inmain.rsfeeds it. - VGI327 — no self-cataloguing
functionsregistry view (it duplicatesduckdb_functions()). - VGI146 — a worker with table functions still needs ONE browsable table/view,
so
example_tokens(a data view decoding the built-inSAMPLE_MAPinto token rows) is that entry point. It is NOT a function registry (its columns are token data, not name/kind/category), so it satisfies VGI146 without tripping VGI327. - VGI182 — backtick DuckDB type names in prose docs (
BLOB,STRUCT(...)). - VGI131 — numeric column comments want a unit/definition hint (a
(parenthetical satisfies it); it checks table/view columns, not table-function result columns.
Scalars: lookup (→ STRUCT(source, src_line, src_col, name)), source_content
(→ VARCHAR). Table functions: tokens, sources, meta. Table-in-out:
lookup_all (bulk, id passthrough). One browsable view: example_tokens (the
decoded token rows of the built-in SAMPLE_MAP, for discovery). The running build
is published as the catalog's implementation_version, not a scalar.