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

@slingts/adapters

v0.1.8

Published

Service adapters for Sling framework

Readme

@slingts/adapters

Service adapters for the Sling framework.

Installation

npm install @slingts/adapters
# or
pnpm add @slingts/adapters

Database Adapters

SQLite Adapter

The SQLite adapter provides a full-featured database interface using better-sqlite3.

Basic Usage

import { createSQLiteAdapter, type DatabaseAdapter } from '@slingts/adapters';
import { globalAdapterRegistry } from '@slingts/core';

// Create and register the adapter
const dbAdapter = createSQLiteAdapter();
globalAdapterRegistry.register('db', dbAdapter, {
  adapter: 'sqlite',
  config: {
    filename: './dev.db', // or ':memory:' for in-memory
    verbose: false,
    wal: true, // Enable WAL mode for better concurrency
    foreignKeys: true, // Enable foreign key constraints
  },
});

// Get the database client
const db = await globalAdapterRegistry.getClient<DatabaseAdapter>('db');

Raw SQL Queries

# @slingts/adapters

Service adapters for the Sling framework.

## Installation

```bash
npm install @slingts/adapters
# or
pnpm add @slingts/adapters

Database Adapters

SQLite Adapter

The SQLite adapter provides a full-featured database interface using better-sqlite3.

Basic Usage

import { createSQLiteAdapter, type DatabaseAdapter } from '@slingts/adapters';
import { globalAdapterRegistry } from '@slingts/core';

// Create and register the adapter
const dbAdapter = createSQLiteAdapter();
globalAdapterRegistry.register('db', dbAdapter, {
  adapter: 'sqlite',
  config: {
    filename: './dev.db', // or ':memory:' for in-memory
    verbose: false,
    wal: true, // Enable WAL mode for better concurrency
    foreignKeys: true, // Enable foreign key constraints
  },
});

// Get the database client
const db = await globalAdapterRegistry.getClient<DatabaseAdapter>('db');

Raw SQL Queries

// Execute a query
await db.execute(`
  CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL
  )
`);

Storage Adapters

Local Storage Adapter

The local storage adapter provides file storage using the local filesystem, perfect for development.

Basic Usage

import { createLocalStorageAdapter, type StorageAdapter } from '@slingts/adapters';
import { globalAdapterRegistry } from '@slingts/core';

// Create and register the adapter
const storageAdapter = createLocalStorageAdapter();
globalAdapterRegistry.register('storage', storageAdapter, {
  adapter: 'local-storage',
  config: {
    basePath: './uploads', // Directory for storing files
    publicUrl: 'http://localhost:3000/uploads', // Base URL for public files
    createIfMissing: true, // Create directory if it doesn't exist
  },
});

// Get the storage client
const storage = await globalAdapterRegistry.getClient<StorageAdapter>('storage');

Upload Files

// Upload from buffer
const result = await storage.upload({
  key: 'uploads/avatar.jpg',
  file: Buffer.from(imageData),
  contentType: 'image/jpeg',
  acl: 'public-read',
  metadata: {
    userId: '123',
    uploadedAt: new Date().toISOString(),
  },
});

console.log(result.url); // Public URL if acl is 'public-read'

// Upload from stream
import { createReadStream } from 'fs';
const stream = createReadStream('./local-file.pdf');

await storage.upload({
  key: 'documents/file.pdf',
  file: stream,
  contentType: 'application/pdf',
});

Download Files

// Download as buffer
const content = await storage.download('uploads/avatar.jpg');

// Download as stream
const stream = await storage.downloadStream('documents/large-file.pdf');

List Files

// List all files
const allFiles = await storage.list();

// List with prefix
const uploads = await storage.list('uploads/');

uploads.forEach((file) => {
  console.log(file.key, file.size, file.lastModified);
});

Delete Files

// Delete single file
await storage.delete('uploads/old-file.jpg');

// Delete multiple files
await storage.deleteMany(['file1.txt', 'file2.txt', 'file3.txt']);

URL Generation

// Get public URL
const publicUrl = storage.getPublicUrl('uploads/avatar.jpg');

// Get signed URL (temporary access)
const signedUrl = await storage.getSignedUrl('private/document.pdf', 3600); // 1 hour

Check File Existence

const exists = await storage.exists('uploads/avatar.jpg');

if (exists) {
  const metadata = await storage.getMetadata('uploads/avatar.jpg');
  console.log(metadata.size, metadata.contentType);
}

Features

  • Path Sanitization: Automatic protection against path traversal attacks
  • Metadata Storage: Store custom metadata alongside files
  • Nested Directories: Automatic directory creation
  • Signed URLs: Generate temporary access URLs
  • Streaming Support: Upload and download large files efficiently

Future Adapters

The following storage adapters are planned for future releases:

  • AWS S3: Enterprise-grade object storage
  • Cloudflare R2: S3-compatible with zero egress fees
  • Google Cloud Storage: GCP object storage
  • Azure Blob Storage: Microsoft Azure storage

// Query multiple rows const users = await db.query('SELECT * FROM users WHERE age > ?', [18]);

// Query single row const user = await db.queryOne('SELECT * FROM users WHERE email = ?', ['[email protected]']);


#### Table Client (ORM-Style)

```typescript
interface User {
  id: number;
  name: string;
  email: string;
  age?: number;
}

