npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wasm-idle/llvm-core

v1.0.6

Published

Browser LLVM runtime hosts for wasm-idle

Readme

@wasm-idle/llvm-core

Browser-side LLVM runtime hosts used by wasm-idle. This package contains JavaScript and TypeScript code only. Compiler modules, sysroots, archives, and other runtime assets must be deployed separately and supplied through explicit HTTP(S) URLs. Published tarballs omit generated source and declaration maps because their TypeScript source tree is not part of the package; executable JavaScript, declarations, and every documented export remain available.

import { createClangCompiler } from '@wasm-idle/llvm-core/clang';

const compiler = await createClangCompiler({
	runtimeBaseUrl: new URL('https://cdn.example.com/llvm/clang/')
});

The corresponding compiler patches, source pins, and reproducible asset producers are maintained in the wasm-llvm repository. This package does not read files from that repository or its npm package.

The wasm-idle sync step deterministically repackages the producer's single-entry archives as native gzip delivery assets. Clang uses memfs.wasm.gz, clang.wasm.gz, lld.wasm.gz, and sysroot.tar.gz; COBOL uses cobc.wasm.gz, rootfs.tar.gz, and c-sysroot.tar.gz. The browser loader pipes gzip response bodies through DecompressionStream('gzip'). Runtime manifests from older external deployments may still reference ZIP files; those load fflate only on the legacy compatibility path.

The bundled application additionally pins two derived sysroot archives from the same verified Clang source: c-sysroot.tar.gz and cpp-addon.tar.gz. A host can opt in by supplying compiler.sysroot.profiles.c.asset and compiler.sysroot.profiles.cppAddon.asset in its runtime manifest. The C base mounts at startup; the add-on mounts before C++ compilation/linking, including mixed workspaces and opaque @response or -x arguments. Manifests without a profile field keep the original full sysroot.tar.gz path; partial profile pairs are rejected. Hosts must independently pin and verify every asset they opt into; the package does not trust a profile merely because its path appears in a manifest.

The bundled Clang runtime-manifest.v1.json is routed through the same worker asset bridge as its four delivery assets. The compressed files remain pinned against their producer receipts in CI, while the browser bridge normalizes transparent HTTP gzip decoding and pins the exact decoded runtime bytes that it transfers to the compiler worker. When HTTP Content-Encoding: gzip hides the original delivery bytes, the bridge verifies the decoded size and digest; loaders that return raw gzip bytes continue to verify both the compressed delivery and decoded runtime identities. The manifest is pinned directly, so the compiler cannot consume unverified metadata before loading an otherwise verified binary.

LLDB debug sessions

@wasm-idle/llvm-core/debug provides the low-level browser session that connects a dedicated LLDB worker to a dedicated WAMR target worker. DAP and GDB RSP are carried on separate SharedArrayBuffer byte queues, and stdin, stdout, and stderr use independent queues. Zero-length asynchronous and blocking reads complete immediately, so an empty consumer buffer cannot wait indefinitely for a queue signal. Blocking operations accept a non-negative finite timeout or Infinity; invalid values are rejected before bytes move or a wait begins. Queue generations are integers from 1 through 2147483647 because the transport stores them in signed Int32Array metadata. Each descriptor must provide distinct control and data buffers so payload writes cannot overwrite queue metadata. Worker transport bindings additionally require DAP input/output as a complete pair and reject every control or data buffer reused across RSP and DAP directions, so one logical stream cannot consume or corrupt another. Direct DapClient construction applies the same buffer-isolation rule to its input/output pair. The target Worker extends that validation across both RSP directions and every configured WASI stdin/stdout/stderr queue before claiming the session generation or loading WAMR. Attaching a descriptor also validates its closed-state flag and cursor distance before runtime code can consume it. Every queue state accessor, including the closed-state check used during cleanup, rejects stale-generation metadata. Session and worker disposal isolate each queue close, so one corrupt transport cannot prevent the remaining queues from closing, worker-global transport state from being released, or either Worker from terminating. The final target-output drain applies the same isolation so a stale channel cannot suppress the target exit or worker-error lifecycle event. The session verifies the module, source, loader, Wasm, and pthread-sidecar hashes before creating either worker. Module inputs must be Uint8Array or ArrayBuffer bytes, source contents must be strings, and any provided module or source SHA-256 must contain exactly 64 lowercase hexadecimal characters. Invalid artifact inputs fail before runtime asset preflight or Worker creation. Debug manifest assets must use canonical relative URL paths; percent-encoded dot segments and path separators are rejected before they can escape or change the configured runtime root. When initialize() starts, it snapshots the module and its expected hash, source files, configured breakpoints, and launch arguments used by the workers and DAP. Mutating the caller-owned option objects while integrity and asset verification are in flight therefore cannot change the already validated debug target. The runtime manifest is parsed into an independent snapshot and the asset base URL is captured at the same boundary, so later mutations cannot redirect LLDB or WAMR asset loading. Transport queue capacity, the asset fetch implementation, and the worker factory are also captured before verification starts, preventing in-flight option changes from resizing queues or replacing the code that loads assets and creates workers.

