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