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

@rehearse/ci

v0.6.19

Published

Type-safe GitHub Actions pipelines in TypeScript

Readme

@rehearse/ci

The TypeScript-authored CI SDK. Write GitHub Actions workflows in typed TypeScript with full IDE autocomplete, compile to standard YAML on your machine, ship the YAML. Zero runtime dependency on us — the compiled YAML works on stock GitHub Actions even if you uninstall the SDK.

@rehearse/ci fills a small but real niche: typed TypeScript that compiles lossless to standard GitHub Actions YAML. The hosted-runner SaaS layer (Blacksmith, Ubicloud, RunsOn, Depot, WarpBuild) doesn't try to do this. YAML-authoring alternatives that do exist (Pulumi, Earthly, Dagger, hand-rolled generators) either lock you into a proprietary execution model, use a non-YAML output format, or don't compile to canonical GH Actions YAML you can ship to stock GitHub Actions.

The forward path (compile: TS → YAML) is 100% lossless — every SDK feature produces canonical GH Actions YAML, verified by a comprehensive snapshot test suite. The reverse path (convert: YAML → TS) is a migration starter: handles the common shapes (triggers / jobs / runner / steps / env / permissions / outputs / conditions) so you can adopt the SDK on existing repos in one command, then hand-port the advanced bits (matrix / services / concurrency).

The CLI ships separately as @rehearse/cli (binary: rh). Pair both if you want rh ci init / compile / convert ergonomics.

npm License Source

Install

# The SDK
npm install -D @rehearse/ci

# Plus the compile/init/convert CLI
npm install -D @rehearse/cli

Hello, world

// .rehearse/pipelines/ci.ts
import { pipeline, job, step, triggers, Runner } from '@rehearse/ci';
import { node } from '@rehearse/ci/presets';

export const ci = pipeline('CI', {
  triggers: [triggers.pullRequest(), triggers.push({ branches: ['main'] })],
  jobs: [
    job('test', {
      runner: Runner.github('ubuntu-latest'),
      steps: [
        step.checkout(),
        node.setup('20'),
        node.install(),
        node.test(),
      ],
    }),
  ],
});
npx rh ci compile
# → .github/workflows/ci.yml

The compiled YAML has zero @rehearse/ci dependency at CI time. You can delete this package after compiling and the YAML still works.

API overview

Builders

| Function | Purpose | | --- | --- | | pipeline(name, config) | Top-level pipeline (triggers + jobs, plus optional permissions, env, concurrency, defaults). | | job(name, config) | A job: runner + steps + optional needs, matrix, services, env, if, etc. | | step.run(cmd, opts?) | Inline shell. | | step.action(uses, opts?) | Reference a GitHub Action by owner/repo@ref. | | step.checkout(opts?) | actions/checkout@v4 with typed options. | | step.uploadArtifact(opts) / step.downloadArtifact(name, path?) | actions/upload-artifact@v4 / download-artifact@v4. | | step.cache(opts) | actions/cache@v4 with key, path, restoreKeys. | | triggers.push|pullRequest|workflowDispatch|schedule|release|workflowRun | Trigger constructors. | | Runner.github(label) / Runner.selfHosted(...) / Runner.custom(spec) | Runner specs. |

Context helpers

import { secrets, vars, github, env, needs, steps, expr, hashFiles } from '@rehearse/ci';

secrets('GITHUB_TOKEN')                  // ${{ secrets.GITHUB_TOKEN }}
vars('REGION')                           // ${{ vars.REGION }}
github('event.pull_request.number')      // ${{ github.event.pull_request.number }}
env('NODE_ENV')                          // ${{ env.NODE_ENV }}
needs('build', 'sha')                    // ${{ needs.build.outputs.sha }}
steps('lint', 'result')                  // ${{ steps.lint.outputs.result }}
expr('matrix.os == \'ubuntu-latest\'')   // ${{ matrix.os == 'ubuntu-latest' }}
hashFiles('**/package-lock.json')        // ${{ hashFiles('**/package-lock.json') }}

All validate input (throw on empty/whitespace).

Presets

@rehearse/ci/presets exports small step-constructor objects with sensible defaults: node, bun, python, rust, go, docker.

import { node, bun, python, rust, go, docker } from '@rehearse/ci/presets';

node.setup('20')        // actions/setup-node@v4 with node-version: '20'
bun.install()           // run: bun install --frozen-lockfile
python.test('pytest')   // run: pytest
docker.buildPush('myimage:${{ github.sha }}', { push: true })

Compile programmatically

import { compile, toYaml } from '@rehearse/ci';
import { ci } from './my-pipeline.js';

// compile() returns the structured workflow object
const workflow = compile(ci);

// toYaml() serializes any compatible object to GH Actions YAML
const yaml = toYaml(workflow);
console.log(yaml);

Convert existing YAML to TypeScript (migration starter)

import { convert } from '@rehearse/ci';

const { source, warnings } = convert(yamlString);
// source: TypeScript source ready to drop into .rehearse/pipelines/
// warnings: array of unmapped actions or constructs

convert() is a migration starter, not a faithful round-trip. It handles common shapes (run / uses / with / env / if and the standard event triggers) but currently drops matrix, services, concurrency, defaults, environment, job-level permissions, and job outputs. Review the generated TS before relying on it; hand-port the dropped blocks; then rh ci compile round-trips back to YAML to verify.

The CLI wrapper is rh ci convert <yaml>.

Estimate runner cost

import { estimate } from '@rehearse/ci';
import { ci } from './my-pipeline.js';

const report = estimate(ci, {
  durations: { test: 7, build: 5 },   // minutes per job
  runsPerMonth: 200,
});
console.log(report.totalUsd, report.savingsVsGitHubUsd);

Pricing tables are a list-price snapshot baked into the package (refresh per release). The math is real (per-job × per-minute × runs against GitHub-hosted public list prices) — verify against current rate cards before quoting numbers to customers.

The CLI wrapper is rh ci estimate.

Runner support

The same Runner constants are honored by @rehearse/cli — the local runner reads the compiled YAML and executes it on your laptop, 5–9× faster than act on standard workflows, 30× on services. So you author in TS, compile to YAML, run locally before pushing — and optionally ship the same workflow to a Rehearse Pro VM with rh run --remote.

Compatibility

This package compiles to standard GitHub Actions YAML. The YAML works on:

  • GitHub-hosted runners (ubuntu-latest, macos-latest, windows-latest)
  • Self-hosted runners (Runner.selfHosted(...))
  • Third-party hosted runner pools via Runner.custom('your-label') — e.g. Ubicloud, BuildJet, Namespace, RunsOn — anywhere the org has configured the corresponding GitHub App or self-hosted listener
  • Locally via @rehearse/cli (or act, if you prefer)

The TypeScript itself runs anywhere Node 18+ does. Tests run on Node 22.

Repo

Source, issues, roadmap: https://github.com/plsft/rehearse.

License

Apache 2.0.