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

@tracecode/tracejvm

v0.4.1

Published

A browser-native Java 23 virtual machine.

Readme

TraceJVM

TraceJVM is a browser-native Java 23 toolchain. Version 0.4 has two deliberately separate components: a persistent OpenJDK javac compiled ahead of time with TeaVM Wasm GC, and disposable TraceJVM runners that execute ordinary classfiles through main(String[]).

TraceJVM is an independent project and is not affiliated with, sponsored by, or endorsed by Oracle Corporation or the OpenJDK project.

The runner is derived from a pinned b-jvm engine snapshot. b-jvm provides the JDK 23-capable WebAssembly VM foundation. The compiler consumes the pinned TeaVM-javac source and the exact OpenJDK 23.0.2+7 javac sources through a reproducible downstream overlay. TraceJVM defines lifecycle and isolation guarantees, exposes host-neutral embedding interfaces, and validates its supported profile against OpenJDK across release browsers.

  • TraceJVMCompiler owns source-to-classfile compilation only. It has no Java VM, process, filesystem, or TraceKernel authority.
  • TraceJVMEngine owns class loading and execution only. It contains no compiler and cannot accept Java source.
  • TraceJVMCompilerWorkerClient keeps compilation warm in a compiler-only Worker. TraceJVMWorkerClient owns one independently disposable runner.
  • TraceJVMRunnerHost may share the immutable JDK/Wasm substrate across disposable runner JVMs. It has no compiler or source API.
  • Embedders own their invocation protocols and integrations for processes, filesystems, environments, terminals, and other host facilities.
  • Instrumentation, probe protocols, event transport, and reconstruction stay outside the VM. TraceJVM executes the resulting ordinary Java bytecode.
  • Host capabilities are explicit interfaces. TraceJVM does not import a consumer package, assume a CDN route, or contain a product request shape.

Status

TraceJVM 0.4 is pre-release. Compatibility is intentionally measured rather than assumed. The OpenJDK compatibility lane is the authority for Java behavior, and known gaps stay visible until the runner implements them.

API

There is intentionally no combined engine role in 0.4. An embedder that wants compile-then-run composes the compiler and runner explicitly:

import {
  TraceJVMCompiler,
  TraceJVMEngine,
} from "@tracecode/tracejvm";

const compiler = new TraceJVMCompiler({
  assets: {
    baseUrl: "/tracejvm/compiler",
  },
  platformArchiveUrl: "/tracejvm/profiles/core/jdk23.jar",
});
const runner = new TraceJVMEngine({
  assets: {
    runtimeProfileBaseUrls: {
      core: "/tracejvm/profiles/core",
    },
    wasmUrl: "/tracejvm/bjvm_main.wasm",
  },
});

await Promise.all([compiler.initialize(), runner.initialize()]);
const compiled = await compiler.compile({
  sources: [{
    path: "example/Hello.java",
    content: `
      package example;
      public final class Hello {
        public static void main(String[] args) {
          System.out.println("hello " + args[0]);
        }
      }
    `,
  }],
});
if (compiled.status !== "completed" || !compiled.program) {
  throw new Error(compiled.stderr);
}
const result = await runner.run({
  program: compiled.program,
  mainClass: "example.Hello",
  args: ["world"],
});

The compiler, engine, and runner Worker client also expose typed Effect operations. Infrastructure failures use the Effect error channel; compiler diagnostics and uncaught Java exceptions remain normal result data.

For the product topology, keep one TraceJVMCompilerWorkerClient warm and construct a TraceJVMWorkerClient for each admitted runner lifetime. The compiler Worker has no VM or process authority; terminating a runner never discards the compiler and a runner crash cannot poison it. The embedder remains authoritative for process admission, files, descriptors, signals, and networking.

When the compiler and runner lifecycle already live inside one provider Worker, pair TraceJVMCompiler with TraceJVMRunnerHost instead. The runner host amortizes immutable JDK/Wasm loading while every createProcess() call still receives a fresh disposable JVM. TraceJVM does not combine the two components; the embedder owns that composition.

