-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathserve-dev.cjs
More file actions
90 lines (78 loc) · 3 KB
/
serve-dev.cjs
File metadata and controls
90 lines (78 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env node
const { createServer } = require('http');
const { readFile, stat } = require('fs/promises');
const { join, extname } = require('path');
const PORT = process.env.PORT || 3111;
const DOCS_DIR = join(__dirname, process.env.DOCS_DIR || 'docs');
const MIME_TYPES = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.wasm': 'application/wasm',
'.gz': 'application/gzip',
'.sqlite': 'application/octet-stream',
'.bin': 'application/octet-stream',
};
const server = createServer(async (req, res) => {
try {
// Remove query strings first
const [urlPath] = req.url.split('?');
// Default to index.html for root
const reqPath = urlPath === '/' ? 'index.html' : urlPath;
let filePath = join(DOCS_DIR, reqPath);
const stats = await stat(filePath);
if (!stats.isFile()) {
res.writeHead(404);
res.end('Not Found');
return;
}
const ext = extname(filePath).toLowerCase();
const mimeType = MIME_TYPES[ext] || 'application/octet-stream';
// Set cache headers based on file type
const headers = { 'Content-Type': mimeType };
const fileName = filePath.split('/').pop();
// Check if this is a manifest/index file (used with ?v= cache busting)
const isManifest = fileName.includes('manifest.json') ||
fileName.includes('-index.json') ||
fileName.includes('-index.bin');
if (ext === '.html') {
// No cache for HTML files
headers['Cache-Control'] = 'no-cache, no-store, must-revalidate';
headers['Pragma'] = 'no-cache';
headers['Expires'] = '0';
} else if (isManifest) {
// 1 day cache with revalidation for manifests/indexes (they use ?v= cache busting)
headers['Cache-Control'] = 'public, max-age=86400, must-revalidate';
} else if ((ext === '.sqlite' || ext === '.gz') && filePath.includes('shard')) {
// Long cache for shards (they have content hashes in filenames)
headers['Cache-Control'] = 'public, max-age=31536000, immutable';
} else {
// Default: reasonable cache for other static files
headers['Cache-Control'] = 'public, max-age=3600';
}
const content = await readFile(filePath);
res.writeHead(200, headers);
res.end(content);
console.log(`${new Date().toLocaleTimeString()} ${req.method} ${req.url} - ${headers['Cache-Control']}`);
} catch (err) {
if (err.code === 'ENOENT') {
res.writeHead(404);
res.end('Not Found');
} else {
res.writeHead(500);
res.end('Internal Server Error');
console.error(err);
}
}
});
server.listen(PORT, () => {
console.log(`\nServing ${process.env.DOCS_DIR || 'docs'}/ on http://localhost:${PORT}`);
console.log('HTML files: no-cache');
console.log('Static assets (.sqlite, .gz, .bin): immutable, max-age=1yr');
console.log('Other files: max-age=1hr\n');
});