npm package: @slingts/adapters
Description: Service adapters for Sling framework
Latest version: 0.1.8

# @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

```typescript
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

````typescript
# @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

```typescript
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

```typescript
// 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

```typescript
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

```typescript
// 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

```typescript
// 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

```typescript
// 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

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

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

#### URL Generation

```typescript
// 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

```typescript
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<User>('SELECT \* FROM users WHERE age > ?', [18]);

// Query single row
const user = await db.queryOne<User>('SELECT \* FROM users WHERE email = ?', ['alice@example.com']);

````

#### 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: 'alice@example.com',
  age: 30,
});

// Find one record
const found = await db.table<User>('users').findOne({ email: 'alice@example.com' });

// 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

```typescript
// 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: 'alice@example.com' }).first();
```

#### Transactions

```typescript
// Execute multiple operations in a transaction
await db.transaction(async (tx) => {
  const user = await tx.table<User>('users').create({
    name: 'Bob',
    email: 'bob@example.com',
  });

  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: 'charlie@example.com',
  });
  return user.id;
});
```

#### Using in Sling Functions

```typescript
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.

```typescript
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.

```typescript
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.

```typescript
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