Files
rea/docs/web-source-location.md
xeropresenceandmorluto 66c21572de fix(bun): report the real runtime and enforce worker memory budgets on Windows (#1730)
* fix(bun): report the real runtime and enforce worker memory budgets on Windows

Under Bun, REA recorded Node/V8 identity it did not observe and its JavaScript helper processes ran without a memory bound, because Bun ignores the V8 heap flags REA passes. Source-map trace Evidence claimed runtime node-v8 with Bun's synthetic V8 heap limit (the decoder then rejected that limit, so trace_web_source_location always failed), analysis resource failures reported heap limits Bun does not enforce, the HAR decoder claimed a fixed 192 MiB heap, and doctor reported Bun's Node.js compatibility version as the Node version.

Identify the runtime once from process.versions. Source-map reports record id bun with the Bun and Node.js compatibility versions and a null v8_heap_limit_bytes; the decoder checks the heap budget only for V8 reports and rejects a report from a runtime other than the launching one. The analysis worker reports a null heap limit under Bun, HAR limitations and failures omit heap sizes under Bun, and the doctor node check reports Bun and passes when its Node.js compatibility version is supported.

On Windows under Bun, each helper's owned Job Object now carries a commit limit of its heap budget plus a measured 512 MiB Bun runtime reserve (704 MiB for the codec and HAR decoder, 1536 MiB for the default analyzer). The native addon accepts an optional page-aligned job memory limit on process_spawn, verifies it by readback, associates a private completion port before the child is created, and reports a sticky memoryLimitReached and peak job commit on each poll; inspect reports jobMemoryLimit. A requested limit with an artifact lacking the control refuses the launch, and non-Windows launchers refuse a limit.

Reaching the limit is classified by the job notification rather than Bun's exit status: the analyzer fails the current source with a memory resource failure reporting worker_process_memory_limit_bytes, the codec returns a memory resource failure, and the HAR decoder returns its memory failure with process_memory_limit_bytes. Bun source-map Evidence records process_memory_limit_bytes, verified against the launcher. Node.js launches and outputs are unchanged.

* fix(windows): drain memory notifications after observing exit

---------

Co-authored-by: morluto <[email protected]>
2026-10-11 19:20:00 +08:00

7.1 KiB

Captured website source locations

trace_web_source_location maps one selected position in a script retained by export_web_scripts through one explicitly paired local source map. The shared CLI/MCP workflow verifies actual manifest, script and map byte identities and returns the original position and embedded source inline. It acquires no browser, fetches no URL and executes no application code.

rea trace-web-source-location \
  /analysis/scripts/manifest.json 0 \
  /analysis/app.js.map 'https://example.test/maps/app.js.map?v=7#context' \
  1 25 --json

The six positional arguments select the manifest, zero-based script index, absolute map path, absolute map URL context, one-based generated line and zero-based UTF-16 column. The MCP arguments are:

{
  "manifest_path": "/analysis/scripts/manifest.json",
  "script_index": 0,
  "source_map": {
    "path": "/analysis/app.js.map",
    "url": "https://example.test/maps/app.js.map?v=7#context"
  },
  "generated_position": { "line": 1, "column": 25 }
}

The URL supplies the context for resolving sources and sourceRoot; it is not an instruction to fetch. Absolute virtual or file contexts can be selected as well. The map/script association is caller-selected: a declaration or local map does not prove deployment authenticity or execution. Use import tracing to inspect outgoing native module edges.

Meaning of the evidence

Lookup follows the greatest generated position less than or equal to the selected point, across lines, and returns every segment at that position. It includes generated-only mappings. This follows ECMA-426 GetOriginalPositions, with one-based lines in REA's public contract. A point result does not establish coverage of a source range, execution or UI causality.

Mapped entries preserve the raw source name/root, section path, section-local source index and flattened index. Duplicate URLs remain distinct, including entries with different embedded contents. Missing source names retain a null resolved URL. Original coordinates outside embedded text retain the reported coordinates and a null offset.

Embedded content hashes identify the UTF-8 encoding of the decoded sourcesContent string, including its line endings. They do not identify an independently captured original file. Raw map text is retained in Evidence's raw_result, including unknown extensions; these extensions are not interpreted. Capture context and source Evidence links accompany independently verified selected file identities.

Adapter and resource boundaries

The adapter uses the locked upstream @jridgewell/trace-mapping 0.3.31 without modifying its code. It validates map/VLQ structure before allocation, normalizes nested indexed offsets before upstream flattening, checks section boundaries and indexes, and retains raw declaration identity. It supports regular maps and inline indexed sections. External section URLs return an actionable unsupported-profile error. This is a defined decoder profile, not a claim to implement every current source-map extension.

One owned Node subprocess decodes the map. Its private request directory is removed before success is returned. Only this child's inherited NODE_OPTIONS is removed; explicit V8 flags set a 192 MiB old-generation and 8 MiB semi-space budget, one V8 worker and a 20-second deadline. Reported actual V8 heap limits are checked. These are heap limits, not an aggregate process RSS limit. When REA runs on Bun, the subprocess is Bun, which ignores these V8 flags: runtime records id: "bun", the Bun version, its Node.js compatibility version and a null v8_heap_limit_bytes. On Windows, the codec's owned Job Object then limits its committed memory to 704 MiB, the 192 MiB budget plus a 512 MiB reserve for Bun itself; process_memory_limit_bytes records that limit, the limitations state it, and reaching it returns resource_constraint with codec_process_memory_limit_bytes. Elsewhere Bun has no enforced codec limit: process_memory_limit_bytes is null and the limitations state that no codec heap limit is established. Cleanup may extend the deadline and an unconfirmed cleanup overrides success with cleanup_incomplete and the owning operation/resources.

Complete evidence is bounded by a 4 MiB map, 32 MiB codec reply, 262144 decoded rows/segments and 64 nested section levels. Exceeding a budget returns an explicit failure, without partial results. The existing 32 MiB manifest and 16 MiB selected script boundaries also apply. No global toolchain or browser configuration changes are made.

Verification

Browser bundle collection uses absolute section offsets for nested inline maps. An enclosing column offset contributes only when the nested section starts on that enclosing section's first generated line. Parent boundaries still clip preceding browser mappings when a nested section starts with a gap or has no child leaves. Raw map text, original source identities and the dedicated decoder profile are preserved.

Browser collection accepts up to 64 MiB of map responses per inspection and preflights indexed sections, sources, names, generated rows and mapping segments against a 262144-record decode budget before upstream flattening. Indexed maps are limited to 64 nested levels. These structural limits bound decoded object growth while preserving large embedded sourcesContent strings within the response-byte budget. It also bounds the expanded inline evidence to 32 MiB across the inspection, checking resolved source identities and mapping rows before publishing them, and bounds raw resolved-source identities to 64 MiB before upstream materializes their arrays. Exceeding either decoder budget returns a fetch_failed item while retaining other maps. Decoded coordinates and source/name indexes are validated per map; a malformed map is reported as invalid without discarding earlier or later maps. Collection reserves each requested map's failure context before fetching it. When the aggregate inline-result budget cannot reserve another item, remaining requests are left unprocessed and reported as unknown through the top-level limitation; requested retains the full count and processed counts returned items. Malformed UTF-8 map responses are reported as invalid rather than repaired with replacement characters.

Focused tests cover duplicate mappings, indexed offsets/overlap/local indexes, 32-bit VLQ overflow, nullable content, UTF-16/CRLF positions, immutable artifact identity, cancellation, late output closure, cleanup failures, SDK schemas and public CLI behavior. The actual codec process is exercised on the test host.

The optional verify:browser:source-maps lane compiles a source-owned TypeScript fixture with isolated esbuild 0.25.10, serves it to real Chromium, captures and exports the actual response, then checks the original compiler point and exact retained content through CLI and stdio MCP. It can target an installed package. Only this lane requires Chromium and the fixture compiler. Linux x64 is the real workflow covered by the dedicated CI job; other host workflows remain unverified.