Monorepo for Aesthetic.Computer aesthetic.computer
at main 273 lines 8.4 kB view raw view rendered
1# Netlify Function Plan: `bundle-keep-html.mjs` → API Endpoint 2 3## Overview 4 5Convert the existing `tezos/bundle-keep-html.mjs` CLI tool into a Netlify serverless function that can generate KidLisp bundles on-demand via HTTP requests. 6 7**Current CLI Usage:** 8```bash 9node tezos/bundle-keep-html.mjs 39j 10``` 11 12**Proposed API Usage:** 13``` 14GET https://aesthetic.computer/api/bundle-html?code=39j 15# Returns: .lisp.html file as download or base64 response 16``` 17 18--- 19 20## Technical Challenges & Solutions 21 22### 1. **File System Access** 23| Challenge | Current CLI | Netlify Function Solution | 24|-----------|-------------|---------------------------| 25| Reading source files | `fs.readFile()` from disk | Use `included_files` in `netlify.toml` to bundle assets | 26| Writing output | `fs.writeFile()` to `keep-bundles/` | Return directly in HTTP response (no file write needed) | 27| Git version info | `execSync('git rev-parse')` | Use build-time env var or hardcode at deploy time | 28 29### 2. **Dependencies** 30| Dependency | Purpose | Netlify Compatible? | 31|------------|---------|---------------------| 32| `@swc/wasm` | JS minification | ✅ WASM version - drop-in replacement for `@swc/core` | 33| `zlib.gzipSync` | Compression | ✅ Built into Node.js | 34| `fetch` | API calls | ✅ Built into Node 18+ | 35 36> **Note:** `@swc/wasm` is the WebAssembly version of SWC with the exact same API as `@swc/core`. 37> Simply change the import from `@swc/core` to `@swc/wasm` - no other code changes needed. 38> - 1.2M+ weekly downloads on npm 39> - Same `transform()` function signature 40> - No native binaries, works in serverless environments 41 42**Required Package Change:** 43```bash 44# Add to system/package.json 45cd system && npm install @swc/wasm 46``` 47 48### 3. **Execution Time** 49- **Current bundle time:** ~5-15 seconds (depending on piece complexity) 50- **Netlify timeout:** 10 seconds (default), 26 seconds (background functions) 51- **Solution:** Use background function or optimize for speed 52 53--- 54 55## Implementation Plan 56 57### Phase 1: Create the Function Structure ✅ IMPLEMENTED 58 59``` 60system/netlify/functions/bundle-html.mjs # Main function (all-in-one) 61``` 62 63### Phase 2: Configure `netlify.toml` ✅ IMPLEMENTED 64 65```toml 66[functions.bundle-html] 67# Include all aesthetic.computer source files needed for VFS 68included_files = [ 69 "public/aesthetic.computer/**/*.mjs", 70 "public/aesthetic.computer/**/*.js", 71 "public/aesthetic.computer/disks/drawings/font_1/**/*.json", 72 "public/aesthetic.computer/dep/**/*.mjs", 73 "backend/**/*.mjs" 74] 75 76# @swc/wasm is the WASM version of SWC - same API as @swc/core 77# It needs to be marked as external so Netlify includes the .wasm files 78external_node_modules = ["@swc/wasm"] 79 80# Environment variables 81included_env_vars = ["GIT_COMMIT", "CONTEXT"] 82 83# Extended timeout for bundle generation (may take 5-15s) 84# Note: Background functions get 26s timeout if needed 85``` 86 87**Key Configuration Notes:** 88- `@swc/wasm` must be in `external_node_modules` so Netlify properly bundles the WASM binary 89- The WASM file is ~15MB but loads fast at runtime 90- No native compilation required - works on any Node.js runtime 91 92### Phase 3: API Design 93 94#### Request 95``` 96GET /api/bundle-html?code=<piece-code> 97 98Query Parameters: 99 code (required) KidLisp piece code without $ (e.g., "39j") 100 format (optional) "html" (default) | "base64" | "json" 101``` 102 103#### Response Formats 104 105**HTML (default)** - Direct download: 106``` 107Content-Type: text/html 108Content-Disposition: attachment; filename="$39j-@fifi-2025.11.27.12.30.00.000.lisp.html" 109 110<!DOCTYPE html>... 111``` 112 113**Base64** - For programmatic use: 114```json 115{ 116 "filename": "$39j-@fifi-2025.11.27.12.30.00.000.lisp.html", 117 "content": "PCFET0NUWVBFIGh0bWw+...", 118 "sizeKB": 180, 119 "author": "@fifi" 120} 121``` 122 123### Phase 4: Code Migration 124 125#### Key Changes from CLI to Function 126 127| CLI Code | Function Equivalent | 128|----------|---------------------| 129| `process.argv[2]` | `event.queryStringParameters.code` | 130| `fs.readFile(fullPath)` | `fs.readFile(path.join(process.cwd(), 'public/aesthetic.computer', relativePath))` | 131| `fs.writeFile(outputPath, content)` | `return { body: content, headers: {...} }` | 132| `execSync('git rev-parse')` | `process.env.GIT_COMMIT` or hardcoded | 133| `console.log(...)` | Kept for CloudWatch logs | 134 135#### Function Skeleton 136 137```javascript 138// system/netlify/functions/bundle-keep.mjs 139 140import { promises as fs } from "fs"; 141import path from "path"; 142import { gzipSync } from "zlib"; 143import { transform } from "@swc/wasm"; // Same API as @swc/core! 144import { respond } from "../../backend/http.mjs"; 145 146// Essential files list (same as CLI) 147const ESSENTIAL_FILES = [ 148 'boot.mjs', 'bios.mjs', 'lib/loop.mjs', 'lib/disk.mjs', 149 // ... rest of essential files 150]; 151 152export async function handler(event) { 153 const code = event.queryStringParameters?.code; 154 155 if (!code) { 156 return respond(400, { error: "Missing 'code' parameter" }); 157 } 158 159 try { 160 // 1. Fetch KidLisp source from API 161 const kidlispSources = await getKidLispSourceWithDeps(code); 162 163 // 2. Build VFS from included files 164 const vfs = await buildVFS(kidlispSources); 165 166 // 3. Generate HTML bundle 167 const html = generateBundle(code, kidlispSources, vfs); 168 169 // 4. Compress with gzip 170 const compressed = createGzipBundle(html); 171 172 // 5. Return as download 173 const filename = generateFilename(code); 174 return { 175 statusCode: 200, 176 headers: { 177 "Content-Type": "text/html", 178 "Content-Disposition": `attachment; filename="${filename}"`, 179 }, 180 body: compressed, 181 }; 182 183 } catch (error) { 184 return respond(500, { error: error.message }); 185 } 186} 187``` 188 189--- 190 191## File Changes Required 192 193### New Files 1941. `system/netlify/functions/bundle-keep.mjs` - Main function 1952. `system/netlify/functions/bundle-keep/helpers.mjs` - Shared utilities 196 197### Modified Files 1981. `system/netlify.toml` - Add function configuration 199 200### Optional Refactoring 201- Extract shared code from `tezos/bundle-keep-html.mjs` into a shared module that both CLI and Netlify function can use 202 203--- 204 205## Risks & Mitigations 206 207| Risk | Likelihood | Impact | Mitigation | 208|------|------------|--------|------------| 209| ~~SWC native binary incompatible~~ | ~~High~~ | ~~Blocker~~ | ✅ **SOLVED:** Use `@swc/wasm` (WASM version) | 210| Timeout on large pieces | Medium | Degraded UX | Use background function or caching | 211| VFS files missing at runtime | Medium | Broken bundles | Extensive `included_files` config | 212| Memory limits (~1GB) | Low | Failure | Already well under limit | 213| WASM cold start | Low | Slower first request | ~100-200ms overhead, acceptable | 214 215--- 216 217## Testing Strategy 218 2191. **Local Testing:** 220 ```bash 221 netlify dev 222 curl "http://localhost:8888/api/bundle-keep?code=39j" > test.html 223 open test.html 224 ``` 225 2262. **Deploy Preview:** 227 - Push to branch, test on deploy preview URL 228 - Verify paintings load, fonts work, piece runs 229 2303. **Production Validation:** 231 - Compare output to CLI-generated bundle 232 - Verify byte-for-byte similarity (minus timestamps) 233 234--- 235 236## Estimated Effort 237 238| Phase | Time Estimate | 239|-------|---------------| 240| Phase 1: Function structure | 2-3 hours | 241| Phase 2: netlify.toml config | 30 min | 242| Phase 3: API implementation | 2-3 hours | 243| Phase 4: Testing & debugging | 2-4 hours | 244| **Total** | **7-11 hours** | 245 246--- 247 248## Future Enhancements 249 2501. **Caching:** Store generated bundles in R2/S3 with TTL 2512. **Webhooks:** Trigger bundle regeneration on piece update 2523. **Batch Generation:** Generate multiple pieces in one request 2534. **IPFS Upload:** Optionally pin bundle to IPFS and return CID 254 255--- 256 257## Decision Points 258 2591. **~~Should we use `esbuild` or try to make `@swc/core` work?~~** 260 - ✅ **RESOLVED:** Use `@swc/wasm` - it's the WASM version of SWC with identical API 261 - No code changes needed beyond `import { transform } from "@swc/wasm"` 262 - Keeps the exact same minification behavior as the CLI tool 263 2642. **Background function (26s timeout) or standard (10s)?** 265 - Recommendation: Start with standard, switch to background if needed 266 2673. **Should CLI and function share code?** 268 - Recommendation: Yes, create `tezos/bundle-keep-shared.mjs` module 269 2704. **Where to store generated bundles?** 271 - Option A: Don't store, generate on every request 272 - Option B: Cache in S3/R2 with piece code + git hash as key 273 - Recommendation: Option A first, add caching if needed