Everything comes together: tinyserve serves a folder over HTTP. The helpers you wrote in week 2
(resolveInRoot, resolveFile) are included at the top; a MIME table and sendText too.
Write createStaticServer(root) and return the server. For each request:
1. Method other than GET/HEAD → 405, Allow: GET, HEAD, body Method Not Allowed.
2. Decode the pathname of req.url (query string ignored). A broken escape → 400
Bad Request.
3. A path segment starting with . (/.env, /.git/config) but not .. → 404
Not Found: hidden files are never served.
4. resolveFile(root, path): 301 with Location (no body), 403 Forbidden,
404 Not Found. Should it throw, 500 Internal Server Error.
5. 200: Content-Type from MIME_TYPES by extension (application/octet-stream when
unknown), Content-Length = the size in bytes, then stream the file into res.
For HEAD, send the same headers and no body.
Error bodies are plain text (sendText). The driver serves a small site and reports status,Content-Type, Location, Allow, Content-Length (for 200) and body.
new URL() removes /../ from paths, but not an encoded %2F: /docs%2F..%2F..%2Fsecret
only becomes /docs/../../secret once you decode it. That is why resolveInRoot checks
the decoded path.
Sample tests