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

@systemfsoftware/effect-microsandbox

v5.0.0

Published

Effect-native microVM integration testing via microsandbox — pure-data container specs, Scope/Layer lifecycle, Schedule-driven waits, zero Docker.

Readme

@systemfsoftware/effect-microsandbox

Effect-native integration-test containers backed by hardware-isolated microVMs. You declare the container as pure data, the library acquires it inside an Effect Scope, and teardown is a finalizer — no Docker daemon, no beforeAll/afterAll, no orphaned guests.

Built on the microsandbox napi SDK, which bundles the msb runtime and libkrunfw per platform. Every microVM runs on real virtualization: KVM on Linux, Hypervisor.framework on Apple Silicon, WHP on Windows. If the host has none, startup fails fast with a typed error that names the fix.

Quick Start

pnpm add @systemfsoftware/effect-microsandbox
import { NodeRuntime } from '@effect/platform-node'
import { layer as nodeServicesLayer } from '@effect/platform-node/NodeServices'
import { MicroVM } from '@systemfsoftware/effect-microsandbox'
import { Readiness } from '@systemfsoftware/effect-readiness'
import { Effect, HashMap, Layer } from 'effect'

const alpine = MicroVM.spec('alpine:3.20')
  .withExposedPorts([6379])
  .withWaitStrategy(MicroVM.Wait.forPort(6379))

const program = Effect.scoped(
  Effect.gen(function*() {
    const vm = yield* alpine.scoped
    const out = yield* vm.exec('echo', ['hello'])
    console.log(out.stdout) // hello
    console.log(HashMap.get(vm.mappedPorts, 6379)) // Option.some(<free 127.0.0.1 port>)
  }),
)

NodeRuntime.runMain(Effect.provide(program, Layer.merge(nodeServicesLayer, Readiness.NodeHostProber.layer)))

When the scope closes — normally or through interruption — the sandbox is stopped, destroyed, and its record removed. There is nothing to clean up by hand.

Architecture

Why MicroVMs

Shared-kernel containers leak state between tests and require a local Docker socket. Process-level fakes drift from real service semantics. A microVM gives each test an isolated hardware kernel with container-like ergonomics: standard image references, port publishing, and execution boundaries governed entirely by an Effect Scope.

Layer & Scoped Execution

A configured container specification (MicroVM.spec(...)) directly exposes .scoped (to acquire inside an Effect.scoped block) and .layer (to provide as a testcontainer Layer). In accordance with compound-packs/cell-architecture, platform dependencies (Crypto, FileSystem, and the readiness HostProber) propagate to R — .scoped and .run carry them in the effect's requirements, and .layer carries them in the layer's input requirements — and are satisfied once at your application or test composition root (such as @effect/platform-node/NodeServices plus Readiness.NodeHostProber.layer).

Specifying Containers

Containers are configured through a lawful staged builder. You start with a mandatory image identity via MicroVM.spec(image) and chain combinators before executing:

import { MicroVM } from '@systemfsoftware/effect-microsandbox'

const redis = MicroVM.spec('redis:7-alpine')
  .withExposedPorts([6379])
  .withMemoryLimit(512)
  .withWaitStrategy(MicroVM.Wait.forPort(6379))

// Acquire dynamically in a test scope:
const vm = yield* redis.scoped

// Or provide as a Layer (its input asks for the readiness prober):
const RedisLive = Layer.provide(redis.layer, Readiness.NodeHostProber.layer)

Spec Combinators

| Combinator | Description | | -------------------------- | --------------------------------------------------------- | | .withEnv(env) | Merges environment variables | | .withExposedPorts(ports) | Replaces exposed guest ports (services only) | | .withMount(mount) | Appends a host bind mount | | .withMemoryLimit(mb) | Sets memory limit in MiB | | .withWaitStrategy(s) | Sets readiness wait strategy (services only) | | .withHostAccess(enabled) | Lets a job reach host services (jobs only) | | .withWorkdir(path) | Sets the working directory of a job's command (jobs only) |

Readiness Strategies

VM startup does not complete until the specified wait strategy passes (30-second budget before raising WaitTimeoutError):

  • Port Probe: MicroVM.Wait.forPort(guestPort) dials the mapped host loopback port.
  • HTTP Probe: MicroVM.Wait.forHttp(path, guestPort) issues a GET request and checks for a 2xx status.
  • Log Pattern: MicroVM.Wait.forLog(pattern) polls the guest log stream for a matching regex.

One-shot Jobs

