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

@profullstack/storage-service

v0.4.0

Published

Flexible storage service abstraction for file storage, retrieval, and metadata management

Readme

@profullstack/storage-service

A flexible storage service abstraction for file storage, retrieval, and metadata management.

Features

  • Multiple Adapters: Support for memory, filesystem, S3, and Supabase storage
  • Bucket Management: Create, list, and delete storage buckets
  • File Operations: Upload, download, copy, move, and delete files
  • Metadata Management: Store and retrieve custom metadata with files
  • Content Type Detection: Automatic content type detection from file data
  • URL Generation: Generate public and signed URLs for file access
  • Search Capabilities: Search files by path prefix and metadata
  • Customizable: Configurable filename generation, content types, and more

Installation

npm install @profullstack/storage-service

Basic Usage

import { createStorageService, MemoryAdapter } from '@profullstack/storage-service';

// Create a storage service with in-memory adapter
const storage = createStorageService({
  adapter: new MemoryAdapter(),
  defaultBucket: 'documents'
});

// Create a bucket
await storage.createBucket('documents', {
  public: false,
  fileSizeLimit: 10 * 1024 * 1024, // 10MB
  allowedMimeTypes: ['application/pdf', 'text/plain']
});

// Upload a file
const uploadResult = await storage.uploadFile({
  bucketName: 'documents',
  path: 'hello.txt',
  data: Buffer.from('Hello, world!'),
  contentType: 'text/plain',
  metadata: {
    description: 'A simple text file',
    author: 'ProFullStack'
  }
});

console.log(`File uploaded: ${uploadResult.path}`);

// Download a file
const downloadResult = await storage.downloadFile({
  bucketName: 'documents',
  path: 'hello.txt',
  responseType: 'text'
});

console.log(`File content: ${downloadResult.data}`);

API Reference

Creating a Storage Service

import { createStorageService, MemoryAdapter } from '@profullstack/storage-service';

const storage = createStorageService({
  // Storage adapter (required)
  adapter: new MemoryAdapter(),
  
  // Default bucket name (default: 'default')
  defaultBucket: 'documents',
  
  // Bucket configuration (optional)
  bucketConfig: {
    documents: {
      public: false,
      fileSizeLimit: 10 * 1024 * 1024, // 10MB
      allowedMimeTypes: ['application/pdf', 'text/plain']
    }
  },
  
  // Metadata options (optional)
  metadataOptions: {
    reservedKeys: ['contentType', 'size', 'createdAt', 'updatedAt'],
    defaultMetadata: {
      application: 'my-app'
    }
  },
  
  // Whether to generate unique filenames (default: true)
  generateUniqueFilenames: true,
  
  // Custom filename generator (optional)
  filenameGenerator: (originalPath) => {
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
    return `${originalPath}_${timestamp}`;
  }
});

Bucket Management

Creating a Bucket

await storage.createBucket('images', {
  public: true,
  fileSizeLimit: 5 * 1024 * 1024, // 5MB
  allowedMimeTypes: ['image/jpeg', 'image/png', 'image/gif']
});

Listing Buckets

const buckets = await storage.listBuckets();
console.log('Buckets:', buckets.map(bucket => bucket.name));

Deleting a Bucket

// Delete a bucket (must be empty)
await storage.deleteBucket('temp');

// Force delete a bucket (even if not empty)
await storage.deleteBucket('temp', { force: true });

File Operations

Uploading a File

// Upload a text file
const textUploadResult = await storage.uploadFile({
  bucketName: 'documents',
  path: 'notes/hello.txt',
  data: Buffer.from('Hello, world!'),
  contentType: 'text/plain',
  metadata: {
    description: 'A simple text file',
    author: 'ProFullStack',
    tags: ['example', 'text']
  },
  upsert: false, // Don't overwrite if exists (default)
  public: false // Not publicly accessible (default)
});

// Upload a JSON file
const jsonUploadResult = await storage.uploadFile({
  bucketName: 'documents',
  path: 'config.json',
  data: Buffer.from(JSON.stringify({ key: 'value' })),
  contentType: 'application/json'
});

// Upload an image file
const imageData = await fs.readFile('image.jpg');
const imageUploadResult = await storage.uploadFile({
  bucketName: 'images',
  path: 'profile.jpg',
  data: imageData,
  contentType: 'image/jpeg',
  metadata: {
    width: 800,
    height: 600
  },
  public: true // Make publicly accessible
});

Downloading a File

// Download as buffer (default)
const bufferResult = await storage.downloadFile({
  bucketName: 'documents',
  path: 'hello.txt'
});

// Download as text
const textResult = await storage.downloadFile({
  bucketName: 'documents',
  path: 'hello.txt',
  responseType: 'text'
});

// Download as JSON
const jsonResult = await storage.downloadFile({
  bucketName: 'documents',
  path: 'config.json',
  responseType: 'json'
});

// Download as stream
const streamResult = await storage.downloadFile({
  bucketName: 'documents',
  path: 'large-file.pdf',
  responseType: 'stream'
});

Getting File Information

const fileInfo = await storage.getFileInfo({
  bucketName: 'documents',
  path: 'hello.txt'
});

