waitlist-mailer
Version:
Modern, modular TypeScript library for managing waitlists with pluggable storage and mail providers. Supports MongoDB, SQL databases, and custom adapters with zero required dependencies for basic usage.
123 lines (107 loc) • 3.46 kB
text/typescript
/**
* In-memory storage adapter for waitlist-mailer.
* Perfect for development, testing, and small-scale applications.
* Data is lost when the process terminates.
*/
import { StorageProvider, WaitlistEntry, SearchOptions } from '../../types';
/**
* Memory-based storage provider implementation.
* No external dependencies required. Ideal for development and testing.
* @template T - Optional metadata type
*/
export class MemoryStorage<T = any> implements StorageProvider<T> {
private storage: Map<string, WaitlistEntry<T>> = new Map();
/**
* Add an email to memory storage.
* @param email - The email to add
* @param data - Optional metadata
* @throws {Error} If the email already exists
*/
async add(email: string, data?: T): Promise<void> {
const normalized = email.toLowerCase();
if (this.storage.has(normalized)) {
throw new Error(`Email already exists: ${email}`);
}
this.storage.set(normalized, {
email,
metadata: data,
createdAt: new Date(),
});
}
/**
* Remove an email from memory storage.
* @param email - The email to remove
* @returns true if the email was found and removed
*/
async remove(email: string): Promise<boolean> {
return this.storage.delete(email.toLowerCase());
}
/**
* Check if an email exists in storage.
* @param email - The email to check
* @returns true if the email exists
*/
async exists(email: string): Promise<boolean> {
return this.storage.has(email.toLowerCase());
}
/**
* Retrieve all entries from storage.
* @returns Array of all WaitlistEntry objects
*/
async getAll(): Promise<WaitlistEntry<T>[]> {
return Array.from(this.storage.values());
}
/**
* Count total entries in storage.
* @returns Number of entries
*/
async count(): Promise<number> {
return this.storage.size;
}
/**
* Clear all entries from storage.
*/
async clear(): Promise<void> {
this.storage.clear();
}
/**
* Search entries by email pattern.
* For MemoryStorage, this filters the in-memory Map.
* @param pattern - The pattern to search for
* @param options - Optional search options
* @returns Matching entries
*/
async search(pattern: string, options: SearchOptions = {}): Promise<WaitlistEntry<T>[]> {
const { limit, offset = 0, caseInsensitive = true } = options;
const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern;
let results: WaitlistEntry<T>[] = [];
for (const entry of this.storage.values()) {
const emailToMatch = caseInsensitive ? entry.email.toLowerCase() : entry.email;
if (emailToMatch.includes(searchPattern)) {
results.push(entry);
}
}
// Apply pagination
if (offset > 0) {
results = results.slice(offset);
}
if (limit !== undefined && limit > 0) {
results = results.slice(0, limit);
}
return results;
}
/**
* Iterate over entries in batches.
* For MemoryStorage, yields entries from the in-memory Map.
* @param batchSize - Number of entries per batch (default: 100)
*/
async *iterate(batchSize: number = 100): AsyncIterableIterator<WaitlistEntry<T>> {
const entries = Array.from(this.storage.values());
for (let i = 0; i < entries.length; i += batchSize) {
const batch = entries.slice(i, i + batchSize);
for (const entry of batch) {
yield entry;
}
}
}
}