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

safemods

v0.1.3

Published

Type-directed codemods for TypeScript 7+ projects, built on Effect.

Readme

safemods

Type-directed codemods for TypeScript 7+ projects, built on Effect.

safemods uses the TypeScript 7 type checker to find exact symbol references across renamed imports, re-exports, and barrel files. Transformations produce range-bounded edits that are validated against type-checking and idempotency policies before anything is written to disk.

Query → Draft → Plan → Preview → Verify → Apply

Requirements

  • TypeScript: 7.0 or higher (minimum requirement)
  • Node.js: 24 or higher

Installation

pnpm add -D safemods effect typescript@7

effect and typescript (v7+) are peer dependencies.


Example Recipe

A recipe finds nodes in your project, drafts replacements, and declares verification policies.

import { Effect } from "effect"
import { isObjectLiteralExpression } from "typescript/unstable/ast/is"
import {
  ConfiguredProject,
  Draft,
  Policy,
  Query,
  Recipe,
  WorkspaceSnapshot,
} from "safemods"

const app = ConfiguredProject.make({
  id: "app",
  config: "tsconfig.json",
})

export default Recipe.define("wrap-target-input", {
  version: "1.0.0",
  policies: [
    Policy.matches({ min: 1 }),
    Policy.noNewErrors(),
    Policy.idempotent(),
  ],
  run: () =>
    Effect.gen(function* () {
      const snapshot = yield* WorkspaceSnapshot
      const project = yield* snapshot.project(app)
      const target = yield* project.symbolNamed("target", {
        within: "src/library.ts",
      })

      const calls = yield* Query.calls(project).pipe(
        Query.where(
          Query.resolvesTo(target, {
            location: (call) => call.expression,
          }),
        ),
        Query.filter(
          ({ value: call }) =>
            call.arguments.length === 1 &&
            !isObjectLiteralExpression(call.arguments[0]!),
        ),
        Query.collect,
      )

      return yield* Draft.replaceEach(calls, ({ value: call }) => {
        const argument = call.arguments[0]!
        return {
          node: argument,
          text: `{ value: ${argument.getText()} }`,
        }
      })
    }),
})

CLI

By default, safemods run previews proposed diffs. Use --verify to run policy checks, or --apply to write changes once verified.

# Preview diff in terminal
safemods run ./recipe.ts --cwd ./my-project

# Run verification policies without writing
safemods run ./recipe.ts --cwd ./my-project --verify

# Verify and apply changes to disk
safemods run ./recipe.ts --cwd ./my-project --apply

If a recipe defines an input Schema, pass parameters via --input:

safemods run ./recipe.ts --cwd ./my-project --input '{"key": "value"}'

To export the recipe's input schema:

safemods tool ./recipe.ts

Core Concepts

  • Exact range edits: Changes target source ranges and verify old-text hashes, leaving surrounding formatting and comments untouched.
  • Symbol renames: Draft.renameSymbol automatically updates declarations, references, and alias imports across the project.
  • In-memory overlays: Recipe.pipe chains recipes in memory, allowing subsequent steps to query against modified ASTs before writing to disk.
  • Policies:
    • Policy.matches({ min: 1 }) — Fails if the query matched no call sites.
    • Policy.noNewErrors() — Checks that the proposed diff introduces no new compiler diagnostics.
    • Policy.idempotent() — Verifies that re-running the recipe against transformed code yields zero further edits.

Programmatic Usage

Recipes can also be executed directly within an Effect workflow:

import { Recipe, Preview, Verification, Application } from "safemods"

const plan = yield* Recipe.run(recipe, input)
const preview = yield* Preview.of(plan)
const verified = yield* Verification.verify(plan, recipe, input)
const receipt = yield* Application.apply(verified)

Development

pnpm check
pnpm test

Examples & References

  • API Tour — Comprehensive walkthrough of available queries and combinators.
  • Examples — Sample recipes for renames, migrations, and calls.
  • Context & Architecture — Project terminology and architectural model.