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

@savvy-web/github-action-effects

v2.0.0

Published

Effect-based utility library for building robust, well-logged, and schema-validated GitHub Actions.

Downloads

1,115

Readme

@savvy-web/github-action-effects

npm License: MIT Node.js 24 TypeScript strict

Effect services for building GitHub Actions. You get schema-validated inputs, structured logging that maps to workflow commands and typed wrappers around the GitHub API, with no @actions/* packages anywhere in the dependency tree.

Features

  • Zero CJS dependencies — native ESM implementations of the GitHub Actions runtime protocol replace all @actions/* packages
  • 37 composable services — action I/O, GitHub API calls, git operations, package publishing and software attestation, each with its own Context.Tag
  • Schema-validated inputs — read action inputs via Effect's Config API with built-in parsing and defaults
  • Structured logging — Effect Logger maps to workflow commands with collapsible groups; buffered verbose output flushes inside its group when a step fails
  • Step-buffered executionStep.withStep buffers debug output per logical step, emits one success line on pass and spills the full buffer prefixed with the step name on failure
  • Software attestation — sign and upload SLSA provenance and CycloneDX SBOMs to GitHub's attestation store via the Attest, SigstoreSigner, OidcTokenIssuer and Sbom services
  • In-memory test layers — every service ships a test layer for fast, deterministic unit tests

Install

npm install @savvy-web/github-action-effects effect @effect/platform @effect/platform-node

Quick start

// src/main.ts
import { Config, Effect } from "effect";
import { Action, ActionOutputs } from "@savvy-web/github-action-effects";

const program = Effect.gen(function* () {
  const name = yield* Config.string("package-name");
  const outputs = yield* ActionOutputs;
  yield* outputs.set("result", `checked ${name}`);
});

Action.run(program);

Action.run provides ActionsRuntime.Default (ConfigProvider, Logger, core services, and Node.js platform layers), catches errors, and sets the workflow exit status automatically.

GitHub API clients

GitHubClientLive builds a GitHubClient layer one of three ways:

  • GitHubClientLive.fromEnv() — reads the ambient process.env.GITHUB_TOKEN, the repo-scoped workflow token. It is a function; call it with no arguments.
  • GitHubClientLive.fromToken(token) — an explicit token with no process.env dependency. The token is a Redacted<string> — wrap a bare string with Redacted.make(...).
  • GitHubClientLive.fromApp({ clientId, privateKey, installationId? }) — mints an installation token from GitHub App credentials, with privateKey as a Redacted<string>. It is a scoped layer that revokes the token on scope close and requires HttpClient.HttpClient; wrap a bare Effect.provide in Effect.scoped.
import { Effect } from "effect";
import { Action, GitHubClient, GitHubClientLive } from "@savvy-web/github-action-effects";

const program = Effect.gen(function* () {
  const client = yield* GitHubClient;
  const { owner, repo } = yield* client.repo;
  return yield* client.rest("issues.list", (octokit) =>
    octokit.rest.issues.listForRepo({ owner, repo }),
  );
}).pipe(Effect.provide(GitHubClientLive.fromEnv()));

Action.run(program);

The repo-scoped token is often too weak for permission-sensitive work. When that happens, pass fromToken a token you constructed yourself, or use fromApp to act as a GitHub App installation.

GitHub App token lifecycle

A GitHub Action runs in three phases — pre, main and post. The GitHubToken namespace generates one installation token in pre, hands main a client built from it and revokes it in post.

GitHubToken.provision and GitHubToken.dispose require a GitHubApp layer. In production, compose GitHubAppLive with OctokitAuthAppLive and provide the result to those effects.

// pre.ts — generate and persist the installation token
import { Effect, Layer } from "effect";
import { Action, GitHubAppLive, GitHubToken, OctokitAuthAppLive } from "@savvy-web/github-action-effects";

const appLayer = Layer.provide(GitHubAppLive, OctokitAuthAppLive);

Action.run(
  GitHubToken.provision({
    permissions: { contents: "write", pull_requests: "write" },
  }).pipe(Effect.provide(appLayer)),
);
// main.ts — build a GitHubClient from the persisted token
import { Effect } from "effect";
import { Action, GitHubClient, GitHubToken } from "@savvy-web/github-action-effects";

const program = Effect.gen(function* () {
  const client = yield* GitHubClient;
  const { owner, repo } = yield* client.repo;
  return yield* client.rest("repos.get", (octokit) =>
    octokit.rest.repos.get({ owner, repo }),
  );
}).pipe(Effect.provide(GitHubToken.client()));

Action.run(program);
// post.ts — revoke the token
import { Effect, Layer } from "effect";
import { Action, GitHubAppLive, GitHubToken, OctokitAuthAppLive } from "@savvy-web/github-action-effects";

const appLayer = Layer.provide(GitHubAppLive, OctokitAuthAppLive);

Action.run(GitHubToken.dispose().pipe(Effect.provide(appLayer)));

provision reads App credentials from its options object or, by default, from the app-client-id and app-private-key action inputs. Passing permissions verifies the generated token grants those scopes before it is persisted. It also resolves the App's public identity (slug, bot user ID, name) best-effort and stores it on the token, so later phases can call GitHubToken.botIdentity() without an extra API call.

Two additional accessors are available in any phase after provision:

  • GitHubToken.read() — an Effect<InstallationToken, ActionStateError, ActionState> that reads the full persisted token envelope, including the optional appSlug, appUserId and appName fields resolved during provision.
  • GitHubToken.botIdentity() — an Effect<BotIdentity, ActionStateError, ActionState> that derives a commit-attribution identity from the persisted token. When the App's slug and user ID were resolved, the returned email uses the <userId>+<slug>[bot]@users.noreply.github.com format that GitHub recognises for verified attribution; otherwise it falls back to the well-known github-actions[bot] identity.

Documentation

License

MIT