UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

45 lines (44 loc) 2.3 kB
import { MethodNotAllowedError, NotFoundError } from "../../error/RequestError.js"; import { ValueError } from "../../error/ValueError.js"; import { requireDictionary } from "../../util/dictionary.js"; import { getResponse, isRequestMethod, parseRequestBody } from "../../util/http.js"; import { isPlainObject } from "../../util/object.js"; import { matchURLPrefix } from "../../util/url.js"; export function handleEndpoints(base, handlers, request, context, caller = handleEndpoints) { const { url, method } = request; if (!isRequestMethod(method)) throw new MethodNotAllowedError("Unsupported request method", { received: method, caller }); const { pathname: requestPath, searchParams } = new URL(url, base); const targetPath = matchURLPrefix(url, base); if (!targetPath) throw new NotFoundError("No matching base path", { received: requestPath, caller }); for (const handler of handlers) { const pathParams = handler.endpoint.match(method, targetPath, caller); if (!pathParams) continue; const params = searchParams.size ? { ...requireDictionary(searchParams), ...pathParams } : pathParams; return _handleEndpoint(handler, params, request, context, handleEndpoints); } throw new NotFoundError("No matching endpoint", { received: targetPath, caller }); } /** * Validate and invoke an endpoint callback after the routing layer has already matched URL params. */ async function _handleEndpoint({ endpoint, callback }, /** Params we already matched/parsed from the URL. */ params, request, context, caller) { const content = await parseRequestBody(request, caller); const unsafePayload = content === undefined ? params : isPlainObject(content) ? { ...content, ...params } : content; const payload = endpoint.payload.validate(unsafePayload); const unsafeResult = await callback(payload, request, context); if (unsafeResult instanceof Response) return unsafeResult; try { return getResponse(endpoint.result.validate(unsafeResult)); } catch (thrown) { if (typeof thrown === "string") throw new ValueError(`Invalid result for ${endpoint.toString()}:\n${thrown}`, { endpoint, callback, cause: thrown, caller }); throw thrown; } }