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

@ttsc/wasm

v0.15.1

Published

Build in-browser ttsc playgrounds. Compose ttsc + typescript-go with your own plugins via the Go host helper.

Readme

@ttsc/wasm

API stability: experimental until v1.0. Public signatures (Plugin.Run, the host.Expose argv shape, the JS ITtscApi surface, the linker-symbol contract below) may change between minor releases. Pin exact versions in production playgrounds.

In-browser ttsc playground scaffolding. Compose ttsc + typescript-go with your own plugins by writing a single Go entry that calls host.Expose(...), then boot it from JS with bootTtsc(...).

What you get

  • A base ttsc.wasm (under dist/) that exposes vanilla build / check / transform, no plugins linked. Useful as a sanity test or a no-plugin baseline.
  • A Go helper package (host/) plugin authors import from their own main_wasm.go to bind a wasm to globalThis[yourApiName].
  • A JS runtime (bootTtsc, createMemFS, typed ITtscApi surface) that loads any host-built wasm into a Web Worker.

The package is plugin-agnostic. Downstream playgrounds (the ttsc.dev website, typia, anything else) build their own wasm against the same host/ package.

Build your own playground in 30 lines

  1. Add the dependency:
npm install -D @ttsc/wasm
  1. Create your-pkg/cmd/your-wasm/main_wasm.go:
//go:build js && wasm

package main

import (
  "github.com/samchon/ttsc/packages/wasm/host"
  yourplugin "example.com/your/plugin"
)

func main() {
  host.Expose("yourApi", host.Config{
    Plugins: []host.Plugin{yourplugin.New()},
  })
}

The native sibling main.go is recommended when you want go run ./cmd/your-wasm to smoke-test the same host.Plugin dispatchers without the browser MemFS bridge. See packages/wasm/cmd/ttsc-wasm/main.go and website/compiler/cmd/playground/main.go in the repo for reference layouts. A minimal custom plugin dispatcher looks like this:

//go:build !js

package main

import (
  "fmt"
  "os"

  "github.com/samchon/ttsc/packages/wasm/host"
  yourplugin "example.com/your/plugin"
)

func main() {
  plugins := []host.Plugin{yourplugin.New()}
  if len(os.Args) >= 3 {
    name, command := os.Args[1], os.Args[2]
    for _, p := range plugins {
      if p.Name() == name {
        os.Exit(p.Run(command, os.Args[3:]))
      }
    }
  }
  fmt.Fprintln(os.Stderr, "usage: your-wasm <plugin> <command> [args...]")
  os.Exit(2)
}
  1. Build the wasm:
GOOS=js GOARCH=wasm go build -trimpath -ldflags "-s -w" \
  -o public/your.wasm ./cmd/your-wasm
cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" public/wasm_exec.js

Keep wasm_exec.js from the same Go toolchain that built your wasm. The prebuilt dist/ttsc.wasm and dist/wasm_exec.js pair already match each other; custom binaries should copy the loader from their own toolchain. Older Go installs may keep the loader under $(go env GOROOT)/misc/wasm/wasm_exec.js instead of lib/wasm.

Stamping version metadata at link time

The host package exposes a build-time variable contract you can override via -ldflags "-X ...":

GOOS=js GOARCH=wasm go build -trimpath \
  -ldflags "-s -w -X github.com/samchon/ttsc/packages/wasm/host.version=$(cat package.json | jq -r .version)" \
  -o public/your.wasm ./cmd/your-wasm

