feat: integrate WebAssembly for zero-latency, offline-first color operations
Some checks failed
Docker Build & Push / build-and-push (push) Failing after 24s
Some checks failed
Docker Build & Push / build-and-push (push) Failing after 24s
Replaced REST API dependency with @valknarthing/pastel-wasm (130KB) for complete browser-based color operations. The application is now fully static (2.2MB total) with zero network latency and offline support. **Key Changes:** 1. **WASM Integration:** - Added @valknarthing/pastel-wasm dependency (0.1.0) - Created lib/api/wasm-client.ts wrapper matching API interface - Updated lib/api/client.ts to use WASM client by default - All 18 color operations now run locally in browser 2. **Static Export Configuration:** - Changed next.config.ts output from 'standalone' to 'export' - Disabled image optimization for static export - Removed API proxy route (app/api/pastel/[...path]/route.ts) - Updated package.json scripts (removed dev:api, added serve) 3. **Docker Optimization:** - Migrated from Node.js standalone to nginx-alpine - Created nginx.conf with SPA routing and WASM mime types - Updated Dockerfile for static file serving - Reduced image size from ~150MB to ~25MB - Changed port from 3000 to 80 (standard HTTP) - Simplified docker-compose.yml (removed pastel-api service) 4. **Documentation Updates:** - Updated README.md with WASM benefits and deployment options - Added Key Benefits section highlighting zero-latency features - Rewrote deployment section for static hosting platforms - Updated CLAUDE.md tech stack and architecture - Removed obsolete docs: DEV_SETUP.md, DOCKER.md, IMPLEMENTATION_PLAN.md **Benefits:** - 🚀 Zero Latency - All operations run locally via WebAssembly - 📱 Offline First - Works completely offline after initial load - 🌐 No Backend - Fully static, deploy anywhere - ⚡ Fast - Native-speed color operations in browser - 📦 Small - 2.2MB total (130KB WASM, 2.07MB HTML/CSS/JS) **Deployment:** Can now be deployed to any static hosting platform: - Vercel, Netlify, Cloudflare Pages (zero config) - GitHub Pages, S3, CDN - Self-hosted nginx/Apache - Docker (optional, nginx-based) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,122 +0,0 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
/**
|
||||
* API Proxy Route for Pastel API
|
||||
*
|
||||
* This proxy allows runtime configuration of the Pastel API URL
|
||||
* without rebuilding the Docker image. The API URL is read from
|
||||
* the server-side environment variable at runtime.
|
||||
*
|
||||
* Client requests go to: /api/pastel/*
|
||||
* Proxied to: PASTEL_API_URL/*
|
||||
*/
|
||||
|
||||
const PASTEL_API_URL = process.env.PASTEL_API_URL || 'http://localhost:3001';
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
const { path } = await params;
|
||||
return proxyRequest(request, path, 'GET');
|
||||
}
|
||||
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
const { path } = await params;
|
||||
return proxyRequest(request, path, 'POST');
|
||||
}
|
||||
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
const { path } = await params;
|
||||
return proxyRequest(request, path, 'PUT');
|
||||
}
|
||||
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
const { path } = await params;
|
||||
return proxyRequest(request, path, 'DELETE');
|
||||
}
|
||||
|
||||
export async function PATCH(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
const { path } = await params;
|
||||
return proxyRequest(request, path, 'PATCH');
|
||||
}
|
||||
|
||||
async function proxyRequest(
|
||||
request: NextRequest,
|
||||
pathSegments: string[],
|
||||
method: string
|
||||
) {
|
||||
try {
|
||||
const path = pathSegments.join('/');
|
||||
const targetUrl = `${PASTEL_API_URL}/api/v1/${path}`;
|
||||
|
||||
// Get request body if present
|
||||
const body = method !== 'GET' && method !== 'DELETE'
|
||||
? await request.text()
|
||||
: undefined;
|
||||
|
||||
// Forward the request to the Pastel API
|
||||
const response = await fetch(targetUrl, {
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
// Forward relevant headers
|
||||
...(request.headers.get('accept') && {
|
||||
'Accept': request.headers.get('accept')!
|
||||
}),
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
// Get response data
|
||||
const data = await response.text();
|
||||
|
||||
// Return response with same status and headers
|
||||
return new NextResponse(data, {
|
||||
status: response.status,
|
||||
headers: {
|
||||
'Content-Type': response.headers.get('content-type') || 'application/json',
|
||||
// Add CORS headers if needed
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Proxy error:', error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: {
|
||||
code: 'PROXY_ERROR',
|
||||
message: error instanceof Error ? error.message : 'Failed to proxy request',
|
||||
},
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle OPTIONS requests for CORS
|
||||
export async function OPTIONS() {
|
||||
return new NextResponse(null, {
|
||||
status: 204,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user