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

@x12i/xronox-storage-s3

v2.2.0

Published

S3-compatible storage adapter for Xronox - supports AWS S3, DigitalOcean Spaces, MinIO, Cloudflare R2, and other S3-compatible services

Downloads

925

Readme

@x12i/xronox-storage-s3

S3-compatible storage adapter for Xronox ecosystem - supports AWS S3, DigitalOcean Spaces, MinIO, Cloudflare R2, and other S3-compatible services.

License: MIT ERC 2.0

🚀 Env-Ready Component (ERC 2.0)

This component supports zero-config initialization via environment variables using @x12i/env.

Quick Start (Zero-Config Mode)

# 1. Install the package
npm install @x12i/xronox-storage-s3 @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

# 2. Copy .env.example to .env (auto-generated in package root)
cp node_modules/@x12i/xronox-storage-s3/.env.example .env

# 3. Fill in required values in .env
# S3_ENDPOINT=https://nyc3.digitaloceanspaces.com
# S3_REGION=nyc3
# S3_ACCESS_KEY_ID=your_access_key
# S3_SECRET_ACCESS_KEY=your_secret_key

# 4. Use with zero config!
import { S3Storage } from '@x12i/xronox-storage-s3';
const storage = new S3Storage(); // Auto-discovers from process.env

// Use it!
await storage.putJSON('my-bucket', 'data/item.json', { id: '123', name: 'Test' });
const data = await storage.getJSON('my-bucket', 'data/item.json');

Advanced Mode (Programmatic Configuration)

import { S3Storage, S3Client } from '@x12i/xronox-storage-s3';
import { S3Client as AWSS3Client } from '@aws-sdk/client-s3';

// Option 1: Explicit config
const storage = new S3Storage({
  config: {
    endpoint: 'https://custom-endpoint.com',
    region: 'us-east-1',
    accessKeyId: 'key',
    secretAccessKey: 'secret'
  }
});

// Option 2: Explicit S3Client
const s3Client = new AWSS3Client({
  endpoint: 'https://custom-endpoint.com',
  region: 'us-east-1',
  credentials: {
    accessKeyId: 'key',
    secretAccessKey: 'secret'
  }
});
const storage = new S3Storage({ s3Client });

Environment Variables

See .env.example (auto-generated in package root) for the complete list of required and optional variables with descriptions.

Required Variables:

  • S3_ENDPOINT - S3-compatible endpoint URL
  • S3_REGION - AWS region or provider region
  • S3_ACCESS_KEY_ID - S3 access key ID
  • S3_SECRET_ACCESS_KEY - S3 secret access key

Optional Variables:

  • S3_FORCE_PATH_STYLE - Force path-style URLs (auto-detected if not provided)
  • S3_BUCKET - Default bucket name

ERC 2.0 Compliance

  • ✅ Auto-discovers configuration from environment variables
  • ✅ Type-safe with automatic coercion and validation
  • ✅ All dependency requirements documented
  • ✅ Transitive requirements automatically merged

Dependencies:

  • @x12i/env (Configuration engine)
  • ℹ️ @aws-sdk/client-s3 (peer dependency, non-ERC - must be installed separately)
  • ℹ️ @aws-sdk/s3-request-presigner (peer dependency, non-ERC - must be installed separately)

Features

Multi-Provider Support - Works with AWS S3, DigitalOcean Spaces, MinIO, Cloudflare R2
JSON & Binary Storage - Automatic serialization for JSON, raw buffer support
Data Integrity - SHA-256 verification for all uploads
Presigned URLs - Generate time-limited access URLs
Stream Handling - Safe conversion of streams to buffers
Error Handling - Clear, actionable error messages
Structured Logging - Built-in logging via @x12i/logxer
Configuration Management - nxconfig integration with ENV token resolution
TypeScript - Full type safety and IntelliSense support

Installation

npm install @x12i/xronox-storage-s3 @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

Automatic Provider Detection

This package automatically detects S3-compatible providers and sets optimal configuration:

import { createS3Client, detectProvider } from '@x12i/xronox-storage-s3';

// Detect provider information
const provider = detectProvider('https://nyc3.digitaloceanspaces.com');
console.log(provider.provider); // 'digitalocean'
console.log(provider.forcePathStyle); // true

Supported Providers:

  • AWS S3 - Virtual-hosted-style URLs
  • DigitalOcean Spaces - Automatic path-style detection
  • MinIO - Automatic path-style detection
  • Cloudflare R2 - Automatic configuration

Usage Examples

Using ERC-Compliant S3Storage Class (Recommended)

import { S3Storage } from '@x12i/xronox-storage-s3';

// Zero-config initialization (reads from process.env)
const storage = new S3Storage();

// Store JSON data
await storage.putJSON('my-bucket', 'data/item.json', { id: '123', name: 'Test' });

// Retrieve JSON data
const data = await storage.getJSON('my-bucket', 'data/item.json');

// Store raw binary data
await storage.putRaw('my-bucket', 'file.bin', Buffer.from('binary data'));

// Get raw data
const buffer = await storage.getRaw('my-bucket', 'file.bin');

