| 1 | import fs from 'node:fs'; |
| 2 | import path from 'node:path'; |
| 3 | import { fileURLToPath } from 'node:url'; |
| 4 | import mime from 'mime-types'; |
| 5 | import { serverDirectory } from './server-directory.js'; |
| 6 | import { getRequestURL, isFileURL, isPathUnderParent } from './util.js'; |
| 7 | |
| 8 | const originalFetch = globalThis.fetch; |
| 9 | |
| 10 | const ALLOWED_EXTENSIONS = [ |
| 11 | '.wasm', |
| 12 | ]; |
| 13 | |
| 14 | // Patched fetch function that handles file URLs |
| 15 | globalThis.fetch = async (/** @type {string | URL | Request} */ request, /** @type {RequestInit | undefined} */ options) => { |
| 16 | if (!isFileURL(request)) { |
| 17 | return originalFetch(request, options); |
| 18 | } |
| 19 | const url = getRequestURL(request); |
| 20 | const filePath = path.resolve(fileURLToPath(url)); |
| 21 | const isUnderServerDirectory = isPathUnderParent(serverDirectory, filePath); |
| 22 | if (!isUnderServerDirectory) { |
| 23 | throw new Error('Requested file path is outside of the server directory.'); |
| 24 | } |
| 25 | const parsedPath = path.parse(filePath); |
| 26 | if (!ALLOWED_EXTENSIONS.includes(parsedPath.ext)) { |
| 27 | throw new Error('Unsupported file extension.'); |
| 28 | } |
| 29 | const fileName = parsedPath.base; |
| 30 | const buffer = await fs.promises.readFile(filePath); |
| 31 | const response = new Response(buffer, { |
| 32 | status: 200, |
| 33 | statusText: 'OK', |
| 34 | headers: { |
| 35 | 'Content-Type': mime.lookup(fileName) || 'application/octet-stream', |
| 36 | 'Content-Length': buffer.length.toString(), |
| 37 | }, |
| 38 | }); |
| 39 | return response; |
| 40 | }; |