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

acture-sandbox

v0.1.0

Published

acture sandbox — the isolation seam for an extension system: one ExtensionRunner port plus an in-process transport. Swap in Worker / iframe / QuickJS / isolated-vm transports when authors are untrusted.

Readme

acture-sandbox

acture is a development tool first. This package is an optional accelerator — and a deliberately small one. The extension host/loader is a ~15-line core-only pattern you hand-write (see docs/hand-written-sandbox.md), with no acture-* dependency. acture-sandbox ships only the one rung that is genuinely hard to hand-write: isolating code you did not author. See docs/positioning.md.

An extension system is two layers (research-9 §1). The host/loader — load/unload/observe bundles of command contributions — is pattern territory: a CommandRecord already is the manifest, so a host that trusts its authors needs nothing beyond acture core. The isolation layer — running code you cannot audit safely — is the first acture surface that genuinely earns a package. acture-sandbox is that layer, and nothing else: one ExtensionRunner port plus an in-process transport.

The port is async and errors-as-data on purpose. A cross-boundary transport (Web Worker, cross-origin iframe, QuickJS-in-WASM, Node isolated-vm) is asynchronous and cannot transport thrown exceptions, so the in-process adapter holds the identical contract. Design for the worst runtime; the local case is then trivial — and moving in-process → isolated is an adapter swap, not a rewrite.

The in-process transport is not a security boundary. It is the v1 adapter for trusted authors. The moment an untrusted author is about to run code, swap in a real isolating transport behind the same port. Admitting untrusted code through the in-process adapter is the one irreversible mistake.

Install

pnpm add acture-sandbox     # `acture` is a peer dependency you already have

Run a trusted extension in-process

import { createInProcessRunner } from 'acture-sandbox';
import type { Registry } from 'acture';

// The runner treats the bridge as opaque (`HostBridge = unknown`), so the host
// owns its shape and the extension narrows to it. Typically a facade over the
// registry / `dispatch` — what an extension may touch is the host's policy.
interface AppBridge {
  registry: Registry;
}

const csvProfiler = {
  activate(bridge: AppBridge) {
    const unload = bridge.registry.registerAll(/* the extension's commands */);
    return { deactivate: () => unload() };
  },
};

const runner = createInProcessRunner();
const hostBridge: AppBridge = { registry: appRegistry }; // appRegistry = your acture registry

// `bridge` is optional — omit it for a pure extension that acquires nothing,
// and `activate` receives `undefined`.
const loaded = await runner.load({ id: 'acme.csv-profiler', module: csvProfiler }, hostBridge);
if (!loaded.ok) console.error(loaded.error.code, loaded.error.message);

runner.loaded();                              // → ['acme.csv-profiler']
await runner.dispose('acme.csv-profiler');    // runs deactivate(), forgets it

Loading is errors-as-data — load and dispose never throw. Error codes: already_loaded, load_failed, activate_threw (load) and not_loaded, deactivate_threw (dispose). A disposed id can be loaded again.

Lazy sources

A source can defer its module behind an import thunk (unwrapping a default export), so the host can populate the palette / AI tool list from a manifest before the extension's code is fetched:

await runner.load({
  id: 'acme.lazy',
  import: () => import('./extensions/lazy.js'),
});

What this package is NOT

Isolation only. It deliberately does not ship the manifest schema, the host/loader, an effect channel, capability grants, an entitlement / install gate, or a marketplace. Those are a core-only pattern (docs/hand-written-sandbox.md) and host product architecture — documented, never bundled (no god-package; the package translates, it does not decide). Real isolating transports (Worker / iframe / QuickJS / isolated-vm) arrive one at a time, only when a real untrusted-author need names them.

API

| Export | What | | --- | --- | | createInProcessRunner() | Create an in-process (no-isolation) ExtensionRunner for trusted authors. | | ExtensionRunner | The isolation port: load(source, bridge?), dispose(id), loaded(). Async, errors-as-data. | | ExtensionSource | { id, module } or { id, import } — how the runner obtains an extension's module (open for future transports). | | ExtensionModule | { activate(bridge) } — the extension's entrypoint, returning an optional ActivationHandle. | | ActivationHandle | { deactivate?() } — teardown, invoked on dispose. | | HostBridge | The host capabilities handed to activate. Opaque to the runner; defined by the host. | | LoadedExtension | { id } — a live handle returned from a successful load. |

See also