// Create a record
const user = await db.table<User>('users').create({
  name: 'Alice',
  email: '[email protected]',
  age: 30,
});

// Find one record
const found = await db.table<User>('users').findOne({ email: '[email protected]' });

// Find multiple records
const users = await db.table<User>('users').findMany({ age: 30 });

// Find all records
const allUsers = await db.table<User>('users').findMany();

// Update a record
const updated = await db.table<User>('users').update(user.id, { age: 31 });

// Delete a record
await db.table<User>('users').delete(user.id);

// Count records
const count = await db.table<User>('users').count({ age: 30 });

Query Builder

// Complex queries with chaining
const users = await db
  .table<User>('users')
  .where({ active: true })
  .orderBy('createdAt', 'desc')
  .limit(10)
  .offset(20)
  .execute();

// Get first result
const user = await db.table<User>('users').where({ email: '[email protected]' }).first();

Transactions

// Execute multiple operations in a transaction
await db.transaction(async (tx) => {
  const user = await tx.table<User>('users').create({
    name: 'Bob',
    email: '[email protected]',
  });

  await tx.table<Post>('posts').create({
    userId: user.id,
    title: 'First Post',
    content: 'Hello World!',
  });

  // If any operation fails, the entire transaction is rolled back
});

// Return values from transactions
const userId = await db.transaction(async (tx) => {
  const user = await tx.table<User>('users').create({
    name: 'Charlie',
    email: '[email protected]',
  });
  return user.id;
});

Using in Sling Functions

import { defineFunction, z } from '@slingts/core';
import type { DatabaseAdapter } from '@slingts/adapters';

export const createUser = defineFunction({
  validate: z.object({
    name: z.string(),
    email: z.string().email(),
  }),
  handler: async (params, ctx) => {
    const db = ctx.services.db as DatabaseAdapter;

    const user = await db.table<User>('users').create({
      name: params.name,
      email: params.email,
    });

    return { success: true, user };
  },
});

Features

SQLite Adapter Features

  • ✅ Raw SQL queries with parameterization
  • ✅ ORM-style table client
  • ✅ Query builder with filtering, sorting, pagination
  • ✅ Transaction support with automatic rollback
  • ✅ In-memory and file-based databases
  • ✅ WAL mode for better concurrency
  • ✅ Foreign key constraints
  • ✅ Full TypeScript support
  • ✅ Health checks
  • ✅ Graceful shutdown

API Reference

DatabaseAdapter

Main interface for database operations.

interface DatabaseAdapter {
  // Raw queries
  query<T>(sql: string, params?: unknown[]): Promise<T[]>;
  queryOne<T>(sql: string, params?: unknown[]): Promise<T | null>;
  execute(sql: string, params?: unknown[]): Promise<void>;

  // Table access
  table<T>(name: string): TableClient<T>;

  // Transactions
  transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T>;

  // Lifecycle
  connect(): Promise<void>;
  disconnect(): Promise<void>;
  healthCheck(): Promise<boolean>;
}

TableClient

ORM-style interface for table operations.

interface TableClient<T> {
  create(data: Partial<T>): Promise<T>;
  findOne(where: Partial<T>): Promise<T | null>;
  findMany(where?: Partial<T>): Promise<T[]>;
  update(id: string | number, data: Partial<T>): Promise<T | null>;
  delete(id: string | number): Promise<boolean>;
  count(where?: Partial<T>): Promise<number>;
  where(condition: Partial<T>): QueryBuilder<T>;
}

QueryBuilder

Chainable query builder for complex queries.

interface QueryBuilder<T> {
  where(condition: Partial<T>): QueryBuilder<T>;
  orderBy(field: keyof T, direction?: 'asc' | 'desc'): QueryBuilder<T>;
  limit(count: number): QueryBuilder<T>;
  offset(count: number): QueryBuilder<T>;
  execute(): Promise<T[]>;
  first(): Promise<T | null>;
}

License

MIT