UNPKG

alepha

Version:

Alepha is a convention-driven TypeScript framework for building robust, end-to-end type-safe applications, from serverless APIs to full-stack React apps.

507 lines (506 loc) 18.3 kB
import * as _alepha_core1 from "alepha"; import { Alepha, AlephaError, Descriptor, FileLike, KIND, Service } from "alepha"; import * as fs from "node:fs"; import * as _alepha_logger0 from "alepha/logger"; import * as typebox0 from "typebox"; //#region src/providers/FileStorageProvider.d.ts declare abstract class FileStorageProvider { /** * Uploads a file to the storage. * * @param bucketName - Container name * @param file - File to upload * @param fileId - Optional file identifier. If not provided, a unique ID will be generated. * @return The identifier of the uploaded file. */ abstract upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>; /** * Downloads a file from the storage. * * @param bucketName - Container name * @param fileId - Identifier of the file to download * @return The downloaded file as a FileLike object. */ abstract download(bucketName: string, fileId: string): Promise<FileLike>; /** * Check if fileId exists in the storage bucket. * * @param bucketName - Container name * @param fileId - Identifier of the file to stream * @return True is the file exists, false otherwise. */ abstract exists(bucketName: string, fileId: string): Promise<boolean>; /** * Delete permanently a file from the storage. * * @param bucketName - Container name * @param fileId - Identifier of the file to delete */ abstract delete(bucketName: string, fileId: string): Promise<void>; } //#endregion //#region src/providers/MemoryFileStorageProvider.d.ts declare class MemoryFileStorageProvider implements FileStorageProvider { readonly files: Record<string, FileLike>; upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>; download(bucketName: string, fileId: string): Promise<FileLike>; exists(bucketName: string, fileId: string): Promise<boolean>; delete(bucketName: string, fileId: string): Promise<void>; protected createId(): string; } //#endregion //#region src/descriptors/$bucket.d.ts /** * Creates a bucket descriptor for file storage and management with configurable validation. * * This descriptor provides a comprehensive file storage system that handles file uploads, * downloads, validation, and management across multiple storage backends. It supports * MIME type validation, size limits, and integrates seamlessly with various storage * providers for scalable file management in applications. * * **Key Features** * * - **Multi-Provider Support**: Works with filesystem, cloud storage (S3, Azure), and in-memory providers * - **File Validation**: Automatic MIME type checking and file size validation * - **Type Safety**: Full TypeScript support with FileLike interface compatibility * - **Event Integration**: Emits events for file operations (upload, delete) for monitoring * - **Flexible Configuration**: Per-bucket and per-operation configuration options * - **Automatic Detection**: Smart file type and size detection with fallback mechanisms * - **Error Handling**: Comprehensive error handling with descriptive error messages * * **Use Cases** * * Perfect for handling file storage requirements across applications: * - User profile picture and document uploads * - Product image and media management * - Document storage and retrieval systems * - Temporary file handling and processing * - Content delivery and asset management * - Backup and archival storage * - File-based data import/export workflows * * @example * **Basic file upload bucket:** * ```ts * import { $bucket } from "alepha/bucket"; * * class MediaService { * images = $bucket({ * name: "user-images", * description: "User uploaded profile images and photos", * mimeTypes: ["image/jpeg", "image/png", "image/gif", "image/webp"], * maxSize: 5 // 5MB limit * }); * * async uploadProfileImage(file: FileLike, userId: string): Promise<string> { * // File is automatically validated against MIME types and size * const fileId = await this.images.upload(file); * * // Update user profile with new image * await this.userService.updateProfileImage(userId, fileId); * * return fileId; * } * * async getUserProfileImage(userId: string): Promise<FileLike> { * const user = await this.userService.getUser(userId); * if (!user.profileImageId) { * throw new Error('User has no profile image'); * } * * return await this.images.download(user.profileImageId); * } * } * ``` * * @example * **Document storage with multiple file types:** * ```ts * class DocumentManager { * documents = $bucket({ * name: "company-documents", * description: "Legal documents, contracts, and reports", * mimeTypes: [ * "application/pdf", * "application/msword", * "application/vnd.openxmlformats-officedocument.wordprocessingml.document", * "text/plain", * "text/csv" * ], * maxSize: 50 // 50MB for large documents * }); * * async uploadDocument(file: FileLike, metadata: { title: string; category: string; userId: string }): Promise<string> { * try { * const fileId = await this.documents.upload(file); * * // Store document metadata in database * await this.database.documents.create({ * id: fileId, * title: metadata.title, * category: metadata.category, * uploadedBy: metadata.userId, * fileName: file.name, * fileSize: file.size, * mimeType: file.type, * uploadedAt: new Date() * }); * * console.log(`Document uploaded successfully: ${metadata.title} (${fileId})`); * return fileId; * * } catch (error) { * console.error(`Failed to upload document: ${metadata.title}`, error); * throw error; * } * } * * async downloadDocument(documentId: string, userId: string): Promise<FileLike> { * // Check permissions * const document = await this.database.documents.findById(documentId); * if (!document) { * throw new Error('Document not found'); * } * * const hasAccess = await this.permissionService.canAccessDocument(userId, documentId); * if (!hasAccess) { * throw new Error('Insufficient permissions to access document'); * } * * // Download and return file * return await this.documents.download(documentId); * } * * async deleteDocument(documentId: string, userId: string): Promise<void> { * // Verify ownership or admin privileges * const document = await this.database.documents.findById(documentId); * if (document.uploadedBy !== userId && !await this.userService.isAdmin(userId)) { * throw new Error('Cannot delete document: insufficient permissions'); * } * * // Delete from storage and database * await this.documents.delete(documentId); * await this.database.documents.delete(documentId); * * console.log(`Document deleted: ${document.title} (${documentId})`); * } * } * ``` */ declare const $bucket: { (options: BucketDescriptorOptions): BucketDescriptor; [KIND]: typeof BucketDescriptor; }; interface BucketDescriptorOptions extends BucketFileOptions { /** * File storage provider configuration for the bucket. * * Options: * - **"memory"**: In-memory storage (default for development, lost on restart) * - **Service<FileStorageProvider>**: Custom provider class (e.g., S3FileStorageProvider, AzureBlobProvider) * - **undefined**: Uses the default file storage provider from dependency injection * * **Provider Selection Guidelines**: * - **Development**: Use "memory" for fast, simple testing without external dependencies * - **Production**: Use cloud providers (S3, Azure Blob, Google Cloud Storage) for scalability * - **Local deployment**: Use filesystem providers for on-premise installations * - **Hybrid**: Use different providers for different bucket types (temp files vs permanent storage) * * **Provider Capabilities**: * - File persistence and durability guarantees * - Scalability and performance characteristics * - Geographic distribution and CDN integration * - Cost implications for storage and bandwidth * - Backup and disaster recovery features * * @default Uses injected FileStorageProvider * @example "memory" * @example S3FileStorageProvider * @example AzureBlobStorageProvider */ provider?: Service<FileStorageProvider> | "memory"; /** * Unique name identifier for the bucket. * * This name is used for: * - Storage backend organization and partitioning * - File path generation and URL construction * - Logging, monitoring, and debugging * - Access control and permissions management * - Backup and replication configuration * * **Naming Conventions**: * - Use lowercase with hyphens for consistency * - Include purpose or content type in the name * - Avoid spaces and special characters * - Consider environment prefixes for deployment isolation * * If not provided, defaults to the property key where the bucket is declared. * * @example "user-avatars" * @example "product-images" * @example "legal-documents" * @example "temp-processing-files" */ name?: string; } interface BucketFileOptions { /** * Human-readable description of the bucket's purpose and contents. * * Used for: * - Documentation generation and API references * - Developer onboarding and system understanding * - Monitoring dashboards and admin interfaces * - Compliance and audit documentation * * **Description Best Practices**: * - Explain what types of files this bucket stores * - Mention any special handling or processing requirements * - Include information about retention policies if applicable * - Note any compliance or security considerations * * @example "User profile pictures and avatar images" * @example "Product catalog images with automated thumbnail generation" * @example "Legal documents requiring long-term retention" * @example "Temporary files for data processing workflows" */ description?: string; /** * Array of allowed MIME types for files uploaded to this bucket. * * When specified, only files with these exact MIME types will be accepted. * Files with disallowed MIME types will be rejected with an InvalidFileError. * * **MIME Type Categories**: * - Images: "image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml" * - Documents: "application/pdf", "text/plain", "text/csv" * - Office: "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" * - Archives: "application/zip", "application/x-tar", "application/gzip" * - Media: "video/mp4", "audio/mpeg", "audio/wav" * * **Security Considerations**: * - Always validate MIME types for user uploads * - Be cautious with executable file types * - Consider using allow-lists rather than deny-lists * - Remember that MIME types can be spoofed by malicious users * * If not specified, all MIME types are allowed (not recommended for user uploads). * * @example ["image/jpeg", "image/png"] // Only JPEG and PNG images * @example ["application/pdf", "text/plain"] // Documents only * @example ["video/mp4", "video/webm"] // Video files */ mimeTypes?: string[]; /** * Maximum file size allowed in megabytes (MB). * * Files larger than this limit will be rejected with an InvalidFileError. * This helps prevent: * - Storage quota exhaustion * - Memory issues during file processing * - Long upload times and timeouts * - Abuse of storage resources * * **Size Guidelines by File Type**: * - Profile images: 1-5 MB * - Product photos: 5-10 MB * - Documents: 10-50 MB * - Video files: 50-500 MB * - Data files: 100-1000 MB * * **Considerations**: * - Consider your storage costs and limits * - Factor in network upload speeds for users * - Account for processing requirements (thumbnails, compression) * - Set reasonable limits based on actual use cases * * @default 10 MB * * @example 1 // 1MB for small images * @example 25 // 25MB for documents * @example 100 // 100MB for media files */ maxSize?: number; } declare class BucketDescriptor extends Descriptor<BucketDescriptorOptions> { readonly provider: FileStorageProvider | MemoryFileStorageProvider; get name(): string; /** * Uploads a file to the bucket. */ upload(file: FileLike, options?: BucketFileOptions): Promise<string>; /** * Delete permanently a file from the bucket. */ delete(fileId: string): Promise<void>; /** * Checks if a file exists in the bucket. */ exists(fileId: string): Promise<boolean>; /** * Downloads a file from the bucket. */ download(fileId: string): Promise<FileLike>; protected $provider(): FileStorageProvider | MemoryFileStorageProvider; } interface BucketFileOptions { /** * Optional description of the bucket. */ description?: string; /** * Allowed MIME types. */ mimeTypes?: string[]; /** * Maximum size of the files in the bucket. * * @default 10 */ maxSize?: number; } //#endregion //#region src/errors/FileNotFoundError.d.ts declare class FileNotFoundError extends AlephaError { readonly status = 404; } //#endregion //#region src/services/FileMetadataService.d.ts interface FileMetadata { name: string; type: string; } /** * Service for encoding/decoding file metadata in storage streams. * * The metadata is stored at the beginning of the file with the following structure: * - 4-byte header: UInt32BE containing the metadata length * - N-byte metadata: JSON object containing file metadata (name, type) * - Remaining bytes: Actual file content * * @example * ```typescript * const service = new FileMetadataService(); * * // Encode metadata and content for storage * const { header, metadata } = service.encodeMetadata({ * name: "document.pdf", * type: "application/pdf" * }); * * // Decode metadata from stored file * const fileHandle = await open(filePath, 'r'); * const { metadata, contentStart } = await service.decodeMetadata(fileHandle); * ``` */ declare class FileMetadataService { /** * Length of the header containing metadata size (4 bytes for UInt32BE) */ static readonly METADATA_HEADER_LENGTH = 4; /** * Encodes file metadata into header and metadata buffers. * * @param file - The file or metadata to encode * @returns Object containing the header buffer and metadata buffer */ encodeMetadata(file: FileLike | FileMetadata): { header: Buffer; metadata: Buffer; }; /** * Decodes file metadata from a file handle. * * @param fileHandle - File handle opened for reading * @returns Object containing the decoded metadata and content start position */ decodeMetadata(fileHandle: { read: (buffer: Buffer, offset: number, length: number, position: number) => Promise<{ bytesRead: number; }>; }): Promise<{ metadata: FileMetadata; contentStart: number; }>; /** * Decodes file metadata from a buffer. * * @param buffer - Buffer containing the file with metadata * @returns Object containing the decoded metadata and content start position */ decodeMetadataFromBuffer(buffer: Buffer): { metadata: FileMetadata; contentStart: number; }; /** * Creates a complete buffer with metadata header, metadata, and content. * * @param file - The file to encode * @param content - The file content as a buffer * @returns Complete buffer ready for storage */ createFileBuffer(file: FileLike | FileMetadata, content: Buffer): Buffer; } //#endregion //#region src/providers/LocalFileStorageProvider.d.ts declare class LocalFileStorageProvider implements FileStorageProvider { protected readonly alepha: Alepha; protected readonly log: _alepha_logger0.Logger; protected readonly metadataService: FileMetadataService; options: { storagePath: string; }; protected readonly configure: _alepha_core1.HookDescriptor<"start">; upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>; download(bucketName: string, fileId: string): Promise<FileLike>; exists(bucketName: string, fileId: string): Promise<boolean>; delete(bucketName: string, fileId: string): Promise<void>; protected stat(bucket: string, fileId: string): Promise<fs.Stats>; protected createId(): string; protected path(bucket: string, fileId?: string): string; protected isErrorNoEntry(error: unknown): boolean; } declare const fileMetadataSchema: typebox0.TObject<{ name: typebox0.TString; type: typebox0.TString; size: typebox0.TNumber; }>; //#endregion //#region src/index.d.ts declare module "alepha" { interface Hooks { /** * Triggered when a file is uploaded to a bucket. * Can be used to perform actions after a file is uploaded, like creating a database record! */ "bucket:file:uploaded": { id: string; file: FileLike; bucket: BucketDescriptor; options: BucketFileOptions; }; /** * Triggered when a file is deleted from a bucket. */ "bucket:file:deleted": { id: string; bucket: BucketDescriptor; }; } } /** * Provides file storage capabilities through declarative bucket descriptors with support for multiple storage backends. * * The bucket module enables unified file operations across different storage systems using the `$bucket` descriptor * on class properties. It abstracts storage provider differences, offering consistent APIs for local filesystem, * cloud storage, or in-memory storage for testing environments. * * @see {@link $bucket} * @see {@link FileStorageProvider} * @module alepha.bucket */ declare const AlephaBucket: _alepha_core1.Service<_alepha_core1.Module<{}>>; //#endregion export { $bucket, AlephaBucket, BucketDescriptor, BucketDescriptorOptions, BucketFileOptions, FileMetadata, FileMetadataService, FileNotFoundError, FileStorageProvider, LocalFileStorageProvider, MemoryFileStorageProvider, fileMetadataSchema }; //# sourceMappingURL=index.d.ts.map