UNPKG

@intuitionrobotics/thunderstorm

Version:
69 lines 3.41 kB
import express from "express"; import {} from "express"; import { Firebase_ExpressFunction, FirebaseFunction } from "@intuitionrobotics/firebase/backend-functions"; import { HttpServer_Class } from "./HttpServer.js"; import { terminalErrorHandler } from "./app-middleware.js"; /** * An Express-backed HTTPS Cloud Function as a *regular module* — registered via * `Storm.addModules(...)` like any other function, named after the module * (no more hardcoded "api"), and instantiable as many times as needed: * * const Api = new ApiFunction("api", {router: rootRouter}); * const ApiTest = new ApiFunction("api_test", {router: rootRouter, options: {generation: "v2"}}); * new Storm().addModules(Api, ApiTest, ...modules).build(); * * `api_test` deploys the exact same code as a separate function * (`firebase deploy --only functions:api_test`) — a deployed test environment, * or a safe gen-2 canary, without touching the real `api`. * * Server config (baseUrl/bodyParserLimit/cors) resolves through the module * config store: the shared "HttpServer" slice applies to every instance, and * the instance's own slice (config[name]) layers on top. * * When no ApiFunction is registered, Storm builds an implicit default named * "api" from its legacy builder methods (setApp/setRoutes/setApiFunctionOptions) * — registering ANY instance disables that default. */ export class ApiFunction extends FirebaseFunction { providedApp; router; _app; constructor(name, args = {}) { super(name, undefined, undefined, args.options ?? {}); this.providedApp = args.app; this.router = args.router; } setRoutes(router) { this.router = router; return this; } // Built lazily at getFunction() time: framework middleware (compression/CORS/ // json — via HttpServer, whose config getter layers the shared "HttpServer" // slice under this instance's own slice) + the instance's router. get app() { if (this._app) return this._app; const app = this.providedApp ?? express(); const httpServer = new HttpServer_Class(app, this.config); httpServer.setup(); // The function name supplies the URL prefix on Cloud Functions (the `api` // function serves `…/api/**`), so the router always mounts at root. if (this.router) httpServer.mountRouter(this.router); // Terminal error handler for raw handler()-wrapped endpoints — MUST be // registered LAST, after the router (Express is order-sensitive). Legacy // ServerApi endpoints catch internally and never reach it, so it is // purely additive: only handler()'s `.catch(next)` routes here. app.use(terminalErrorHandler()); return this._app = app; } getFunction = () => { // Delegate the actual trigger wiring (v1/v2 branch, labels, lifecycle) to // the low-level express wrapper, resolving generation lazily. const expressFunction = new Firebase_ExpressFunction(this.app, this.getName(), undefined, undefined, { ...this.options, generation: this.generation }); this.onStart.forEach(hook => expressFunction.addOnStart(hook)); this.onDestroy.forEach(hook => expressFunction.addOnDestroy(hook)); return expressFunction.getFunction(); }; } //# sourceMappingURL=ApiFunction.js.map