@float.js/core 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/devtools/index.ts"],"sourcesContent":["/**\n * Float.js Dev Dashboard\n * Visual development tools integrated into the framework\n * \n * Next.js doesn't have this! πŸš€\n */\n\nimport { IncomingMessage, ServerResponse } from 'http';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface RouteInfo {\n path: string;\n type: 'page' | 'api' | 'layout' | 'error' | 'loading' | 'not-found';\n file: string;\n methods?: string[];\n params?: string[];\n middleware?: boolean;\n}\n\nexport interface BuildInfo {\n duration: number;\n timestamp: Date;\n success: boolean;\n errors?: string[];\n warnings?: string[];\n}\n\nexport interface RequestLog {\n id: string;\n method: string;\n path: string;\n status: number;\n duration: number;\n timestamp: Date;\n headers?: Record<string, string>;\n body?: unknown;\n response?: unknown;\n}\n\nexport interface PerformanceMetrics {\n requests: number;\n avgResponseTime: number;\n errorRate: number;\n activeConnections: number;\n memoryUsage: NodeJS.MemoryUsage;\n uptime: number;\n}\n\nexport interface DevDashboardOptions {\n enabled?: boolean;\n path?: string;\n maxLogs?: number;\n auth?: {\n username: string;\n password: string;\n };\n}\n\n// ============================================================================\n// DEV DASHBOARD STATE\n// ============================================================================\n\nclass DevDashboardState {\n routes: RouteInfo[] = [];\n builds: BuildInfo[] = [];\n requestLogs: RequestLog[] = [];\n startTime: Date = new Date();\n maxLogs: number = 100;\n \n private requestCount = 0;\n private totalResponseTime = 0;\n private errorCount = 0;\n\n addRoute(route: RouteInfo): void {\n const existing = this.routes.findIndex(r => r.path === route.path);\n if (existing >= 0) {\n this.routes[existing] = route;\n } else {\n this.routes.push(route);\n }\n }\n\n addBuild(build: BuildInfo): void {\n this.builds.unshift(build);\n if (this.builds.length > 20) {\n this.builds.pop();\n }\n }\n\n logRequest(log: RequestLog): void {\n this.requestLogs.unshift(log);\n if (this.requestLogs.length > this.maxLogs) {\n this.requestLogs.pop();\n }\n \n this.requestCount++;\n this.totalResponseTime += log.duration;\n if (log.status >= 400) {\n this.errorCount++;\n }\n }\n\n getMetrics(): PerformanceMetrics {\n return {\n requests: this.requestCount,\n avgResponseTime: this.requestCount > 0 \n ? Math.round(this.totalResponseTime / this.requestCount) \n : 0,\n errorRate: this.requestCount > 0 \n ? Math.round((this.errorCount / this.requestCount) * 100) \n : 0,\n activeConnections: 0, // Updated by server\n memoryUsage: process.memoryUsage(),\n uptime: Date.now() - this.startTime.getTime(),\n };\n }\n\n clear(): void {\n this.requestLogs = [];\n this.requestCount = 0;\n this.totalResponseTime = 0;\n this.errorCount = 0;\n }\n}\n\nexport const dashboardState = new DevDashboardState();\n\n// ============================================================================\n// MIDDLEWARE\n// ============================================================================\n\nexport function createRequestLogger() {\n return (req: IncomingMessage, res: ServerResponse, next: () => void) => {\n const startTime = Date.now();\n const id = `req_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;\n\n // Capture response\n const originalEnd = res.end.bind(res);\n res.end = function(chunk?: any, encoding?: any, callback?: any) {\n const duration = Date.now() - startTime;\n \n dashboardState.logRequest({\n id,\n method: req.method || 'GET',\n path: req.url || '/',\n status: res.statusCode,\n duration,\n timestamp: new Date(),\n });\n\n return originalEnd(chunk, encoding, callback);\n } as typeof res.end;\n\n next();\n };\n}\n\n// ============================================================================\n// DASHBOARD HTML\n// ============================================================================\n\nfunction generateDashboardHTML(state: DevDashboardState): string {\n const metrics = state.getMetrics();\n const memoryMB = Math.round(metrics.memoryUsage.heapUsed / 1024 / 1024);\n const memoryTotal = Math.round(metrics.memoryUsage.heapTotal / 1024 / 1024);\n const uptimeSeconds = Math.round(metrics.uptime / 1000);\n const uptimeFormatted = uptimeSeconds < 60 \n ? `${uptimeSeconds}s` \n : uptimeSeconds < 3600 \n ? `${Math.floor(uptimeSeconds / 60)}m ${uptimeSeconds % 60}s`\n : `${Math.floor(uptimeSeconds / 3600)}h ${Math.floor((uptimeSeconds % 3600) / 60)}m`;\n \n const lastBuild = state.builds.length > 0 ? state.builds[0] : null;\n const lastBuildTime = lastBuild \n ? new Date(lastBuild.timestamp).toLocaleTimeString() \n : 'Never';\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Float.js Dev Dashboard</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap\" rel=\"stylesheet\">\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n \n :root {\n --bg-dark: #09090b;\n --bg-card: #18181b;\n --bg-card-hover: #1f1f23;\n --bg-sidebar: #0f0f12;\n --text-primary: #fafafa;\n --text-secondary: #71717a;\n --text-muted: #52525b;\n --accent: #a855f7;\n --accent-glow: rgba(168, 85, 247, 0.15);\n --success: #22c55e;\n --warning: #f59e0b;\n --error: #ef4444;\n --border: #27272a;\n --border-subtle: #1f1f23;\n }\n\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n background: var(--bg-dark);\n color: var(--text-primary);\n min-height: 100vh;\n display: flex;\n }\n\n /* Sidebar */\n .sidebar {\n width: 280px;\n background: var(--bg-sidebar);\n border-right: 1px solid var(--border);\n display: flex;\n flex-direction: column;\n position: fixed;\n height: 100vh;\n z-index: 100;\n }\n\n .sidebar-header {\n padding: 1.5rem;\n border-bottom: 1px solid var(--border);\n }\n\n .logo {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n }\n\n .logo-icon {\n width: 40px;\n height: 40px;\n background: linear-gradient(135deg, #a855f7 0%, #ec4899 50%, #f97316 100%);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n font-size: 1.25rem;\n box-shadow: 0 0 20px rgba(168, 85, 247, 0.3);\n }\n\n .logo-text {\n display: flex;\n flex-direction: column;\n }\n\n .logo-title {\n font-weight: 700;\n font-size: 1.125rem;\n background: linear-gradient(135deg, #fff 0%, #a1a1aa 100%);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n }\n\n .logo-version {\n font-size: 0.75rem;\n color: var(--text-muted);\n font-family: 'JetBrains Mono', monospace;\n }\n\n /* Status Cards */\n .status-section {\n padding: 1.5rem;\n border-bottom: 1px solid var(--border);\n }\n\n .status-grid {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n }\n\n .status-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1rem;\n background: var(--bg-card);\n border-radius: 10px;\n border: 1px solid var(--border-subtle);\n }\n\n .status-label {\n font-size: 0.8rem;\n color: var(--text-secondary);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n\n .status-value {\n font-size: 0.875rem;\n font-weight: 600;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .status-value.connected {\n color: var(--success);\n }\n\n .status-value.active {\n color: var(--accent);\n }\n\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n animation: pulse 2s infinite;\n }\n\n .status-dot.green { background: var(--success); box-shadow: 0 0 8px var(--success); }\n .status-dot.purple { background: var(--accent); box-shadow: 0 0 8px var(--accent); }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.7; transform: scale(0.95); }\n }\n\n /* Navigation */\n .nav-section {\n padding: 1rem;\n flex: 1;\n }\n\n .nav-label {\n font-size: 0.7rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--text-muted);\n padding: 0.5rem 1rem;\n margin-bottom: 0.5rem;\n }\n\n .nav-item {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n border-radius: 10px;\n color: var(--text-secondary);\n text-decoration: none;\n font-size: 0.875rem;\n font-weight: 500;\n transition: all 0.2s;\n cursor: pointer;\n margin-bottom: 0.25rem;\n }\n\n .nav-item:hover {\n background: var(--bg-card);\n color: var(--text-primary);\n }\n\n .nav-item.active {\n background: var(--accent-glow);\n color: var(--accent);\n border: 1px solid rgba(168, 85, 247, 0.2);\n }\n\n .nav-icon {\n font-size: 1.1rem;\n }\n\n .nav-badge {\n margin-left: auto;\n background: var(--bg-card);\n padding: 0.125rem 0.5rem;\n border-radius: 6px;\n font-size: 0.7rem;\n font-family: 'JetBrains Mono', monospace;\n color: var(--text-muted);\n }\n\n /* Sidebar Footer */\n .sidebar-footer {\n padding: 1rem 1.5rem;\n border-top: 1px solid var(--border);\n }\n\n .docs-link {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem;\n background: linear-gradient(135deg, rgba(168, 85, 247, 0.1), rgba(236, 72, 153, 0.1));\n border: 1px solid rgba(168, 85, 247, 0.2);\n border-radius: 10px;\n color: var(--accent);\n text-decoration: none;\n font-size: 0.875rem;\n font-weight: 500;\n transition: all 0.2s;\n }\n\n .docs-link:hover {\n background: linear-gradient(135deg, rgba(168, 85, 247, 0.2), rgba(236, 72, 153, 0.2));\n transform: translateY(-1px);\n }\n\n /* Main Content */\n .main {\n margin-left: 280px;\n flex: 1;\n padding: 2rem;\n min-height: 100vh;\n }\n\n .page-header {\n margin-bottom: 2rem;\n }\n\n .page-title {\n font-size: 1.75rem;\n font-weight: 700;\n margin-bottom: 0.5rem;\n }\n\n .page-subtitle {\n color: var(--text-secondary);\n font-size: 0.9rem;\n }\n\n /* Metrics Grid */\n .metrics-grid {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1rem;\n margin-bottom: 2rem;\n }\n\n .metric-card {\n background: var(--bg-card);\n border: 1px solid var(--border-subtle);\n border-radius: 16px;\n padding: 1.5rem;\n transition: all 0.3s;\n position: relative;\n overflow: hidden;\n }\n\n .metric-card::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 2px;\n background: linear-gradient(90deg, var(--accent), #ec4899);\n opacity: 0;\n transition: opacity 0.3s;\n }\n\n .metric-card:hover {\n border-color: var(--accent);\n transform: translateY(-2px);\n box-shadow: 0 8px 32px rgba(168, 85, 247, 0.1);\n }\n\n .metric-card:hover::before {\n opacity: 1;\n }\n\n .metric-icon {\n font-size: 1.5rem;\n margin-bottom: 1rem;\n }\n\n .metric-value {\n font-size: 2rem;\n font-weight: 700;\n font-family: 'JetBrains Mono', monospace;\n margin-bottom: 0.25rem;\n }\n\n .metric-label {\n font-size: 0.8rem;\n color: var(--text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .metric-change {\n position: absolute;\n top: 1rem;\n right: 1rem;\n font-size: 0.75rem;\n padding: 0.25rem 0.5rem;\n border-radius: 6px;\n font-weight: 500;\n }\n\n .metric-change.up { background: rgba(34, 197, 94, 0.1); color: var(--success); }\n .metric-change.down { background: rgba(239, 68, 68, 0.1); color: var(--error); }\n\n /* Sections */\n .content-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1.5rem;\n }\n\n .section {\n background: var(--bg-card);\n border: 1px solid var(--border-subtle);\n border-radius: 16px;\n overflow: hidden;\n }\n\n .section.full-width {\n grid-column: 1 / -1;\n }\n\n .section-header {\n padding: 1.25rem 1.5rem;\n border-bottom: 1px solid var(--border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .section-title {\n font-size: 1rem;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n\n .section-badge {\n background: var(--bg-dark);\n padding: 0.25rem 0.75rem;\n border-radius: 8px;\n font-size: 0.75rem;\n color: var(--text-muted);\n font-family: 'JetBrains Mono', monospace;\n }\n\n .section-content {\n max-height: 400px;\n overflow-y: auto;\n }\n\n /* Routes List */\n .route-item {\n display: flex;\n align-items: center;\n gap: 1rem;\n padding: 1rem 1.5rem;\n border-bottom: 1px solid var(--border-subtle);\n transition: background 0.2s;\n }\n\n .route-item:last-child {\n border-bottom: none;\n }\n\n .route-item:hover {\n background: var(--bg-card-hover);\n }\n\n .route-type {\n padding: 0.25rem 0.75rem;\n border-radius: 6px;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n min-width: 60px;\n text-align: center;\n }\n\n .route-type.page { background: rgba(59, 130, 246, 0.15); color: #60a5fa; }\n .route-type.api { background: rgba(34, 197, 94, 0.15); color: #4ade80; }\n .route-type.layout { background: rgba(168, 85, 247, 0.15); color: #c084fc; }\n\n .route-path {\n font-family: 'JetBrains Mono', monospace;\n font-size: 0.875rem;\n flex: 1;\n }\n\n .route-file {\n color: var(--text-muted);\n font-size: 0.75rem;\n font-family: 'JetBrains Mono', monospace;\n }\n\n /* Request Logs */\n .log-item {\n display: flex;\n align-items: center;\n gap: 1rem;\n padding: 0.875rem 1.5rem;\n border-bottom: 1px solid var(--border-subtle);\n font-size: 0.875rem;\n transition: background 0.2s;\n }\n\n .log-item:hover {\n background: var(--bg-card-hover);\n }\n\n .log-method {\n font-weight: 600;\n padding: 0.25rem 0.5rem;\n border-radius: 4px;\n font-size: 0.7rem;\n min-width: 50px;\n text-align: center;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .log-method.GET { background: rgba(59, 130, 246, 0.15); color: #60a5fa; }\n .log-method.POST { background: rgba(34, 197, 94, 0.15); color: #4ade80; }\n .log-method.PUT { background: rgba(245, 158, 11, 0.15); color: #fbbf24; }\n .log-method.DELETE { background: rgba(239, 68, 68, 0.15); color: #f87171; }\n\n .log-status {\n font-weight: 600;\n font-family: 'JetBrains Mono', monospace;\n font-size: 0.8rem;\n }\n\n .log-status.s2xx { color: var(--success); }\n .log-status.s3xx { color: #60a5fa; }\n .log-status.s4xx { color: var(--warning); }\n .log-status.s5xx { color: var(--error); }\n\n .log-path {\n font-family: 'JetBrains Mono', monospace;\n flex: 1;\n color: var(--text-secondary);\n }\n\n .log-duration {\n font-family: 'JetBrains Mono', monospace;\n color: var(--text-muted);\n font-size: 0.8rem;\n }\n\n .log-time {\n color: var(--text-muted);\n font-size: 0.75rem;\n }\n\n /* Empty State */\n .empty-state {\n text-align: center;\n padding: 3rem;\n color: var(--text-muted);\n }\n\n .empty-icon {\n font-size: 2.5rem;\n margin-bottom: 1rem;\n opacity: 0.5;\n }\n\n /* Scrollbar */\n ::-webkit-scrollbar { width: 6px; }\n ::-webkit-scrollbar-track { background: transparent; }\n ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }\n ::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }\n\n /* Responsive */\n @media (max-width: 1200px) {\n .metrics-grid { grid-template-columns: repeat(2, 1fr); }\n .content-grid { grid-template-columns: 1fr; }\n }\n </style>\n</head>\n<body>\n <!-- Sidebar -->\n <aside class=\"sidebar\">\n <div class=\"sidebar-header\">\n <div class=\"logo\">\n <div class=\"logo-icon\">⚑</div>\n <div class=\"logo-text\">\n <span class=\"logo-title\">Float.js</span>\n <span class=\"logo-version\">v2.0.1</span>\n </div>\n </div>\n </div>\n\n <div class=\"status-section\">\n <div class=\"status-grid\">\n <div class=\"status-item\">\n <span class=\"status-label\">\n <span class=\"status-dot green\"></span>\n Estado\n </span>\n <span class=\"status-value connected\">Conectado</span>\n </div>\n <div class=\"status-item\">\n <span class=\"status-label\">\n <span class=\"status-dot purple\"></span>\n HMR\n </span>\n <span class=\"status-value active\">Activo</span>\n </div>\n <div class=\"status-item\">\n <span class=\"status-label\">Último build</span>\n <span class=\"status-value\">${lastBuildTime}</span>\n </div>\n <div class=\"status-item\">\n <span class=\"status-label\">Uptime</span>\n <span class=\"status-value\">${uptimeFormatted}</span>\n </div>\n </div>\n </div>\n\n <nav class=\"nav-section\">\n <div class=\"nav-label\">Dashboard</div>\n <div class=\"nav-item active\">\n <span class=\"nav-icon\">πŸ“Š</span>\n Overview\n </div>\n <div class=\"nav-item\">\n <span class=\"nav-icon\">πŸ›€οΈ</span>\n Routes\n <span class=\"nav-badge\">${state.routes.length}</span>\n </div>\n <div class=\"nav-item\">\n <span class=\"nav-icon\">πŸ“</span>\n Logs\n <span class=\"nav-badge\">${state.requestLogs.length}</span>\n </div>\n <div class=\"nav-item\">\n <span class=\"nav-icon\">βš™οΈ</span>\n Settings\n </div>\n </nav>\n\n <div class=\"sidebar-footer\">\n <a href=\"https://floatjs.dev/docs\" target=\"_blank\" class=\"docs-link\">\n <span>πŸ“š</span>\n Documentation\n </a>\n </div>\n </aside>\n\n <!-- Main Content -->\n <main class=\"main\">\n <div class=\"page-header\">\n <h1 class=\"page-title\">Dev Dashboard</h1>\n <p class=\"page-subtitle\">Monitor your Float.js application in real-time</p>\n </div>\n\n <!-- Metrics -->\n <div class=\"metrics-grid\">\n <div class=\"metric-card\">\n <div class=\"metric-icon\">πŸ“‘</div>\n <div class=\"metric-value\">${metrics.requests}</div>\n <div class=\"metric-label\">Total Requests</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-icon\">⚑</div>\n <div class=\"metric-value\">${metrics.avgResponseTime}ms</div>\n <div class=\"metric-label\">Avg Response</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-icon\">πŸ’Ύ</div>\n <div class=\"metric-value\">${memoryMB}/${memoryTotal}</div>\n <div class=\"metric-label\">Memory (MB)</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-icon\">${metrics.errorRate > 0 ? '⚠️' : 'βœ…'}</div>\n <div class=\"metric-value\">${metrics.errorRate}%</div>\n <div class=\"metric-label\">Error Rate</div>\n </div>\n </div>\n\n <!-- Content Grid -->\n <div class=\"content-grid\">\n <!-- Routes -->\n <div class=\"section\">\n <div class=\"section-header\">\n <span class=\"section-title\">πŸ›€οΈ Routes</span>\n <span class=\"section-badge\">${state.routes.length} registered</span>\n </div>\n <div class=\"section-content\">\n ${state.routes.length === 0 ? `\n <div class=\"empty-state\">\n <div class=\"empty-icon\">πŸ›€οΈ</div>\n <p>No routes registered</p>\n </div>\n ` : state.routes.map(route => `\n <div class=\"route-item\">\n <span class=\"route-type ${route.type}\">${route.type}</span>\n <span class=\"route-path\">${route.path}</span>\n <span class=\"route-file\">${route.file.split('/').pop()}</span>\n </div>\n `).join('')}\n </div>\n </div>\n\n <!-- Request Logs -->\n <div class=\"section\">\n <div class=\"section-header\">\n <span class=\"section-title\">πŸ“ Request Logs</span>\n <span class=\"section-badge\">Live</span>\n </div>\n <div class=\"section-content\">\n ${state.requestLogs.length === 0 ? `\n <div class=\"empty-state\">\n <div class=\"empty-icon\">πŸ“</div>\n <p>No requests yet</p>\n </div>\n ` : state.requestLogs.slice(0, 20).map(log => {\n const statusClass = log.status < 300 ? 's2xx' : log.status < 400 ? 's3xx' : log.status < 500 ? 's4xx' : 's5xx';\n return `\n <div class=\"log-item\">\n <span class=\"log-method ${log.method}\">${log.method}</span>\n <span class=\"log-status ${statusClass}\">${log.status}</span>\n <span class=\"log-path\">${log.path}</span>\n <span class=\"log-duration\">${log.duration}ms</span>\n </div>\n `;\n }).join('')}\n </div>\n </div>\n </div>\n </main>\n\n <script>\n // Auto-refresh every 3 seconds\n setTimeout(() => location.reload(), 3000);\n </script>\n</body>\n</html>`;\n}\n\n// ============================================================================\n// DASHBOARD API\n// ============================================================================\n\nfunction generateAPIResponse(state: DevDashboardState): string {\n return JSON.stringify({\n metrics: state.getMetrics(),\n routes: state.routes,\n builds: state.builds.slice(0, 10),\n requestLogs: state.requestLogs.slice(0, 50),\n });\n}\n\n// ============================================================================\n// DASHBOARD HANDLER\n// ============================================================================\n\nexport function createDevDashboard(options: DevDashboardOptions = {}) {\n const {\n enabled = process.env.NODE_ENV !== 'production',\n path = '/__float',\n auth,\n } = options;\n\n if (!enabled) {\n return (_req: IncomingMessage, _res: ServerResponse, next: () => void) => next();\n }\n\n return (req: IncomingMessage, res: ServerResponse, next: () => void) => {\n const url = req.url || '';\n \n // Check if this is a dashboard request\n if (!url.startsWith(path)) {\n return next();\n }\n\n // Basic auth if configured\n if (auth) {\n const authHeader = req.headers.authorization;\n if (!authHeader || !authHeader.startsWith('Basic ')) {\n res.setHeader('WWW-Authenticate', 'Basic realm=\"Float.js Dev Dashboard\"');\n res.statusCode = 401;\n res.end('Unauthorized');\n return;\n }\n\n const credentials = Buffer.from(authHeader.slice(6), 'base64').toString();\n const [username, password] = credentials.split(':');\n \n if (username !== auth.username || password !== auth.password) {\n res.statusCode = 401;\n res.end('Invalid credentials');\n return;\n }\n }\n\n // Route dashboard requests\n const subPath = url.slice(path.length);\n\n if (subPath === '' || subPath === '/') {\n // Main dashboard\n res.setHeader('Content-Type', 'text/html');\n res.end(generateDashboardHTML(dashboardState));\n return;\n }\n\n if (subPath === '/api' || subPath === '/api/') {\n // API endpoint\n res.setHeader('Content-Type', 'application/json');\n res.end(generateAPIResponse(dashboardState));\n return;\n }\n\n if (subPath === '/api/routes') {\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify(dashboardState.routes));\n return;\n }\n\n if (subPath === '/api/metrics') {\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify(dashboardState.getMetrics()));\n return;\n }\n\n if (subPath === '/api/logs') {\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify(dashboardState.requestLogs.slice(0, 100)));\n return;\n }\n\n if (subPath === '/api/clear' && req.method === 'POST') {\n dashboardState.clear();\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ success: true }));\n return;\n }\n\n // 404 for unknown dashboard routes\n res.statusCode = 404;\n res.end('Not found');\n };\n}\n\n// ============================================================================\n// EXPORTS\n// ============================================================================\n\nexport const devtools = {\n dashboard: createDevDashboard,\n logger: createRequestLogger,\n state: dashboardState,\n \n // Helpers for manual logging\n logRoute: (route: RouteInfo) => dashboardState.addRoute(route),\n logBuild: (build: BuildInfo) => dashboardState.addBuild(build),\n logRequest: (log: RequestLog) => dashboardState.logRequest(log),\n getMetrics: () => dashboardState.getMetrics(),\n clear: () => dashboardState.clear(),\n};\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","names":[]}
@@ -0,0 +1,114 @@
1
+ import { IncomingMessage, ServerResponse } from 'http';
2
+
3
+ /**
4
+ * Float.js Image Optimization
5
+ * Automatic image optimization, resizing, and format conversion
6
+ *
7
+ * Similar to Next.js Image but with more features!
8
+ */
9
+
10
+ interface ImageConfig {
11
+ /** Supported image widths for responsive images */
12
+ deviceSizes: number[];
13
+ /** Smaller sizes for use with next/image */
14
+ imageSizes: number[];
15
+ /** Supported output formats */
16
+ formats: ImageFormat[];
17
+ /** Quality for lossy formats (1-100) */
18
+ quality: number;
19
+ /** Cache directory for optimized images */
20
+ cacheDir: string;
21
+ /** Base path for images */
22
+ basePath: string;
23
+ /** Remote image domains allowed */
24
+ domains: string[];
25
+ /** Minimum cache TTL in seconds */
26
+ minimumCacheTTL: number;
27
+ /** Disable static image imports */
28
+ disableStaticImages: boolean;
29
+ /** Enable AVIF format (experimental) */
30
+ avif: boolean;
31
+ }
32
+ type ImageFormat = 'webp' | 'avif' | 'jpeg' | 'png' | 'gif' | 'svg';
33
+ interface ImageProps {
34
+ src: string;
35
+ alt: string;
36
+ width?: number;
37
+ height?: number;
38
+ fill?: boolean;
39
+ sizes?: string;
40
+ quality?: number;
41
+ priority?: boolean;
42
+ placeholder?: 'blur' | 'empty' | 'data:image/...';
43
+ blurDataURL?: string;
44
+ loading?: 'lazy' | 'eager';
45
+ className?: string;
46
+ style?: Record<string, string>;
47
+ onLoad?: () => void;
48
+ onError?: () => void;
49
+ }
50
+ interface OptimizedImage {
51
+ src: string;
52
+ width: number;
53
+ height: number;
54
+ blurDataURL?: string;
55
+ }
56
+ interface ImageLoaderProps {
57
+ src: string;
58
+ width: number;
59
+ quality?: number;
60
+ }
61
+ declare function configureImages(config: Partial<ImageConfig>): void;
62
+ declare function getImageConfig(): ImageConfig;
63
+ /**
64
+ * Generate blur placeholder
65
+ */
66
+ declare function generateBlurDataURL(input: Buffer): Promise<string>;
67
+ declare function createImageHandler(): (req: IncomingMessage, res: ServerResponse, next: () => void) => Promise<void>;
68
+ /**
69
+ * Default image loader
70
+ */
71
+ declare function floatImageLoader({ src, width, quality }: ImageLoaderProps): string;
72
+ /**
73
+ * Generate srcset for responsive images
74
+ */
75
+ declare function generateSrcSet(src: string, sizes: number[]): string;
76
+ /**
77
+ * Generate responsive image props
78
+ */
79
+ declare function getImageProps(props: ImageProps): {
80
+ src: string;
81
+ srcSet: string;
82
+ sizes: string;
83
+ width?: number;
84
+ height?: number;
85
+ loading: 'lazy' | 'eager';
86
+ decoding: 'async' | 'sync';
87
+ style?: Record<string, string>;
88
+ };
89
+ /**
90
+ * Generate Image component HTML (for SSR)
91
+ */
92
+ declare function renderImageToString(props: ImageProps): string;
93
+ interface StaticImageData {
94
+ src: string;
95
+ width: number;
96
+ height: number;
97
+ blurDataURL?: string;
98
+ }
99
+ /**
100
+ * Import static image (for build-time optimization)
101
+ */
102
+ declare function importImage(imagePath: string): StaticImageData;
103
+ declare const image: {
104
+ configure: typeof configureImages;
105
+ getConfig: typeof getImageConfig;
106
+ handler: typeof createImageHandler;
107
+ loader: typeof floatImageLoader;
108
+ srcSet: typeof generateSrcSet;
109
+ props: typeof getImageProps;
110
+ render: typeof renderImageToString;
111
+ import: typeof importImage;
112
+ };
113
+
114
+ export { type ImageConfig, type ImageFormat, type ImageLoaderProps, type ImageProps, type OptimizedImage, type StaticImageData, configureImages, createImageHandler, floatImageLoader, generateBlurDataURL, generateSrcSet, getImageConfig, getImageProps, image, importImage, renderImageToString };
@@ -0,0 +1,242 @@
1
+ // src/image/index.ts
2
+ import { createHash } from "crypto";
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync } from "fs";
4
+ import { join } from "path";
5
+ var defaultConfig = {
6
+ deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
7
+ imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
8
+ formats: ["webp", "jpeg"],
9
+ quality: 75,
10
+ cacheDir: ".float/cache/images",
11
+ basePath: "/_float/image",
12
+ domains: [],
13
+ minimumCacheTTL: 60,
14
+ disableStaticImages: false,
15
+ avif: false
16
+ };
17
+ var imageConfig = { ...defaultConfig };
18
+ function configureImages(config) {
19
+ imageConfig = { ...defaultConfig, ...config };
20
+ if (!existsSync(imageConfig.cacheDir)) {
21
+ mkdirSync(imageConfig.cacheDir, { recursive: true });
22
+ }
23
+ }
24
+ function getImageConfig() {
25
+ return imageConfig;
26
+ }
27
+ function generateCacheKey(url, width, quality, format) {
28
+ const hash = createHash("md5").update(`${url}-${width}-${quality}-${format}`).digest("hex");
29
+ return `${hash}.${format}`;
30
+ }
31
+ function getBestFormat(acceptHeader) {
32
+ if (imageConfig.avif && acceptHeader.includes("image/avif")) {
33
+ return "avif";
34
+ }
35
+ if (acceptHeader.includes("image/webp")) {
36
+ return "webp";
37
+ }
38
+ return "jpeg";
39
+ }
40
+ function parseImageParams(url) {
41
+ const src = url.searchParams.get("url");
42
+ const width = parseInt(url.searchParams.get("w") || "0", 10);
43
+ const quality = parseInt(url.searchParams.get("q") || String(imageConfig.quality), 10);
44
+ if (!src || !width) {
45
+ return null;
46
+ }
47
+ const allSizes = [...imageConfig.deviceSizes, ...imageConfig.imageSizes];
48
+ if (!allSizes.includes(width)) {
49
+ return null;
50
+ }
51
+ if (quality < 1 || quality > 100) {
52
+ return null;
53
+ }
54
+ return { src, width, quality };
55
+ }
56
+ function isExternalUrl(url) {
57
+ return url.startsWith("http://") || url.startsWith("https://");
58
+ }
59
+ function isAllowedDomain(url) {
60
+ if (!isExternalUrl(url)) return true;
61
+ try {
62
+ const { hostname } = new URL(url);
63
+ return imageConfig.domains.includes(hostname);
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
68
+ async function optimizeImage(input, _width, _quality, _format) {
69
+ return input;
70
+ }
71
+ async function generateBlurDataURL(input) {
72
+ return `data:image/jpeg;base64,${input.slice(0, 50).toString("base64")}`;
73
+ }
74
+ function createImageHandler() {
75
+ if (!existsSync(imageConfig.cacheDir)) {
76
+ mkdirSync(imageConfig.cacheDir, { recursive: true });
77
+ }
78
+ return async (req, res, next) => {
79
+ const urlString = req.url || "";
80
+ if (!urlString.startsWith(imageConfig.basePath)) {
81
+ return next();
82
+ }
83
+ try {
84
+ const url = new URL(urlString, `http://${req.headers.host}`);
85
+ const params = parseImageParams(url);
86
+ if (!params) {
87
+ res.statusCode = 400;
88
+ res.end("Invalid image parameters");
89
+ return;
90
+ }
91
+ const { src, width, quality } = params;
92
+ if (!isAllowedDomain(src)) {
93
+ res.statusCode = 400;
94
+ res.end("Domain not allowed");
95
+ return;
96
+ }
97
+ const acceptHeader = req.headers.accept || "";
98
+ const format = getBestFormat(acceptHeader);
99
+ const cacheKey = generateCacheKey(src, width, quality, format);
100
+ const cachePath = join(imageConfig.cacheDir, cacheKey);
101
+ if (existsSync(cachePath)) {
102
+ const cachedImage = readFileSync(cachePath);
103
+ const stat = statSync(cachePath);
104
+ res.setHeader("Content-Type", `image/${format}`);
105
+ res.setHeader("Cache-Control", `public, max-age=${imageConfig.minimumCacheTTL}, stale-while-revalidate`);
106
+ res.setHeader("X-Float-Image-Cache", "HIT");
107
+ res.setHeader("Last-Modified", stat.mtime.toUTCString());
108
+ res.end(cachedImage);
109
+ return;
110
+ }
111
+ let imageBuffer;
112
+ if (isExternalUrl(src)) {
113
+ const response = await fetch(src);
114
+ if (!response.ok) {
115
+ res.statusCode = 404;
116
+ res.end("Image not found");
117
+ return;
118
+ }
119
+ imageBuffer = Buffer.from(await response.arrayBuffer());
120
+ } else {
121
+ const imagePath = join(process.cwd(), "public", src);
122
+ if (!existsSync(imagePath)) {
123
+ res.statusCode = 404;
124
+ res.end("Image not found");
125
+ return;
126
+ }
127
+ imageBuffer = readFileSync(imagePath);
128
+ }
129
+ const optimizedBuffer = await optimizeImage(imageBuffer, width, quality, format);
130
+ writeFileSync(cachePath, optimizedBuffer);
131
+ res.setHeader("Content-Type", `image/${format}`);
132
+ res.setHeader("Cache-Control", `public, max-age=${imageConfig.minimumCacheTTL}, stale-while-revalidate`);
133
+ res.setHeader("X-Float-Image-Cache", "MISS");
134
+ res.end(optimizedBuffer);
135
+ } catch (error) {
136
+ console.error("Image optimization error:", error);
137
+ res.statusCode = 500;
138
+ res.end("Image optimization failed");
139
+ }
140
+ };
141
+ }
142
+ function floatImageLoader({ src, width, quality }) {
143
+ const q = quality || imageConfig.quality;
144
+ return `${imageConfig.basePath}?url=${encodeURIComponent(src)}&w=${width}&q=${q}`;
145
+ }
146
+ function generateSrcSet(src, sizes) {
147
+ return sizes.map((size) => `${floatImageLoader({ src, width: size })} ${size}w`).join(", ");
148
+ }
149
+ function getImageProps(props) {
150
+ const {
151
+ src,
152
+ width,
153
+ height,
154
+ fill,
155
+ sizes = "100vw",
156
+ quality,
157
+ priority,
158
+ loading = priority ? "eager" : "lazy"
159
+ } = props;
160
+ const allSizes = [...imageConfig.imageSizes, ...imageConfig.deviceSizes].sort((a, b) => a - b);
161
+ const relevantSizes = width ? allSizes.filter((s) => s <= width * 2) : allSizes;
162
+ return {
163
+ src: floatImageLoader({ src, width: width || relevantSizes[relevantSizes.length - 1], quality }),
164
+ srcSet: generateSrcSet(src, relevantSizes),
165
+ sizes,
166
+ width: fill ? void 0 : width,
167
+ height: fill ? void 0 : height,
168
+ loading,
169
+ decoding: "async",
170
+ style: fill ? {
171
+ position: "absolute",
172
+ top: "0",
173
+ left: "0",
174
+ width: "100%",
175
+ height: "100%",
176
+ objectFit: "cover"
177
+ } : void 0
178
+ };
179
+ }
180
+ function renderImageToString(props) {
181
+ const imageProps = getImageProps(props);
182
+ const attributes = [
183
+ `src="${imageProps.src}"`,
184
+ `srcset="${imageProps.srcSet}"`,
185
+ `sizes="${imageProps.sizes}"`,
186
+ `alt="${props.alt}"`,
187
+ `loading="${imageProps.loading}"`,
188
+ `decoding="${imageProps.decoding}"`
189
+ ];
190
+ if (imageProps.width) {
191
+ attributes.push(`width="${imageProps.width}"`);
192
+ }
193
+ if (imageProps.height) {
194
+ attributes.push(`height="${imageProps.height}"`);
195
+ }
196
+ if (props.className) {
197
+ attributes.push(`class="${props.className}"`);
198
+ }
199
+ if (imageProps.style) {
200
+ const styleString = Object.entries(imageProps.style).map(([key, value]) => `${key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}: ${value}`).join("; ");
201
+ attributes.push(`style="${styleString}"`);
202
+ }
203
+ return `
204
+ <picture>
205
+ <source type="image/webp" srcset="${imageProps.srcSet}">
206
+ <img ${attributes.join(" ")}>
207
+ </picture>
208
+ `.trim();
209
+ }
210
+ function importImage(imagePath) {
211
+ return {
212
+ src: imagePath,
213
+ width: 0,
214
+ // Determined at build time
215
+ height: 0,
216
+ // Determined at build time
217
+ blurDataURL: void 0
218
+ };
219
+ }
220
+ var image = {
221
+ configure: configureImages,
222
+ getConfig: getImageConfig,
223
+ handler: createImageHandler,
224
+ loader: floatImageLoader,
225
+ srcSet: generateSrcSet,
226
+ props: getImageProps,
227
+ render: renderImageToString,
228
+ import: importImage
229
+ };
230
+ export {
231
+ configureImages,
232
+ createImageHandler,
233
+ floatImageLoader,
234
+ generateBlurDataURL,
235
+ generateSrcSet,
236
+ getImageConfig,
237
+ getImageProps,
238
+ image,
239
+ importImage,
240
+ renderImageToString
241
+ };
242
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/image/index.ts"],"sourcesContent":["/**\n * Float.js Image Optimization\n * Automatic image optimization, resizing, and format conversion\n * \n * Similar to Next.js Image but with more features!\n */\n\nimport { createHash } from 'crypto';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, statSync } from 'fs';\nimport { join } from 'path';\nimport type { IncomingMessage, ServerResponse } from 'http';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface ImageConfig {\n /** Supported image widths for responsive images */\n deviceSizes: number[];\n /** Smaller sizes for use with next/image */\n imageSizes: number[];\n /** Supported output formats */\n formats: ImageFormat[];\n /** Quality for lossy formats (1-100) */\n quality: number;\n /** Cache directory for optimized images */\n cacheDir: string;\n /** Base path for images */\n basePath: string;\n /** Remote image domains allowed */\n domains: string[];\n /** Minimum cache TTL in seconds */\n minimumCacheTTL: number;\n /** Disable static image imports */\n disableStaticImages: boolean;\n /** Enable AVIF format (experimental) */\n avif: boolean;\n}\n\nexport type ImageFormat = 'webp' | 'avif' | 'jpeg' | 'png' | 'gif' | 'svg';\n\nexport interface ImageProps {\n src: string;\n alt: string;\n width?: number;\n height?: number;\n fill?: boolean;\n sizes?: string;\n quality?: number;\n priority?: boolean;\n placeholder?: 'blur' | 'empty' | 'data:image/...';\n blurDataURL?: string;\n loading?: 'lazy' | 'eager';\n className?: string;\n style?: Record<string, string>;\n onLoad?: () => void;\n onError?: () => void;\n}\n\nexport interface OptimizedImage {\n src: string;\n width: number;\n height: number;\n blurDataURL?: string;\n}\n\nexport interface ImageLoaderProps {\n src: string;\n width: number;\n quality?: number;\n}\n\n// ============================================================================\n// DEFAULT CONFIG\n// ============================================================================\n\nconst defaultConfig: ImageConfig = {\n deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],\n imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],\n formats: ['webp', 'jpeg'],\n quality: 75,\n cacheDir: '.float/cache/images',\n basePath: '/_float/image',\n domains: [],\n minimumCacheTTL: 60,\n disableStaticImages: false,\n avif: false,\n};\n\nlet imageConfig: ImageConfig = { ...defaultConfig };\n\nexport function configureImages(config: Partial<ImageConfig>): void {\n imageConfig = { ...defaultConfig, ...config };\n \n // Ensure cache directory exists\n if (!existsSync(imageConfig.cacheDir)) {\n mkdirSync(imageConfig.cacheDir, { recursive: true });\n }\n}\n\nexport function getImageConfig(): ImageConfig {\n return imageConfig;\n}\n\n// ============================================================================\n// IMAGE UTILITIES\n// ============================================================================\n\n/**\n * Generate a hash for cache key\n */\nfunction generateCacheKey(url: string, width: number, quality: number, format: string): string {\n const hash = createHash('md5')\n .update(`${url}-${width}-${quality}-${format}`)\n .digest('hex');\n return `${hash}.${format}`;\n}\n\n/**\n * Get the best format for the request\n */\nfunction getBestFormat(acceptHeader: string): ImageFormat {\n if (imageConfig.avif && acceptHeader.includes('image/avif')) {\n return 'avif';\n }\n if (acceptHeader.includes('image/webp')) {\n return 'webp';\n }\n return 'jpeg';\n}\n\n/**\n * Parse image URL parameters\n */\nfunction parseImageParams(url: URL): { src: string; width: number; quality: number } | null {\n const src = url.searchParams.get('url');\n const width = parseInt(url.searchParams.get('w') || '0', 10);\n const quality = parseInt(url.searchParams.get('q') || String(imageConfig.quality), 10);\n\n if (!src || !width) {\n return null;\n }\n\n // Validate width\n const allSizes = [...imageConfig.deviceSizes, ...imageConfig.imageSizes];\n if (!allSizes.includes(width)) {\n return null;\n }\n\n // Validate quality\n if (quality < 1 || quality > 100) {\n return null;\n }\n\n return { src, width, quality };\n}\n\n/**\n * Check if URL is external\n */\nfunction isExternalUrl(url: string): boolean {\n return url.startsWith('http://') || url.startsWith('https://');\n}\n\n/**\n * Validate external domain\n */\nfunction isAllowedDomain(url: string): boolean {\n if (!isExternalUrl(url)) return true;\n \n try {\n const { hostname } = new URL(url);\n return imageConfig.domains.includes(hostname);\n } catch {\n return false;\n }\n}\n\n// ============================================================================\n// IMAGE OPTIMIZATION (Sharp-like without Sharp dependency)\n// ============================================================================\n\n/**\n * Simple image resize using canvas (for basic optimization)\n * In production, you'd use Sharp or similar\n */\nasync function optimizeImage(\n input: Buffer,\n _width: number,\n _quality: number,\n _format: ImageFormat\n): Promise<Buffer> {\n // For now, return original with proper content type\n // In real implementation, use Sharp or similar:\n // const sharp = await import('sharp');\n // return sharp(input).resize(_width).toFormat(_format, { quality: _quality }).toBuffer();\n \n // Placeholder - returns original image\n // Users can install sharp for real optimization\n return input;\n}\n\n/**\n * Generate blur placeholder\n */\nexport async function generateBlurDataURL(input: Buffer): Promise<string> {\n // Generate a tiny version for blur placeholder\n // In real implementation, resize to 8x8 or 10x10 and base64 encode\n return `data:image/jpeg;base64,${input.slice(0, 50).toString('base64')}`;\n}\n\n// ============================================================================\n// IMAGE HANDLER\n// ============================================================================\n\nexport function createImageHandler() {\n // Ensure cache directory exists\n if (!existsSync(imageConfig.cacheDir)) {\n mkdirSync(imageConfig.cacheDir, { recursive: true });\n }\n\n return async (req: IncomingMessage, res: ServerResponse, next: () => void) => {\n const urlString = req.url || '';\n \n // Check if this is an image optimization request\n if (!urlString.startsWith(imageConfig.basePath)) {\n return next();\n }\n\n try {\n const url = new URL(urlString, `http://${req.headers.host}`);\n const params = parseImageParams(url);\n\n if (!params) {\n res.statusCode = 400;\n res.end('Invalid image parameters');\n return;\n }\n\n const { src, width, quality } = params;\n\n // Validate domain for external URLs\n if (!isAllowedDomain(src)) {\n res.statusCode = 400;\n res.end('Domain not allowed');\n return;\n }\n\n // Determine best format\n const acceptHeader = req.headers.accept || '';\n const format = getBestFormat(acceptHeader);\n\n // Check cache\n const cacheKey = generateCacheKey(src, width, quality, format);\n const cachePath = join(imageConfig.cacheDir, cacheKey);\n\n if (existsSync(cachePath)) {\n const cachedImage = readFileSync(cachePath);\n const stat = statSync(cachePath);\n \n res.setHeader('Content-Type', `image/${format}`);\n res.setHeader('Cache-Control', `public, max-age=${imageConfig.minimumCacheTTL}, stale-while-revalidate`);\n res.setHeader('X-Float-Image-Cache', 'HIT');\n res.setHeader('Last-Modified', stat.mtime.toUTCString());\n res.end(cachedImage);\n return;\n }\n\n // Fetch or read image\n let imageBuffer: Buffer;\n\n if (isExternalUrl(src)) {\n const response = await fetch(src);\n if (!response.ok) {\n res.statusCode = 404;\n res.end('Image not found');\n return;\n }\n imageBuffer = Buffer.from(await response.arrayBuffer());\n } else {\n const imagePath = join(process.cwd(), 'public', src);\n if (!existsSync(imagePath)) {\n res.statusCode = 404;\n res.end('Image not found');\n return;\n }\n imageBuffer = readFileSync(imagePath);\n }\n\n // Optimize image\n const optimizedBuffer = await optimizeImage(imageBuffer, width, quality, format);\n\n // Save to cache\n writeFileSync(cachePath, optimizedBuffer);\n\n // Send response\n res.setHeader('Content-Type', `image/${format}`);\n res.setHeader('Cache-Control', `public, max-age=${imageConfig.minimumCacheTTL}, stale-while-revalidate`);\n res.setHeader('X-Float-Image-Cache', 'MISS');\n res.end(optimizedBuffer);\n } catch (error) {\n console.error('Image optimization error:', error);\n res.statusCode = 500;\n res.end('Image optimization failed');\n }\n };\n}\n\n// ============================================================================\n// IMAGE LOADER (for client-side)\n// ============================================================================\n\n/**\n * Default image loader\n */\nexport function floatImageLoader({ src, width, quality }: ImageLoaderProps): string {\n const q = quality || imageConfig.quality;\n return `${imageConfig.basePath}?url=${encodeURIComponent(src)}&w=${width}&q=${q}`;\n}\n\n/**\n * Generate srcset for responsive images\n */\nexport function generateSrcSet(src: string, sizes: number[]): string {\n return sizes\n .map(size => `${floatImageLoader({ src, width: size })} ${size}w`)\n .join(', ');\n}\n\n/**\n * Generate responsive image props\n */\nexport function getImageProps(props: ImageProps): {\n src: string;\n srcSet: string;\n sizes: string;\n width?: number;\n height?: number;\n loading: 'lazy' | 'eager';\n decoding: 'async' | 'sync';\n style?: Record<string, string>;\n} {\n const {\n src,\n width,\n height,\n fill,\n sizes = '100vw',\n quality,\n priority,\n loading = priority ? 'eager' : 'lazy',\n } = props;\n\n const allSizes = [...imageConfig.imageSizes, ...imageConfig.deviceSizes].sort((a, b) => a - b);\n \n // Filter sizes based on image width\n const relevantSizes = width \n ? allSizes.filter(s => s <= width * 2) \n : allSizes;\n\n return {\n src: floatImageLoader({ src, width: width || relevantSizes[relevantSizes.length - 1], quality }),\n srcSet: generateSrcSet(src, relevantSizes),\n sizes,\n width: fill ? undefined : width,\n height: fill ? undefined : height,\n loading,\n decoding: 'async',\n style: fill ? {\n position: 'absolute',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n } : undefined,\n };\n}\n\n// ============================================================================\n// REACT COMPONENT (Server Component compatible)\n// ============================================================================\n\n/**\n * Generate Image component HTML (for SSR)\n */\nexport function renderImageToString(props: ImageProps): string {\n const imageProps = getImageProps(props);\n \n const attributes = [\n `src=\"${imageProps.src}\"`,\n `srcset=\"${imageProps.srcSet}\"`,\n `sizes=\"${imageProps.sizes}\"`,\n `alt=\"${props.alt}\"`,\n `loading=\"${imageProps.loading}\"`,\n `decoding=\"${imageProps.decoding}\"`,\n ];\n\n if (imageProps.width) {\n attributes.push(`width=\"${imageProps.width}\"`);\n }\n if (imageProps.height) {\n attributes.push(`height=\"${imageProps.height}\"`);\n }\n if (props.className) {\n attributes.push(`class=\"${props.className}\"`);\n }\n if (imageProps.style) {\n const styleString = Object.entries(imageProps.style)\n .map(([key, value]) => `${key.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`)}: ${value}`)\n .join('; ');\n attributes.push(`style=\"${styleString}\"`);\n }\n\n // Wrap in picture element for format fallback\n return `\n <picture>\n <source type=\"image/webp\" srcset=\"${imageProps.srcSet}\">\n <img ${attributes.join(' ')}>\n </picture>\n `.trim();\n}\n\n// ============================================================================\n// STATIC IMPORT HELPERS\n// ============================================================================\n\nexport interface StaticImageData {\n src: string;\n width: number;\n height: number;\n blurDataURL?: string;\n}\n\n/**\n * Import static image (for build-time optimization)\n */\nexport function importImage(imagePath: string): StaticImageData {\n // This would be processed at build time\n // Returns placeholder data for runtime\n return {\n src: imagePath,\n width: 0, // Determined at build time\n height: 0, // Determined at build time\n blurDataURL: undefined,\n };\n}\n\n// ============================================================================\n// EXPORTS\n// ============================================================================\n\nexport const image = {\n configure: configureImages,\n getConfig: getImageConfig,\n handler: createImageHandler,\n loader: floatImageLoader,\n srcSet: generateSrcSet,\n props: getImageProps,\n render: renderImageToString,\n import: importImage,\n};\n"],"mappings":";AAOA,SAAS,kBAAkB;AAC3B,SAAS,YAAY,WAAW,cAAc,eAAe,gBAAgB;AAC7E,SAAS,YAAY;AAmErB,IAAM,gBAA6B;AAAA,EACjC,aAAa,CAAC,KAAK,KAAK,KAAK,MAAM,MAAM,MAAM,MAAM,IAAI;AAAA,EACzD,YAAY,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,KAAK,GAAG;AAAA,EAC9C,SAAS,CAAC,QAAQ,MAAM;AAAA,EACxB,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS,CAAC;AAAA,EACV,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,MAAM;AACR;AAEA,IAAI,cAA2B,EAAE,GAAG,cAAc;AAE3C,SAAS,gBAAgB,QAAoC;AAClE,gBAAc,EAAE,GAAG,eAAe,GAAG,OAAO;AAG5C,MAAI,CAAC,WAAW,YAAY,QAAQ,GAAG;AACrC,cAAU,YAAY,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,EACrD;AACF;AAEO,SAAS,iBAA8B;AAC5C,SAAO;AACT;AASA,SAAS,iBAAiB,KAAa,OAAe,SAAiB,QAAwB;AAC7F,QAAM,OAAO,WAAW,KAAK,EAC1B,OAAO,GAAG,GAAG,IAAI,KAAK,IAAI,OAAO,IAAI,MAAM,EAAE,EAC7C,OAAO,KAAK;AACf,SAAO,GAAG,IAAI,IAAI,MAAM;AAC1B;AAKA,SAAS,cAAc,cAAmC;AACxD,MAAI,YAAY,QAAQ,aAAa,SAAS,YAAY,GAAG;AAC3D,WAAO;AAAA,EACT;AACA,MAAI,aAAa,SAAS,YAAY,GAAG;AACvC,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKA,SAAS,iBAAiB,KAAkE;AAC1F,QAAM,MAAM,IAAI,aAAa,IAAI,KAAK;AACtC,QAAM,QAAQ,SAAS,IAAI,aAAa,IAAI,GAAG,KAAK,KAAK,EAAE;AAC3D,QAAM,UAAU,SAAS,IAAI,aAAa,IAAI,GAAG,KAAK,OAAO,YAAY,OAAO,GAAG,EAAE;AAErF,MAAI,CAAC,OAAO,CAAC,OAAO;AAClB,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,CAAC,GAAG,YAAY,aAAa,GAAG,YAAY,UAAU;AACvE,MAAI,CAAC,SAAS,SAAS,KAAK,GAAG;AAC7B,WAAO;AAAA,EACT;AAGA,MAAI,UAAU,KAAK,UAAU,KAAK;AAChC,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,KAAK,OAAO,QAAQ;AAC/B;AAKA,SAAS,cAAc,KAAsB;AAC3C,SAAO,IAAI,WAAW,SAAS,KAAK,IAAI,WAAW,UAAU;AAC/D;AAKA,SAAS,gBAAgB,KAAsB;AAC7C,MAAI,CAAC,cAAc,GAAG,EAAG,QAAO;AAEhC,MAAI;AACF,UAAM,EAAE,SAAS,IAAI,IAAI,IAAI,GAAG;AAChC,WAAO,YAAY,QAAQ,SAAS,QAAQ;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUA,eAAe,cACb,OACA,QACA,UACA,SACiB;AAQjB,SAAO;AACT;AAKA,eAAsB,oBAAoB,OAAgC;AAGxE,SAAO,0BAA0B,MAAM,MAAM,GAAG,EAAE,EAAE,SAAS,QAAQ,CAAC;AACxE;AAMO,SAAS,qBAAqB;AAEnC,MAAI,CAAC,WAAW,YAAY,QAAQ,GAAG;AACrC,cAAU,YAAY,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,EACrD;AAEA,SAAO,OAAO,KAAsB,KAAqB,SAAqB;AAC5E,UAAM,YAAY,IAAI,OAAO;AAG7B,QAAI,CAAC,UAAU,WAAW,YAAY,QAAQ,GAAG;AAC/C,aAAO,KAAK;AAAA,IACd;AAEA,QAAI;AACF,YAAM,MAAM,IAAI,IAAI,WAAW,UAAU,IAAI,QAAQ,IAAI,EAAE;AAC3D,YAAM,SAAS,iBAAiB,GAAG;AAEnC,UAAI,CAAC,QAAQ;AACX,YAAI,aAAa;AACjB,YAAI,IAAI,0BAA0B;AAClC;AAAA,MACF;AAEA,YAAM,EAAE,KAAK,OAAO,QAAQ,IAAI;AAGhC,UAAI,CAAC,gBAAgB,GAAG,GAAG;AACzB,YAAI,aAAa;AACjB,YAAI,IAAI,oBAAoB;AAC5B;AAAA,MACF;AAGA,YAAM,eAAe,IAAI,QAAQ,UAAU;AAC3C,YAAM,SAAS,cAAc,YAAY;AAGzC,YAAM,WAAW,iBAAiB,KAAK,OAAO,SAAS,MAAM;AAC7D,YAAM,YAAY,KAAK,YAAY,UAAU,QAAQ;AAErD,UAAI,WAAW,SAAS,GAAG;AACzB,cAAM,cAAc,aAAa,SAAS;AAC1C,cAAM,OAAO,SAAS,SAAS;AAE/B,YAAI,UAAU,gBAAgB,SAAS,MAAM,EAAE;AAC/C,YAAI,UAAU,iBAAiB,mBAAmB,YAAY,eAAe,0BAA0B;AACvG,YAAI,UAAU,uBAAuB,KAAK;AAC1C,YAAI,UAAU,iBAAiB,KAAK,MAAM,YAAY,CAAC;AACvD,YAAI,IAAI,WAAW;AACnB;AAAA,MACF;AAGA,UAAI;AAEJ,UAAI,cAAc,GAAG,GAAG;AACtB,cAAM,WAAW,MAAM,MAAM,GAAG;AAChC,YAAI,CAAC,SAAS,IAAI;AAChB,cAAI,aAAa;AACjB,cAAI,IAAI,iBAAiB;AACzB;AAAA,QACF;AACA,sBAAc,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC;AAAA,MACxD,OAAO;AACL,cAAM,YAAY,KAAK,QAAQ,IAAI,GAAG,UAAU,GAAG;AACnD,YAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,cAAI,aAAa;AACjB,cAAI,IAAI,iBAAiB;AACzB;AAAA,QACF;AACA,sBAAc,aAAa,SAAS;AAAA,MACtC;AAGA,YAAM,kBAAkB,MAAM,cAAc,aAAa,OAAO,SAAS,MAAM;AAG/E,oBAAc,WAAW,eAAe;AAGxC,UAAI,UAAU,gBAAgB,SAAS,MAAM,EAAE;AAC/C,UAAI,UAAU,iBAAiB,mBAAmB,YAAY,eAAe,0BAA0B;AACvG,UAAI,UAAU,uBAAuB,MAAM;AAC3C,UAAI,IAAI,eAAe;AAAA,IACzB,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAChD,UAAI,aAAa;AACjB,UAAI,IAAI,2BAA2B;AAAA,IACrC;AAAA,EACF;AACF;AASO,SAAS,iBAAiB,EAAE,KAAK,OAAO,QAAQ,GAA6B;AAClF,QAAM,IAAI,WAAW,YAAY;AACjC,SAAO,GAAG,YAAY,QAAQ,QAAQ,mBAAmB,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC;AACjF;AAKO,SAAS,eAAe,KAAa,OAAyB;AACnE,SAAO,MACJ,IAAI,UAAQ,GAAG,iBAAiB,EAAE,KAAK,OAAO,KAAK,CAAC,CAAC,IAAI,IAAI,GAAG,EAChE,KAAK,IAAI;AACd;AAKO,SAAS,cAAc,OAS5B;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,UAAU,WAAW,UAAU;AAAA,EACjC,IAAI;AAEJ,QAAM,WAAW,CAAC,GAAG,YAAY,YAAY,GAAG,YAAY,WAAW,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAG7F,QAAM,gBAAgB,QAClB,SAAS,OAAO,OAAK,KAAK,QAAQ,CAAC,IACnC;AAEJ,SAAO;AAAA,IACL,KAAK,iBAAiB,EAAE,KAAK,OAAO,SAAS,cAAc,cAAc,SAAS,CAAC,GAAG,QAAQ,CAAC;AAAA,IAC/F,QAAQ,eAAe,KAAK,aAAa;AAAA,IACzC;AAAA,IACA,OAAO,OAAO,SAAY;AAAA,IAC1B,QAAQ,OAAO,SAAY;AAAA,IAC3B;AAAA,IACA,UAAU;AAAA,IACV,OAAO,OAAO;AAAA,MACZ,UAAU;AAAA,MACV,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,WAAW;AAAA,IACb,IAAI;AAAA,EACN;AACF;AASO,SAAS,oBAAoB,OAA2B;AAC7D,QAAM,aAAa,cAAc,KAAK;AAEtC,QAAM,aAAa;AAAA,IACjB,QAAQ,WAAW,GAAG;AAAA,IACtB,WAAW,WAAW,MAAM;AAAA,IAC5B,UAAU,WAAW,KAAK;AAAA,IAC1B,QAAQ,MAAM,GAAG;AAAA,IACjB,YAAY,WAAW,OAAO;AAAA,IAC9B,aAAa,WAAW,QAAQ;AAAA,EAClC;AAEA,MAAI,WAAW,OAAO;AACpB,eAAW,KAAK,UAAU,WAAW,KAAK,GAAG;AAAA,EAC/C;AACA,MAAI,WAAW,QAAQ;AACrB,eAAW,KAAK,WAAW,WAAW,MAAM,GAAG;AAAA,EACjD;AACA,MAAI,MAAM,WAAW;AACnB,eAAW,KAAK,UAAU,MAAM,SAAS,GAAG;AAAA,EAC9C;AACA,MAAI,WAAW,OAAO;AACpB,UAAM,cAAc,OAAO,QAAQ,WAAW,KAAK,EAChD,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,IAAI,QAAQ,UAAU,OAAK,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC,KAAK,KAAK,EAAE,EACtF,KAAK,IAAI;AACZ,eAAW,KAAK,UAAU,WAAW,GAAG;AAAA,EAC1C;AAGA,SAAO;AAAA;AAAA,0CAEiC,WAAW,MAAM;AAAA,aAC9C,WAAW,KAAK,GAAG,CAAC;AAAA;AAAA,IAE7B,KAAK;AACT;AAgBO,SAAS,YAAY,WAAoC;AAG9D,SAAO;AAAA,IACL,KAAK;AAAA,IACL,OAAO;AAAA;AAAA,IACP,QAAQ;AAAA;AAAA,IACR,aAAa;AAAA,EACf;AACF;AAMO,IAAM,QAAQ;AAAA,EACnB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AACV;","names":[]}