UNPKG

@whatwg-node/server

Version:

Fetch API compliant HTTP Server adapter

188 lines (187 loc) • 7.18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createServerAdapter = void 0; const tslib_1 = require("tslib"); /* eslint-disable @typescript-eslint/ban-types */ const DefaultFetchAPI = tslib_1.__importStar(require("@whatwg-node/fetch")); const useFetchEvent_js_1 = require("./internal-plugins/useFetchEvent.js"); const useNodeAdapter_js_1 = require("./internal-plugins/useNodeAdapter.js"); const useUWSAdapter_js_1 = require("./internal-plugins/useUWSAdapter.js"); const utils_js_1 = require("./utils.js"); const EMPTY_OBJECT = {}; function createServerAdapter(serverAdapterBaseObject, options) { const fetchAPI = { ...DefaultFetchAPI, ...options?.fetchAPI, }; const givenHandleRequest = typeof serverAdapterBaseObject === 'function' ? serverAdapterBaseObject : serverAdapterBaseObject.handle; const onRequestAdaptHooks = []; const onRequestHooks = []; const onResponseHooks = []; const plugins = options?.plugins ?? []; plugins.push((0, useUWSAdapter_js_1.useUWSAdapter)(), (0, useNodeAdapter_js_1.useNodeAdapter)(), (0, useFetchEvent_js_1.useFetchEvent)()); for (const plugin of plugins) { if (plugin.onRequestAdapt) { onRequestAdaptHooks.push(plugin.onRequestAdapt); } if (plugin.onRequest) { onRequestHooks.push(plugin.onRequest); } if (plugin.onResponse) { onResponseHooks.push(plugin.onResponse); } } async function handleRequest(request, serverContext) { let url = new Proxy(EMPTY_OBJECT, { get(_target, prop, _receiver) { url = new fetchAPI.URL(request.url, 'http://localhost'); return Reflect.get(url, prop, url); }, }); let requestHandler = givenHandleRequest; let response; let waitUntilPromises; if (serverContext['waitUntil'] == null) { waitUntilPromises = new Set(); serverContext['waitUntil'] = (promise) => { waitUntilPromises.add(promise); promise.then(() => { waitUntilPromises.delete(promise); }); }; } for (const onRequestHook of onRequestHooks) { await onRequestHook({ request, serverContext, fetchAPI, url, requestHandler, setRequestHandler(newRequestHandler) { requestHandler = newRequestHandler; }, endResponse(newResponse) { response = newResponse; }, }); if (response) { break; } } if (!response) { response = await requestHandler(request, serverContext); } if (!response) { response = new fetchAPI.Response(undefined, { status: 404, statusText: 'Not Found', }); } for (const onResponseHook of onResponseHooks) { await onResponseHook({ request, response, serverContext, }); } if (waitUntilPromises?.size) { const waitUntils = await Promise.allSettled(waitUntilPromises); waitUntils.forEach(waitUntil => { if (waitUntil.status === 'rejected') { console.error(waitUntil.reason); } }); } return response; } const fetchFn = (input, ...maybeCtx) => { if (typeof input === 'string' || 'href' in input) { const [initOrCtx, ...restOfCtx] = maybeCtx; if ((0, utils_js_1.isRequestInit)(initOrCtx)) { const serverContext = restOfCtx.length > 0 ? (0, utils_js_1.completeAssign)(...restOfCtx) : {}; return handleRequest(new fetchAPI.Request(input, initOrCtx), serverContext); } const serverContext = maybeCtx.length > 0 ? (0, utils_js_1.completeAssign)(...maybeCtx) : {}; return handleRequest(new fetchAPI.Request(input), serverContext); } const serverContext = maybeCtx.length > 0 ? (0, utils_js_1.completeAssign)(...maybeCtx) : {}; return handleRequest(input, serverContext); }; const genericRequestHandler = (...args) => { let request; let serverContext; for (const onRequestAdapt of onRequestAdaptHooks) { onRequestAdapt({ args, setRequest(newRequest) { request = newRequest; }, setServerContext(newServerContext) { serverContext = newServerContext; }, fetchAPI, }); } if (request) { if (!serverContext) { serverContext = {}; } return handleRequest(request, serverContext); } return fetchFn(...args); }; const adapterObj = { handleRequest, fetch: fetchFn, requestListener: genericRequestHandler, handleNodeRequest: genericRequestHandler, handleEvent: genericRequestHandler, handle: genericRequestHandler, }; const serverAdapter = new Proxy(genericRequestHandler, { // It should have all the attributes of the handler function and the server instance has: (_, prop) => { return (prop in adapterObj || prop in genericRequestHandler || (serverAdapterBaseObject && prop in serverAdapterBaseObject)); }, get: (_, prop) => { const adapterProp = adapterObj[prop]; if (adapterProp) { if (adapterProp.bind) { return adapterProp.bind(adapterObj); } return adapterProp; } const handleProp = genericRequestHandler[prop]; if (handleProp) { if (handleProp.bind) { return handleProp.bind(genericRequestHandler); } return handleProp; } if (serverAdapterBaseObject) { const serverAdapterBaseObjectProp = serverAdapterBaseObject[prop]; if (serverAdapterBaseObjectProp) { if (serverAdapterBaseObjectProp.bind) { return function (...args) { const returnedVal = serverAdapterBaseObject[prop](...args); if (returnedVal === serverAdapterBaseObject) { return serverAdapter; } return returnedVal; }; } return serverAdapterBaseObjectProp; } } }, apply(_, __, args) { return genericRequestHandler(...args); }, }); return serverAdapter; } exports.createServerAdapter = createServerAdapter;