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

@nodules/linode-storage

v1.0.2

Published

A production-ready, fully-typed Linode Object Storage client, following fs/promises interface that provides seamless cloud operations with enterprise-grade reliability and developer experience.

Readme

Nodules Linode Object Storage

A production-ready, fully-typed Linode Object Storage client, following fs/promises interface that provides seamless cloud operations with enterprise-grade reliability and developer experience.

Installation

npm install @nodules/linode-storage

Usage

Simple Factory Pattern

The easiest way to get started - works with any IAsyncFileSystem implementation:

import { FS } from '@nodules/linode-storage';

// Development - local filesystem
const devFs = FS.async.node();

// Testing - in-memory filesystem  
const testFs = FS.async.memory();

// Production - Linode Object Storage
const prodFs = FS.async.linode({
  region: 'us-southeast-1',
  bucket: 'my-bucket',
  accessKey: process.env.LINODE_ACCESS_KEY,
  secretKey: process.env.LINODE_SECRET_KEY
});

// All share the same interface - swap implementations instantly
await prodFs.writeFile('data.json', JSON.stringify(data));
const content = await prodFs.readFile('data.json');

Interface Injection Pattern

Design your services to accept any filesystem implementation:

import { IAsyncFileSystem } from '@nodules/linode-storage';

class DocumentService {
  constructor(private fs: IAsyncFileSystem) {}
  
  async saveDocument(id: string, content: string) {
    await this.fs.writeFile(`documents/${id}.txt`, content);
  }
  
  async loadDocument(id: string): Promise<string> {
    return await this.fs.readFile(`documents/${id}.txt`);
  }
}

// Development environment
const devService = new DocumentService(FS.async.node());

// Test environment  
const testService = new DocumentService(FS.async.memory());

// Production environment
const prodService = new DocumentService(FS.async.linode({
  region: 'us-southeast-1',
  bucket: 'production-storage',
  accessKey: process.env.LINODE_ACCESS_KEY,
  secretKey: process.env.LINODE_SECRET_KEY
}));

Environment-Based Configuration

import { FS } from '@nodules/linode-storage';

function createFileSystem() {
  switch (process.env.NODE_ENV) {
    case 'test':
      return FS.async.memory();
    case 'development':
      return FS.async.node();
    case 'production':
      return FS.async.linode({
        region: process.env.LINODE_REGION!,
        bucket: process.env.LINODE_BUCKET!,
        accessKey: process.env.LINODE_ACCESS_KEY!,
        secretKey: process.env.LINODE_SECRET_KEY!
      });
    default:
      throw new Error('Unknown environment');
  }
}

const fs = createFileSystem();
await fs.writeFile('config.json', '{}');

Direct Class Usage

For advanced configuration, use the class directly:

import { LinodeObjectStorageFileSystem } from '@nodules/linode-storage';

const storage = new LinodeObjectStorageFileSystem({
  region: 'us-southeast-1',
  bucket: 'my-bucket',
  accessKey: process.env.LINODE_ACCESS_KEY,
  secretKey: process.env.LINODE_SECRET_KEY
});

// Same interface as FS factory methods
await storage.writeFile('documents/file.txt', 'content');
const content = await storage.readFile('documents/file.txt');
const exists = await storage.exists('documents/file.txt');

Benefits of this approach:

Seamless Environment Switching

  • Same code works across development, testing, and production
  • No vendor lock-in - switch storage providers without code changes
  • Easy A/B testing between different storage solutions

Developer Experience

  • Three-line setup for any environment
  • TypeScript-first with complete type safety
  • Familiar fs/promises interface - no learning curve

Production Ready

  • Stable interface used across all applications
  • Reverse dependency makes testing trivial
  • Zero configuration required for basic usage

Configuration Options

