UNPKG

convex

Version:

Client for the Convex Cloud

135 lines (134 loc) 4.72 kB
"use strict"; import { setupAuth } from "./impl/authentication_impl.js"; import { setupHttpCalls } from "./impl/http_impl.js"; import { performJsSyscall } from "./impl/syscall.js"; const routableMethods = [ "GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH" ]; function normalizeMethod(method) { if (method === "HEAD") return "GET"; return method; } export const httpRouter = () => new HttpRouter(); export class HttpRouter { constructor() { this.exactRoutes = /* @__PURE__ */ new Map(); this.prefixRoutes = /* @__PURE__ */ new Map(); this.isRouter = true; this.route = (spec) => { if (!spec.handler) throw new Error(`route requires handler`); if (!spec.method) throw new Error(`route requires method`); const { method, handler } = spec; if (!routableMethods.includes(method)) { throw new Error( `'${method}' is not an allowed HTTP method (like GET, POST, PUT etc.)` ); } if ("path" in spec) { if (!spec.path.startsWith("/")) { throw new Error(`path '${spec.path}' does not start with a /`); } const prefixes = this.prefixRoutes.get(method) || /* @__PURE__ */ new Map(); for (const [prefix, _] of prefixes.entries()) { if (spec.path.startsWith(prefix)) { throw new Error( `${spec.method} path ${spec.path} is shadowed by pathPrefix ${prefix}` ); } } const methods = this.exactRoutes.has(spec.path) ? this.exactRoutes.get(spec.path) : /* @__PURE__ */ new Map(); if (methods.has(method)) { throw new Error( `Path '${spec.path}' for method ${method} already in use` ); } methods.set(method, handler); this.exactRoutes.set(spec.path, methods); } else { if (!spec.pathPrefix.startsWith("/")) { throw new Error(`path '${spec.pathPrefix}' does not start with a /`); } if (!spec.pathPrefix.endsWith("/")) { throw new Error(`pathPrefix ${spec.pathPrefix} must end with a /`); } const prefixes = this.prefixRoutes.get(method) || /* @__PURE__ */ new Map(); for (const [prefix, _] of prefixes.entries()) { if (spec.pathPrefix.startsWith(prefix)) { throw new Error( `${spec.method} pathPrefix ${spec.pathPrefix} is shadowed by pathPrefix ${prefix}` ); } } prefixes.set(spec.pathPrefix, handler); this.prefixRoutes.set(method, prefixes); } }; this.getRoutes = () => { const exactPaths = [...this.exactRoutes.keys()].sort(); const exact = exactPaths.flatMap( (path) => [...this.exactRoutes.get(path).keys()].sort().map( (method) => [path, method, this.exactRoutes.get(path).get(method)] ) ); const prefixPathMethods = [...this.prefixRoutes.keys()].sort(); const prefixes = prefixPathMethods.flatMap( (method) => [...this.prefixRoutes.get(method).keys()].sort().map( (pathPrefix) => [ `${pathPrefix}*`, method, this.prefixRoutes.get(method).get(pathPrefix) ] ) ); return [...exact, ...prefixes]; }; this.lookup = (path, method) => { method = normalizeMethod(method); const exactMatch = this.exactRoutes.get(path)?.get(method); if (exactMatch) return [exactMatch, method, path]; const prefixes = this.prefixRoutes.get(method) || /* @__PURE__ */ new Map(); for (const [pathPrefix, endpoint] of prefixes.entries()) { if (path.startsWith(pathPrefix)) { return [endpoint, method, `${pathPrefix}*`]; } } return null; }; this.runRequest = async (argsStr) => { const request = performJsSyscall("requestFromConvexJson", { convexJson: JSON.parse(argsStr) }); const pathname = new URL(request.url).pathname; const method = request.method; const match = this.lookup(pathname, method); if (!match) { const response2 = new Response(`No HttpEndpoint routed for ${pathname}`, { status: 404 }); return JSON.stringify( performJsSyscall("convexJsonFromResponse", { response: response2 }) ); } const [endpoint, _method, _path] = match; const calls = setupHttpCalls(); const ctx = { ...calls, auth: setupAuth() }; const response = await endpoint(ctx, request); return JSON.stringify( performJsSyscall("convexJsonFromResponse", { response }) ); }; } } //# sourceMappingURL=router.js.map