The version, commit, and date variables in host/host.go are all overridable. globalThis[apiName].version() reads from them.

  1. Boot it from JS (a Web Worker is required: Go's syscall/js blocks the runtime while the wasm is alive; running on the main thread freezes the page). Use a classic Worker or a bundler target that still exposes importScripts; bootTtsc imports wasm_exec.js before starting Go.
import { bootTtsc } from "@ttsc/wasm";

const { api, host } = await bootTtsc({
  wasmUrl: "/your.wasm",
  apiName: "yourApi",
});

host.writeFile("/work/tsconfig.json", '{"compilerOptions":{"strict":true}}');
host.writeFile("/work/src/index.ts", "export const x: number = 1;");

const result = await api.build({ cwd: "/work" });
console.log(result.result); // JSON: { diagnostics, output }

Booting two wasms with the same apiName overwrites the previous global binding; pick a unique apiName per binary. bootTtsc also installs shared fs and process globals in its Worker, so use separate Workers when binaries need independent filesystems.

Fountain API (snapshot, AST, type checker)

For embedders that want embed-typescript-style raw access to the program, diagnostics, AST nodes, the type checker at a position. The same globalThis[apiName] object also exposes fountain verbs. They share the standard {code, stdout, stderr, result} envelope; result is JSON.

import { bootTtsc, parseResult } from "@ttsc/wasm";
import type {
  ITtscSnapshotResult,
  ITtscTypeAtPositionResult,
} from "@ttsc/wasm";

const { api, host } = await bootTtsc({ wasmUrl, apiName });

host.writeFile("/work/tsconfig.json", "{}");
host.writeFile("/work/src/index.ts", "export const x: number = 1;");

const snap = parseResult<ITtscSnapshotResult>(
  await api.snapshot({ cwd: "/work" }),
);
const handle = snap!.handle;

try {
  const typeAt = parseResult<ITtscTypeAtPositionResult>(
    await api.getTypeAtPosition({
      handle,
      path: "src/index.ts",
      position: 18, // byte offset of `x`
    }),
  );
  console.log(typeAt?.type?.text); // → "number"
} finally {
  await api.releaseSnapshot({ handle });
}

Verbs and payload types:

| Verb | Payload type | | --- | --- | | snapshot({ cwd, tsconfig? }) | ITtscSnapshotResult { handle } | | releaseSnapshot({ handle }) | ITtscReleaseSnapshotResult { released } | | snapshots() | ITtscSnapshotsResult { handles } | | getSourceFiles({ handle }) | ITtscSourceFilesResult { files } | | getSourceFileText({ handle, path }) | ITtscSourceFileTextResult { text } | | getDiagnostics({ handle, file? }) | ITtscFountainDiagnosticsResult { diagnostics } | | getNodeAtPosition({ handle, path, position }) | ITtscNodeAtPositionResult { node } | | getTypeAtPosition({ handle, path, position }) | ITtscTypeAtPositionResult { type } | | getSymbolAtPosition({ handle, path, position }) | ITtscSymbolAtPositionResult { symbol } |

position is a byte offset into the source text. The same coordinate TypeScript-Go uses internally. JS callers that have a UTF-16 (line, character) pair (e.g. From Monaco) must convert it before calling.

Lifecycle: JS owns the handle. The wasm keeps the program (parsed AST, checker pool lease, every source file) alive until you call releaseSnapshot. Leaking handles leaks memory in the wasm linear heap.

Plugin contract

host.Plugin matches ttsc's existing CLI sidecar dispatch:

type Plugin interface {
  Name() string                            // e.g. "@ttsc/banner"
  Run(command string, args []string) int   // returns CLI exit code
}

The host installs globalThis[apiName].plugin({ name, command, ...opts }) that translates the JS options object into a CLI-shaped argv and calls your plugin's Run. Your Run body can forward to the same function the native sidecar's main.go calls, for example utility.RunBuild(args) for plugins backed by packages/ttsc/utility.

Published-tarball Go module layout

The published @ttsc/wasm tarball ships:

  • A rewritten root go.mod whose replace directives point at ./shim-vendor/shim/* (vendored at pack time from packages/ttsc/shim/).
  • The full host/, cmd/, build/ Go source so consumers can go build -tags '...' their own wasm against your host helper.
  • dist/ttsc.wasm + dist/wasm_exec.js: the no-plugin sanity binary and the Go runtime loader.

The tarball intentionally drops the replace github.com/samchon/ttsc/packages/ttsc => ../ttsc directive that the in-repo go.mod carries: consumers of the published module who want to rebuild the wasm must supply their own replace (or vendor packages/ttsc themselves). The published dist/ttsc.wasm is plug-and-play for runtime use; the Go module is for plugin authors extending the host, not for vanilla consumers.

Documents

  • The base wasm (dist/ttsc.wasm) is the binary cmd/ttsc-wasm/main_wasm.go produces. Look there for a minimal example.
  • See the @ttsc/wasm guide for the Worker, MemFS, plugin-host, and troubleshooting walkthrough.