interface LinodeObjectStorageOptions {
  region: string;           // Linode region (e.g., 'us-southeast-1')
  bucket: string;           // Bucket name
  accessKey: string;        // Linode Object Storage access key
  secretKey: string;        // Linode Object Storage secret key
  prefix?: string;          // Optional path prefix
  endpoint?: string;        // Custom endpoint (optional)
}

Complete API Reference

All implementations follow the same IAsyncFileSystem interface:

File Operations

// Write file
await fs.writeFile(path: string, content: string): Promise<void>

// Read file
await fs.readFile(path: string): Promise<string>

// Check if file exists
await fs.exists(path: string): Promise<boolean>

// Delete file
await fs.deleteFile(path: string): Promise<void>
await fs.unlink(path: string): Promise<void>  // Alias

// Get file statistics
await fs.stat(path: string): Promise<FileStats>

Directory Operations

// List directory contents
await fs.readDir(path: string): Promise<string[]>

// Create directory (no-op in object storage)
await fs.mkdir(path: string): Promise<void>
await fs.ensureDir(path: string): Promise<void>  // Alias

// Remove directory and contents
await fs.rmdir(path: string): Promise<void>
await fs.deleteDir(path: string): Promise<void>  // Alias

File Permissions

// Set file permissions (no-op in object storage)
await fs.chmod(path: string, mode: number): Promise<void>

Real-World Examples

Testing with In-Memory Filesystem

import { FS } from '@nodules/linode-storage';

describe('DocumentService', () => {
  const testFs = FS.async.memory();
  const service = new DocumentService(testFs);
  
  beforeEach(() => {
    // In-memory filesystem is automatically clean for each test
  });
  
  it('should save and load documents', async () => {
    await service.saveDocument('test-doc', 'content');
    const content = await service.loadDocument('test-doc');
    expect(content).toBe('content');
  });
});

Development with Local Filesystem

import { FS } from '@nodules/linode-storage';

// Development environment - files stored locally
const fs = FS.async.node();
const service = new DocumentService(fs);

// Files go to local filesystem for easy debugging
await service.saveDocument('debug-doc', 'debug content');
// Check ./documents/debug-doc.txt on your filesystem

Production with Linode Object Storage

import { FS } from '@nodules/linode-storage';

const fs = FS.async.linode({
  region: 'us-southeast-1',
  bucket: 'production-documents', 
  accessKey: process.env.LINODE_ACCESS_KEY,
  secretKey: process.env.LINODE_SECRET_KEY,
  prefix: 'app-v2/'  // Namespace your application data
});

const service = new DocumentService(fs);
// Same code, now running on cloud storage

Error Handling

try {
  const content = await fs.readFile('nonexistent.txt');
} catch (error) {
  if (error.message.includes('not found')) {
    console.log('File does not exist');
  } else {
    console.error('Storage error:', error.message);
  }
}

Testing

import { FS } from '@nodules/linode-storage';

// Testing is simple - use memory filesystem
const testFs = FS.async.memory();
const service = new DocumentService(testFs);

// No setup required, no cleanup needed
await service.saveDocument('test', 'content');
expect(await service.loadDocument('test')).toBe('content');

TypeScript Support

Full TypeScript definitions included. No additional @types packages required.

interface FileStats {
  size: number;
  mtime: Date;
  ctime: Date;
  atime: Date;
  mode: number;
  isFile(): boolean;
  isDirectory(): boolean;
  isSymbolicLink(): boolean;
}

Changelog

See CHANGELOG.md for version history and migration notes.

Support

Enterprise Services

Professional support available through Nodules.io

  • Enterprise licensing for commercial applications
  • Priority support with SLA guarantees
  • Security audits with every release
  • Penetration testing and vulnerability assessment programs
  • Dedicated technical account management and escalation procedures
  • Comprehensive testing and validation reports
  • Compliance validation for SOC 2, GDPR, HIPAA, and industry-specific regulations
  • White-glove onboarding and integration support
  • Private module development and customization

Contact: [email protected]

License

MIT License - see LICENSE for details.


Security: Report vulnerabilities to [email protected]