const compiler = new TraceJVMCompilerWorkerClient({
  compiler: {
    assets: { baseUrl: "/tracejvm/compiler" },
    platformArchiveUrl: "/tracejvm/profiles/core/jdk23.jar",
    platformClasspath: [{
      path: "tracekernel-api.jar",
      url: "/tracejvm/profiles/core/tracekernel-api.jar",
    }],
  },
  createWorker: createCompilerWorker,
});
await compiler.initialize();
const compiled = await compiler.compile({
  sources: [{ path: "Main.java", content: source }],
});
const runner = new TraceJVMWorkerClient({
  engine: {
    assets: runtimeAssets,
    workingDirectory: "/workspace",
  },
  host: processBoundTraceKernelPort,
  createWorker: createRunnerWorker,
});
const result = await runner.run({
  program: compiled.program!,
  mainClass: "Main",
});
runner.terminate();

For a standalone isolated runner, use TraceJVMWorkerClient with the exported browser Worker. Terminating the Worker is the hard disposal boundary, so the client uses hard Worker termination for aborts by default. Normal executions reuse the warm VM only after the Java execution scope restores its covered process state. Every run exposes an isolation report. If a scope cannot prove safe reuse, including after application thread creation, it returns status: "tainted" and recommends retirement. The default Worker client closes that idle Worker automatically. It also retires after a configurable maximum number of executions.

import {
  TraceJVMWorkerClient,
  type TraceJVMWorkerLike,
} from "@tracecode/tracejvm";

const java = new TraceJVMWorkerClient({
  engine: {
    assets: {
      runtimeProfileBaseUrls: {
        core: "/tracejvm/profiles/core",
      },
      wasmUrl: "/tracejvm/bjvm_main.wasm",
    },
  },
  createWorker: () =>
    new Worker("/tracejvm/browser-worker.js", {
      type: "module",
    }) as unknown as TraceJVMWorkerLike,
});

const result = await java.run({
  program: compiled.program,
  mainClass: "example.Hello",
});

TraceJVM intentionally does not provide a combined compiler/runner host. Embedders that want a different lifecycle compose the two Worker clients without giving either component authority over the other.

Optional host controls

Hosted Java programs use ordinary Java APIs for files, descriptors, child processes, sockets, selectors, and watch services. Operations without a Java SE equivalent are exposed by the small runtime API:

import io.tracecode.tracekernel.TraceKernel;
import java.time.Duration;

var identity = TraceKernel.currentProcess();
var armed = TraceKernel.armWatchdog(
    Duration.ofSeconds(5),
    TraceKernel.WatchdogSignal.SIGKILL);
TraceKernel.petWatchdog();
TraceKernel.disarmWatchdog();

TraceKernel.setProcessGroup(0, 0); // setpgid(0, 0)
long foreground = TraceKernel.terminalForegroundProcessGroup(0);
TraceKernel.setTerminalForegroundProcessGroup(0, foreground);

The API is compiled into a standalone tracekernel-api.jar; it is not patched into java.base and does not add a product dependency to TraceJVM. Its native methods use the same generic host port as the standard Java adapters. A standalone engine without the corresponding host capability fails these calls with IOException instead of emulating process control locally.

Build

The runtime build is reproducible and pinned:

  • Eclipse Temurin/OpenJDK 23.0.2+7
  • Emscripten 4.0.2
  • the b-jvm upstream revision recorded in engine/UPSTREAM.md
  • the TeaVM-javac revision recorded in compiler/teavm-javac/manifest.json
pnpm install
source .cache/emsdk/emsdk_env.sh
pnpm build:runtime
TRACEJVM_JAVA23_HOME=/path/to/jdk-23 pnpm build:teavm-javac
pnpm build

runtime/assets and .cache/teavm-javac/artifacts are generated build inputs and intentionally not committed. The derived, content-addressed runtime-release/ package directory is committed with each TraceJVM release, so a clean checkout, CI, and npm packaging all verify the same browser bytes.