console.log(`File: ${fileInfo.path}`);
console.log(`Size: ${fileInfo.size} bytes`);
console.log(`Content Type: ${fileInfo.contentType}`);
console.log(`Created At: ${fileInfo.createdAt}`);
console.log(`Metadata: ${JSON.stringify(fileInfo.metadata)}`);

Listing Files

// List all files in a bucket
const allFiles = await storage.listFiles({
  bucketName: 'documents',
  limit: 100
});

// List files with a prefix
const noteFiles = await storage.listFiles({
  bucketName: 'documents',
  prefix: 'notes/',
  limit: 100
});

// List files with pagination
const firstPage = await storage.listFiles({
  bucketName: 'documents',
  limit: 10
});

const secondPage = await storage.listFiles({
  bucketName: 'documents',
  limit: 10,
  cursor: firstPage.cursor
});

Copying Files

const copyResult = await storage.copyFile({
  sourceBucket: 'documents',
  sourcePath: 'hello.txt',
  destinationBucket: 'backup',
  destinationPath: 'hello-backup.txt',
  overwrite: false // Don't overwrite if exists (default)
});

Moving Files

const moveResult = await storage.moveFile({
  sourceBucket: 'documents',
  sourcePath: 'draft.txt',
  destinationBucket: 'documents',
  destinationPath: 'published/final.txt',
  overwrite: true // Overwrite if exists
});

Deleting Files

const deleted = await storage.deleteFile({
  bucketName: 'documents',
  path: 'temp.txt'
});

Metadata Management

Updating Metadata

const updatedMetadata = await storage.updateMetadata({
  bucketName: 'documents',
  path: 'hello.txt',
  metadata: {
    description: 'Updated description',
    version: '1.1.0',
    tags: ['updated', 'example']
  },
  merge: true // Merge with existing metadata (default)
});

URL Generation

Getting a Public URL

// Only works for files in public buckets or with public=true
const publicUrl = await storage.getFileUrl({
  bucketName: 'images',
  path: 'profile.jpg'
});

Getting a Signed URL

// Get a signed URL for reading a file
const readUrl = await storage.getSignedUrl({
  bucketName: 'documents',
  path: 'private.pdf',
  expiresIn: 3600, // 1 hour
  action: 'read'
});

// Get a signed URL for writing a file
const writeUrl = await storage.getSignedUrl({
  bucketName: 'documents',
  path: 'uploads/new-file.txt',
  expiresIn: 3600, // 1 hour
  action: 'write'
});

Searching Files

// Search by prefix
const prefixResults = await storage.searchFiles({
  bucketName: 'documents',
  prefix: 'notes/',
  limit: 100
});

// Search by metadata
const metadataResults = await storage.searchFiles({
  bucketName: 'documents',
  metadata: {
    author: 'ProFullStack',
    tags: ['example']
  },
  limit: 100
});

// Combined search
const combinedResults = await storage.searchFiles({
  bucketName: 'documents',
  prefix: 'notes/',
  metadata: {
    author: 'ProFullStack'
  },
  limit: 100
});

Storage Adapters

Memory Adapter

Stores files in memory. Suitable for development or testing.

import { createStorageService, MemoryAdapter } from '@profullstack/storage-service';

const storage = createStorageService({
  adapter: new MemoryAdapter()
});

Filesystem Adapter

Stores files on the local filesystem. Suitable for server-side applications.

import { createStorageService, FilesystemAdapter } from '@profullstack/storage-service';

const storage = createStorageService({
  adapter: new FilesystemAdapter({
    rootDir: '/path/to/storage'
  })
});

S3 Adapter

Stores files in Amazon S3 or compatible services. Suitable for production use.

import { createStorageService, S3Adapter } from '@profullstack/storage-service';

const storage = createStorageService({
  adapter: new S3Adapter({
    region: 'us-west-2',
    credentials: {
      accessKeyId: 'YOUR_ACCESS_KEY',
      secretAccessKey: 'YOUR_SECRET_KEY'
    },
    endpoint: 'https://s3.amazonaws.com' // Optional for non-AWS S3-compatible services
  })
});

Supabase Adapter

Stores files in Supabase Storage. Suitable for applications using Supabase.

import { createStorageService, SupabaseAdapter } from '@profullstack/storage-service';
import { createClient } from '@supabase/supabase-js';

const supabaseClient = createClient(
  'https://your-project.supabase.co',
  'your-supabase-key'
);

const storage = createStorageService({
  adapter: new SupabaseAdapter({
    client: supabaseClient
  })
});

Creating Custom Adapters

You can create custom adapters by implementing the adapter interface:

class CustomAdapter {
  async createBucket(bucketName, options) { /* ... */ }
  async listBuckets() { /* ... */ }
  async deleteBucket(bucketName, options) { /* ... */ }
  async uploadFile(options) { /* ... */ }
  async downloadFile(options) { /* ... */ }
  async getFileInfo(options) { /* ... */ }
  async listFiles(options) { /* ... */ }
  async deleteFile(options) { /* ... */ }
  async copyFile(options) { /* ... */ }
  async moveFile(options) { /* ... */ }
  async getFileUrl(options) { /* ... */ }
  async getSignedUrl(options) { /* ... */ }
  async updateMetadata(options) { /* ... */ }
  async searchFiles(options) { /* ... */ }
}

Examples

See the examples directory for complete usage examples.

License

MIT