Initial attach input is limited to 256 workspace source files and 256 source-breakpoint requests. Across those requests, each source may contain at most 1,024 lines and all initially configured sources may contain at most 4,096 lines in total. Repeating a source does not reset its per-source allowance. A max-plus-one input is rejected before runtime asset fetching or Worker creation. After LLDB sends initialized, setup can therefore issue no more than 256 sequential setBreakpoints requests. Each retains its own transport-write and DAP response deadlines, followed by the bounded configurationDone and final attach-response waits; the attach request itself is intentionally not given a cumulative deadline while configuration is still in progress.

import { createBrowserLldbSession } from '@wasm-idle/llvm-core/debug';

const session = await createBrowserLldbSession({
	manifest,
	runtimeBaseUrl: new URL('https://cdn.example.com/llvm/clang/'),
	module: artifact.bytes,
	moduleSha256: artifact.debug?.moduleSha256,
	sources,
	launch: {
		program: '/workspace/program.wasm',
		stopOnEntry: true,
		args: ['demo'],
		env: { MODE: 'debug' },
		cwd: '/workspace'
	},
	onMemory: (worker, bytes) => {
		console.info(`${worker} linear memory: ${bytes} bytes`);
	}
});
await session.initialize();
await session.setBreakpoints({ path: '/workspace/main.cpp' }, [12, 18]);

Configured and dynamic breakpoint lines must be positive safe integers. The product session validates and snapshots them before changing its per-source state; invalid configured lines are rejected before debug workers are created, while invalid dynamic lines are rejected before a DAP request is created and do not supersede an in-flight valid update. Values above Number.MAX_SAFE_INTEGER are rejected instead of being rounded in a DAP message.

setBreakpoints() updates the session's resolved-breakpoint cache as well as sending DAP. Later breakpoint events therefore retain the IDs and source mapping from dynamic editor changes; only new, changed, and removed reasons can mutate the cache, while malformed reasons remain available to raw event listeners without changing state. Out-of-order responses cannot replace the newest cached set. A superseded call resolves to that current snapshot even when its obsolete DAP response fails; only the current request propagates its failure. Use getResolvedBreakpoints(path) to read the current normalized snapshot. Returned breakpoint and source objects are isolated copies, and the request source is captured before awaiting DAP. Callers and DAP event listeners therefore cannot mutate the cache through a retained reference or an in-flight request argument. If the response omitted an ID, a later event can promote the matching source line to its assigned ID. Once an ID is known, later events match it before comparing the resolved source path; the cache remains keyed by the source whose breakpoints were requested even when LLDB reports the executable location in another file. IDs retired by a subsequent source replacement remain ignored, so delayed events cannot attach themselves to a new ID-less breakpoint on the same line.