The npm package owns both the TraceJVM API and the exact browser runtime it was released with. That runtime includes the OpenJDK images, TeaVM compiler, tracekernel-api.jar, bjvm_main.wasm, browser client, browser Worker, and a generated byte ledger. The runtime travels as one verified Zstandard archive so the public npm artifact stays below the registry size limit; the exported @tracecode/tracejvm/runtime-package helper expands it for serving. An embedder may install that immutable tree at its own static origin, but it must not substitute independently versioned JVM assets. Release preparation reuses an existing archive whenever the generated file inventory and digests are unchanged, and rebuilds it when any runtime byte changes. TraceJVM still accepts explicit URLs so an embedder can choose where to serve the tree without choosing its contents.

Verify the two release surfaces independently:

pnpm build
pnpm verify:package
pnpm verify:runtime-assets
pnpm verify:teavm-javac

Distribution

TraceJVM has one versioned package with two representations of the same release:

  • @tracecode/tracejvm contains the host-neutral API, browser client, browser Worker, a content-addressed runtime archive, its exact expanded-file manifest, and the complete license and notice material for those bytes. Consumers use the package's runtime helper (directly or through Harness) to verify and expand the archive before serving it.
  • The optional hosted runtime release contains the same TeaVM-javac module, WebAssembly VM, and Java 23 runtime profiles. Its legal/ directory carries TraceJVM's third-party notices, the complete applicable Temurin/OpenJDK legal material, the b-jvm MIT license, and TeaVM-javac's Apache 2.0 license and downstream modification notice. Its source/ directory carries the exact corresponding OpenJDK and TeaVM-javac sources plus TraceJVM's manifests, patches, and build scripts. Runtime release preparation fails if those source archives are absent or do not match their pinned checksums. When published to object storage it lives below tracejvm/<package-version>/<content-hash>/ and is never addressed through a mutable latest alias.

Prepare a runtime release with:

pnpm prepare:runtime-release

The generated release.json is the engine release contract. The npm package also contains runtime-release/manifest.json, which declares the compressed archive, its exact browser-serving and legal inventory, and its installation path; the large exact corresponding-source archives remain in the immutable hosted release. Together they record every payload path, byte size, SHA-256 digest, SRI value, content type, cache policy, runtime profile, and browser entrypoint. The content hash is derived from the payload tree, so rerunning the command for identical inputs produces the same prefix and descriptor.

Regenerating runtime-release/ is an explicit release operation. Commit the new content-addressed directory and manifest together. Ordinary prepack verifies that tracked inventory and never substitutes an ignored local cache. pnpm materialize:package-runtime performs the inverse operation: it verifies the committed archive and expands its runtime and compiler trees into the ignored build locations used by browser tests. This makes pnpm release:check a clean-checkout verification path; it never rebuilds or replaces the release candidate being checked.

The asset origin is part of the browser security boundary, not merely file storage. It must serve every release object with CORS, cross-origin resource policy, MIME hardening, and immutable caching exactly as recorded in release.json. The module Worker additionally requires cross-origin embedder policy and the descriptor's Worker CSP. pnpm check:runtime-release verifies those headers as well as the bytes; uploading objects without the response policy is not a completed release.

TraceJVM itself remains independent of Cloudflare. This repository's release tooling can publish TraceCode's copy to any selected R2 bucket:

pnpm upload:runtime-release -- --dry-run
pnpm deploy:runtime-release

The upload command always rebuilds and verifies the package and runtime assets before it constructs the object tree. This prevents a successful upload from quietly pairing current source with stale dist output.

Payload objects upload first and release.json uploads last as the release commit marker. pnpm check:runtime-release then downloads and hashes the published objects; pass --metadata-only only for a faster follow-up health check. Consumers pin the exact descriptor URL once per deployed application version. They do not poll for runtime releases.

Before publishing the npm package, run:

pnpm release:check
npm publish

Compatibility

The checked-in OpenJDK sources are copied byte-for-byte from the pinned jdk23u revision and verified by SHA-256:

pnpm sync:openjdk-tests
pnpm test:compatibility

The compatibility lane runs in real browser Workers under cross-origin isolation. Do not edit an upstream test to make TraceJVM pass. A failure is a VM gap, a missing explicit host capability, or a documented unsupported jtreg mode.

See the architecture, the supported Java profile, and the OpenJDK compatibility policy. The embedding boundary and its deliberately unsupported host ports are recorded in embedding TraceJVM. The current evidence, blockers, and exact CheerpJ replacement criteria are in release readiness.