Build and platform maintenance
This page explains how to keep Meratyn healthy over time: dependency updates, the compatibility mirrors that silent bumps can break, the verification gates every change must pass, and the platform-specific policies the desktop shell applies at runtime.
Dependency maintenance
One root bun.lock wires every JavaScript workspace (apps/*,
packages/*, services/*), so a single Bun entry covers the whole JS
tree. Python and Rust ecosystems have their own manifests and lockfiles.
Dependabot opens
grouped weekly PRs for each ecosystem (.github/dependabot.yml):
| Ecosystem | Directory | Notes |
|---|---|---|
bun | / | One lockfile for all JS workspaces |
pip | /services/server | The FastAPI sidecar |
pip | /python | The meratyn Jupyter anywidget |
cargo | /apps/mappview/src-tauri | The Tauri desktop crate |
github-actions | / | Workflow actions |
Security fixes always land as their own immediately actionable PRs;
routine minor/patch bumps are batched into one PR per ecosystem. The CI
audit job runs bun audit --audit-level=high (blocking) plus a
non-blocking pip-audit of the resolved backend environment.
When reviewing a dependency PR:
- Run the full local gate (
bun run ci) before merging — several libraries are mirrored or pinned by behavior, and the suite catches drift (see the next section). - Refresh the sidecar lockfile whenever
services/server/pyproject.tomlchanges:uv lock --project services/server. The lock is committed because the desktop installers launch the sidecar withuv run --frozenfrom a read-only resource directory; a stale lock fails CI’s “Check the bundled sidecar lockfile is in sync” step, and a missing one fails at the user’s desktop with an opaque exit code 2. - Re-check the Tauri plugin option contracts described under
Windows native sidecar transport
when
@tauri-apps/plugin-httpis upgraded.
Guarded compatibility mirrors
Some external libraries expose constants or class names Meratyn depends on but does not export. Copies live in our source and must be re-checked whenever the external package moves (including Dependabot PRs). Each has a test that fails in CI when the mirror drifts, so running the frontend suite after a bump is usually enough:
| Mirror | Mirrors | Owner in this repo | Re-check when |
|---|---|---|---|
MAX_VECTOR_PMTILES_ZOOM | The deepest zoom vector_to_pmtiles accepts (18), compiled into the WASM binary | packages/processing/src/conversion/wasm.ts | @mappview/wasm is rebuilt |
| Whitebox menu catalog | The WASM tool registry | apps/mappview/src/lib/whitebox-menu-catalog.ts (generated, do not hand-edit) | @mappview/wasm is rebuilt: run bun scripts/assets/whitebox-catalog.ts and commit the result |
MAX_VECTOR_BYTES | maplibre-gl-vector’s internal MAX_REMOTE_FILE_BYTES (2 GiB) | packages/plugins/src/plugins/shared/remote/file-formats.ts | maplibre-gl-vector is bumped |
MAP_PANEL_SELECTOR | Rendered class names from maplibre-gl-components | apps/mappview/src/features/recording/video/operations.ts | maplibre-gl-components is bumped |
propertySpecFor | @maplibre/maplibre-gl-style-spec’s unexported StylePropertySpecification | packages/core/src/style/expressions.ts | @maplibre/maplibre-gl-style-spec is bumped: run tests/styles/expressions.test.ts |
Verification gates
Every change should pass the full local gate:
bun run ci
This runs lint, the web build, workspace type checks, the frontend suite with coverage, the worker type check, backend coverage, and a Rust check of the Tauri crate.
- Coverage floors are a ratchet:
test:frontend:coveragefails below 78% lines / 78% branches / 63% functions, andtest:backend:coveragefails below 55%. When coverage rises comfortably above a floor, raise the floor to lock in the gain. - The backend coverage run needs
pytest-cov; installing thetestextra (pip install -e "services/server[test]") enables the optional engine tests (geopandas/rasterio/sedona/httpx) that otherwise skip. .pre-commit-config.yamlincludes a localbun-buildhook that compiles the whole app; scope it to the files you changed withpre-commit run --files <paths>.- Playwright smoke tests live under
e2e/and run against the built web app viabun run test:e2e.
Platform compatibility
The rest of this page documents platform-specific policies the desktop shell applies at runtime. They are part of the maintenance surface: when a dependency in the chain (WebKitGTK, WebView2, Tauri plugins) changes, re-read the relevant section before shipping.
Redistributable artifact boundary
Meratyn’s web build only exposes the VITE_ names listed in
apps/mappview/vite.config.ts. Unknown names are removed before Vite
resolves the explicit client environment (their process values are blanked
first). Embed builds (MAPPVIEW_EMBED=1), Tauri builds, and any
build with MAPPVIEW_STRIP_CREDENTIALS=1 blank provider credentials so a key from
the build machine cannot enter a wheel or desktop installer. Users provide their
own provider keys at runtime in Settings → Environment variables.
The boundary is enforced twice:
scripts/scan-credentials.tsscans emitted JavaScript, CSS, HTML, JSON, maps, and text files for known provider-token shapes, credential environment assignments, and URLs containing userinfo. It redacts matches in diagnostics.python/hatch_build.pyruns the same scan for every wheel/sdist build, including whenpython/src/mappview/static/appalready contains a stale build.
The scanner shares its definitions through scripts/credential-patterns.json.
The only allowlisted token is CesiumJS’s vendor-owned public Ion token; refresh
its hash only after confirming a dependency upgrade still ships that token and
identifies CesiumJS. Do not allowlist Meratyn, provider, or operator keys.
Linux WebKitGTK/NVIDIA pacing
At startup the desktop shell selects a compatibility policy from the WebKitGTK version and the active NVIDIA renderer:
| WebKitGTK / renderer | Default policy |
|---|---|
| older than 2.48 | WEBKIT_DISABLE_DMABUF_RENDERER=1 |
| NVIDIA, 2.48–2.51 | WEBKIT_DISABLE_DMABUF_RENDERER=1 |
| NVIDIA, 2.52 or newer | keep DMABUF enabled and set WEBKIT_DMABUF_RENDERER_FORCE_SHM=1 |
| non-NVIDIA, 2.48 or newer | no override |
An explicit WEBKIT_DISABLE_DMABUF_RENDERER or
WEBKIT_DMABUF_RENDERER_FORCE_SHM value always wins. CI covers the pure policy
matrix; actual driver/WebKit combinations remain distribution-specific, so a
blank window or frame drops should be reproduced with the distro’s WebKitGTK
and NVIDIA packages before changing the defaults.
Linux CPUs without AVX
On x86 Linux, JavaScriptCore’s WebAssembly tier-up trampoline can execute AVX
instructions without checking the CPU. A machine without AVX can therefore
lose its WebKit web process with SIGILL as soon as DuckDB-WASM or Whitebox
gets hot, leaving a blank desktop window. At startup Meratyn checks the
runtime feature bit and, only when AVX is absent, sets both options below before
WebKit starts:
JSC_useWasmOSR=false
JSC_useBBQTierUpChecks=false
The options are an all-or-nothing pair: either one left on still reaches the
unsafe trampoline. An explicit value parsed as on (true, yes, 1, or an
unknown value) on either option is an opt-out and leaves both untouched; an
explicit off value (false, no, 0, case-insensitive) is preserved while
the missing peer is filled in. WebAssembly remains baseline-compiled, so this
preserves functionality with a bounded performance cost. arm64 and CPUs with
AVX are unchanged. The pure decision and JavaScriptCore value matrix live in
the Tauri Rust tests; do not replace this runtime feature check with a CPU model
or release-date list.
Windows native sidecar transport
Windows WebView2 can reject the local processing sidecar because of browser
CORS and Local Network Access policy. The desktop bootstrap therefore installs
Tauri’s native HTTP fetch on Windows before mounting the app. Only the exact
loopback origin http://127.0.0.1:8765/* is sent through that client; custom
VITE_SIDECAR_URL values and same-origin /sidecar deployments keep browser
fetch. The matching Tauri capability is in
apps/mappview/src-tauri/capabilities/default.json.
Two plugin options are security and connectivity invariants, not merely type choices:
maxRedirections: 0prevents the per-launchX-MappView-Tokenfrom being replayed to another host by a redirect.proxy: { all: { url: "http://127.0.0.1:8765", noProxy: "*" } }disables reqwest’s system-proxy lookup and excludes every host from the supplied proxy, keeping loopback traffic direct even on a corporate-proxied machine.
The option names are derived from @tauri-apps/plugin-http’s fetch type and
the adapter tests pin the exact values. When that plugin is upgraded, re-check
the reqwest/hyper-util behavior as well as the compiler-checked names: neither
redirect nor proxy semantics are guaranteed by the TypeScript declarations.