alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
554 lines • 20.7 kB
TypeScript
import { Alepha, AlephaError, FileLike, KIND, Primitive, Service, Static } from "alepha";
import { FileDetector, FileSystemProvider } from "alepha/system";
import { CryptoProvider } from "alepha/crypto";
import * as fs from "node:fs";
import { S3mini } from "s3mini";
import { R2Bucket } from "@cloudflare/workers-types";
//#region ../../src/bucket/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>;
/**
* Delete multiple files in one round-trip where the provider supports
* batch (R2/S3, up to 1000 per call). Memory/Local fall back to a loop.
*
* @param bucketName - Container name
* @param fileIds - Identifiers of the files to delete
*/
abstract deleteMany(bucketName: string, fileIds: string[]): Promise<void>;
/**
* Lists the file identifiers stored in a bucket, like `ls` on a directory.
*
* This is a flat, unpaginated listing intended for small buckets. Providers
* cap the result at their natural page size (~1000 for S3/R2); anything beyond
* that is silently dropped. It is NOT a search API — use `alepha/api/files`
* when you need querying or pagination.
*
* @param bucketName - Container name
* @return The identifiers of the files in the bucket (empty if none).
*/
abstract list(bucketName: string): Promise<string[]>;
}
//#endregion
//#region ../../src/bucket/providers/MemoryFileStorageProvider.d.ts
interface StoredFile {
buffer: Buffer;
name: string;
type: string;
size: number;
}
declare class MemoryFileStorageProvider implements FileStorageProvider {
readonly files: Record<string, StoredFile>;
protected readonly alepha: Alepha;
protected readonly fileSystem: FileSystemProvider;
protected readonly fileDetector: FileDetector;
protected readonly crypto: CryptoProvider;
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>;
deleteMany(bucketName: string, fileIds: string[]): Promise<void>;
list(bucketName: string): Promise<string[]>;
/**
* In-memory key, tenant-scoped when a tenant is active (`currentTenantAtom`),
* mirroring the R2/Local/S3 layout: `{tenantId}/{bucket}/{fileId}`.
*/
protected key(bucket: string, fileId?: string): string;
protected createId(): string;
}
//#endregion
//#region ../../src/bucket/primitives/$bucket.d.ts
/**
* Creates a bucket primitive for file storage and management with configurable validation.
*
* Provides a comprehensive file storage system that handles uploads, downloads, validation,
* and management across multiple storage backends with MIME type and size limit controls.
*
* **Key Features**
* - Multi-provider support (filesystem, cloud storage, in-memory)
* - Automatic MIME type and file size validation
* - Event integration for file operations monitoring
* - Flexible per-bucket and per-operation configuration
* - Smart file type and size detection
*
* **Common Use Cases**
* - User profile pictures and document uploads
* - Product images and media management
* - Document storage and retrieval systems
*
* @example
* ```ts
* class MediaService {
* images = $bucket({
* name: "user-images",
* mimeTypes: ["image/jpeg", "image/png", "image/gif"],
* maxSize: 5 // 5MB limit
* });
*
* documents = $bucket({
* name: "documents",
* mimeTypes: ["application/pdf", "text/plain"],
* maxSize: 50 // 50MB limit
* });
*
* async uploadProfileImage(file: FileLike, userId: string): Promise<string> {
* const fileId = await this.images.upload(file);
* await this.userService.updateProfileImage(userId, fileId);
* return fileId;
* }
*
* async downloadDocument(documentId: string): Promise<FileLike> {
* return await this.documents.download(documentId);
* }
*
* async deleteDocument(documentId: string): Promise<void> {
* await this.documents.delete(documentId);
* }
* }
* ```
*/
declare const $bucket: {
(options: BucketPrimitiveOptions): BucketPrimitive;
[KIND]: typeof BucketPrimitive;
};
interface BucketPrimitiveOptions 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 BucketPrimitive extends Primitive<BucketPrimitiveOptions> {
readonly provider: FileStorageProvider | MemoryFileStorageProvider;
protected readonly fileSystem: FileSystemProvider;
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, skipHook?: boolean): Promise<void>;
/**
* Delete many files in one round-trip when the underlying provider supports
* batch (R2/S3 up to 1000 keys per call). Emits one `bucket:file:deleted`
* event per id unless `skipHook` is set.
*/
deleteMany(fileIds: string[], skipHook?: boolean): Promise<void>;
/**
* Checks if a file exists in the bucket.
*/
exists(fileId: string): Promise<boolean>;
/**
* Lists the file identifiers stored in the bucket, like `ls` on a directory.
*
* This is a flat, unpaginated listing meant for small buckets — the result is
* capped at the provider's natural page size (~1000 for S3/R2) and anything
* beyond that is silently dropped. It is NOT a search API; reach for
* `alepha/api/files` when you need querying or pagination.
*/
list(): Promise<string[]>;
/**
* 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/bucket/errors/FileNotFoundError.d.ts
declare class FileNotFoundError extends AlephaError {
readonly status = 404;
}
//#endregion
//#region ../../src/bucket/providers/LocalFileStorageProvider.d.ts
/**
* Local file storage configuration atom
*/
declare const localFileStorageOptions: import("alepha").Atom<import("zod").ZodObject<{
storagePath: import("zod").ZodString;
}, import("zod/v4/core").$strip>, "alepha.bucket.local.options">;
type LocalFileStorageProviderOptions = Static<typeof localFileStorageOptions.schema>;
declare module "alepha" {
interface State {
[localFileStorageOptions.key]: LocalFileStorageProviderOptions;
}
}
declare class LocalFileStorageProvider implements FileStorageProvider {
protected readonly alepha: Alepha;
protected readonly log: import("alepha/logger").Logger;
protected readonly fileDetector: FileDetector;
protected readonly fileSystemProvider: FileSystemProvider;
protected readonly crypto: CryptoProvider;
protected readonly options: Readonly<{
storagePath: string;
}>;
protected get storagePath(): string;
protected readonly onConfigure: import("alepha").HookPrimitive<"configure">;
protected readonly onStart: import("alepha").HookPrimitive<"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>;
deleteMany(bucketName: string, fileIds: string[]): Promise<void>;
list(bucketName: string): Promise<string[]>;
protected stat(bucket: string, fileId: string): Promise<fs.Stats>;
protected createId(mimeType: string): string;
protected path(bucket: string, fileId?: string): string;
protected isErrorNoEntry(error: unknown): boolean;
}
//#endregion
//#region ../../src/bucket/providers/R2FileStorageProvider.d.ts
/**
* Cloudflare R2 storage provider.
*
* Uses a single R2 bucket binding for all $bucket primitives.
* Files are organized as: {APP_NAME}/{bucketName}/{fileId}
*
* **Required environment variables:**
* - `R2_BUCKET_NAME` - The actual R2 bucket name in Cloudflare
*
* **Optional (uses core Alepha env):**
* - `APP_NAME` - Prefix for all files (for multi-app setups sharing one R2 bucket)
*
* @example
* ```bash
* # .env
* APP_NAME=myapp # optional, used as prefix
* R2_BUCKET_NAME=storage
* ```
*
* @example
* ```toml
* # wrangler.toml - automatically generated by alepha build
* [[r2_buckets]]
* binding = "R2"
* bucket_name = "storage"
* ```
*
* @example
* ```ts
* // Define buckets with validation rules
* const avatars = $bucket({
* name: "avatars",
* maxFileSize: 5 * 1024 * 1024,
* allowedMimeTypes: ["image/*"],
* });
*
* const documents = $bucket({
* name: "documents",
* maxFileSize: 50 * 1024 * 1024,
* allowedMimeTypes: ["application/pdf"],
* });
*
* // Files stored at: myapp/avatars/uuid.png, myapp/documents/uuid.pdf
* ```
*/
declare class R2FileStorageProvider implements FileStorageProvider {
protected readonly alepha: Alepha;
protected readonly log: import("alepha/logger").Logger;
protected readonly crypto: CryptoProvider;
protected readonly env: {
R2_BUCKET_NAME: string;
};
protected r2?: R2Bucket;
/**
* Get the R2 bucket name from environment.
*/
get bucketName(): string;
/**
* Get the optional prefix from APP_NAME environment variable.
* Used for multi-app setups sharing the same R2 bucket.
*/
get prefix(): string | undefined;
protected readonly onStart: import("alepha").HookPrimitive<"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>;
deleteMany(bucketName: string, fileIds: string[]): Promise<void>;
list(bucketName: string): Promise<string[]>;
/**
* Build the full R2 key: {prefix}/{tenantId}/{bucketName}/{fileId}
*
* When a tenant is active on the current request/job (`currentTenantAtom`),
* its id is inserted as a directory so a pooled multi-tenant worker keeps
* each tenant's objects isolated. No tenant → the historical
* `{prefix}/{bucketName}/{fileId}` layout is unchanged.
*/
protected key(bucketName: string, fileId: string): string;
protected getR2(): R2Bucket;
protected createId(filename: string): string;
}
//#endregion
//#region ../../src/bucket/providers/S3FileStorageProvider.d.ts
declare const envSchema: import("zod").ZodObject<{
S3_ENDPOINT: import("zod").ZodString;
S3_REGION: import("zod").ZodOptional<import("zod").ZodString>;
S3_ACCESS_KEY_ID: import("zod").ZodString;
S3_SECRET_ACCESS_KEY: import("zod").ZodString;
}, import("zod/v4/core").$strip>;
declare module "alepha" {
interface Env extends Partial<Static<typeof envSchema>> {}
}
/**
* S3-compatible file storage provider for Node.js.
*
* Backed by `s3mini` (zero-dep, ~20 KB). Works with AWS S3, Cloudflare R2,
* MinIO, DigitalOcean Spaces, Backblaze B2, and any other S3-compatible service.
*
* Uses path-style addressing (`<endpoint>/<bucket>`).
*/
declare class S3FileStorageProvider implements FileStorageProvider {
protected readonly log: import("alepha/logger").Logger;
protected readonly env: {
S3_ENDPOINT: string;
S3_REGION?: string | undefined;
S3_ACCESS_KEY_ID: string;
S3_SECRET_ACCESS_KEY: string;
};
protected readonly alepha: Alepha;
protected readonly fileSystem: FileSystemProvider;
protected readonly fileDetector: FileDetector;
protected readonly crypto: CryptoProvider;
protected readonly clients: Map<string, S3mini>;
/**
* Convert bucket name to S3-compatible format.
* S3 bucket names must be lowercase, 3-63 characters, no underscores.
*/
convertName(name: string): string;
protected getClient(bucketName: string): S3mini;
protected readonly onStart: import("alepha").HookPrimitive<"start">;
/**
* Object key, tenant-scoped when a tenant is active (`currentTenantAtom`):
* `{tenantId}/{fileId}` within the per-bucket S3 bucket. No tenant → `{fileId}`.
*/
protected key(fileId: string): string;
protected createId(mimeType: string): string;
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>;
list(bucketName: string): Promise<string[]>;
deleteMany(bucketName: string, fileIds: string[]): Promise<void>;
}
//#endregion
//#region ../../src/bucket/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: BucketPrimitive;
options: BucketFileOptions;
};
/**
* Triggered when a file is deleted from a bucket.
*/
"bucket:file:deleted": {
id: string;
bucket: BucketPrimitive;
};
/**
* Triggered when a file is downloaded from a bucket.
*/
"bucket:file:downloaded": {
id: string;
file: FileLike;
bucket: BucketPrimitive;
};
}
}
/**
* Unified file storage abstraction across multiple backends.
*
* **Features:**
* - File storage buckets with constraints
* - Unified API across all storage backends
* - MIME type validation
* - File size limits
* - Upload/download/delete operations
* - TTL-based file expiration
* - Providers: Memory (testing), Local filesystem, AWS S3 / Cloudflare R2 / MinIO, Azure Blob Storage, Vercel Blob
*
* @module alepha.bucket
*/
declare const AlephaBucket: import("alepha").Service<import("alepha").Module>;
//#endregion
export { $bucket, AlephaBucket, BucketFileOptions, BucketPrimitive, BucketPrimitiveOptions, FileNotFoundError, FileStorageProvider, LocalFileStorageProvider, LocalFileStorageProviderOptions, MemoryFileStorageProvider, R2FileStorageProvider, S3FileStorageProvider, localFileStorageOptions };
//# sourceMappingURL=index.d.ts.map