Compile C/C++ LLDB artifacts with compileArtifact(..., { debugMode: 'lldb' }). They contain untouched source, embedded DWARF, stable /workspace/... paths, and exact Clang provenance. Lowercase .c, .cc, .cpp, and .cxx workspace siblings are compiled as independent translation units in canonical path order and linked through collision-free internal object names; headers and data files remain mounted without being compiled. Normal and LLDB runs therefore use the same multi-TU program. Trace instrumentation remains single-TU and rejects a workspace with additional translation units instead of silently omitting their debug metadata. Source-level compileArgs such as definitions and warning controls remain available, but LLDB mode rejects target triples, CPUs, target features, machine attributes, raw LLVM backend options, and thread, SIMD, atomics, memory64, shared-memory, or multi-memory switches before invoking Clang. This keeps the producer inside the pinned wasm32 WAMR classic-interpreter profile instead of letting a browser-valid artifact fail only after the debugger runtime has started. Before fetching debugger assets or creating Workers, the LLDB session structurally validates the guest as a bounded core WebAssembly v1 module for the supported WAMR profile. Only the 45 wasi_snapshot_preview1 function names and signatures emitted by pinned WASI SDK 33 are accepted; unknown names, ABI mismatches, non-function imports, and legacy or extension namespaces fail closed. Initial guest memory is limited to 2,048 pages (128 MiB), and an initial table is limited to 1,000,000 elements, so compact modules cannot defer oversized startup allocations to the WAMR Worker. SIMD, atomics and shared memory, memory64, table64, multiple memories or tables, multi-memory instruction encodings, extended constant expressions, typed references, exception handling, GC instructions, dynamic linking, and relocatable modules also fail with an explicit unsupported-module error. The target Worker repeats this check before claiming its generation or loading WAMR, so direct Worker messages cannot bypass the preflight boundary. compileLinkRun() rejects lldb because an LLDB artifact must not be instantiated through the normal browser execution path. Launch arguments, WASI environment variables, and the /workspace working directory are forwarded to WAMR rather than being applied only to LLDB's attach request. Launch configuration is validated before runtime assets are fetched or workers are created: the program and working directory stay at their reserved /workspace paths, entry pause is boolean, arguments are strings without NUL bytes, and environment keys/values follow WAMR's --env format. The target Worker repeats the working-directory, collection-shape, value-type, and byte-level argument/environment checks as a defense-in-depth boundary for direct worker messages. It performs that validation before claiming the session generation, creating transports, loading WAMR, or mounting files, so a rejected message neither executes runtime code nor leaves the Worker occupied. Once a target generation is active, initialization for a competing generation is rejected before starting another lifecycle; its failure cannot close the live target's stdout or stderr queues. Both Workers treat an initialization replay carrying their already-active generation as idempotent: they neither start the runtime twice nor emit a fatal current-generation error. A competing generation is still rejected explicitly. At the direct Worker boundary, both runtimes require a non-empty generation string without NUL bytes before claiming any state. This preserves the generation guard's truthiness and prevents an LLDB argv identifier from being truncated. If WAMR loading or target setup fails after claiming a generation, the target Worker closes every active RSP and WASI stream, clears its worker-global transport, and releases that generation before reporting the error. A later valid initialization can therefore recover without reusing stale queues. If target disposal wins while the Emscripten loader or factory is pending, initialization stops at that async boundary; it does not mount files, report readiness, or enter WAMR with already-closed streams. The LLDB Worker applies the same recovery contract to all DAP and RSP streams and clears its shared ring registry when its loader, module setup, or adapter lifecycle fails. It releases the failed generation before emitting the worker error, so a replacement adapter cannot observe stale connections. LLDB disposal also wins at both pending Emscripten loader boundaries, preventing a late module from mounting the program, reporting readiness, or entering the adapter main loop after its DAP/RSP queues have closed. The same preflight-validated /workspace/... files provided to LLDB are mounted into WAMR's MEMFS before launch, so guest WASI file access observes the workspace used to compile the DWARF artifact. File paths must remain canonical beneath /workspace, and /workspace/program.wasm is reserved for the debug target. The shared LLDB/WAMR mount boundary revalidates the program byte view plus every source entry, content value, canonical path, and duplicate path before making any MEMFS call. A malformed direct Worker artifact therefore cannot leave a partially mounted target. The target launch also reserves a 1 MiB WAMR app heap for the debugger's scratch region, matching the verified native source-debug baseline, and runs WAMR at verbosity zero so runtime diagnostics do not contaminate the guest's stdout/stderr streams. The LLDB producer runs its blocking DAP main loop on an Emscripten pthread. Its JavaScript proxy-main stub returns as soon as that pthread starts, so the LLDB worker remains alive until the runtime reports a real exit, abort, or session disposal. LLDB may defer continue, next, stepIn, and stepOut responses until the target stops again. Those execution requests therefore opt out of the DAP response timeout while retaining the transport-write timeout; ordinary DAP requests still use the configured response deadline. The initial attach response is likewise causal: LLDB sends it only after the client has installed every source's breakpoints and sent configurationDone. Attach therefore has no cumulative response deadline, while the initialized event, each breakpoint request, and configurationDone retain their individual configured timeouts. Once configurationDone succeeds, the pending attach must complete within one configured request timeout; a missing final attach response fails and disposes the session instead of leaving startup pending indefinitely. A deferred execution failure is fatal only while that request still owns the current run state. If a valid continued or newer stopped event has already advanced the session, the obsolete failure is ignored and cannot tear down the running target or its newer pause. Pause requests are ordered separately. Once a stopped event has consumed a pending interrupt (or a newer pause or execution request supersedes it), a late pause failure is ignored instead of being reported after the target is already paused; a failure from the current request still propagates. Configured DAP response, transport-write, and worker-ready timeouts must be positive finite millisecond values. The session validates and snapshots them before loading assets or creating workers, so invalid or concurrently changed timer settings cannot produce immediate or overflowing browser timers. Per-request DAP response timeout overrides follow the same numeric rule and are rejected before the request is sent; null is the explicit opt-out for execution requests whose response arrives only after the target stops. Product scopes, variables, readMemory, and writeMemory calls validate frame IDs, variable references, pagination offsets/counts, and memory offsets/counts as safe integers before sending DAP. Invalid caller input rejects with RangeError without changing the selected frame, stopping the session, or creating a Worker request. DAP requests are fully encoded before pending transport state or send timers are registered. Circular, BigInt, or otherwise non-JSON arguments reject the returned Promise with the original encoding failure as its cause, while the client remains available for later valid requests. Request commands are likewise checked for a non-empty string before encoding or touching the transport, so invalid low-level client calls cannot become opaque response timeouts. DAP responses are correlated by both request sequence and command. A mismatched command is treated as a transport protocol failure: every pending request is rejected and both byte queues are closed instead of resolving a request with another command's body. Each queue close is best-effort and isolated, so stale or corrupt metadata in one queue cannot block closing the other queue or rejecting every pending request with the original failure. Response envelopes are also validated at runtime: request_seq must be a positive safe integer, command a non-empty string, and success a boolean; an optional failure message must be a string. Malformed values fail the stream instead of timing out, stringifying an arbitrary object as an adapter error, or treating a string such as "false" as success. The session also validates the initial DAP capability body before finishing startup. It must be an object, and every capability consumed by the browser integration must be boolean when present. Malformed capability data raises DapProtocolError and disposes both workers instead of publishing an initialized session with corrupt feature detection. Every incoming DAP message must also carry a positive safe-integer seq and a recognized request, response, or event type. Event names must be non-empty strings. Invalid common or event envelopes fail the stream before listener dispatch or request timeout can hide the protocol error. The browser client does not implement adapter-to-client reverse requests such as runInTerminal. Receiving one fails and closes the DAP stream explicitly, instead of silently leaving LLDB waiting for a response that the browser host cannot provide. Reverse-request commands are validated before the unsupported-operation error is reported. The live playground session also validates the command-specific bodies it consumes after envelope parsing. Malformed scopes, variables, readMemory, writeMemory, or evaluate data raises a ProtocolError, stops the debug view, and disposes both workers. In particular, memory data must be valid Base64 and the readable-plus-unreadable total cannot exceed the requested byte count; expression fallback to ? applies only to ordinary LLDB evaluation failures, not to a malformed successful response. Optional namedVariables and indexedVariables counts are also validated and retained on scopes and variables, along with a variable's evaluateName, so lazy paging and later evaluation do not lose metadata at the product adapter boundary. Stopped-state publication has the same boundary: stopped, continued, and exited event fields plus the follow-up threads and stackTrace responses are checked before they can change the selected thread, frame, source location, call stack, stopped-snapshot generation, or process exit code. A malformed value fails and disposes the live session instead of publishing a corrupt pause, resume, or exit snapshot. A valid continued event invalidates every in-flight stopped-state lookup. Late stackTrace or scopes success and failure therefore remain ignored and cannot stop a target that has already resumed. Explicit frame selections are ordered independently as well. Only the newest successful scopes request can change the frame used by watch evaluation, while a protocol error from a superseded selection rejects only its original caller and does not dispose the live session. Lazy variables, watch evaluate, and readMemory calls also capture their stopped-state generation before sending DAP. Once the target resumes or the selected frame changes, late valid or malformed replies resolve to [], ?, or null respectively and cannot terminate the current session. A malformed reply for the current stopped state remains fatal. BrowserLldbSession also normalizes every resolved breakpoint before updating its cache. A malformed current setBreakpoints response rejects with the exported DapProtocolError; a stale response remains ignored, while an omitted per-line result stays unverified at its requested location. A malformed asynchronous breakpoint event is delivered to raw listeners but cannot mutate the cache used by the playground. For the live playground, a current DapProtocolError during initial configuration or a later breakpoint update is converted to the shared Core ProtocolError. The session publishes one stop, disposes both debug workers, and rejects its completion with the low-level error retained as the cause. Other active initialization failures preserve their original Error while following the same single-stop and Worker-disposal lifecycle, including failures while flushing startup input. Target stdin writes are serialized on a recovered internal tail. Disconnect aborts a blocked write with the standard session-disposed error, waits for the recovered tail, and still permits an already queued EOF operation to settle without an unhandled rejection. The playground additionally captures the owning session generation for each flush: disconnect, target exit, and failure discard the retired generation's queued input and EOF, so a late write completion cannot feed a replacement debug session. A current-generation stdin transport failure remains fatal and is reported through the normal single-stop session completion path. Reusing a playground session controller is also serialized across teardown. A relaunch reserves its own lifecycle and completion before awaiting the prior Worker disposal, so a concurrent disconnect can still cancel that wait and two starts cannot pass the same teardown barrier. Finish, failure, and disconnect capture the completion they own before publishing stop; a synchronous relaunch from that callback therefore cannot be settled early by the retired generation. If teardown itself fails, disconnect rejects both its own operation and the completion it owns with that same error, so a cancelled relaunch cannot remain pending behind a failed disposal barrier. Completion settlement also precedes the final stop notification during an explicit disconnect, and throwing consumer stop callbacks are isolated from finish, failure, and disconnect teardown semantics. Direct DapClient consumers can set onEventError(error, event) to observe an event-listener exception. Throwing listeners and a throwing error hook are isolated from one another, and the client continues parsing later events and responses on the same byte stream. DapClient and BrowserLldbSession snapshot listener registrations when each event dispatch starts. A listener registered by another callback begins with the next event, while a listener removed before its turn is skipped. Dispatch therefore cannot grow without bound during one event. The streaming DAP parser rejects malformed UTF-8, malformed JSON, and duplicate Content-Length fields, caps an unterminated header at 8 KiB, and caps a declared JSON body at 16 MiB. Outbound encoding applies the same body limit before pending request or shared-ring write state is created. Invalid bytes therefore cannot be replacement-decoded into silently changed source paths, expressions, or variable names. JSON syntax failures use a stable protocol error while retaining the engine's SyntaxError as their cause. Oversized declarations fail before the body is buffered, preventing a corrupt worker frame from growing browser memory without bound. Once a valid length is known, the parser allocates that body buffer once and copies later transport fragments into place; large variables and memory replies therefore use linear allocation instead of repeatedly copying every previously received byte.

