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

@tssuite/ggwsm

v0.0.4

Published

The gg command line, compiled to WebAssembly and runnable from Node via npx.

Readme

ggwsm

The gg command line — the tool that drives commits, reviews and releases across all repositories of a ticket — compiled to WebAssembly and published on npm.

npx @tssuite/ggwsm do ls repos

No Dart SDK, no dart pub global activate. Node 22+ is enough.

Install

Run it straight from npm:

npx @tssuite/ggwsm --help

…or put it in a project so everyone working on it gets the same version:

pnpm add -D @tssuite/ggwsm

which makes ggwsm available to your scripts:

{
  "scripts": {
    "check": "ggwsm one can commit"
  }
}

What works

Everything the native gg does, with the same arguments and the same output — the file system and the programs gg starts are handed to it by Node:

npx @tssuite/ggwsm --version
npx @tssuite/ggwsm --help
npx @tssuite/ggwsm do ls repos
npx @tssuite/ggwsm do ls tickets
npx @tssuite/ggwsm one did commit

gg one can commit runs the real checks — dart analyze, dart format and dart test with coverage — and reports what the native gg reports.

Two things behave differently from the native executable — see Limitations.

Using it from TypeScript

The package is a library as well as a binary. runGg loads the module, gives it a Node host and runs a command line:

import { runGg } from '@tssuite/ggwsm';

const exitCode = await runGg(['do', 'ls', 'repos']);

To capture the output instead of printing it, build the host yourself:

import { createNodeHost, init } from '@tssuite/ggwsm';

const output: string[] = [];
const gg = await init();

gg.setHost(
  createNodeHost({
    workingDirectory: '/path/to/workspace',
    onStdout: (text) => output.push(text),
    onStderr: (text) => output.push(text),
  }),
);

const exitCode = await gg.run(['do', 'ls', 'repos']);
console.log(output.join(''));

createNodeHost never touches the surrounding Node process: gg's working directory is tracked inside the host, so process.chdir is never called.

Running gg on something that is not a disk

setHost takes any object implementing GgHost — file system, processes, platform and console. Nothing in gg knows where its files come from, so a host backed by a map works as well as one backed by node:fs:

import { EntityType, type GgHost } from '@tssuite/ggwsm';

const files = new Map<string, string>([['/work/pubspec.yaml', 'name: demo']]);

const host: GgHost = {
  fs: {
    typeOf: (p) => (files.has(p) ? EntityType.File : EntityType.NotFound),
    readBytes: (p) => new TextEncoder().encode(files.get(p) ?? ''),
    // …the rest of FileSystemHost
  },
  // …process, platform, console
};

That is how gg is tested, and how you would sandbox it.

Limitations

  • Interactive prompts are rebuilt, not borrowed. The native gg draws them with package:interact, which needs dart:ffi — something WebAssembly does not have. They are rebuilt on node:readline here and behave the same: the selection lists are walked with the arrow keys, and the message editors start with gg's proposal in the buffer, ready to be edited. Away from a terminal — a pipe, CI — there is nothing to draw on, so a list falls back to numbered choices and an editor to a suggestion in brackets that an empty answer keeps. Pass prompts: false to createNodeHost to turn them off entirely, and gg refuses the interactive commands instead.
  • Node only, for now. The module loads in a browser, but package:path derives its path style from the page URL and would treat gg's paths as URLs.
  • Windows is new. CI runs there now, and the known Windows-specific traps are handled: package:path picks the Windows style, batch wrappers like pana.bat are given the shell Node requires, and the prompts read through readline rather than blocking on a console handle — which is also what gives them their line editing. What remains unproven is dart:io's own stdin.readLineSync(), which gg uses in the interactive publish flow and which cannot be anything but a synchronous read.

Requirements

Node 22 or newer, on a runtime with WebAssembly-GC and the JS-string builtins. init() probes for both and throws a readable error if either is missing, rather than failing inside the loader.

Building from source

pnpm install
pnpm run build     # dart compile wasm + tsc + vite
pnpm test          # dart tests, vitest, e2e, lint

pnpm run build:dart:debug produces an unoptimised module with source maps when a stack trace out of the Wasm side needs reading.

How it works

gg is a Dart program, and a Dart program compiled to WebAssembly has no file system, no way to start a process and no terminal. gg therefore takes all of that as a set of callbacks, and this package supplies them from Node.

README.architecture.md has the details; the gg side of the story documents what had to change in gg itself.

License

MIT