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

@cleverbrush/storage

v5.2.0

Published

Provider-neutral object storage contracts for Node.js

Readme

@cleverbrush/storage

Unit coverage

Provider-neutral object storage contracts and key/URL helpers for Node.js 24+. The core has no runtime dependencies. Use an adapter such as @cleverbrush/storage-s3 for self-hosted or hosted S3 services.

Contract

| Method | Behavior | | --- | --- | | put(key, bytesOrStream, options?) | Replace an object; return logical key, actual byte size and optional ETag. | | get(key, { signal }?) | Return metadata and a readable body; throw not_found when missing. | | stat(key, { signal }?) | Return metadata, or undefined on a missing object; other errors throw. | | copy(source, destination, { signal }?) | Copy within one storage instance, preserving metadata and replacing the destination. | | delete(key, { signal }?) | Delete the current object; missing objects succeed. | | publicUrl(key) | Return a stable public URL, or undefined without a configured public base URL. | | close() | Abort active work, release resources and permanently close the instance. | | [Symbol.asyncDispose]() | Await close() automatically when an await using scope ends. |

StorageBody accepts Uint8Array (including Buffer) or a binary Node Readable. PutOptions supports size, contentType, cacheControl, contentDisposition, string metadata and signal. Supplying size asserts the exact byte count. Metadata names are case-insensitive and returned in lowercase. ObjectMetadata adds these headers and an optional lastModified date to the write receipt. ETags are opaque; compare bytes or calculate an independent checksum when needed.

Ownership and errors

The adapter owns a supplied write stream for that operation, consuming it once and destroying it on failure or cancellation. A returned read stream belongs to the caller: consume it, pipe it with pipeline, or destroy it. A read's abort signal stays active until its body closes. Adapters must not buffer complete streamed objects.

Every ObjectStorage implements AsyncDisposable. Use await using when your scope owns the instance; leaving that scope awaits close(), including when an exception is thrown. Cleanup is asynchronous so active operations and multipart cleanup finish before resources are released. Explicit close() remains available and is safe to call more than once.

StorageError.code is one of not_found, access_denied, aborted, invalid_argument, unavailable, closed or provider_error. Errors omit raw provider requests, signed URLs, credentials and source error messages. Permission errors are not interpreted as absent objects. Cancellation can race a completed write; object storage operations are not database transactions.

Keys and public URLs

Keys are logical relative names, not filesystem paths or URLs. They are never normalized or decoded. Empty keys, leading slashes, backslashes, control characters and . / .. path segments are rejected. Spaces, Unicode, literal percent signs and repeated slashes are retained. prefixedObjectKey applies a configured prefix once; publicObjectUrl encodes segments without encoding slash separators. Public base URLs must be HTTP(S), without credentials, query or hash.

A public URL is a mapping, not an access grant or an existence check. Configure public bucket access or a proxy separately. Ownership checks, generated object names, reference tracking, rendering and database consistency belong to the application. Signed URLs and bucket management are not part of this contract.

Dependency injection

Create application-owned schema tokens, just like other Framework services:

import { any } from '@cleverbrush/schema';
import type { ObjectStorage } from '@cleverbrush/storage';

export const AssetStorageToken = any().hasType<ObjectStorage>();
// services.addSingletonInstance(AssetStorageToken, storage);
// endpoint.inject({ storage: AssetStorageToken });

Register multiple tokens/instances when assets use different providers or buckets. Keep application-wide adapters alive until application shutdown, then close them after stopping incoming work. Handlers borrowing injected storage must not dispose it. Use await using only in the scope that owns the adapter's lifetime. The reusable contract suite under testing/ is a repository test utility and is not included in the published package.