MicroVM.job(image, cmd) declares a VM whose only purpose is to run cmd once. Its .run boots the VM, runs the command as the image's default workload, waits for it to end, and returns how it ended. The VM is torn down when the enclosing scope closes, whether the job succeeded, failed, or was interrupted.

import { NodeRuntime } from '@effect/platform-node'
import { layer as nodeServicesLayer } from '@effect/platform-node/NodeServices'
import { MicroVM } from '@systemfsoftware/effect-microsandbox'
import { Readiness } from '@systemfsoftware/effect-readiness'
import { Effect, Layer, Match } from 'effect'

const probe = MicroVM.job('alpine:3.20', ['wget', '-T', '5', '-qO-', 'http://host.microsandbox.internal:4318/health'])
  .withHostAccess(true)
  .withWorkdir('/tmp')

const program = Effect.scoped(
  Effect.gen(function*() {
    const completion = yield* probe.run
    const verdict = Match.value(completion.status).pipe(
      Match.tag('JobExited', ({ code }) => `exited with ${code}`),
      Match.tag('JobSignaled', () => 'killed by a signal'),
      Match.exhaustive,
    )
    console.log(verdict, new TextDecoder().decode(completion.stdout))
  }),
)

NodeRuntime.runMain(Effect.provide(program, Layer.merge(nodeServicesLayer, Readiness.NodeHostProber.layer)))

A JobCompletion holds:

  • status: JobExited with the exit code, or JobSignaled when a signal ended the workload. A non-zero exit code is a successful .run; judging it is up to you. JobSignaled carries no signal number, because the runtime reports every signal death the same way.
  • stdout and stderr: the complete output as bytes (Uint8Array), exactly as the workload wrote it. .run holds all of it in memory until the workload ends.

.run fails with ExecError only when the workload could not be started or its result could not be collected. It waits as long as the workload runs; for a deadline, wrap it in Effect.timeout. The VM is torn down when the enclosing scope closes.

Host Access

A job cannot reach the host by default. .withHostAccess(true) opens the host to the guest: every service listening on the host, including one bound to 127.0.0.1, becomes reachable at host.microsandbox.internal. Public internet access stays on. Opt in only for jobs whose command you trust with every host service.

Runtime Behaviour

Automatic Port Allocation

Exposed guest ports are automatically paired with unallocated ephemeral ports on 127.0.0.1 prior to boot. Port mappings are accessible via vm.mappedPorts (HashMap<number, number>). Any rendered configuration targeting non-loopback interfaces fails immediately with LoopbackViolationError.

The RunningVM Handle

RunningVM provides safe, typed primitives to control the active microVM:

  • exec(cmd, args): Executes commands as argv lists (no shell injection vulnerabilities); resolves with { code, stdout, stderr }.
  • logs: Returns an Effect Stream of structured log entries (source, text).
  • ping: Returns an Effect resolving to true if the guest agent responds.
  • mappedPorts: Read-only HashMap of guest-to-host port bindings.
  • name: Unique sandbox identifier (effect-microsandbox-<pid>-<suffix>).

Failure Model

All operational failures are returned as typed errors in the MicroVMError.MicroVMError union:

| Error | Cause | | -------------------------------- | -------------------------------------------------------------------------------------------------- | | VirtualizationUnsupportedError | Missing KVM (/dev/kvm), Hypervisor.framework, or WHP with specific diagnostic remediation | | SandboxBootError | Runtime failed to initialize or start the microVM sandbox | | PortAllocationError | Unable to bind a free host loopback port | | LoopbackViolationError | Port mapping targeted a disallowed non-loopback address | | WaitTimeoutError | Readiness condition was not satisfied within the 30-second deadline | | ExecError | An in-guest command or a job's workload could not be started, or its result could not be collected |

Verification

System Requirements

  • Effect: v4 (effect catalog dependency).
  • Hardware Virtualization:
    • Linux: KVM (/dev/kvm accessible with read/write permissions).
    • macOS: Apple Silicon with Hypervisor.framework.
    • Windows: Windows Hypervisor Platform (WHP).

Smoke Journey

To verify end-to-end integration on a host with virtualization support:

pnpm --filter @systemfsoftware/effect-microsandbox smoke

The smoke journey boots Alpine Linux, executes an in-guest command, tests port mapping, confirms resource cleanup on interruption, and exercises layer reuse across sequential VM runs. Its job journeys check exit codes, signals, byte-exact output, host access with and without opt-in, and the working directory. On a host without virtualization it exits non-zero with VirtualizationUnsupportedError.

License

Licensed under the Apache-2.0 License.