Current producer modules expose Emscripten's canonical HEAPU8 view. Each worker samples only its backing buffer length, emits an initial onMemory(worker, bytes) value, and emits again only when the linear memory grows. A final sample is taken during worker shutdown. These messages carry the session generation and stale-session samples are ignored by the same boundary as output, error, and exit events. Callers can retain the maximum value per worker as the session's peak linear memory; the callback does not expose memory contents and does not include the browser engine's separate JavaScript heap. Older compatible producer assets without HEAPU8 continue to run but do not emit memory samples. Every current-generation Worker message is runtime-validated before it can report readiness, output, memory, exit, or failure. Worker identity, channel, data, byte count, exit code, and error text must match the protocol; an invalid message becomes a worker-error and disposes the session instead of changing lifecycle state. A message carrying another well-formed generation remains ignored before its type-specific fields are inspected, preserving stale-session isolation. Consumer onOutput, onMemory, onLifecycle, and DAP event-listener exceptions are isolated from the byte transports and required session cleanup. Set onCallbackError(error, callbackKind) to observe the original exception. Exceptions thrown by that error hook are also contained so reporting code cannot terminate the debugger. The required product Chromium gate retains the peak reported by each worker during its basic C fixture and requires at least one sample from both workers. Its default linear-memory ceilings are 320 MiB for LLDB and 80 MiB for WAMR, leaving 25% headroom above their pinned 256 MiB and 64 MiB initial memories. Override them with WASM_IDLE_DEBUG_LLDB_LINEAR_MEMORY_LIMIT_BYTES and WASM_IDLE_DEBUG_TARGET_LINEAR_MEMORY_LIMIT_BYTES only when validating an intentional producer memory change. The current basic fixture reports one sample at exactly 256 MiB and 64 MiB, respectively. These values are the Emscripten linear-memory backing-buffer sizes, not total browser RSS; both workers retain bounded memory growth for workloads that exceed the initial allocation.