// Check if object exists
const exists = await storage.head('my-bucket', 'data/item.json');
console.log(exists.exists); // true/false

// List objects
const list = await storage.list('my-bucket', 'data/');
console.log(list.keys); // ['data/item.json', 'data/other.json']

// Generate presigned URL
const url = await storage.presignGet('my-bucket', 'data/item.json', 3600);

Using S3 Operations Directly

import { S3Client } from '@aws-sdk/client-s3';
import { putJSON, getJSON } from '@x12i/xronox-storage-s3';

// Create S3 client
const s3 = new S3Client({
  endpoint: 'https://nyc3.digitaloceanspaces.com',
  region: 'nyc3',
  credentials: {
    accessKeyId: process.env.SPACE_ACCESS_KEY!,
    secretAccessKey: process.env.SPACE_SECRET_KEY!
  },
  forcePathStyle: false
});

// Store JSON data
const data = { id: '123', name: 'Test Item' };
const result = await putJSON(s3, 'my-bucket', 'data/item.json', data);
console.log('Stored:', result.sha256, 'Size:', result.size);

// Retrieve JSON data
const retrieved = await getJSON(s3, 'my-bucket', 'data/item.json');
console.log('Retrieved:', retrieved);

Using with nxconfig

import { autoLoadConfig } from '@nx-intelligence/nxconfig';
import { S3Client } from '@aws-sdk/client-s3';
import { putJSON, getJSON } from '@x12i/xronox-storage-s3';

// Load configuration
const { config } = await autoLoadConfig();
const s3Config = config.spacesConnections.default;

// Create S3 client from config
const s3 = new S3Client({
  endpoint: s3Config.endpoint,
  region: s3Config.region,
  credentials: {
    accessKeyId: s3Config.accessKey,
    secretAccessKey: s3Config.secretKey
  },
  forcePathStyle: s3Config.forcePathStyle
});

// Use S3 operations
await putRecords(s3, 'bucket', 'key.json', { data: 'value' });

Configuration File (xronox-config.json)

{
  "xronox": {
    "spacesConnections": {
      "default": {
        "endpoint": "ENV.SPACE_ENDPOINT",
        "region": "ENV.SPACE_REGION",
        "accessKey": "ENV.SPACE_ACCESS_KEY",
        "secretKey": "ENV.SPACE_SECRET_KEY",
        "forcePathStyle": false
      }
    }
  }
}

Environment Variables (.env)

S3_ENDPOINT=https://nyc3.digitaloceanspaces.com
S3_REGION=nyc3
S3_ACCESS_KEY=your_access_key
S3_SECRET_KEY=your_secret_key

# Optional: Configure logging
XRONOX_STORAGE_LOG_LEVEL=info
XRONOX_STORAGE_LOG_FORMAT=json

API Reference

Records Operations

putRecords(s3, bucket, key, payload, contentType?)

Store records data in S3.

const result = await putRecords(s3, 'bucket', 'path/file.json', {
  id: '123',
  data: 'value'
});
// Returns: { size: number, sha256: string, etag?: string }

getRecords(s3, bucket, key)

Retrieve records data from S3.

const data = await getRecords(s3, 'bucket', 'path/file.json');
// Returns: Record<string, unknown>

Raw Data Operations

putRaw(s3, bucket, key, body, contentType?)

Store raw binary data in S3.

const buffer = Buffer.from('raw data');
const result = await putRaw(s3, 'bucket', 'path/file.bin', buffer, 'application/octet-stream');
// Returns: { size: number, sha256: string, etag?: string }

get(s3, bucket, key)

Retrieve raw data as Buffer.

const buffer = await get(s3, 'bucket', 'path/file.bin');
// Returns: Buffer

Metadata Operations

head(s3, bucket, key)

Get object metadata without downloading.

const metadata = await head(s3, 'bucket', 'path/file.json');
// Returns: { contentLength?: number, contentType?: string, lastModified?: Date, etag?: string, metadata?: Record<string, string> }

exists(s3, bucket, key)

Check if an object exists.

const fileExists = await exists(s3, 'bucket', 'path/file.json');
// Returns: boolean

List Operations

list(s3, bucket, prefix, options?)

List objects with a prefix (async generator).

for await (const key of list(s3, 'bucket', 'path/')) {
  console.log('Found:', key);
}
// Yields: string (object keys)

Management Operations

del(s3, bucket, key)

Delete an object.

await del(s3, 'bucket', 'path/file.json');

copy(s3, bucket, sourceKey, destKey)

Copy an object within the same bucket.

await copy(s3, 'bucket', 'source.json', 'destination.json');

URL Operations

presignGet(s3, bucket, key, ttlSeconds?)

Generate presigned URL for direct access.

const url = await presignGet(s3, 'bucket', 'path/file.json', 3600);
// Returns: string (presigned URL, default: 1 hour expiration)

Provider Configuration Examples

AWS S3

const s3 = new S3Client({
  region: 'us-east-1',
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!
  }
});

DigitalOcean Spaces

import { createS3Client } from '@x12i/xronox-storage-s3';

