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

@absolutejs/blob

v0.2.1

Published

Object storage substrate for AbsoluteJS. One BlobStore interface with bounded streaming local and S3-compatible adapters, plus official AWS SDK wiring for multipart object storage.

Downloads

430

Readme

@absolutejs/blob

Object storage substrate for AbsoluteJS. One BlobStore interface, multiple adapters, and bounded streaming for large artifacts.

const store: BlobStore = /* localBlobStore(...) | s3BlobStore(...) */;
await store.put('users/42/avatar.png', body, { contentType: 'image/png' });
const bytes = await store.get('users/42/avatar.png');
const url = await store.presign('users/42/avatar.png', { ttlSeconds: 900 });

Adapters

| Subpath | Backs | | --- | --- | | @absolutejs/blob/local | Filesystem (dev / single-host prod / tests) | | @absolutejs/blob/s3 | AWS S3, Cloudflare R2, Backblaze B2, MinIO, Wasabi, Tigris — any S3-compatible HTTP API | | @absolutejs/blob/aws-s3 | Official AWS SDK wiring, including multipart streaming uploads |

Both implement the same BlobStore interface — swap providers with one constructor change.

Local

import { localBlobStore } from '@absolutejs/blob/local';

const blobs = localBlobStore({ root: './var/blobs' });
await blobs.put('uploads/file.pdf', body);

Files at <root>/<key>. Metadata (contentType, user metadata, cache headers) at <root>/<key>.meta.json. Atomic writes via temp file + rename. presign() throws BlobError('UNSUPPORTED') — use the S3 adapter against a local MinIO if you need presign in dev.

S3 (any S3-compatible service)

import { S3Client } from '@aws-sdk/client-s3';
import { awsS3BlobStore } from '@absolutejs/blob/aws-s3';

const aws = new S3Client({ region: 'us-east-1' });

const blobs = awsS3BlobStore({ bucket: 'my-bucket', client: aws });

awsS3BlobStore uses the official SDK command clients and @aws-sdk/lib-storage multipart uploads. Streams are never materialized as one control-plane buffer. The lower-level s3BlobStore and S3ClientLike remain available for custom clients.

Cloudflare R2

const aws = new S3Client({
  region: 'auto',
  endpoint: `https://${ACCOUNT_ID}.r2.cloudflarestorage.com`,
  credentials: { accessKeyId, secretAccessKey },
});

R2 is fully S3-compatible — the only thing that changes is the endpoint. Same wiring + s3BlobStore adapter.

Backblaze B2, MinIO, Wasabi, Tigris

All the same pattern. Point endpoint at the provider's URL, provide credentials, hand the client into s3BlobStore.

BlobStore interface

type BlobStore = {
  readonly description: string;
  put: (key: string, body: BlobBody, options?: PutOptions) => Promise<BlobObject>;
  get: (key: string) => Promise<Uint8Array | null>;
  getStream: (key: string) => Promise<ReadableStream<Uint8Array> | null>;
  head: (key: string) => Promise<BlobObject | null>;
  delete: (key: string) => Promise<void>;
  list: (options?: ListOptions) => Promise<ListResult>;
  presign: (key: string, options?: PresignOptions) => Promise<string>;
};

type BlobBody = Uint8Array | string | ReadableStream<Uint8Array>;
  • put returns the stored object's metadata (size, contentType, etag, user metadata).
  • get returns null for missing keys (not throw).
  • getStream for large blobs — avoids loading the body into memory.
  • delete is idempotent: deleting a missing key is success.
  • list paginates via cursor — pass back into the next call as options.cursor.
  • presign builds a time-limited URL for direct browser upload/ download. operation: 'put' for uploads, 'get' (default) for downloads. Throws BlobError('UNSUPPORTED') on local.

Key validation

validateKey('users/42/avatar.png');  // ok
validateKey('/etc/passwd');          // BlobError('INVALID_KEY')
validateKey('../escape');            // BlobError('INVALID_KEY')
validateKey('with\0nul');            // BlobError('INVALID_KEY')

Adapters call validateKey() on every operation. Leading slashes, NUL bytes, and . / .. path segments throw BlobError('INVALID_KEY') — closes the path-traversal class of bugs at the substrate level.

License

BSL-1.1 with named carveout against hosted object-storage services. Change date: 2030-05-31 (Apache 2.0).