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

caskit

v1.2.0

Published

Content-addressed storage CLI for S3-compatible endpoints (R2), with automatic deduplication

Downloads

17

Readme

caskit

npm CI license

Content-addressed storage over an S3-compatible endpoint, built for org-wide "marketing as code" asset pipelines. Upload a file and get back a stable, content-derived URL — with automatic deduplication.

Uploading Logo FINAL.png produces an object like:

logo-final-cc451006.png

...at the root of the bucket, where cc451006 is derived from the file's bytes. Identical content always yields the same key, so re-uploading is a no-op and the same URL comes back.

Install

npm install -g caskit

Or run it without installing:

npx caskit logo.png

Requires Node 18+. The published package ships compiled JavaScript with type declarations — no build step for consumers.

Quick start

# 1. Write a .env with the variables caskit needs.
caskit init

# 2. Fill in your R2 credentials (see the table below).
$EDITOR .env

# 3. Upload.
caskit logo.png
# https://assets.example.com/logo-cc451006.png

New to R2? CLOUDFLARE_R2_SETUP.md walks through creating a bucket, API tokens, and a public hostname from scratch.

Configuration

Set these as environment variables or in a .env file in the directory you run caskit from. All variables are prefixed with CASKIT_ to avoid clashing with other tooling. Real environment variables always override .env.

| Variable | Required | Description | | --------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | | CASKIT_S3_ENDPOINT | yes | R2 S3 API endpoint (https://<account>.r2.cloudflarestorage.com) | | CASKIT_S3_ACCESS_KEY_ID | yes | Access key | | CASKIT_S3_SECRET_ACCESS_KEY | yes | Secret key | | CASKIT_S3_BUCKET | yes | Target bucket | | CASKIT_S3_REGION | no | Defaults to auto (correct for R2) | | CASKIT_S3_PUBLIC_URL | no | Public base URL for the printed result (r2.dev or custom domain). Falls back to the S3 endpoint, which is not publicly readable. |

caskit init

Creates .env if it does not exist, or appends only the CASKIT_ variables that are missing from it.

It is append-only: it never rewrites, reorders, or clears anything already in the file, so it is safe to run against a .env that holds real secrets or variables belonging to other tools. Re-running it is a no-op once every variable is present.

Usage

# Upload one or more files; each public URL is printed to stdout, one per line.
caskit upload logo.png banner.jpg

# `upload` is the default command, so this also works:
caskit logo.png

# Capture the URL straight into a variable:
URL=$(caskit logo.png)

# Re-upload even if the content already exists:
caskit --force logo.png

JSON output

--json emits a single JSON array, one entry per file — so stdout is one valid JSON document you can store as an asset manifest:

caskit --json assets/* > assets.json
[
  {
    "file": "logo.png",
    "key": "logo-cc451006.png",
    "url": "https://assets.example.com/logo-cc451006.png",
    "deduplicated": false
  },
  {
    "file": "banner.jpg",
    "key": "banner-e5f6a7b8.jpg",
    "url": "https://assets.example.com/banner-e5f6a7b8.jpg",
    "deduplicated": true
  }
]

It is always an array, even for a single file, so consumers never have to type-check the shape.

Output contract

  • stdout carries only the URLs — or the JSON array under --json. Clean for piping.
  • stderr carries dedup notices and errors.
  • Exit code is non-zero if any file fails.
  • A file that fails to upload is reported on stderr and omitted from the --json array. The array only ever describes objects that really landed in the bucket, so check the exit code rather than assuming the manifest is complete.

How it works

  1. Reads the file and computes a SHA-256 of its bytes (first 8 hex chars).
  2. Builds the key slug(name)-<hash>.ext at the bucket root.
  3. HEADs the key; if it already exists, skips the upload (dedup).
  4. Otherwise PUTs the bytes with a detected Content-Type.
  5. Prints the public URL.

Requests are SigV4-signed via aws4fetch.

Dependencies

Runtime dependencies are deliberately minimal — three zero-dependency, maintained packages: aws4fetch (S3 signing), commander (CLI), dotenv (.env loading). TypeScript is a dev-only dependency.

Contributing

See CONTRIBUTING.md for the development workflow.

Releases are automated with semantic-release: the version, changelog, git tag, GitHub release, and npm publish are all derived from Conventional Commits on main. Commit messages therefore matter — fix: cuts a patch, feat: a minor, and a BREAKING CHANGE: footer a major. Anything else (chore:, docs:) releases nothing.

License

MIT