The debug runtime requires a cross-origin-isolated page with SharedArrayBuffer. Its verified Blob-backed modules and nested pthread Workers also require blob: in the deployment CSP's script-src, worker-src, and connect-src. LLDB and WAMR assets are lazy-loaded from the versioned producer manifest and are not included in this npm package. The bundled /wasm-debug/ profile pins the raw runtime-manifest.v2.json response to its exact byte length and SHA-256 receipt. The consumer verifies those response bytes before JSON parsing or Worker creation; the verified manifest then supplies the six LLDB/WAMR asset receipts. A custom runtimeAssets.debug.baseUrl or manifestUrl must include runtimeAssets.debug.manifestSha256. Deployments configured through public environment variables must pair PUBLIC_WASM_DEBUG_RUNTIME_URL with PUBLIC_WASM_DEBUG_RUNTIME_MANIFEST_SHA256. A custom profile without a valid expected digest fails closed before fetching the manifest or creating a debugger Worker, allowing the product's pre-session capability check to offer a new trace run instead. Only authenticated manifest preflight failures may offer a new trace run before compilation. Manifest downloads are capped at 64 KiB. An invalid or oversized Content-Length fails before the body reader starts; a missing or understated header still uses a counted byte stream that cancels as soon as the cap is crossed. The loader never calls the unbounded Response.arrayBuffer() path for this outer trust boundary. Before compiling an LLDB run, the application verifies only the bounded outer manifest. Once the DWARF artifact is ready, BrowserLldbSession resolves all six LLDB/WAMR assets from that manifest, downloads and verifies each one exactly once, and transfers the owned verified bytes to its Workers. The Workers never re-fetch executable URLs. A missing or corrupt binary fails the LLDB session before Worker creation. Direct package consumers must authenticate the raw manifest before passing its parsed value to BrowserLldbSession; the session then enforces the same single-fetch asset boundary. A binary asset failure after DWARF compilation fails that LLDB session explicitly, before any debugger Worker starts, and never continues the compiled artifact through trace debugging. The tracked consumer profile and copied manifest/assets form one release compatibility unit. sync:wasm-debug derives the profile bytes and digest from the exact copied manifest and rolls both the asset directory and profile module back after ordinary publication failures; the dedicated post-sync receipt test is the release drift gate. A machine power loss between filesystem renames is outside the runtime publication contract, so release commits must include and verify code, profile, manifest, and assets together.

