UNPKG

@akomalabs/kemono

Version:

A production-grade TypeScript API wrapper for the Kemono/Coomer platforms

337 lines (272 loc) 8.8 kB
# @akomalabs/kemono A production-grade TypeScript API wrapper for the Kemono/Coomer platforms with advanced features like schema validation, caching, and rate limiting. ## Features - 🚀 Full TypeScript support with comprehensive type definitions - ✅ Zod schema validation for type safety - 🔄 Built-in caching with in-memory and Redis support - 🛡️ Configurable rate limiting - 📝 Detailed logging options - ⚡ Bun-optimized for maximum performance - 🔒 Session management for favorites - 🌐 Support for multiple base URLs (kemono.su and coomer.su) ## Installation ```bash bun add @akomalabs/kemono ``` ## Quick Start ```typescript import { KemonoClient } from '@akomalabs/kemono'; // Create a client instance const client = new KemonoClient({ baseUrl: 'https://kemono.su/api/v1', // or 'https://coomer.su/api/v1' cache: { enabled: true, ttl: 300, // 5 minutes }, }); // Fetch a creator's profile const profile = await client.creators.getProfile('onlyfans', 'creatorId'); // Get recent posts const posts = await client.posts.listRecent(); // Get posts from a specific creator const creatorPosts = await client.posts.getByCreator('onlyfans', 'creatorId'); // Get a specific post with attachments const post = await client.posts.getPost('onlyfans', 'creatorId', 'postId'); const attachments = await client.posts.getAttachments('onlyfans', 'creatorId', 'postId'); ``` ## Type System The library uses Zod for runtime type validation and TypeScript for static typing. All types are inferred from Zod schemas, ensuring runtime type safety. ### Available Types and Schemas ```typescript import { // Schemas (for runtime validation) KemonoBaseUrlSchema, KemonoServiceSchema, KemonoConfigSchema, FileSchema, AttachmentSchema, PostSchema, CreatorSchema, SearchParamsSchema, // Types (for static typing) type KemonoBaseUrl, type KemonoService, type KemonoConfig, type File, type Attachment, type Post, type Creator, type SearchParams, // Error types KemonoApiError, AuthenticationError, NotFoundError, ValidationError, RateLimitError, } from '@akomalabs/kemono'; // Example: Using schemas for runtime validation const config = KemonoConfigSchema.parse({ baseUrl: 'https://kemono.su/api/v1', cache: { enabled: true }, }); // Example: Using types for static typing const post: Post = { id: '123', title: 'Example Post', content: 'Hello World', // ...other required fields }; ``` ## Configuration The client accepts a comprehensive configuration object that is validated at runtime: ```typescript interface KemonoConfig { // Base URL for API requests baseUrl?: 'https://kemono.su/api/v1' | 'https://coomer.su/api/v1'; // Session key for favorites management sessionKey?: string; // Caching configuration cache?: { enabled: boolean; ttl?: number; redis?: { host: string; port: number; password?: string; }; }; // Rate limiting configuration rateLimit?: { maxRequests: number; windowMs: number; }; // Logging configuration logging?: { enabled: boolean; level?: 'error' | 'warn' | 'info' | 'debug'; }; } ``` ## API Reference ### Creators API ```typescript // List all creators const creators = await client.creators.listAll(); // Get a creator's profile const profile = await client.creators.getProfile(service, creatorId); ``` ### Posts API ```typescript // List recent posts const posts = await client.posts.listRecent({ q?: string; // Search query o?: number; // Offset limit?: number; // Number of posts to return }); // Get posts by creator const posts = await client.posts.getByCreator(service, creatorId); // Get a specific post const post = await client.posts.getPost(service, creatorId, postId); // Get post attachments const attachments = await client.posts.getAttachments(service, creatorId, postId); ``` ### Favorites API The Favorites API requires a valid session key to be provided in the client configuration. All favorites methods will throw an `AuthenticationError` if the session key is missing or invalid. ```typescript // Initialize client with session key const client = new KemonoClient({ baseUrl: 'https://kemono.su/api/v1', sessionKey: 'your-session-key', // Required for favorites }); // List favorite creators with error handling try { const creators = await client.favorites.listCreators(); console.log(`Found ${creators.length} favorite creators`); // Access creator properties creators.forEach(creator => { console.log(`${creator.name} (${creator.service})`); console.log(`Last imported: ${creator.last_imported}`); console.log(`Favorite sequence: ${creator.faved_seq}`); }); } catch (error) { if (error instanceof AuthenticationError) { console.error('Please provide a valid session key'); } else { console.error('Failed to fetch favorite creators:', error); } } // List favorite posts with pagination example try { const posts = await client.favorites.listPosts(); console.log(`Found ${posts.length} favorite posts`); // Access post properties posts.forEach(post => { console.log(`${post.title} by ${post.user}`); console.log(`Published: ${post.published}`); console.log(`Content: ${post.content.substring(0, 100)}...`); // Handle attachments if (post.attachments.length > 0) { console.log('Attachments:', post.attachments.map(a => a.name).join(', ')); } }); } catch (error) { if (error instanceof AuthenticationError) { console.error('Please provide a valid session key'); } else { console.error('Failed to fetch favorite posts:', error); } } // Managing favorites async function manageFavorites() { try { // Add a creator to favorites await client.favorites.addCreator('fanbox', 'creator123'); // Add a post to favorites await client.favorites.addPost('fanbox', 'creator123', 'post456'); // Remove from favorites await client.favorites.removeCreator('fanbox', 'creator123'); await client.favorites.removePost('fanbox', 'creator123', 'post456'); } catch (error) { if (error instanceof AuthenticationError) { console.error('Session key required'); } else if (error instanceof NotFoundError) { console.error('Creator or post not found'); } else if (error instanceof RateLimitError) { console.error('Too many requests, please try again later'); } else { console.error('Operation failed:', error); } } } // Response Types interface FavoriteCreator { id: string; // Creator's unique identifier name: string; // Creator's display name service: KemonoService; // Platform (e.g., 'fanbox', 'patreon') indexed: string; // ISO date when the creator was indexed updated: string; // ISO date of last update faved_seq: number; // Sequence number in favorites last_imported: string; // ISO date of last import public_id: string | null; // Public identifier if available relation_id: string | null; // Related identifier if available } interface FavoritePost { id: string; // Post unique identifier user: string; // Creator's ID service: KemonoService; // Platform service title: string; // Post title content: string; // Post content added: string; // ISO date when added to favorites published: string; // ISO date when published edited: string | null; // ISO date of last edit file: { // Main file if present name: string; // File name path: string; // File path }; attachments: any[]; // Additional files embed: Record<string, any>; // Embedded content shared_file: boolean; // Indicates if file is shared faved_seq: number; // Sequence number in favorites } ``` ## Supported Services - fanbox - patreon - gumroad - subscribestar - dlsite - fantia - onlyfans - fansly ## Error Handling The client includes custom error classes for better error handling: ```typescript try { await client.creators.getProfile('onlyfans', 'nonexistent'); } catch (error) { if (error instanceof NotFoundError) { // Handle 404 error } else if (error instanceof AuthenticationError) { // Handle authentication error } else if (error instanceof ValidationError) { // Handle validation errors (e.g., invalid config) } else if (error instanceof RateLimitError) { // Handle rate limit errors } else if (error instanceof KemonoApiError) { // Handle other API errors } } ``` ## Development ```bash # Install dependencies bun install # Run tests bun test # Build package bun run build ``` ## License MIT License ## Contributing Contributions are welcome! Please feel free to submit a Pull Request.