// Automatic provider detection - forcePathStyle is automatically set to true
const s3 = createS3Client({
  endpoint: 'https://nyc3.digitaloceanspaces.com',
  region: 'nyc3',
  credentials: {
    accessKeyId: process.env.DO_SPACES_KEY!,
    secretAccessKey: process.env.DO_SPACES_SECRET!
  }
  // forcePathStyle automatically detected and set to true
});

// Or explicitly set it
const s3 = new S3Client({
  endpoint: 'https://nyc3.digitaloceanspaces.com',
  region: 'nyc3',
  credentials: {
    accessKeyId: process.env.DO_SPACES_KEY!,
    secretAccessKey: process.env.DO_SPACES_SECRET!
  },
  forcePathStyle: true // Required for DigitalOcean Spaces compatibility
});

MinIO

import { createS3Client } from '@x12i/xronox-storage-s3';

// Automatic provider detection - forcePathStyle automatically detected
const s3 = createS3Client({
  endpoint: 'http://localhost:9000',
  region: 'us-east-1',
  credentials: {
    accessKeyId: process.env.MINIO_ACCESS_KEY!,
    secretAccessKey: process.env.MINIO_SECRET_KEY!
  }
  // forcePathStyle automatically detected and set to true
});

Cloudflare R2

const s3 = new S3Client({
  endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
  region: 'auto',
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID!,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!
  }
});

Error Handling

All operations throw descriptive errors:

try {
  await getJSON(s3, 'bucket', 'missing.json');
} catch (error) {
  // Error: Object 'missing.json' not found in bucket 'bucket'.
  console.error(error.message);
}

Common errors:

  • NoSuchBucket - Bucket doesn't exist
  • NoSuchKey - Object not found
  • AccessDenied - Insufficient permissions
  • InvalidBucketName - Invalid bucket name format

Logging

Built-in structured logging via @x12i/logxer:

# Configure log level
export XRONOX_STORAGE_LOG_LEVEL=debug  # debug|info|warn|error

# Configure log format
export XRONOX_STORAGE_LOG_FORMAT=json  # text|json

# Enable file logging
export XRONOX_STORAGE_LOG_TO_FILE=true
export XRONOX_STORAGE_LOG_FILE=/var/log/xronox-storage.log

Example log output (JSON format):

{
  "timestamp": "2025-10-12T10:30:00.000Z",
  "package": "XRONOX_STORAGE_S3",
  "level": "INFO",
  "message": "putJSON completed successfully",
  "data": {
    "bucket": "my-bucket",
    "key": "data/item.json",
    "size": 1234,
    "sha256": "abc123...",
    "duration": 45
  }
}

Testing

This package includes comprehensive real-world testing in the peer demos package following Poiesis Rule 25 principles - no mocks, no JEST, only real S3 connections.

Running Demos

Demos for this package are maintained in the peer demos repository:

# Clone the demos repository
git clone https://github.com/xronoces/xronox-demos.git

# Navigate to storage-s3 demos
cd xronox-demos/packages/xronox-storage-s3

# Run demos
npm run demo:all

Demo Coverage

The demos package includes:

  • S3 Operations (10 demos) - Direct S3 operations
  • Adapter Operations (9 demos) - StorageAdapter interface
  • Integration Workflows (3 demos) - Complete scenarios

Evidence Capture

Every demo captures real I/O evidence:

  • io/input/ - What was sent to S3
  • io/output/ - What was received from S3
  • io/debug/demo.log - Complete execution log

See the demos repository for detailed testing documentation.

Development

# Install dependencies
npm install

# Build
npm run build

# Watch mode
npm run dev

# Clean
npm run clean

Package Boundaries

✅ This Package Does

  • S3 operations (PUT, GET, DELETE, HEAD, LIST, COPY)
  • JSON serialization/deserialization
  • Stream to buffer conversion
  • SHA-256 hash computation
  • Presigned URL generation
  • Multi-provider S3 compatibility

❌ This Package Does NOT

  • Bucket management (create/delete buckets)
  • Permission management (IAM policies)
  • Data transformation (compression, encryption)
  • Caching strategies
  • Multi-part uploads
  • Custom retry logic

Dependencies

  • nx-config2 (^2.5.0) - Configuration management and ERC 2.0 compliance
  • logs-gateway (^1.4.0) - Structured logging
  • @aws-sdk/client-s3 (peer, ^3.450.0) - AWS S3 SDK (non-ERC, external dependency)
  • @aws-sdk/s3-request-presigner (peer, ^3.450.0) - Presigned URLs (non-ERC, external dependency)

License

MIT

Contributing

This package follows Poiesis Development Methodology:

  • One expertise per component
  • Clear boundaries
  • Real tests with I/O evidence
  • Structured logging via logs-gateway

See docs/context.md for package context and boundaries.

Support

For issues and questions:

Changelog

1.0.0 (2025-10-12)

  • Initial release
  • Full S3-compatible storage operations
  • Multi-provider support (AWS, DigitalOcean, MinIO, Cloudflare R2)
  • SHA-256 data integrity verification
  • Structured logging via logs-gateway
  • nxconfig integration