Clean Pages builds run prepare:wasm-debug-release before layering or compression. That command reads the tracked release profile, fetches only its immutable wasm-llvm revision, authenticates the manifest receipt before trusting its asset list, and installs the six verified assets through the same transactional sync path. Each producer request has a 30-second attempt timeout and up to three attempts for transient failures. Cancellation is forwarded through download and publication; an abort during replacement rolls both the runtime tree and generated receipt back. Overlapping publishers are serialized per destination, while owner-checked filesystem locks make separate processes fail closed instead of modifying the same release concurrently. Repeated page builds may replace either the raw or already-compressed destination. A bounded streaming snapshot binds the exact staged and installed trees, and rollback restores the same storage representation if replacement fails. Failed rollback phases revalidate both recovery generations before any compensating reattachment and preserve them in place on an ownership mismatch. Every destructive rename rechecks its source and destination, both success and rollback postconditions are verified before cleanup, and transaction-root cleanup is non-recursive outside the owned runtime directory. Immediately before gh-pages publication, the deployment verifier authenticates the logical manifest and every asset again; it accepts either raw bytes or the deterministic gzip representation recorded by compressed-runtime-assets.v1.json, and refuses missing, ambiguous, stale, oversized, or mismatched output. Browser behavior remains lazy because this is a build-time static-tree preparation step, not an application-start preload.

