/** * Universal Path Generator * * Provider-agnostic path generation based on CONTENT TYPE, not provider. * Any adapter (R2, Supabase, S3, etc.) can use any path strategy. * * Path Strategies: * - COMPLIANCE: Financial/legal documents requiring audit trail * - MEDIA: Images/videos with optimization variants * - GENERAL: Other files with flexible organization * * Naming Conventions (provider-agnostic): * - Compliance: ////
/_. * - Media: /////
// * - General: /////
/ * * @module @plyaz/storage/utils */ import type { UploadParams, CompliancePathOptions, MediaPathOptions, GeneralPathOptions } from '@plyaz/types/storage'; import { PATH_STRATEGY } from '@plyaz/types/storage'; /** * Universal Path Generator * Provider-agnostic path generation based on content type */ export declare class PathGenerator { /** * Generate compliance document path * Works on ANY provider (R2, S3, Supabase, Azure, GCS, etc.) * * Pattern: ////
/_. * Optional: //... for B2B/B2C separation * * Examples (same path works on R2, S3, Supabase): * - club-fc-bern/invoice/2025/11/05/inv_92f3a7_order-5843.pdf * - b2b/vendor-xyz/tax/2025/11/05/tax_2025_q4_summary.pdf */ static generateCompliancePath(options: CompliancePathOptions): string; /** * Generate media path * Works on ANY provider (R2, S3, Supabase, Azure, GCS, etc.) * * Pattern: /////
// * Optional: //... for B2B/B2C separation * * Examples (same path works on R2, S3, Supabase): * - public/club/42/2025/11/05/banner/lg.webp * - b2b/private/user/903/2025/11/05/avatar/original.png */ static generateMediaPath(options: MediaPathOptions): string; /** * Generate general file path * Works on ANY provider * * Pattern: /////
/ * * Examples: * - documents/user/123/2025/11/05/report-abc.pdf * - attachments/order/456/2025/11/05/uuid-file.txt */ static generateGeneralPath(options: GeneralPathOptions): string; /** * Auto-detect strategy and generate path from UploadParams * Returns null if params don't match any pattern */ static detectAndGenerate(params: UploadParams): { path: string; strategy: PATH_STRATEGY; } | null; /** * Try to generate compliance path from params */ private static tryGenerateCompliancePath; /** * Try to generate media path from params */ private static tryGenerateMediaPath; /** * Try to generate general path from params */ private static tryGenerateGeneralPath; /** * Validate path against naming conventions */ static validatePath(path: string, strategy: PATH_STRATEGY): boolean; /** * Validate compliance path */ private static validateCompliancePath; /** * Validate media path */ private static validateMediaPath; /** * Validate general path */ private static validateGeneralPath; /** * Get date parts for path */ private static getDateParts; /** * Sanitize tenant slug (lowercase, ASCII, kebab-case, no PII) */ private static sanitizeTenant; /** * Sanitize slug (lowercase, ASCII, kebab-case, max 80 chars) */ private static sanitizeSlug; /** * Sanitize filename (lowercase, ASCII, correct extension) */ private static sanitizeFilename; /** * Extract file extension from filename */ private static getExtension; /** * Generate short UUID (first segment only) * Note: crypto.randomUUID() is available in Node.js 19.0.0+ as stable API */ private static generateShortUuid; } /** * NOTE: Bucket naming is handled by individual adapters. * Each adapter translates bucket PURPOSE to their naming convention: * * - CloudflareR2Adapter: plyaz-{env}-{region}-{purpose} * - SupabaseStorageAdapter: {purpose}-{type} * - S3Adapter: plyaz-{purpose}-{env}-{region} * - AzureAdapter: {purpose}{hash} * * Use StorageCategoryClassifier to determine bucket purpose, * then let adapters handle naming internally. */