Monorepo for Aesthetic.Computer
aesthetic.computer
Netlify Function Plan: bundle-keep-html.mjs → API Endpoint#
Overview#
Convert the existing tezos/bundle-keep-html.mjs CLI tool into a Netlify serverless function that can generate KidLisp bundles on-demand via HTTP requests.
Current CLI Usage:
node tezos/bundle-keep-html.mjs 39j
Proposed API Usage:
GET https://aesthetic.computer/api/bundle-html?code=39j
# Returns: .lisp.html file as download or base64 response
Technical Challenges & Solutions#
1. File System Access#
| Challenge | Current CLI | Netlify Function Solution |
|---|---|---|
| Reading source files | fs.readFile() from disk |
Use included_files in netlify.toml to bundle assets |
| Writing output | fs.writeFile() to keep-bundles/ |
Return directly in HTTP response (no file write needed) |
| Git version info | execSync('git rev-parse') |
Use build-time env var or hardcode at deploy time |
2. Dependencies#
| Dependency | Purpose | Netlify Compatible? |
|---|---|---|
@swc/wasm |
JS minification | ✅ WASM version - drop-in replacement for @swc/core |
zlib.gzipSync |
Compression | ✅ Built into Node.js |
fetch |
API calls | ✅ Built into Node 18+ |
Note:
@swc/wasmis the WebAssembly version of SWC with the exact same API as@swc/core. Simply change the import from@swc/coreto@swc/wasm- no other code changes needed.
- 1.2M+ weekly downloads on npm
- Same
transform()function signature- No native binaries, works in serverless environments
Required Package Change:
# Add to system/package.json
cd system && npm install @swc/wasm
3. Execution Time#
- Current bundle time: ~5-15 seconds (depending on piece complexity)
- Netlify timeout: 10 seconds (default), 26 seconds (background functions)
- Solution: Use background function or optimize for speed
Implementation Plan#
Phase 1: Create the Function Structure ✅ IMPLEMENTED#
system/netlify/functions/bundle-html.mjs # Main function (all-in-one)
Phase 2: Configure netlify.toml ✅ IMPLEMENTED#
[functions.bundle-html]
# Include all aesthetic.computer source files needed for VFS
included_files = [
"public/aesthetic.computer/**/*.mjs",
"public/aesthetic.computer/**/*.js",
"public/aesthetic.computer/disks/drawings/font_1/**/*.json",
"public/aesthetic.computer/dep/**/*.mjs",
"backend/**/*.mjs"
]
# @swc/wasm is the WASM version of SWC - same API as @swc/core
# It needs to be marked as external so Netlify includes the .wasm files
external_node_modules = ["@swc/wasm"]
# Environment variables
included_env_vars = ["GIT_COMMIT", "CONTEXT"]
# Extended timeout for bundle generation (may take 5-15s)
# Note: Background functions get 26s timeout if needed
Key Configuration Notes:
@swc/wasmmust be inexternal_node_modulesso Netlify properly bundles the WASM binary- The WASM file is ~15MB but loads fast at runtime
- No native compilation required - works on any Node.js runtime
Phase 3: API Design#
Request#
GET /api/bundle-html?code=<piece-code>
Query Parameters:
code (required) KidLisp piece code without $ (e.g., "39j")
format (optional) "html" (default) | "base64" | "json"
Response Formats#
HTML (default) - Direct download:
Content-Type: text/html
Content-Disposition: attachment; filename="$39j-@fifi-2025.11.27.12.30.00.000.lisp.html"
<!DOCTYPE html>...
Base64 - For programmatic use:
{
"filename": "$39j-@fifi-2025.11.27.12.30.00.000.lisp.html",
"content": "PCFET0NUWVBFIGh0bWw+...",
"sizeKB": 180,
"author": "@fifi"
}
Phase 4: Code Migration#
Key Changes from CLI to Function#
| CLI Code | Function Equivalent |
|---|---|
process.argv[2] |
event.queryStringParameters.code |
fs.readFile(fullPath) |
fs.readFile(path.join(process.cwd(), 'public/aesthetic.computer', relativePath)) |
fs.writeFile(outputPath, content) |
return { body: content, headers: {...} } |
execSync('git rev-parse') |
process.env.GIT_COMMIT or hardcoded |
console.log(...) |
Kept for CloudWatch logs |
Function Skeleton#
// system/netlify/functions/bundle-keep.mjs
import { promises as fs } from "fs";
import path from "path";
import { gzipSync } from "zlib";
import { transform } from "@swc/wasm"; // Same API as @swc/core!
import { respond } from "../../backend/http.mjs";
// Essential files list (same as CLI)
const ESSENTIAL_FILES = [
'boot.mjs', 'bios.mjs', 'lib/loop.mjs', 'lib/disk.mjs',
// ... rest of essential files
];
export async function handler(event) {
const code = event.queryStringParameters?.code;
if (!code) {
return respond(400, { error: "Missing 'code' parameter" });
}
try {
// 1. Fetch KidLisp source from API
const kidlispSources = await getKidLispSourceWithDeps(code);
// 2. Build VFS from included files
const vfs = await buildVFS(kidlispSources);
// 3. Generate HTML bundle
const html = generateBundle(code, kidlispSources, vfs);
// 4. Compress with gzip
const compressed = createGzipBundle(html);
// 5. Return as download
const filename = generateFilename(code);
return {
statusCode: 200,
headers: {
"Content-Type": "text/html",
"Content-Disposition": `attachment; filename="${filename}"`,
},
body: compressed,
};
} catch (error) {
return respond(500, { error: error.message });
}
}
File Changes Required#
New Files#
system/netlify/functions/bundle-keep.mjs- Main functionsystem/netlify/functions/bundle-keep/helpers.mjs- Shared utilities
Modified Files#
system/netlify.toml- Add function configuration
Optional Refactoring#
- Extract shared code from
tezos/bundle-keep-html.mjsinto a shared module that both CLI and Netlify function can use
Risks & Mitigations#
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
✅ SOLVED: Use @swc/wasm (WASM version) |
|||
| Timeout on large pieces | Medium | Degraded UX | Use background function or caching |
| VFS files missing at runtime | Medium | Broken bundles | Extensive included_files config |
| Memory limits (~1GB) | Low | Failure | Already well under limit |
| WASM cold start | Low | Slower first request | ~100-200ms overhead, acceptable |
Testing Strategy#
-
Local Testing:
netlify dev curl "http://localhost:8888/api/bundle-keep?code=39j" > test.html open test.html -
Deploy Preview:
- Push to branch, test on deploy preview URL
- Verify paintings load, fonts work, piece runs
-
Production Validation:
- Compare output to CLI-generated bundle
- Verify byte-for-byte similarity (minus timestamps)
Estimated Effort#
| Phase | Time Estimate |
|---|---|
| Phase 1: Function structure | 2-3 hours |
| Phase 2: netlify.toml config | 30 min |
| Phase 3: API implementation | 2-3 hours |
| Phase 4: Testing & debugging | 2-4 hours |
| Total | 7-11 hours |
Future Enhancements#
- Caching: Store generated bundles in R2/S3 with TTL
- Webhooks: Trigger bundle regeneration on piece update
- Batch Generation: Generate multiple pieces in one request
- IPFS Upload: Optionally pin bundle to IPFS and return CID
Decision Points#
-
Should we useesbuildor try to make@swc/corework?- ✅ RESOLVED: Use
@swc/wasm- it's the WASM version of SWC with identical API - No code changes needed beyond
import { transform } from "@swc/wasm" - Keeps the exact same minification behavior as the CLI tool
- ✅ RESOLVED: Use
-
Background function (26s timeout) or standard (10s)?
- Recommendation: Start with standard, switch to background if needed
-
Should CLI and function share code?
- Recommendation: Yes, create
tezos/bundle-keep-shared.mjsmodule
- Recommendation: Yes, create
-
Where to store generated bundles?
- Option A: Don't store, generate on every request
- Option B: Cache in S3/R2 with piece code + git hash as key
- Recommendation: Option A first, add caching if needed