Repository CI runs test:browser:debug:lldb for every pull request and main push in a dedicated Chromium job. The gate installs Chromium, downloads the four external Clang delivery assets, verifies every pinned SHA-256 receipt, and requires the product LLDB/WAMR binaries published by wasm-llvm commit 3e4e327efad978f0598c07f79b33cc08ea9ec19d for C, C++, and Rust. The V2 manifest and all six debug assets are downloaded from that immutable revision and verified before the browser starts; the test cannot silently fall back to trace debugging. At each C, C++, and Rust source pause, the gate also verifies that LLDB scopes remain lazy until their variablesReference is requested and then contain the expected local value. The C++ fixture additionally follows the structure's and a pointer's own variablesReference values and verifies their first and second fields without eagerly flattening either variable tree. Rust coverage includes a basic argument/output fixture, a structure and field tree plus an enum value, three selectable recursive frames with distinct arguments, and a panic that must preserve stderr before WAMR reports its abort as an LLDB exception stop. The panic fixture verifies its local at the source breakpoint; after Rust has aborted, WAMR has no inspectable scope for that terminal stop, so the gate explicitly expects an empty scope list and no source line there. These fixtures continue to use the pinned generic DWARF path: the runtime does not advertise arbitrary Rust expression evaluation. A WASI file fixture mounts /workspace/data/input.txt into both debugger workers, pauses before the guest opens it, and requires WAMR to print the file's value after continuing. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-wasi-file to run only this fixture locally. A dedicated C fixture executes __builtin_trap(), verifies that LLDB reports an exception stop on the trap source line with scopes still available, and then exercises a clean Stop Debug disconnect. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-trap to run only this fixture locally. A second C fixture continues into an infinite loop, requires the UI to enter its running state without waiting for LLDB's deferred continue response, sends Pause through the live WAMR RSP transport, and verifies that the resulting LLDB interrupt is normalized to a pause stop with source frames and scopes still available. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-interrupt to run only this fixture locally. A third C fixture enters the same running loop and invokes Stop Debug before another stop event. The session gives DAP disconnect a short best-effort grace period, then closes its queues and terminates both workers without waiting for LLDB's deferred response. The fixture requires the UI to return to Ready within five seconds. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-disconnect to run only this fixture locally. Two transport-adversary fixtures exercise the same cleanup boundary under blocked WASI I/O. The output fixture stalls the browser's queue consumers for three seconds while an infinite C target alternates 16 KiB writes to stdout and stderr. It requires both independent rings to reach within 16 KiB of capacity, requests Pause in the same browser turn, and verifies a source stop before disconnecting and launching a clean recovery program. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-transport-saturation to select it. The stdin fixture continues past a source breakpoint, observes a flushed prompt, deliberately supplies no input, and requests Pause while WAMR is blocked in scanf. A synchronous WAMR stdin read may not service that interrupt, so the gate records either a pause or an explicit five-second timeout, then requires disconnect and Worker cleanup within twice that interval and an immediate successful relaunch. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-blocked-stdin to select it. Override the bounded pause interval with WASM_IDLE_DEBUG_TRANSPORT_PAUSE_TIMEOUT_MS; the existing disconnect timeout override remains available for slower CI hosts. A fourth C fixture repeats that running-target launch and disconnect sequence three times in one page. It instruments page-created workers, requires every extra LLDB/WAMR worker pair to terminate, and verifies that the active worker count returns to the first-run baseline after each disconnect. If a target-exit or worker-error path has already started disposal, the playground disconnect waits for that same disposal before reporting completion, so the UI cannot return to Ready before both debug Worker termination calls are observable. Each debug Worker also owns Blob URLs for its verified Emscripten JavaScript, WebAssembly, and pthread sidecar assets. A live-session dispose revokes those URLs synchronously before the outer Worker can reach its bounded force-termination fallback; the normal and failure finally paths use the same idempotent cleanup. Repeated relaunches therefore cannot retain one complete verified runtime asset set merely because a blocking native entry point did not unwind before termination. The execution preflight signal also remains attached after manifest verification and is checked before and after terminal clear, prepare, and run. Stop or Restart during the clear/compile startup window therefore retires that execution before it can create a late debugger session; a Restart waits for the retired execution to settle and then starts one fresh generation. A retired aborted generation is ignored even when the shared sandbox boundary wraps the abort reason in its typed cancellation error. Debug executions opt into the shared boundary's explicit interactive-run contract, so the final guest run is not killed by the ordinary compile-plus-run wall-clock deadline while the user is paused or stepping. Asset loading, compilation/prepare, DAP startup requests, Worker watchdogs, AbortSignal cancellation, output and diagnostic limits, Stop, and disposal remain bounded. Normal Run does not opt in and retains its configured wall-clock deadline. If the shared execution boundary cancels a run for timeout or output limits, it reports that limit promptly but keeps the sandbox busy until both the guest operation and asynchronous debugger teardown have settled. A following clear, restart, or run therefore cannot overlap the retiring LLDB/WAMR Workers. It also requests garbage collection and limits renderer JS heap growth to 64 MiB by default; set WASM_IDLE_DEBUG_HEAP_GROWTH_LIMIT_BYTES to tune that budget for constrained CI environments. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-relaunch to run only this fixture locally. The nightly schedule and each manual release-candidate dispatch run the relaunch fixture 100 times with a two-hour browser-test budget. Pull requests and main pushes continue to run the complete LLDB C, C++, and Rust fixture matrix instead of substituting the soak-only shard. If a relaunch does not reach its source pause, the fixture reports the exact iteration, debugger state, Worker and heap counters, preview-server status, failed request URLs, browser console tail, page errors, and terminal transcript. Run a local soak without another build or browser probe mutating the same preview output; an unavailable application chunk is a preview-environment failure, not an LLDB stopped-state timeout. A companion fixture force-terminates the real target Worker and LLDB Worker after separate source pauses. It dispatches the browser worker-error boundary, requires both workers from each failed session to terminate within five seconds, and launches a final clean session that must print lldb-worker-recovery=73. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-worker-crash to run only this fixture locally. A fifth C fixture intercepts the outer runtime manifest with a synthetic 404. It requires the pre-session capability check to select trace, complete the guest with trace-manifest-fallback=73, and leave all debugger Worker lifecycle counters unchanged. The manifest fallback fixture proves that no debugger Worker starts before the authenticated manifest boundary succeeds. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-manifest-fallback to run only this fixture locally. A sixth C fixture intercepts the LLDB WebAssembly asset with a synthetic 404 after a valid manifest load and DWARF compilation. It requires an explicit LLDB session failure with that exact asset status, proves that neither debugger Worker started, and rejects both trace fallback and guest execution. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-asset-session-failure to run only this fixture locally. A seventh C fixture stops after stepping inside a three-level recursive call, selects every recurse frame through the product UI adapter, and requires distinct frame IDs with n=1, n=2, and n=3 scope values. This guards the pinned synthetic-CFA fix against reusing the top frame's Wasm locals for callers. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-recursive-frames to run only this fixture locally. It then sends a DAP readMemory request from the LLDB hexadecimal memory reference 0x0 for four bytes of Wasm linear memory through the complete Sandbox and Terminal control path and verifies that the response is readable before resuming. The hexadecimal form matters because LLDB-DAP treats memory references as opaque strings and accepts addresses emitted by its own memoryReference encoder. A dedicated C memory-write fixture stops before an addition, resolves the local value variable's LLDB memoryReference, writes the little-endian bytes for 100, reads the same four bytes back, and resumes to require lldb-memory-write=103. Set WASM_IDLE_DEBUG_BROWSER_CASES=c-memory-write to run only this fixture locally. The capability is fail-closed across the producer manifest and DAP initialize response, and does not imply variable assignment or expression evaluation support. This pinned LLDB build registers the inert no-script interpreter required by native formatter matching, so the first lazy variables request cannot fall through a missing plugin callback. Its call-depth frame identities also keep caller frames stable while a callee is pushed, allowing the C++ gate to verify that next steps over a function call instead of stopping inside it.