@lpdjs/firestore-repo-service 2.2.9-beta.13 → 2.2.9-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@
9
9
  <script id="api-reference" data-url="${n}"></script>
10
10
  <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
11
11
  </body>
12
- </html>`}var y=class{constructor(e){this.cachedSpec=null;this.options=e,this.app=new hono.Hono,this.mountedRoutes=L(e.routes,e.api);let n=[...e.middlewares??[],...e.globalMiddlewares??[]];for(let o of n)this.app.use("*",o);this.mountRoutes(),this.mountOpenApi(),e.notFound&&this.app.notFound(e.notFound),e.onError&&this.app.onError(e.onError);}get hono(){return this.app}get nodeHandler(){return nodeServer.getRequestListener(this.app.fetch,{overrideGlobalObjects:false})}toFunction(e,n){let o=this.nodeHandler;return n?e(n,o):e(o)}buildOpenApiSpec(){if(this.cachedSpec)return this.cachedSpec;if(!this.options.openapi)throw new Error("[HonoServer] openapi config not set");return this.cachedSpec=v(this.mountedRoutes,this.options.basePath??"",this.options.openapi),this.cachedSpec}mountRoutes(){let e=this.options.basePath??"",n=this.options.validateOutput??false,o=this.options.verbose??false;for(let r of this.mountedRoutes){if(!r.path)throw new Error(`[HonoServer] route "${r.method.toUpperCase()} (no path)" \u2014 missing \`path\`. Run the codegen so the path is derived from the file location, or set it explicitly.`);let i=P(e,r.path),s=r.middlewares??[],a=r.source??(r.method==="get"?"query":"json"),u=B(r,a,n,this.options.interceptor),p=r.method.toUpperCase();this.app.on(p,[i],...s,u),o&&console.log(`[HonoServer] ${r.method.toUpperCase().padEnd(6)} ${i}`);}}mountOpenApi(){let e=this.options.openapi;if(!e)return;let n=e.path??"/openapi.json",o=e.docsPath===void 0?"/docs":e.docsPath,r=P(this.options.basePath??"",n),i=o===false?null:P(this.options.basePath??"",o);if(this.app.get(r,s=>s.json(this.buildOpenApiSpec())),i){let s=U(i,r);this.app.get(i,a=>a.html(E(s,e.info.title)));}}};function L(t,e){return e?t.filter(n=>n.api===e):t.slice()}function P(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function U(t,e){let n=t.split("/").filter(Boolean),o=e.split("/").filter(Boolean);n.pop();let r=0;for(;r<n.length&&r<o.length&&n[r]===o[r];)r++;let i=n.length-r;return [...Array(i).fill(".."),...o.slice(r)].join("/")||"./"}function B(t,e,n,o){let r=t.input,i=t.output,s=t.status??200;return async a=>{let u=async()=>{let c;if(r){let l;try{l=await W(a,e,t.method);}catch(g){throw new h(g instanceof Error?g.message:String(g))}let f=r.safeParse(l);if(!f.success)throw new m(f.error,e);c=f.data;}let d=await t.handler({input:c,c:a});if(n&&i&&!(d instanceof Response)){let l=i.safeParse(d);if(!l.success)throw new R(l.error);return l.data}return d},p;if(o)p=await o({next:u,route:t,c:a});else try{p=await u();}catch(c){let d=V(a,c);if(d)return d;throw c}return p instanceof Response?p:a.json(p,s)}}var h=class extends Error{constructor(n){super(n);this.statusCode=400;this.name="BadRequestError";}},R=class extends Error{constructor(n){super("Output validation failed");this.zodError=n;this.statusCode=500;this.name="OutputValidationError";}};function V(t,e){return e instanceof m?t.json({success:false,error:"Validation failed",issues:x(e.zodError)},400):e instanceof h?t.json({success:false,error:"Bad Request",message:e.message},400):e instanceof R?t.json({success:false,error:"Output validation failed",issues:x(e.zodError)},500):null}async function W(t,e,n){switch(e){case "json":{if(n==="get")return t.req.query();let o=await t.req.text();if(!o)return {};try{return JSON.parse(o)}catch(r){throw new Error(`Invalid JSON body: ${r instanceof Error?r.message:String(r)}`)}}case "query":return t.req.query();case "form":return await t.req.parseBody();case "param":return t.req.param();default:return {}}}function x(t){return t.issues.map(e=>({path:e.path.join("."),code:e.code,message:e.message}))}function J(t){return {configs:t,defineRoute(e){return e},serverFor(e,n){let o=t[e];if(!o)throw new Error(`[ApiRegistry] unknown api "${e}". Registered: ${Object.keys(t).join(", ")}`);return new y({...o,api:e,routes:n})},toFunctions(e,n,o){let r={};for(let i of Object.keys(t)){let s={...o?.defaults??{},...o?.per?.[i]??{}},a=new y({...t[i],api:i,routes:e});r[i]=Object.keys(s).length?a.toFunction(n,s):a.toFunction(n);}return r}}}var D={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function T(t,e=D){let n=new Set(e.skipSegments.map(r=>r.toLowerCase()));return "/"+t.split("/").filter(Boolean).filter(r=>!n.has(r.toLowerCase())).map(r=>e.casing==="kebab"?K(r):r).join("/")}function K(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function A(t,e,n){let o=O(t),r=O(e),i=0;for(;i<o.length&&i<r.length&&o[i]===r[i];)i++;let s=o.length-i,a=r.slice(i),p=(a[a.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),c=n===""?p:`${p}${n}`;return a[a.length-1]=c,(s===0?"./":"../".repeat(s))+a.join("/")}function O(t){return t.replace(/\\/g,"/").replace(/\/+$/,"").split("/").filter((n,o)=>!(o===0&&n===""))}var b={routesFile:"routes.ts",excludeSegments:["node_modules","__generated__","tests","__tests__",".turbo","dist","build",".next"]};function ne(t,e=b){let n=[];return k(t,t,e,n),n.sort((o,r)=>o.relPath.localeCompare(r.relPath)),n}function k(t,e,n,o){let r;try{r=fs.readdirSync(e);}catch{return}for(let i of r){if(n.excludeSegments.includes(i))continue;let s=path.join(e,i),a;try{a=fs.statSync(s);}catch{continue}if(a.isDirectory())k(t,s,n,o);else if(a.isFile()&&i===n.routesFile){let u=path.relative(t,s).split(path.sep).join("/"),p=u.replace(/\/?[^/]+$/,"");o.push({absPath:s,relPath:u,relDir:p});}}}var M="/**\n * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\n * Do not edit by hand \u2014 re-run `hono:gen` after adding / removing route files.\n */\n";function I(t,e){let n=path.dirname(e.outFile);fs.mkdirSync(n,{recursive:true});let o=e.banner??M,r=(e.now??new Date).toISOString(),i=e.importExtension,s=[],a=[],u=[];t.forEach((c,d)=>{let l=A(n,c.absPath,i),f=T(c.relDir,e.derive);s.push(`import mod${d} from ${JSON.stringify(l)};`),a.push(` { __derivedPath: ${JSON.stringify(f)}, mod: mod${d} },`),u.push({source:c.relPath,url:f});});let p=`${o}// Generated at ${r} \u2014 ${t.length} route file${t.length===1?"":"s"}.
12
+ </html>`}var y=class{constructor(e){this.cachedSpec=null;this.options=e,this.app=new hono.Hono,this.mountedRoutes=L(e.routes,e.api);let n=[...e.middlewares??[],...e.globalMiddlewares??[]];for(let o of n)this.app.use("*",o);this.mountRoutes(),this.mountOpenApi(),e.notFound&&this.app.notFound(e.notFound),e.onError&&this.app.onError(e.onError);}get hono(){return this.app}get nodeHandler(){return nodeServer.getRequestListener(this.app.fetch,{overrideGlobalObjects:false})}toFunction(e,n){let o=this.nodeHandler;return n?e(n,o):e(o)}buildOpenApiSpec(){if(this.cachedSpec)return this.cachedSpec;if(!this.options.openapi)throw new Error("[HonoServer] openapi config not set");return this.cachedSpec=v(this.mountedRoutes,this.options.basePath??"",this.options.openapi),this.cachedSpec}mountRoutes(){let e=this.options.basePath??"",n=this.options.validateOutput??false,o=this.options.verbose??false;for(let r of this.mountedRoutes){if(!r.path)throw new Error(`[HonoServer] route "${r.method.toUpperCase()} (no path)" \u2014 missing \`path\`. Run the codegen so the path is derived from the file location, or set it explicitly.`);let i=A(e,r.path),s=r.middlewares??[],a=r.source??(r.method==="get"?"query":"json"),u=B(r,a,n,this.options.interceptor),p=r.method.toUpperCase();this.app.on(p,[i],...s,u),o&&console.log(`[HonoServer] ${r.method.toUpperCase().padEnd(6)} ${i}`);}}mountOpenApi(){let e=this.options.openapi;if(!e)return;let n=e.path??"/openapi.json",o=e.docsPath===void 0?"/docs":e.docsPath,r=A(this.options.basePath??"",n),i=o===false?null:A(this.options.basePath??"",o);if(this.app.get(r,s=>s.json(this.buildOpenApiSpec())),i){let s=U(i,r);this.app.get(i,a=>a.html(E(s,e.info.title)));}}};function L(t,e){return e?t.filter(n=>Array.isArray(n.api)?n.api.includes(e):n.api===e):t.slice()}function A(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function U(t,e){let n=t.split("/").filter(Boolean),o=e.split("/").filter(Boolean);n.pop();let r=0;for(;r<n.length&&r<o.length&&n[r]===o[r];)r++;let i=n.length-r;return [...Array(i).fill(".."),...o.slice(r)].join("/")||"./"}function B(t,e,n,o){let r=t.input,i=t.output,s=t.status??200;return async a=>{let u=async()=>{let c;if(r){let l;try{l=await W(a,e,t.method);}catch(g){throw new h(g instanceof Error?g.message:String(g))}let f=r.safeParse(l);if(!f.success)throw new m(f.error,e);c=f.data;}let d=await t.handler({input:c,c:a});if(n&&i&&!(d instanceof Response)){let l=i.safeParse(d);if(!l.success)throw new R(l.error);return l.data}return d},p;if(o)p=await o({next:u,route:t,c:a});else try{p=await u();}catch(c){let d=V(a,c);if(d)return d;throw c}return p instanceof Response?p:a.json(p,s)}}var h=class extends Error{constructor(n){super(n);this.statusCode=400;this.name="BadRequestError";}},R=class extends Error{constructor(n){super("Output validation failed");this.zodError=n;this.statusCode=500;this.name="OutputValidationError";}};function V(t,e){return e instanceof m?t.json({success:false,error:"Validation failed",issues:x(e.zodError)},400):e instanceof h?t.json({success:false,error:"Bad Request",message:e.message},400):e instanceof R?t.json({success:false,error:"Output validation failed",issues:x(e.zodError)},500):null}async function W(t,e,n){switch(e){case "json":{if(n==="get")return t.req.query();let o=await t.req.text();if(!o)return {};try{return JSON.parse(o)}catch(r){throw new Error(`Invalid JSON body: ${r instanceof Error?r.message:String(r)}`)}}case "query":return t.req.query();case "form":return await t.req.parseBody();case "param":return t.req.param();default:return {}}}function x(t){return t.issues.map(e=>({path:e.path.join("."),code:e.code,message:e.message}))}function J(t){return {configs:t,defineRoute(e){return e},serverFor(e,n){let o=t[e];if(!o)throw new Error(`[ApiRegistry] unknown api "${e}". Registered: ${Object.keys(t).join(", ")}`);return new y({...o,api:e,routes:n})},toFunctions(e,n,o){let r={};for(let i of Object.keys(t)){let s={...o?.defaults??{},...o?.per?.[i]??{}},a=new y({...t[i],api:i,routes:e});r[i]=Object.keys(s).length?a.toFunction(n,s):a.toFunction(n);}return r}}}var D={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function P(t,e=D){let n=new Set(e.skipSegments.map(r=>r.toLowerCase()));return "/"+t.split("/").filter(Boolean).filter(r=>!n.has(r.toLowerCase())).map(r=>e.casing==="kebab"?K(r):r).join("/")}function K(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function T(t,e,n){let o=O(t),r=O(e),i=0;for(;i<o.length&&i<r.length&&o[i]===r[i];)i++;let s=o.length-i,a=r.slice(i),p=(a[a.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),c=n===""?p:`${p}${n}`;return a[a.length-1]=c,(s===0?"./":"../".repeat(s))+a.join("/")}function O(t){return t.replace(/\\/g,"/").replace(/\/+$/,"").split("/").filter((n,o)=>!(o===0&&n===""))}var b={routesFile:"routes.ts",excludeSegments:["node_modules","__generated__","tests","__tests__",".turbo","dist","build",".next"]};function ne(t,e=b){let n=[];return k(t,t,e,n),n.sort((o,r)=>o.relPath.localeCompare(r.relPath)),n}function k(t,e,n,o){let r;try{r=fs.readdirSync(e);}catch{return}for(let i of r){if(n.excludeSegments.includes(i))continue;let s=path.join(e,i),a;try{a=fs.statSync(s);}catch{continue}if(a.isDirectory())k(t,s,n,o);else if(a.isFile()&&i===n.routesFile){let u=path.relative(t,s).split(path.sep).join("/"),p=u.replace(/\/?[^/]+$/,"");o.push({absPath:s,relPath:u,relDir:p});}}}var M="/**\n * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\n * Do not edit by hand \u2014 re-run `hono:gen` after adding / removing route files.\n */\n";function I(t,e){let n=path.dirname(e.outFile);fs.mkdirSync(n,{recursive:true});let o=e.banner??M,r=(e.now??new Date).toISOString(),i=e.importExtension,s=[],a=[],u=[];t.forEach((c,d)=>{let l=T(n,c.absPath,i),f=P(c.relDir,e.derive);s.push(`import mod${d} from ${JSON.stringify(l)};`),a.push(` { __derivedPath: ${JSON.stringify(f)}, mod: mod${d} },`),u.push({source:c.relPath,url:f});});let p=`${o}// Generated at ${r} \u2014 ${t.length} route file${t.length===1?"":"s"}.
13
13
 
14
14
  import type { AnyRouteDef, RouteModuleDefault } from "@lpdjs/firestore-repo-service/servers/hono";
15
15
 
@@ -26,5 +26,5 @@ export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) =>
26
26
  const list = Array.isArray(mod) ? mod : [mod];
27
27
  return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));
28
28
  });
29
- `;return fs.writeFileSync(e.outFile,p,"utf8"),{outFile:e.outFile,routeCount:t.length,derivedPaths:u}}function ae(t,e,n,o,r){let i=r(t),s=path.join(t,e);return I(i,{outFile:s,derive:n,importExtension:o})}exports.DEFAULT_DERIVE=D;exports.DEFAULT_GENERATOR_BANNER=M;exports.DEFAULT_SCANNER=b;exports.HonoServer=y;exports.ValidationError=m;exports.buildOpenApiDocument=v;exports.createApiRegistry=J;exports.derivePath=T;exports.generateFromRoot=ae;exports.generateRoutesManifest=I;exports.renderDocsHtml=E;exports.scanRoutes=ne;exports.toImportSpecifier=A;//# sourceMappingURL=index.cjs.map
29
+ `;return fs.writeFileSync(e.outFile,p,"utf8"),{outFile:e.outFile,routeCount:t.length,derivedPaths:u}}function ae(t,e,n,o,r){let i=r(t),s=path.join(t,e);return I(i,{outFile:s,derive:n,importExtension:o})}exports.DEFAULT_DERIVE=D;exports.DEFAULT_GENERATOR_BANNER=M;exports.DEFAULT_SCANNER=b;exports.HonoServer=y;exports.ValidationError=m;exports.buildOpenApiDocument=v;exports.createApiRegistry=J;exports.derivePath=P;exports.generateFromRoot=ae;exports.generateRoutesManifest=I;exports.renderDocsHtml=E;exports.scanRoutes=ne;exports.toImportSpecifier=T;//# sourceMappingURL=index.cjs.map
30
30
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/servers/hono/types.ts","../../../src/servers/hono/openapi.ts","../../../src/servers/hono/server.ts","../../../src/servers/hono/api-registry.ts","../../../src/servers/hono/codegen/path-utils.ts","../../../src/servers/hono/codegen/scanner.ts","../../../src/servers/hono/codegen/generator.ts"],"names":["ValidationError","zodError","source","extendZodWithOpenApi","z","DEFAULT_RESPONSE_DESCRIPTION","defaultSource","method","buildOpenApiDocument","routes","basePath","config","registry","OpenAPIRegistry","name","scheme","route","fullPath","joinPath","status","requestBody","buildRequestBody","requestQuery","buildQueryOrParam","requestParams","operationId","makeOperationId","convertExpressPathToOpenApi","OpenApiGeneratorV31","schema","target","path","base","left","right","merged","cleaned","renderDocsHtml","specUrl","title","safeUrl","HonoServer","options","Hono","filterRoutes","globalMws","mw","getRequestListener","onRequest","httpsOptions","handler","validateOutput","verbose","middlewares","makeRouteHandler","httpMethod","cfg","specPath","docsPath","fullSpecPath","fullDocsPath","c","relativeSpecUrl","relativeUrlFromTo","api","r","from","to","fromSegs","toSegs","common","ups","interceptor","inputSchema","outputSchema","callNext","payload","raw","readPayload","err","BadRequestError","parsed","result","checked","OutputValidationError","handled","defaultErrorResponse","message","formatZodIssues","text","error","i","createApiRegistry","configs","def","opts","out","httpsOpts","server","DEFAULT_DERIVE","derivePath","relativeDir","skip","s","p","kebab","toImportSpecifier","fromDir","toFile","ext","fromParts","splitAbs","toParts","up","down","stripped","finalLast","part","DEFAULT_SCANNER","scanRoutes","rootAbs","found","walk","a","b","root","dir","entries","readdirSync","abs","join","st","statSync","relPath","relative","sep","relDir","DEFAULT_GENERATOR_BANNER","generateRoutesManifest","outDir","dirname","mkdirSync","banner","now","importLines","entryLines","derivedPaths","importPath","url","body","writeFileSync","generateFromRoot","outFileRel","derive","importExtension","scan","outFile"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Public types for the Hono file-based API server.\n *\n * Designed to be:\n * - **fully typed** end-to-end (Zod input/output → handler payload type),\n * - **runtime-safe** via Zod parsing on every request,\n * - **bundle-friendly** for Firebase Cloud Functions v2 cold-start (codegen\n * emits static imports — no `fs`/`import()` at runtime).\n */\n\nimport type { z, ZodError } from \"zod\";\nimport type { Context, Env, MiddlewareHandler } from \"hono\";\n\nexport type HttpMethod = \"get\" | \"post\" | \"put\" | \"patch\" | \"delete\";\n\n/** Where the validated payload comes from. */\nexport type PayloadSource = \"json\" | \"query\" | \"form\" | \"param\";\n\n/** Handler signature — receives a single typed context object. */\nexport type RouteHandler<TIn, TOut, TEnv extends Env = Env> = (ctx: {\n /** Validated (and typed) request payload. `void` when no `input` schema is defined. */\n input: TIn;\n /** Raw Hono `Context` for headers, set status, redirect, etc. */\n c: Context<TEnv>;\n}) => Promise<TOut | Response> | TOut | Response;\n\n/**\n * One route declaration. Default-exported by every `routes.ts` file inside the\n * domain tree. Use {@link defineRoute} for full type inference.\n */\nexport interface RouteDef<\n TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined,\n TOut extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined,\n TEnv extends Env = Env,\n> {\n /**\n * Logical API tag — routes sharing the same `api` are mounted on the same\n * `HonoServer` (typically one Cloud Function per `api`).\n */\n api: string;\n\n /** HTTP method. */\n method: HttpMethod;\n\n /**\n * URL path appended to the server `basePath`. If omitted, the codegen will\n * derive it from the file location (e.g. `domains/activities/useCases/createCustom/routes.ts`\n * → `/activities/createCustom`).\n */\n path?: string;\n\n /**\n * Where the request payload comes from.\n * Default: `\"json\"` for body methods (POST/PUT/PATCH/DELETE), `\"query\"` for GET.\n */\n source?: PayloadSource;\n\n /** Zod schema validating the payload. Failures yield a 400 response. */\n input?: TIn;\n\n /**\n * Zod schema for the success response. Used to populate the OpenAPI spec\n * and (when `validateOutput` is enabled on the server) to assert the\n * runtime payload returned by the handler.\n */\n output?: TOut;\n\n /** Status code for the success response. Default: 200. */\n status?: number;\n\n /** Hono middlewares applied to this route only (after global middlewares). */\n middlewares?: MiddlewareHandler<TEnv>[];\n\n // ── OpenAPI metadata ─────────────────────────────────────────────────\n summary?: string;\n description?: string;\n tags?: string[];\n /** Mark the operation as deprecated in the generated spec. */\n deprecated?: boolean;\n /** Security requirements (operationId-level override). */\n security?: Array<Record<string, string[]>>;\n\n /** The request handler. */\n handler: RouteHandler<\n TIn extends z.ZodTypeAny ? z.infer<TIn> : void,\n TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown,\n TEnv\n >;\n}\n\n/** Erased `RouteDef` used by registry/codegen — handler signature is opaque. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyRouteDef = RouteDef<any, any>;\n\n/**\n * What `routes.ts` can default-export:\n * - a single {@link RouteDef} (most common case),\n * - or an array of {@link RouteDef} (e.g. expose the same useCase on\n * multiple `api` tags or under multiple paths).\n */\nexport type RouteModuleDefault = AnyRouteDef | AnyRouteDef[];\n\n/**\n * Thrown by the server when the incoming request fails Zod validation.\n * Caught by the {@link RouteInterceptor} (if any) so users can shape the\n * response envelope however they like; otherwise yields a default 400.\n */\nexport class ValidationError extends Error {\n readonly statusCode = 400 as const;\n constructor(\n /** Original Zod error — `error.issues` to enumerate field-level problems. */\n readonly zodError: ZodError,\n /** Where the offending payload came from. */\n readonly source: PayloadSource,\n ) {\n super(\"Request validation failed\");\n this.name = \"ValidationError\";\n }\n}\n\n/**\n * Cross-cutting interceptor applied around every handler.\n * Use it for response envelopes, business-error → HTTP mapping, structured\n * logging, tracing spans, etc.\n *\n * Wrap `next()` in `try/catch` to intercept BOTH Zod {@link ValidationError}s\n * (thrown before the handler runs) AND business errors thrown by the handler.\n *\n * @example\n * ```ts\n * interceptor: async ({ next, route, c }) => {\n * try {\n * const data = await next();\n * return c.json({ success: true, data, error: null });\n * } catch (err) {\n * if (err instanceof ValidationError) {\n * return c.json({ success: false, error: \"validation\", issues: err.zodError.issues }, 400);\n * }\n * if (err instanceof DomainError) {\n * return c.json({ success: false, error: err.code }, err.statusCode);\n * }\n * throw err; // → falls back to onError or Hono's default 500\n * }\n * }\n * ```\n */\nexport type RouteInterceptor<TEnv extends Env = Env> = (ctx: {\n /**\n * Calls validation + handler and returns the raw value.\n * Throws {@link ValidationError} on Zod failure or any error thrown by the handler.\n */\n next: () => Promise<unknown>;\n /** Route metadata (read-only). */\n route: AnyRouteDef;\n /** Hono request context. */\n c: Context<TEnv>;\n}) => Promise<Response | unknown> | Response | unknown;\n\n/** OpenAPI document info (subset of the spec used by the helper). */\nexport interface OpenAPIInfo {\n title: string;\n version: string;\n description?: string;\n}\n\n/** OpenAPI configuration on the server. */\nexport interface OpenAPIConfig {\n /** Path served by the JSON spec (e.g. `/openapi.json`). Default: `/openapi.json`. */\n path?: string;\n /** Path serving the documentation UI. Set to `false` to disable. Default: `/docs`. */\n docsPath?: string | false;\n /** OpenAPI document info. */\n info: OpenAPIInfo;\n /** Optional servers list for the spec. */\n servers?: { url: string; description?: string }[];\n /** Optional security schemes (e.g. bearer auth). */\n securitySchemes?: Record<string, unknown>;\n /** Default security requirement applied to every operation. */\n security?: Array<Record<string, string[]>>;\n}\n\n/** Options consumed by the {@link HonoServer} constructor. */\nexport interface HonoServerOptions<TEnv extends Env = Env> {\n /**\n * API tag — only routes whose `api` matches this value are mounted.\n * If omitted, every route in the registry is mounted.\n */\n api?: string;\n\n /** Pre-resolved route registry (typically the codegen output). */\n routes: AnyRouteDef[];\n\n /** URL prefix mounted before every route path. Default: `\"\"`. */\n basePath?: string;\n\n /** Hono middlewares applied to every route (after the built-ins). */\n middlewares?: MiddlewareHandler<TEnv>[];\n\n /**\n * Alias for `middlewares` — global middlewares applied to every route.\n * If both are provided, `globalMiddlewares` is appended after `middlewares`.\n */\n globalMiddlewares?: MiddlewareHandler<TEnv>[];\n\n /**\n * If `true`, the server validates the value returned by every handler\n * against the route's `output` schema and rejects mismatches with a 500\n * response. Useful in dev / staging. Default: `false`.\n */\n validateOutput?: boolean;\n\n /** Enable verbose logging of mounted routes at startup. Default: `false`. */\n verbose?: boolean;\n\n /** OpenAPI configuration. Omit to disable. */\n openapi?: OpenAPIConfig;\n\n /** Custom 404 handler. */\n notFound?: (c: Context<TEnv>) => Response | Promise<Response>;\n\n /** Custom error handler. */\n onError?: (err: unknown, c: Context<TEnv>) => Response | Promise<Response>;\n\n /**\n * Cross-cutting interceptor wrapping every handler call.\n * Ideal for response envelopes, business-error mapping, tracing.\n * See {@link RouteInterceptor}.\n */\n interceptor?: RouteInterceptor<TEnv>;\n}\n","/**\n * OpenAPI 3.1 spec generator from {@link RouteDef} entries.\n *\n * Uses `@asteasolutions/zod-to-openapi` directly so users keep importing the\n * vanilla `zod` package (no opinionated `z` re-export required).\n */\n\nimport {\n OpenAPIRegistry,\n OpenApiGeneratorV31,\n extendZodWithOpenApi,\n} from \"@asteasolutions/zod-to-openapi\";\nimport { z } from \"zod\";\nimport type {\n AnyRouteDef,\n HttpMethod,\n OpenAPIConfig,\n PayloadSource,\n} from \"./types\";\n\n// Patches Zod prototype with `.openapi()` and enables schema → OpenAPI\n// conversion for vanilla zod schemas. Idempotent — safe to call multiple times.\nextendZodWithOpenApi(z);\n\nconst DEFAULT_RESPONSE_DESCRIPTION = \"Successful response\";\n\nfunction defaultSource(method: HttpMethod): PayloadSource {\n return method === \"get\" ? \"query\" : \"json\";\n}\n\n/** Build the OpenAPI document from the mounted route registry. */\nexport function buildOpenApiDocument(\n routes: AnyRouteDef[],\n basePath: string,\n config: OpenAPIConfig,\n): Record<string, unknown> {\n const registry = new OpenAPIRegistry();\n\n if (config.securitySchemes) {\n for (const [name, scheme] of Object.entries(config.securitySchemes)) {\n // The registry's runtime accepts any spec-shaped object; cast through\n // `unknown` to satisfy zod-to-openapi's stricter typings.\n registry.registerComponent(\n \"securitySchemes\",\n name,\n scheme as unknown as Parameters<\n typeof registry.registerComponent\n >[2],\n );\n }\n }\n\n for (const route of routes) {\n const method = route.method;\n const source = route.source ?? defaultSource(method);\n const fullPath = joinPath(basePath, route.path ?? \"/\");\n const status = route.status ?? 200;\n\n const requestBody = buildRequestBody(method, source, route.input);\n const requestQuery = buildQueryOrParam(source, route.input, \"query\");\n const requestParams = buildQueryOrParam(source, route.input, \"param\");\n const operationId = makeOperationId(method, fullPath);\n\n registry.registerPath({\n method,\n path: convertExpressPathToOpenApi(fullPath),\n operationId,\n summary: route.summary,\n description: route.description,\n tags: route.tags,\n deprecated: route.deprecated,\n security: route.security,\n // Cast: registerPath types narrow query/params to ZodObject — we accept\n // any ZodTypeAny at runtime and let users pass plain objects via z.object.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n request: {\n ...(requestQuery ? { query: requestQuery } : {}),\n ...(requestParams ? { params: requestParams } : {}),\n ...(requestBody ? { body: requestBody } : {}),\n } as any,\n responses: route.output\n ? {\n [status]: {\n description: DEFAULT_RESPONSE_DESCRIPTION,\n content: {\n \"application/json\": { schema: route.output },\n },\n },\n }\n : {\n [status]: { description: DEFAULT_RESPONSE_DESCRIPTION },\n },\n });\n }\n\n const generator = new OpenApiGeneratorV31(registry.definitions);\n const document = generator.generateDocument({\n openapi: \"3.1.0\",\n // OpenAPIInfo is structurally compatible; cast to satisfy the `x-*` index.\n info: config.info as Parameters<\n typeof generator.generateDocument\n >[0][\"info\"],\n servers: config.servers,\n security: config.security,\n });\n return document as unknown as Record<string, unknown>;\n}\n\nfunction buildRequestBody(\n method: HttpMethod,\n source: PayloadSource,\n schema: z.ZodTypeAny | undefined,\n): { content: Record<string, { schema: z.ZodTypeAny }> } | null {\n if (!schema) return null;\n if (method === \"get\") return null;\n if (source === \"json\") {\n return { content: { \"application/json\": { schema } } };\n }\n if (source === \"form\") {\n return {\n content: { \"application/x-www-form-urlencoded\": { schema } },\n };\n }\n return null;\n}\n\nfunction buildQueryOrParam(\n source: PayloadSource,\n schema: z.ZodTypeAny | undefined,\n target: \"query\" | \"param\",\n): z.ZodTypeAny | undefined {\n if (!schema) return undefined;\n if (target === \"query\" && source === \"query\") return schema;\n if (target === \"param\" && source === \"param\") return schema;\n return undefined;\n}\n\n/** Convert `:foo` style express params to `{foo}` OpenAPI placeholders. */\nfunction convertExpressPathToOpenApi(path: string): string {\n return path.replace(/:([A-Za-z0-9_]+)/g, \"{$1}\");\n}\n\nfunction joinPath(base: string, path: string): string {\n const left = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const right = path.startsWith(\"/\") ? path : `/${path}`;\n const merged = `${left}${right}`;\n return merged === \"\" ? \"/\" : merged;\n}\n\nfunction makeOperationId(method: HttpMethod, path: string): string {\n const cleaned = path\n .replace(/[{}]/g, \"\")\n .replace(/\\/+/g, \"_\")\n .replace(/[^A-Za-z0-9_]/g, \"\")\n .replace(/^_+|_+$/g, \"\");\n return `${method}_${cleaned || \"root\"}`;\n}\n\n/**\n * Render a self-contained Scalar API Reference HTML page that points to the\n * generated spec. Loaded from CDN — no build step required.\n */\nexport function renderDocsHtml(specUrl: string, title: string): string {\n const safeUrl = specUrl.replace(/\"/g, \"&quot;\");\n const safeTitle = title\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<title>${safeTitle}</title>\n</head>\n<body>\n<script id=\"api-reference\" data-url=\"${safeUrl}\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"></script>\n</body>\n</html>`;\n}\n","/**\n * `HonoServer` — high-performance, fully-typed file-based API server for\n * Firebase Cloud Functions v2 (`onRequest`).\n *\n * Designed to:\n * - rely on **prebuild codegen** (`hono:gen` CLI) for static imports → zero\n * runtime filesystem scan, optimal cold-start;\n * - expose handlers receiving a Zod-parsed payload typed end-to-end;\n * - generate the OpenAPI 3.1 spec automatically from the same Zod schemas;\n * - bridge Hono's Web Fetch API to Cloud Functions' Express-style\n * `(req, res)` via `@hono/node-server`'s request listener.\n */\n\nimport { Hono } from \"hono\";\nimport { getRequestListener } from \"@hono/node-server\";\nimport { z, ZodError } from \"zod\";\nimport type { Env } from \"hono\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nimport type {\n AnyRouteDef,\n HonoServerOptions,\n HttpMethod,\n PayloadSource,\n RouteInterceptor,\n} from \"./types\";\nimport { ValidationError } from \"./types\";\nimport { buildOpenApiDocument, renderDocsHtml } from \"./openapi\";\n\n/**\n * Minimal shape of `firebase-functions/v2/https` `onRequest` so the package\n * stays decoupled from a specific firebase-functions version. We import the\n * real type only when users pass `onRequest` to `toFunction(...)`.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype OnRequestFn = (...args: any[]) => any;\n\nexport class HonoServer<TEnv extends Env = Env> {\n private readonly app: Hono<TEnv>;\n private readonly options: HonoServerOptions<TEnv>;\n private readonly mountedRoutes: AnyRouteDef[];\n private cachedSpec: Record<string, unknown> | null = null;\n\n constructor(options: HonoServerOptions<TEnv>) {\n this.options = options;\n this.app = new Hono<TEnv>();\n this.mountedRoutes = filterRoutes(options.routes, options.api);\n\n const globalMws = [\n ...(options.middlewares ?? []),\n ...(options.globalMiddlewares ?? []),\n ];\n for (const mw of globalMws) this.app.use(\"*\", mw);\n\n this.mountRoutes();\n this.mountOpenApi();\n\n if (options.notFound) this.app.notFound(options.notFound);\n if (options.onError) this.app.onError(options.onError);\n }\n\n /** Underlying Hono instance — useful for advanced composition / tests. */\n get hono(): Hono<TEnv> {\n return this.app;\n }\n\n /** Raw `(req, res)` handler suitable for `onRequest()` / `http.createServer`. */\n get nodeHandler(): (req: IncomingMessage, res: ServerResponse) => void {\n return getRequestListener(this.app.fetch, {\n overrideGlobalObjects: false,\n });\n }\n\n /**\n * Wrap the server as a Cloud Functions v2 HTTP function.\n *\n * @param onRequest The `onRequest` factory imported from\n * `firebase-functions/v2/https` (or `firebase-functions/https`).\n * @param httpsOptions Options forwarded as the first argument to\n * `onRequest()` (region, memory, invoker, etc.).\n */\n toFunction(onRequest: OnRequestFn, httpsOptions?: Record<string, unknown>) {\n const handler = this.nodeHandler;\n if (httpsOptions) {\n return onRequest(httpsOptions, handler);\n }\n return onRequest(handler);\n }\n\n /** Generate (and cache) the OpenAPI 3.1 spec for the mounted routes. */\n buildOpenApiSpec(): Record<string, unknown> {\n if (this.cachedSpec) return this.cachedSpec;\n if (!this.options.openapi) {\n throw new Error(\"[HonoServer] openapi config not set\");\n }\n this.cachedSpec = buildOpenApiDocument(\n this.mountedRoutes,\n this.options.basePath ?? \"\",\n this.options.openapi,\n );\n return this.cachedSpec;\n }\n\n // ── Internals ─────────────────────────────────────────────────────────\n\n private mountRoutes(): void {\n const basePath = this.options.basePath ?? \"\";\n const validateOutput = this.options.validateOutput ?? false;\n const verbose = this.options.verbose ?? false;\n\n for (const route of this.mountedRoutes) {\n if (!route.path) {\n throw new Error(\n `[HonoServer] route \"${route.method.toUpperCase()} (no path)\" — missing \\`path\\`. ` +\n \"Run the codegen so the path is derived from the file location, or set it explicitly.\",\n );\n }\n\n const fullPath = joinPath(basePath, route.path);\n const middlewares = route.middlewares ?? [];\n const source: PayloadSource =\n route.source ?? (route.method === \"get\" ? \"query\" : \"json\");\n\n const handler = makeRouteHandler(\n route,\n source,\n validateOutput,\n this.options.interceptor as RouteInterceptor | undefined,\n );\n const httpMethod = route.method.toUpperCase() as\n | \"GET\"\n | \"POST\"\n | \"PUT\"\n | \"PATCH\"\n | \"DELETE\";\n // `app.on(method, path, handlers[])` accepts a variadic array of\n // handlers/middlewares — the typed `.get/.post/...` overloads don't\n // accept a spread of generic `MiddlewareHandler[]`.\n this.app.on(\n httpMethod,\n [fullPath],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...([...middlewares, handler] as any[]),\n );\n\n if (verbose) {\n // eslint-disable-next-line no-console\n console.log(\n `[HonoServer] ${route.method.toUpperCase().padEnd(6)} ${fullPath}`,\n );\n }\n }\n }\n\n private mountOpenApi(): void {\n const cfg = this.options.openapi;\n if (!cfg) return;\n const specPath = cfg.path ?? \"/openapi.json\";\n const docsPath = cfg.docsPath === undefined ? \"/docs\" : cfg.docsPath;\n const fullSpecPath = joinPath(this.options.basePath ?? \"\", specPath);\n const fullDocsPath =\n docsPath === false ? null : joinPath(this.options.basePath ?? \"\", docsPath);\n\n this.app.get(fullSpecPath, (c) => c.json(this.buildOpenApiSpec()));\n\n if (fullDocsPath) {\n // Resolve the spec URL relative to the docs page so it works whether the\n // server is mounted at `/`, behind a Firebase Functions prefix\n // (`/<project>/<region>/<funcName>/...`), or behind any reverse proxy.\n const relativeSpecUrl = relativeUrlFromTo(fullDocsPath, fullSpecPath);\n this.app.get(fullDocsPath, (c) =>\n c.html(renderDocsHtml(relativeSpecUrl, cfg.info.title)),\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction filterRoutes(\n routes: AnyRouteDef[],\n api: string | undefined,\n): AnyRouteDef[] {\n if (!api) return routes.slice();\n return routes.filter((r) => r.api === api);\n}\n\nfunction joinPath(base: string, path: string): string {\n const left = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const right = path.startsWith(\"/\") ? path : `/${path}`;\n const merged = `${left}${right}`;\n return merged === \"\" ? \"/\" : merged;\n}\n\n/**\n * Compute a URL relative to `from` that points to `to`, both being absolute\n * pathnames (e.g. `/v1/docs` → `/v1/openapi.json` becomes `openapi.json`).\n * Lets the OpenAPI UI fetch the spec without knowing the upstream prefix\n * added by Firebase Functions / reverse proxies.\n */\nfunction relativeUrlFromTo(from: string, to: string): string {\n const fromSegs = from.split(\"/\").filter(Boolean);\n const toSegs = to.split(\"/\").filter(Boolean);\n // Drop the docs page filename so we resolve relative to its directory.\n fromSegs.pop();\n let common = 0;\n while (\n common < fromSegs.length &&\n common < toSegs.length &&\n fromSegs[common] === toSegs[common]\n ) {\n common++;\n }\n const ups = fromSegs.length - common;\n const rel = [\n ...Array(ups).fill(\"..\"),\n ...toSegs.slice(common),\n ].join(\"/\");\n return rel || \"./\";\n}\n\n/**\n * Build the actual Hono handler with input validation, output validation\n * (optional), and error normalisation.\n */\nfunction makeRouteHandler(\n route: AnyRouteDef,\n source: PayloadSource,\n validateOutput: boolean,\n interceptor: RouteInterceptor | undefined,\n) {\n const inputSchema = route.input as z.ZodTypeAny | undefined;\n const outputSchema = route.output as z.ZodTypeAny | undefined;\n const status = route.status ?? 200;\n\n return async (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any,\n ): Promise<Response> => {\n // `next()` runs validation + handler. Any Zod failure throws\n // `ValidationError` so the interceptor (or default catcher) can shape it.\n const callNext = async (): Promise<unknown> => {\n let payload: unknown = undefined;\n\n if (inputSchema) {\n let raw: unknown;\n try {\n raw = await readPayload(c, source, route.method);\n } catch (err) {\n // Body parse failure → wrap as a generic Error so the interceptor\n // can decide. Use a 400-shaped Error subclass.\n throw new BadRequestError(\n err instanceof Error ? err.message : String(err),\n );\n }\n const parsed = inputSchema.safeParse(raw);\n if (!parsed.success) {\n throw new ValidationError(parsed.error, source);\n }\n payload = parsed.data;\n }\n\n const result = await (route.handler as (ctx: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n input: any;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any;\n }) => unknown)({ input: payload, c });\n\n if (validateOutput && outputSchema && !(result instanceof Response)) {\n const checked = outputSchema.safeParse(result);\n if (!checked.success) {\n throw new OutputValidationError(checked.error);\n }\n return checked.data;\n }\n return result;\n };\n\n let result: unknown;\n if (interceptor) {\n // Interceptor owns the response shape — including validation errors.\n result = await interceptor({ next: callNext, route, c });\n } else {\n // Default behaviour — handles ValidationError / BadRequestError with\n // a JSON envelope, lets unknown errors bubble to onError / Hono.\n try {\n result = await callNext();\n } catch (err) {\n const handled = defaultErrorResponse(c, err);\n if (handled) return handled;\n throw err;\n }\n }\n\n if (result instanceof Response) return result;\n return c.json(result, status);\n };\n}\n\nclass BadRequestError extends Error {\n readonly statusCode = 400 as const;\n constructor(message: string) {\n super(message);\n this.name = \"BadRequestError\";\n }\n}\n\nclass OutputValidationError extends Error {\n readonly statusCode = 500 as const;\n constructor(readonly zodError: ZodError) {\n super(\"Output validation failed\");\n this.name = \"OutputValidationError\";\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction defaultErrorResponse(c: any, err: unknown): Response | null {\n if (err instanceof ValidationError) {\n return c.json(\n {\n success: false,\n error: \"Validation failed\",\n issues: formatZodIssues(err.zodError),\n },\n 400,\n );\n }\n if (err instanceof BadRequestError) {\n return c.json(\n { success: false, error: \"Bad Request\", message: err.message },\n 400,\n );\n }\n if (err instanceof OutputValidationError) {\n return c.json(\n {\n success: false,\n error: \"Output validation failed\",\n issues: formatZodIssues(err.zodError),\n },\n 500,\n );\n }\n return null;\n}\n\nasync function readPayload(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any,\n source: PayloadSource,\n method: HttpMethod,\n): Promise<unknown> {\n switch (source) {\n case \"json\": {\n if (method === \"get\") return c.req.query();\n const text = await c.req.text();\n if (!text) return {};\n try {\n return JSON.parse(text);\n } catch (err) {\n throw new Error(\n `Invalid JSON body: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n case \"query\":\n return c.req.query();\n case \"form\": {\n const form = await c.req.parseBody();\n return form;\n }\n case \"param\":\n return c.req.param();\n default:\n return {};\n }\n}\n\nfunction formatZodIssues(error: ZodError): unknown {\n return error.issues.map((i) => ({\n path: i.path.join(\".\"),\n code: i.code,\n message: i.message,\n }));\n}\n","/**\n * Typed multi-API registry.\n *\n * Lets you declare every API tag (= every Cloud Function) in **one place**,\n * with full TypeScript safety: the `api` field of {@link defineRoute} is\n * narrowed to the registered tags, and {@link toFunctions} returns one\n * `onRequest` Cloud Function per tag, named after its key.\n *\n * @example\n * ```ts\n * // apis.ts\n * import { createApiRegistry } from \"@lpdjs/firestore-repo-service/servers/hono\";\n * import { enrichUser } from \"./middlewares/enrich-user.js\";\n *\n * export const apis = createApiRegistry({\n * v1: {\n * basePath: \"/v1\",\n * middlewares: [enrichUser],\n * openapi: { info: { title: \"Public API\", version: \"1.0.0\" } },\n * },\n * webhooks: {\n * basePath: \"/hooks\",\n * openapi: { info: { title: \"Webhooks\", version: \"1.0.0\" } },\n * },\n * });\n *\n * // Use in routes — `api` is now typed \"v1\" | \"webhooks\".\n * export const defineRoute = apis.defineRoute;\n *\n * // index.ts (Cloud Functions entrypoint)\n * import { onRequest } from \"firebase-functions/v2/https\";\n * import { apis } from \"./apis.js\";\n * import { routes } from \"./domains/__generated__/routes.js\";\n *\n * export const { v1, webhooks } = apis.toFunctions(routes, onRequest, {\n * defaults: { region: \"us-central1\", invoker: \"public\" },\n * per: { v1: { memory: \"512MiB\" } },\n * });\n * // → URLs: https://<region>-<project>.cloudfunctions.net/v1/posts\n * // https://<region>-<project>.cloudfunctions.net/webhooks/...\n * ```\n */\n\nimport type { Env } from \"hono\";\nimport type { z } from \"zod\";\n\nimport type {\n AnyRouteDef,\n HonoServerOptions,\n RouteDef,\n} from \"./types\";\nimport { HonoServer } from \"./server\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype OnRequestFn = (...args: any[]) => any;\n\n/**\n * Per-API configuration. Same shape as {@link HonoServerOptions} minus the\n * `routes` (resolved by the registry) and `api` (the registry key).\n */\nexport type ApiConfig<TEnv extends Env = Env> = Omit<\n HonoServerOptions<TEnv>,\n \"routes\" | \"api\"\n>;\n\n/** Map of API tag → its config. */\nexport type ApiConfigMap = Record<string, ApiConfig>;\n\nexport interface ApiRegistry<TMap extends ApiConfigMap> {\n /** The registered configs (read-only). */\n readonly configs: TMap;\n\n /**\n * Typed `defineRoute` — the `api` field is constrained to `keyof TMap`.\n */\n defineRoute<\n TIn extends z.ZodTypeAny | undefined = undefined,\n TOut extends z.ZodTypeAny | undefined = undefined,\n >(\n def: Omit<RouteDef<TIn, TOut>, \"api\"> & { api: keyof TMap & string },\n ): RouteDef<TIn, TOut> & { api: keyof TMap & string };\n\n /**\n * Build one Cloud Function per registered API and return them as a map\n * keyed by API tag — spread it directly into your `index.ts` exports.\n *\n * @param routes Pre-resolved route registry (typically the codegen output).\n * @param onRequest The `onRequest` factory imported from\n * `firebase-functions/v2/https`.\n * @param opts Optional defaults and per-API overrides for `httpsOptions`.\n */\n toFunctions(\n routes: AnyRouteDef[],\n onRequest: OnRequestFn,\n opts?: {\n defaults?: Record<string, unknown>;\n per?: Partial<Record<keyof TMap & string, Record<string, unknown>>>;\n },\n ): { [K in keyof TMap & string]: ReturnType<OnRequestFn> };\n\n /** Build the underlying {@link HonoServer} for a given API (escape hatch). */\n serverFor<K extends keyof TMap & string>(\n api: K,\n routes: AnyRouteDef[],\n ): HonoServer;\n}\n\n/**\n * Factory — declare every API tag once and get back a typed `defineRoute`\n * + `toFunctions`. See the file-level example.\n */\nexport function createApiRegistry<const TMap extends ApiConfigMap>(\n configs: TMap,\n): ApiRegistry<TMap> {\n return {\n configs,\n\n defineRoute(def) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return def as any;\n },\n\n serverFor(api, routes) {\n const cfg = configs[api];\n if (!cfg) {\n throw new Error(\n `[ApiRegistry] unknown api \"${api}\". Registered: ${Object.keys(configs).join(\", \")}`,\n );\n }\n return new HonoServer({ ...cfg, api, routes });\n },\n\n toFunctions(routes, onRequest, opts) {\n const out = {} as { [K in keyof TMap & string]: ReturnType<OnRequestFn> };\n for (const api of Object.keys(configs) as Array<keyof TMap & string>) {\n const httpsOpts = {\n ...(opts?.defaults ?? {}),\n ...(opts?.per?.[api] ?? {}),\n };\n const server = new HonoServer({ ...configs[api], api, routes });\n out[api] = Object.keys(httpsOpts).length\n ? server.toFunction(onRequest, httpsOpts)\n : server.toFunction(onRequest);\n }\n return out;\n },\n };\n}\n","/**\n * URL path inference from filesystem layout.\n *\n * Convention: every `routes.ts` file under the configured root contributes\n * one route. The URL path is derived from its directory chain, optionally\n * skipping segments such as `useCases` so that\n *\n * domains/activities/useCases/createOrUpdateCustom/routes.ts\n *\n * becomes\n *\n * /activities/createOrUpdateCustom\n */\n\nexport interface PathDeriveOptions {\n /** Segments to drop from the derived path (case-insensitive). */\n skipSegments: string[];\n /**\n * Casing convention applied to each remaining segment.\n * - `\"preserve\"` — keep the directory name as-is (default),\n * - `\"kebab\"` — convert camelCase / PascalCase to kebab-case.\n */\n casing: \"preserve\" | \"kebab\";\n}\n\nexport const DEFAULT_DERIVE: PathDeriveOptions = {\n skipSegments: [\"useCases\", \"useCase\", \"use-cases\", \"use-case\"],\n casing: \"preserve\",\n};\n\n/**\n * @param relativeDir POSIX-style directory path of the routes file relative\n * to the codegen root (no leading slash, no `routes.ts`).\n */\nexport function derivePath(\n relativeDir: string,\n options: PathDeriveOptions = DEFAULT_DERIVE,\n): string {\n const skip = new Set(options.skipSegments.map((s) => s.toLowerCase()));\n const parts = relativeDir\n .split(\"/\")\n .filter(Boolean)\n .filter((p) => !skip.has(p.toLowerCase()))\n .map((p) => (options.casing === \"kebab\" ? kebab(p) : p));\n return \"/\" + parts.join(\"/\");\n}\n\nfunction kebab(s: string): string {\n return s\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n}\n\n/**\n * Convert an absolute filesystem path to a POSIX-style import specifier\n * relative to a directory (the generated file's directory). Always uses\n * forward slashes and prefixes with `./` or `../` as needed.\n */\nexport function toImportSpecifier(\n fromDir: string,\n toFile: string,\n ext: string,\n): string {\n // Both paths are absolute POSIX (the CLI normalises them).\n const fromParts = splitAbs(fromDir);\n const toParts = splitAbs(toFile);\n let common = 0;\n while (\n common < fromParts.length &&\n common < toParts.length &&\n fromParts[common] === toParts[common]\n ) {\n common++;\n }\n const up = fromParts.length - common;\n const down = toParts.slice(common);\n const last = down[down.length - 1] ?? \"\";\n const stripped = last.replace(/\\.[mc]?[tj]sx?$/i, \"\");\n const finalLast = ext === \"\" ? stripped : `${stripped}${ext}`;\n down[down.length - 1] = finalLast;\n const prefix = up === 0 ? \"./\" : \"../\".repeat(up);\n return prefix + down.join(\"/\");\n}\n\nfunction splitAbs(p: string): string[] {\n const norm = p.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n return norm.split(\"/\").filter((part, i) => !(i === 0 && part === \"\"));\n}\n","/**\n * Filesystem scanner — walks the configured root and yields every route file.\n * Synchronous and dependency-free (no `fast-glob` etc.) for a tiny CLI footprint.\n */\n\nimport { readdirSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\n\nexport interface ScannerOptions {\n /** Filename to look for (default: `routes.ts`). */\n routesFile: string;\n /** Glob-like exclude segments (matched against any path part). */\n excludeSegments: string[];\n}\n\nexport const DEFAULT_SCANNER: ScannerOptions = {\n routesFile: \"routes.ts\",\n excludeSegments: [\n \"node_modules\",\n \"__generated__\",\n \"tests\",\n \"__tests__\",\n \".turbo\",\n \"dist\",\n \"build\",\n \".next\",\n ],\n};\n\nexport interface ScannedRoute {\n /** Absolute path to the routes file. */\n absPath: string;\n /** Path relative to the scan root (POSIX style). */\n relPath: string;\n /** Directory portion of `relPath` (what {@link derivePath} consumes). */\n relDir: string;\n}\n\nexport function scanRoutes(\n rootAbs: string,\n options: ScannerOptions = DEFAULT_SCANNER,\n): ScannedRoute[] {\n const found: ScannedRoute[] = [];\n walk(rootAbs, rootAbs, options, found);\n // Stable, deterministic order — important for reproducible builds.\n found.sort((a, b) => a.relPath.localeCompare(b.relPath));\n return found;\n}\n\nfunction walk(\n root: string,\n dir: string,\n opts: ScannerOptions,\n out: ScannedRoute[],\n): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n if (opts.excludeSegments.includes(name)) continue;\n const abs = join(dir, name);\n let st;\n try {\n st = statSync(abs);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n walk(root, abs, opts, out);\n } else if (st.isFile() && name === opts.routesFile) {\n const relPath = relative(root, abs).split(sep).join(\"/\");\n const relDir = relPath.replace(/\\/?[^/]+$/, \"\");\n out.push({ absPath: abs, relPath, relDir });\n }\n }\n}\n","/**\n * Generator — emits `__generated__/routes.ts` from a list of {@link ScannedRoute}s.\n *\n * The generated module statically imports every `routes.ts` so that bundlers\n * (esbuild/tsup) can tree-shake unused dependencies and Cloud Functions get\n * the smallest possible cold-start footprint.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\n\nimport {\n derivePath,\n toImportSpecifier,\n type PathDeriveOptions,\n} from \"./path-utils\";\nimport type { ScannedRoute } from \"./scanner\";\n\nexport interface GeneratorOptions {\n /** Absolute path of the file to write (e.g. `…/__generated__/routes.ts`). */\n outFile: string;\n /** Path-derivation options (skipSegments, casing). */\n derive: PathDeriveOptions;\n /**\n * Import extension used in the generated file:\n * - `\".js\"` (default) — required for ESM Node.js with `\"type\": \"module\"`,\n * - `\"\"` — bundler-friendly (Vite, no extension),\n * - `\".ts\"` — for TS-ESM runners (`tsx`, `bun`).\n */\n importExtension: string;\n /** Friendly banner placed at the top of the generated file. */\n banner?: string;\n /** Generation timestamp included in the banner. Default: `new Date()`. */\n now?: Date;\n}\n\nexport const DEFAULT_GENERATOR_BANNER =\n \"/**\\n\" +\n \" * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\\n\" +\n \" * Do not edit by hand — re-run `hono:gen` after adding / removing route files.\\n\" +\n \" */\\n\";\n\nexport interface GenerationResult {\n /** Absolute path of the file written. */\n outFile: string;\n /** Number of routes captured in the manifest. */\n routeCount: number;\n /** Human-readable summary of the derived URLs. */\n derivedPaths: { source: string; url: string }[];\n}\n\nexport function generateRoutesManifest(\n routes: ScannedRoute[],\n opts: GeneratorOptions,\n): GenerationResult {\n const outDir = dirname(opts.outFile);\n mkdirSync(outDir, { recursive: true });\n\n const banner = opts.banner ?? DEFAULT_GENERATOR_BANNER;\n const now = (opts.now ?? new Date()).toISOString();\n const ext = opts.importExtension;\n\n const importLines: string[] = [];\n const entryLines: string[] = [];\n const derivedPaths: GenerationResult[\"derivedPaths\"] = [];\n\n routes.forEach((r, i) => {\n const importPath = toImportSpecifier(outDir, r.absPath, ext);\n const url = derivePath(r.relDir, opts.derive);\n importLines.push(\n `import mod${i} from ${JSON.stringify(importPath)};`,\n );\n entryLines.push(` { __derivedPath: ${JSON.stringify(url)}, mod: mod${i} },`);\n derivedPaths.push({ source: r.relPath, url });\n });\n\n const body =\n `${banner}` +\n `// Generated at ${now} — ${routes.length} route file${routes.length === 1 ? \"\" : \"s\"}.\\n` +\n `\\n` +\n `import type { AnyRouteDef, RouteModuleDefault } from \"@lpdjs/firestore-repo-service/servers/hono\";\\n` +\n `\\n` +\n importLines.join(\"\\n\") +\n (importLines.length ? \"\\n\\n\" : \"\\n\") +\n `const __defs: { __derivedPath: string; mod: RouteModuleDefault }[] = [\\n` +\n entryLines.join(\"\\n\") +\n (entryLines.length ? \"\\n\" : \"\") +\n `];\\n\\n` +\n `export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) => {\\n` +\n ` const list = Array.isArray(mod) ? mod : [mod];\\n` +\n ` return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));\\n` +\n `});\\n`;\n\n writeFileSync(opts.outFile, body, \"utf8\");\n return {\n outFile: opts.outFile,\n routeCount: routes.length,\n derivedPaths,\n };\n}\n\n/** Convenience helper used by the CLI — combines scan + generate in one call. */\nexport function generateFromRoot(\n rootAbs: string,\n outFileRel: string,\n derive: PathDeriveOptions,\n importExtension: string,\n scan: (root: string) => ScannedRoute[],\n): GenerationResult {\n const routes = scan(rootAbs);\n const outFile = join(rootAbs, outFileRel);\n return generateRoutesManifest(routes, {\n outFile,\n derive,\n importExtension,\n });\n}\n"]}
1
+ {"version":3,"sources":["../../../src/servers/hono/types.ts","../../../src/servers/hono/openapi.ts","../../../src/servers/hono/server.ts","../../../src/servers/hono/api-registry.ts","../../../src/servers/hono/codegen/path-utils.ts","../../../src/servers/hono/codegen/scanner.ts","../../../src/servers/hono/codegen/generator.ts"],"names":["ValidationError","zodError","source","extendZodWithOpenApi","z","DEFAULT_RESPONSE_DESCRIPTION","defaultSource","method","buildOpenApiDocument","routes","basePath","config","registry","OpenAPIRegistry","name","scheme","route","fullPath","joinPath","status","requestBody","buildRequestBody","requestQuery","buildQueryOrParam","requestParams","operationId","makeOperationId","convertExpressPathToOpenApi","OpenApiGeneratorV31","schema","target","path","base","left","right","merged","cleaned","renderDocsHtml","specUrl","title","safeUrl","HonoServer","options","Hono","filterRoutes","globalMws","mw","getRequestListener","onRequest","httpsOptions","handler","validateOutput","verbose","middlewares","makeRouteHandler","httpMethod","cfg","specPath","docsPath","fullSpecPath","fullDocsPath","c","relativeSpecUrl","relativeUrlFromTo","api","r","from","to","fromSegs","toSegs","common","ups","interceptor","inputSchema","outputSchema","callNext","payload","raw","readPayload","err","BadRequestError","parsed","result","checked","OutputValidationError","handled","defaultErrorResponse","message","formatZodIssues","text","error","i","createApiRegistry","configs","def","opts","out","httpsOpts","server","DEFAULT_DERIVE","derivePath","relativeDir","skip","s","p","kebab","toImportSpecifier","fromDir","toFile","ext","fromParts","splitAbs","toParts","up","down","stripped","finalLast","part","DEFAULT_SCANNER","scanRoutes","rootAbs","found","walk","a","b","root","dir","entries","readdirSync","abs","join","st","statSync","relPath","relative","sep","relDir","DEFAULT_GENERATOR_BANNER","generateRoutesManifest","outDir","dirname","mkdirSync","banner","now","importLines","entryLines","derivedPaths","importPath","url","body","writeFileSync","generateFromRoot","outFileRel","derive","importExtension","scan","outFile"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Public types for the Hono file-based API server.\n *\n * Designed to be:\n * - **fully typed** end-to-end (Zod input/output → handler payload type),\n * - **runtime-safe** via Zod parsing on every request,\n * - **bundle-friendly** for Firebase Cloud Functions v2 cold-start (codegen\n * emits static imports — no `fs`/`import()` at runtime).\n */\n\nimport type { z, ZodError } from \"zod\";\nimport type { Context, Env, MiddlewareHandler } from \"hono\";\n\nexport type HttpMethod = \"get\" | \"post\" | \"put\" | \"patch\" | \"delete\";\n\n/** Where the validated payload comes from. */\nexport type PayloadSource = \"json\" | \"query\" | \"form\" | \"param\";\n\n/** Handler signature — receives a single typed context object. */\nexport type RouteHandler<TIn, TOut, TEnv extends Env = Env> = (ctx: {\n /** Validated (and typed) request payload. `void` when no `input` schema is defined. */\n input: TIn;\n /** Raw Hono `Context` for headers, set status, redirect, etc. */\n c: Context<TEnv>;\n}) => Promise<TOut | Response> | TOut | Response;\n\n/**\n * One route declaration. Default-exported by every `routes.ts` file inside the\n * domain tree. Use {@link defineRoute} for full type inference.\n */\nexport interface RouteDef<\n TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined,\n TOut extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined,\n TEnv extends Env = Env,\n> {\n /**\n * Logical API tag — routes sharing the same `api` are mounted on the same\n * `HonoServer` (typically one Cloud Function per `api`).\n *\n * To expose the **same logic** under several APIs with different\n * inputs/outputs, export multiple `defineRoute({...})` from the same file\n * (default + named exports are both picked up by the codegen).\n */\n api: string;\n\n /** HTTP method. */\n method: HttpMethod;\n\n /**\n * URL path appended to the server `basePath`. If omitted, the codegen will\n * derive it from the file location (e.g. `domains/activities/useCases/createCustom/routes.ts`\n * → `/activities/createCustom`).\n */\n path?: string;\n\n /**\n * Where the request payload comes from.\n * Default: `\"json\"` for body methods (POST/PUT/PATCH/DELETE), `\"query\"` for GET.\n */\n source?: PayloadSource;\n\n /** Zod schema validating the payload. Failures yield a 400 response. */\n input?: TIn;\n\n /**\n * Zod schema for the success response. Used to populate the OpenAPI spec\n * and (when `validateOutput` is enabled on the server) to assert the\n * runtime payload returned by the handler.\n */\n output?: TOut;\n\n /** Status code for the success response. Default: 200. */\n status?: number;\n\n /** Hono middlewares applied to this route only (after global middlewares). */\n middlewares?: MiddlewareHandler<TEnv>[];\n\n // ── OpenAPI metadata ─────────────────────────────────────────────────\n summary?: string;\n description?: string;\n tags?: string[];\n /** Mark the operation as deprecated in the generated spec. */\n deprecated?: boolean;\n /** Security requirements (operationId-level override). */\n security?: Array<Record<string, string[]>>;\n\n /** The request handler. */\n handler: RouteHandler<\n TIn extends z.ZodTypeAny ? z.infer<TIn> : void,\n TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown,\n TEnv\n >;\n}\n\n/** Erased `RouteDef` used by registry/codegen — handler signature is opaque. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyRouteDef = RouteDef<any, any>;\n\n/**\n * What `routes.ts` can default-export:\n * - a single {@link RouteDef} (most common case),\n * - or an array of {@link RouteDef} (e.g. expose the same useCase on\n * multiple `api` tags or under multiple paths).\n */\nexport type RouteModuleDefault = AnyRouteDef | AnyRouteDef[];\n\n/**\n * Thrown by the server when the incoming request fails Zod validation.\n * Caught by the {@link RouteInterceptor} (if any) so users can shape the\n * response envelope however they like; otherwise yields a default 400.\n */\nexport class ValidationError extends Error {\n readonly statusCode = 400 as const;\n constructor(\n /** Original Zod error — `error.issues` to enumerate field-level problems. */\n readonly zodError: ZodError,\n /** Where the offending payload came from. */\n readonly source: PayloadSource,\n ) {\n super(\"Request validation failed\");\n this.name = \"ValidationError\";\n }\n}\n\n/**\n * Cross-cutting interceptor applied around every handler.\n * Use it for response envelopes, business-error → HTTP mapping, structured\n * logging, tracing spans, etc.\n *\n * Wrap `next()` in `try/catch` to intercept BOTH Zod {@link ValidationError}s\n * (thrown before the handler runs) AND business errors thrown by the handler.\n *\n * @example\n * ```ts\n * interceptor: async ({ next, route, c }) => {\n * try {\n * const data = await next();\n * return c.json({ success: true, data, error: null });\n * } catch (err) {\n * if (err instanceof ValidationError) {\n * return c.json({ success: false, error: \"validation\", issues: err.zodError.issues }, 400);\n * }\n * if (err instanceof DomainError) {\n * return c.json({ success: false, error: err.code }, err.statusCode);\n * }\n * throw err; // → falls back to onError or Hono's default 500\n * }\n * }\n * ```\n */\nexport type RouteInterceptor<TEnv extends Env = Env> = (ctx: {\n /**\n * Calls validation + handler and returns the raw value.\n * Throws {@link ValidationError} on Zod failure or any error thrown by the handler.\n */\n next: () => Promise<unknown>;\n /** Route metadata (read-only). */\n route: AnyRouteDef;\n /** Hono request context. */\n c: Context<TEnv>;\n}) => Promise<Response | unknown> | Response | unknown;\n\n/** OpenAPI document info (subset of the spec used by the helper). */\nexport interface OpenAPIInfo {\n title: string;\n version: string;\n description?: string;\n}\n\n/** OpenAPI configuration on the server. */\nexport interface OpenAPIConfig {\n /** Path served by the JSON spec (e.g. `/openapi.json`). Default: `/openapi.json`. */\n path?: string;\n /** Path serving the documentation UI. Set to `false` to disable. Default: `/docs`. */\n docsPath?: string | false;\n /** OpenAPI document info. */\n info: OpenAPIInfo;\n /** Optional servers list for the spec. */\n servers?: { url: string; description?: string }[];\n /** Optional security schemes (e.g. bearer auth). */\n securitySchemes?: Record<string, unknown>;\n /** Default security requirement applied to every operation. */\n security?: Array<Record<string, string[]>>;\n}\n\n/** Options consumed by the {@link HonoServer} constructor. */\nexport interface HonoServerOptions<TEnv extends Env = Env> {\n /**\n * API tag — only routes whose `api` matches this value are mounted.\n * If omitted, every route in the registry is mounted.\n */\n api?: string;\n\n /** Pre-resolved route registry (typically the codegen output). */\n routes: AnyRouteDef[];\n\n /** URL prefix mounted before every route path. Default: `\"\"`. */\n basePath?: string;\n\n /** Hono middlewares applied to every route (after the built-ins). */\n middlewares?: MiddlewareHandler<TEnv>[];\n\n /**\n * Alias for `middlewares` — global middlewares applied to every route.\n * If both are provided, `globalMiddlewares` is appended after `middlewares`.\n */\n globalMiddlewares?: MiddlewareHandler<TEnv>[];\n\n /**\n * If `true`, the server validates the value returned by every handler\n * against the route's `output` schema and rejects mismatches with a 500\n * response. Useful in dev / staging. Default: `false`.\n */\n validateOutput?: boolean;\n\n /** Enable verbose logging of mounted routes at startup. Default: `false`. */\n verbose?: boolean;\n\n /** OpenAPI configuration. Omit to disable. */\n openapi?: OpenAPIConfig;\n\n /** Custom 404 handler. */\n notFound?: (c: Context<TEnv>) => Response | Promise<Response>;\n\n /** Custom error handler. */\n onError?: (err: unknown, c: Context<TEnv>) => Response | Promise<Response>;\n\n /**\n * Cross-cutting interceptor wrapping every handler call.\n * Ideal for response envelopes, business-error mapping, tracing.\n * See {@link RouteInterceptor}.\n */\n interceptor?: RouteInterceptor<TEnv>;\n}\n","/**\n * OpenAPI 3.1 spec generator from {@link RouteDef} entries.\n *\n * Uses `@asteasolutions/zod-to-openapi` directly so users keep importing the\n * vanilla `zod` package (no opinionated `z` re-export required).\n */\n\nimport {\n OpenAPIRegistry,\n OpenApiGeneratorV31,\n extendZodWithOpenApi,\n} from \"@asteasolutions/zod-to-openapi\";\nimport { z } from \"zod\";\nimport type {\n AnyRouteDef,\n HttpMethod,\n OpenAPIConfig,\n PayloadSource,\n} from \"./types\";\n\n// Patches Zod prototype with `.openapi()` and enables schema → OpenAPI\n// conversion for vanilla zod schemas. Idempotent — safe to call multiple times.\nextendZodWithOpenApi(z);\n\nconst DEFAULT_RESPONSE_DESCRIPTION = \"Successful response\";\n\nfunction defaultSource(method: HttpMethod): PayloadSource {\n return method === \"get\" ? \"query\" : \"json\";\n}\n\n/** Build the OpenAPI document from the mounted route registry. */\nexport function buildOpenApiDocument(\n routes: AnyRouteDef[],\n basePath: string,\n config: OpenAPIConfig,\n): Record<string, unknown> {\n const registry = new OpenAPIRegistry();\n\n if (config.securitySchemes) {\n for (const [name, scheme] of Object.entries(config.securitySchemes)) {\n // The registry's runtime accepts any spec-shaped object; cast through\n // `unknown` to satisfy zod-to-openapi's stricter typings.\n registry.registerComponent(\n \"securitySchemes\",\n name,\n scheme as unknown as Parameters<\n typeof registry.registerComponent\n >[2],\n );\n }\n }\n\n for (const route of routes) {\n const method = route.method;\n const source = route.source ?? defaultSource(method);\n const fullPath = joinPath(basePath, route.path ?? \"/\");\n const status = route.status ?? 200;\n\n const requestBody = buildRequestBody(method, source, route.input);\n const requestQuery = buildQueryOrParam(source, route.input, \"query\");\n const requestParams = buildQueryOrParam(source, route.input, \"param\");\n const operationId = makeOperationId(method, fullPath);\n\n registry.registerPath({\n method,\n path: convertExpressPathToOpenApi(fullPath),\n operationId,\n summary: route.summary,\n description: route.description,\n tags: route.tags,\n deprecated: route.deprecated,\n security: route.security,\n // Cast: registerPath types narrow query/params to ZodObject — we accept\n // any ZodTypeAny at runtime and let users pass plain objects via z.object.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n request: {\n ...(requestQuery ? { query: requestQuery } : {}),\n ...(requestParams ? { params: requestParams } : {}),\n ...(requestBody ? { body: requestBody } : {}),\n } as any,\n responses: route.output\n ? {\n [status]: {\n description: DEFAULT_RESPONSE_DESCRIPTION,\n content: {\n \"application/json\": { schema: route.output },\n },\n },\n }\n : {\n [status]: { description: DEFAULT_RESPONSE_DESCRIPTION },\n },\n });\n }\n\n const generator = new OpenApiGeneratorV31(registry.definitions);\n const document = generator.generateDocument({\n openapi: \"3.1.0\",\n // OpenAPIInfo is structurally compatible; cast to satisfy the `x-*` index.\n info: config.info as Parameters<\n typeof generator.generateDocument\n >[0][\"info\"],\n servers: config.servers,\n security: config.security,\n });\n return document as unknown as Record<string, unknown>;\n}\n\nfunction buildRequestBody(\n method: HttpMethod,\n source: PayloadSource,\n schema: z.ZodTypeAny | undefined,\n): { content: Record<string, { schema: z.ZodTypeAny }> } | null {\n if (!schema) return null;\n if (method === \"get\") return null;\n if (source === \"json\") {\n return { content: { \"application/json\": { schema } } };\n }\n if (source === \"form\") {\n return {\n content: { \"application/x-www-form-urlencoded\": { schema } },\n };\n }\n return null;\n}\n\nfunction buildQueryOrParam(\n source: PayloadSource,\n schema: z.ZodTypeAny | undefined,\n target: \"query\" | \"param\",\n): z.ZodTypeAny | undefined {\n if (!schema) return undefined;\n if (target === \"query\" && source === \"query\") return schema;\n if (target === \"param\" && source === \"param\") return schema;\n return undefined;\n}\n\n/** Convert `:foo` style express params to `{foo}` OpenAPI placeholders. */\nfunction convertExpressPathToOpenApi(path: string): string {\n return path.replace(/:([A-Za-z0-9_]+)/g, \"{$1}\");\n}\n\nfunction joinPath(base: string, path: string): string {\n const left = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const right = path.startsWith(\"/\") ? path : `/${path}`;\n const merged = `${left}${right}`;\n return merged === \"\" ? \"/\" : merged;\n}\n\nfunction makeOperationId(method: HttpMethod, path: string): string {\n const cleaned = path\n .replace(/[{}]/g, \"\")\n .replace(/\\/+/g, \"_\")\n .replace(/[^A-Za-z0-9_]/g, \"\")\n .replace(/^_+|_+$/g, \"\");\n return `${method}_${cleaned || \"root\"}`;\n}\n\n/**\n * Render a self-contained Scalar API Reference HTML page that points to the\n * generated spec. Loaded from CDN — no build step required.\n */\nexport function renderDocsHtml(specUrl: string, title: string): string {\n const safeUrl = specUrl.replace(/\"/g, \"&quot;\");\n const safeTitle = title\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<title>${safeTitle}</title>\n</head>\n<body>\n<script id=\"api-reference\" data-url=\"${safeUrl}\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"></script>\n</body>\n</html>`;\n}\n","/**\n * `HonoServer` — high-performance, fully-typed file-based API server for\n * Firebase Cloud Functions v2 (`onRequest`).\n *\n * Designed to:\n * - rely on **prebuild codegen** (`hono:gen` CLI) for static imports → zero\n * runtime filesystem scan, optimal cold-start;\n * - expose handlers receiving a Zod-parsed payload typed end-to-end;\n * - generate the OpenAPI 3.1 spec automatically from the same Zod schemas;\n * - bridge Hono's Web Fetch API to Cloud Functions' Express-style\n * `(req, res)` via `@hono/node-server`'s request listener.\n */\n\nimport { Hono } from \"hono\";\nimport { getRequestListener } from \"@hono/node-server\";\nimport { z, ZodError } from \"zod\";\nimport type { Env } from \"hono\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nimport type {\n AnyRouteDef,\n HonoServerOptions,\n HttpMethod,\n PayloadSource,\n RouteInterceptor,\n} from \"./types\";\nimport { ValidationError } from \"./types\";\nimport { buildOpenApiDocument, renderDocsHtml } from \"./openapi\";\n\n/**\n * Minimal shape of `firebase-functions/v2/https` `onRequest` so the package\n * stays decoupled from a specific firebase-functions version. We import the\n * real type only when users pass `onRequest` to `toFunction(...)`.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype OnRequestFn = (...args: any[]) => any;\n\nexport class HonoServer<TEnv extends Env = Env> {\n private readonly app: Hono<TEnv>;\n private readonly options: HonoServerOptions<TEnv>;\n private readonly mountedRoutes: AnyRouteDef[];\n private cachedSpec: Record<string, unknown> | null = null;\n\n constructor(options: HonoServerOptions<TEnv>) {\n this.options = options;\n this.app = new Hono<TEnv>();\n this.mountedRoutes = filterRoutes(options.routes, options.api);\n\n const globalMws = [\n ...(options.middlewares ?? []),\n ...(options.globalMiddlewares ?? []),\n ];\n for (const mw of globalMws) this.app.use(\"*\", mw);\n\n this.mountRoutes();\n this.mountOpenApi();\n\n if (options.notFound) this.app.notFound(options.notFound);\n if (options.onError) this.app.onError(options.onError);\n }\n\n /** Underlying Hono instance — useful for advanced composition / tests. */\n get hono(): Hono<TEnv> {\n return this.app;\n }\n\n /** Raw `(req, res)` handler suitable for `onRequest()` / `http.createServer`. */\n get nodeHandler(): (req: IncomingMessage, res: ServerResponse) => void {\n return getRequestListener(this.app.fetch, {\n overrideGlobalObjects: false,\n });\n }\n\n /**\n * Wrap the server as a Cloud Functions v2 HTTP function.\n *\n * @param onRequest The `onRequest` factory imported from\n * `firebase-functions/v2/https` (or `firebase-functions/https`).\n * @param httpsOptions Options forwarded as the first argument to\n * `onRequest()` (region, memory, invoker, etc.).\n */\n toFunction(onRequest: OnRequestFn, httpsOptions?: Record<string, unknown>) {\n const handler = this.nodeHandler;\n if (httpsOptions) {\n return onRequest(httpsOptions, handler);\n }\n return onRequest(handler);\n }\n\n /** Generate (and cache) the OpenAPI 3.1 spec for the mounted routes. */\n buildOpenApiSpec(): Record<string, unknown> {\n if (this.cachedSpec) return this.cachedSpec;\n if (!this.options.openapi) {\n throw new Error(\"[HonoServer] openapi config not set\");\n }\n this.cachedSpec = buildOpenApiDocument(\n this.mountedRoutes,\n this.options.basePath ?? \"\",\n this.options.openapi,\n );\n return this.cachedSpec;\n }\n\n // ── Internals ─────────────────────────────────────────────────────────\n\n private mountRoutes(): void {\n const basePath = this.options.basePath ?? \"\";\n const validateOutput = this.options.validateOutput ?? false;\n const verbose = this.options.verbose ?? false;\n\n for (const route of this.mountedRoutes) {\n if (!route.path) {\n throw new Error(\n `[HonoServer] route \"${route.method.toUpperCase()} (no path)\" — missing \\`path\\`. ` +\n \"Run the codegen so the path is derived from the file location, or set it explicitly.\",\n );\n }\n\n const fullPath = joinPath(basePath, route.path);\n const middlewares = route.middlewares ?? [];\n const source: PayloadSource =\n route.source ?? (route.method === \"get\" ? \"query\" : \"json\");\n\n const handler = makeRouteHandler(\n route,\n source,\n validateOutput,\n this.options.interceptor as RouteInterceptor | undefined,\n );\n const httpMethod = route.method.toUpperCase() as\n | \"GET\"\n | \"POST\"\n | \"PUT\"\n | \"PATCH\"\n | \"DELETE\";\n // `app.on(method, path, handlers[])` accepts a variadic array of\n // handlers/middlewares — the typed `.get/.post/...` overloads don't\n // accept a spread of generic `MiddlewareHandler[]`.\n this.app.on(\n httpMethod,\n [fullPath],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...([...middlewares, handler] as any[]),\n );\n\n if (verbose) {\n // eslint-disable-next-line no-console\n console.log(\n `[HonoServer] ${route.method.toUpperCase().padEnd(6)} ${fullPath}`,\n );\n }\n }\n }\n\n private mountOpenApi(): void {\n const cfg = this.options.openapi;\n if (!cfg) return;\n const specPath = cfg.path ?? \"/openapi.json\";\n const docsPath = cfg.docsPath === undefined ? \"/docs\" : cfg.docsPath;\n const fullSpecPath = joinPath(this.options.basePath ?? \"\", specPath);\n const fullDocsPath =\n docsPath === false ? null : joinPath(this.options.basePath ?? \"\", docsPath);\n\n this.app.get(fullSpecPath, (c) => c.json(this.buildOpenApiSpec()));\n\n if (fullDocsPath) {\n // Resolve the spec URL relative to the docs page so it works whether the\n // server is mounted at `/`, behind a Firebase Functions prefix\n // (`/<project>/<region>/<funcName>/...`), or behind any reverse proxy.\n const relativeSpecUrl = relativeUrlFromTo(fullDocsPath, fullSpecPath);\n this.app.get(fullDocsPath, (c) =>\n c.html(renderDocsHtml(relativeSpecUrl, cfg.info.title)),\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction filterRoutes(\n routes: AnyRouteDef[],\n api: string | undefined,\n): AnyRouteDef[] {\n if (!api) return routes.slice();\n return routes.filter((r) =>\n Array.isArray(r.api) ? r.api.includes(api) : r.api === api,\n );\n}\n\nfunction joinPath(base: string, path: string): string {\n const left = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const right = path.startsWith(\"/\") ? path : `/${path}`;\n const merged = `${left}${right}`;\n return merged === \"\" ? \"/\" : merged;\n}\n\n/**\n * Compute a URL relative to `from` that points to `to`, both being absolute\n * pathnames (e.g. `/v1/docs` → `/v1/openapi.json` becomes `openapi.json`).\n * Lets the OpenAPI UI fetch the spec without knowing the upstream prefix\n * added by Firebase Functions / reverse proxies.\n */\nfunction relativeUrlFromTo(from: string, to: string): string {\n const fromSegs = from.split(\"/\").filter(Boolean);\n const toSegs = to.split(\"/\").filter(Boolean);\n // Drop the docs page filename so we resolve relative to its directory.\n fromSegs.pop();\n let common = 0;\n while (\n common < fromSegs.length &&\n common < toSegs.length &&\n fromSegs[common] === toSegs[common]\n ) {\n common++;\n }\n const ups = fromSegs.length - common;\n const rel = [\n ...Array(ups).fill(\"..\"),\n ...toSegs.slice(common),\n ].join(\"/\");\n return rel || \"./\";\n}\n\n/**\n * Build the actual Hono handler with input validation, output validation\n * (optional), and error normalisation.\n */\nfunction makeRouteHandler(\n route: AnyRouteDef,\n source: PayloadSource,\n validateOutput: boolean,\n interceptor: RouteInterceptor | undefined,\n) {\n const inputSchema = route.input as z.ZodTypeAny | undefined;\n const outputSchema = route.output as z.ZodTypeAny | undefined;\n const status = route.status ?? 200;\n\n return async (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any,\n ): Promise<Response> => {\n // `next()` runs validation + handler. Any Zod failure throws\n // `ValidationError` so the interceptor (or default catcher) can shape it.\n const callNext = async (): Promise<unknown> => {\n let payload: unknown = undefined;\n\n if (inputSchema) {\n let raw: unknown;\n try {\n raw = await readPayload(c, source, route.method);\n } catch (err) {\n // Body parse failure → wrap as a generic Error so the interceptor\n // can decide. Use a 400-shaped Error subclass.\n throw new BadRequestError(\n err instanceof Error ? err.message : String(err),\n );\n }\n const parsed = inputSchema.safeParse(raw);\n if (!parsed.success) {\n throw new ValidationError(parsed.error, source);\n }\n payload = parsed.data;\n }\n\n const result = await (route.handler as (ctx: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n input: any;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any;\n }) => unknown)({ input: payload, c });\n\n if (validateOutput && outputSchema && !(result instanceof Response)) {\n const checked = outputSchema.safeParse(result);\n if (!checked.success) {\n throw new OutputValidationError(checked.error);\n }\n return checked.data;\n }\n return result;\n };\n\n let result: unknown;\n if (interceptor) {\n // Interceptor owns the response shape — including validation errors.\n result = await interceptor({ next: callNext, route, c });\n } else {\n // Default behaviour — handles ValidationError / BadRequestError with\n // a JSON envelope, lets unknown errors bubble to onError / Hono.\n try {\n result = await callNext();\n } catch (err) {\n const handled = defaultErrorResponse(c, err);\n if (handled) return handled;\n throw err;\n }\n }\n\n if (result instanceof Response) return result;\n return c.json(result, status);\n };\n}\n\nclass BadRequestError extends Error {\n readonly statusCode = 400 as const;\n constructor(message: string) {\n super(message);\n this.name = \"BadRequestError\";\n }\n}\n\nclass OutputValidationError extends Error {\n readonly statusCode = 500 as const;\n constructor(readonly zodError: ZodError) {\n super(\"Output validation failed\");\n this.name = \"OutputValidationError\";\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction defaultErrorResponse(c: any, err: unknown): Response | null {\n if (err instanceof ValidationError) {\n return c.json(\n {\n success: false,\n error: \"Validation failed\",\n issues: formatZodIssues(err.zodError),\n },\n 400,\n );\n }\n if (err instanceof BadRequestError) {\n return c.json(\n { success: false, error: \"Bad Request\", message: err.message },\n 400,\n );\n }\n if (err instanceof OutputValidationError) {\n return c.json(\n {\n success: false,\n error: \"Output validation failed\",\n issues: formatZodIssues(err.zodError),\n },\n 500,\n );\n }\n return null;\n}\n\nasync function readPayload(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any,\n source: PayloadSource,\n method: HttpMethod,\n): Promise<unknown> {\n switch (source) {\n case \"json\": {\n if (method === \"get\") return c.req.query();\n const text = await c.req.text();\n if (!text) return {};\n try {\n return JSON.parse(text);\n } catch (err) {\n throw new Error(\n `Invalid JSON body: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n case \"query\":\n return c.req.query();\n case \"form\": {\n const form = await c.req.parseBody();\n return form;\n }\n case \"param\":\n return c.req.param();\n default:\n return {};\n }\n}\n\nfunction formatZodIssues(error: ZodError): unknown {\n return error.issues.map((i) => ({\n path: i.path.join(\".\"),\n code: i.code,\n message: i.message,\n }));\n}\n","/**\n * Typed multi-API registry.\n *\n * Lets you declare every API tag (= every Cloud Function) in **one place**,\n * with full TypeScript safety: the `api` field of {@link defineRoute} is\n * narrowed to the registered tags, and {@link toFunctions} returns one\n * `onRequest` Cloud Function per tag, named after its key.\n *\n * @example\n * ```ts\n * // apis.ts\n * import { createApiRegistry } from \"@lpdjs/firestore-repo-service/servers/hono\";\n * import { enrichUser } from \"./middlewares/enrich-user.js\";\n *\n * export const apis = createApiRegistry({\n * v1: {\n * basePath: \"/v1\",\n * middlewares: [enrichUser],\n * openapi: { info: { title: \"Public API\", version: \"1.0.0\" } },\n * },\n * webhooks: {\n * basePath: \"/hooks\",\n * openapi: { info: { title: \"Webhooks\", version: \"1.0.0\" } },\n * },\n * });\n *\n * // Use in routes — `api` is now typed \"v1\" | \"webhooks\".\n * export const defineRoute = apis.defineRoute;\n *\n * // index.ts (Cloud Functions entrypoint)\n * import { onRequest } from \"firebase-functions/v2/https\";\n * import { apis } from \"./apis.js\";\n * import { routes } from \"./domains/__generated__/routes.js\";\n *\n * export const { v1, webhooks } = apis.toFunctions(routes, onRequest, {\n * defaults: { region: \"us-central1\", invoker: \"public\" },\n * per: { v1: { memory: \"512MiB\" } },\n * });\n * // → URLs: https://<region>-<project>.cloudfunctions.net/v1/posts\n * // https://<region>-<project>.cloudfunctions.net/webhooks/...\n * ```\n */\n\nimport type { Env } from \"hono\";\nimport type { z } from \"zod\";\n\nimport type {\n AnyRouteDef,\n HonoServerOptions,\n RouteDef,\n} from \"./types\";\nimport { HonoServer } from \"./server\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype OnRequestFn = (...args: any[]) => any;\n\n/**\n * Per-API configuration. Same shape as {@link HonoServerOptions} minus the\n * `routes` (resolved by the registry) and `api` (the registry key).\n */\nexport type ApiConfig<TEnv extends Env = Env> = Omit<\n HonoServerOptions<TEnv>,\n \"routes\" | \"api\"\n>;\n\n/** Map of API tag → its config. */\nexport type ApiConfigMap = Record<string, ApiConfig>;\n\nexport interface ApiRegistry<TMap extends ApiConfigMap> {\n /** The registered configs (read-only). */\n readonly configs: TMap;\n\n /**\n * Typed `defineRoute` — the `api` field is constrained to `keyof TMap`.\n *\n * To expose the same logical endpoint under several APIs with different\n * `input` / `output` schemas, call `defineRoute` once per route and wrap\n * them in an array — per-call inference is preserved:\n *\n * ```ts\n * export default [\n * defineRoute({ api: \"v1\", input: V1Input, handler: ({ input }) => ... }),\n * defineRoute({ api: \"v2\", input: V2Input, handler: ({ input }) => ... }),\n * ];\n * ```\n */\n defineRoute<\n TIn extends z.ZodTypeAny | undefined = undefined,\n TOut extends z.ZodTypeAny | undefined = undefined,\n >(\n def: Omit<RouteDef<TIn, TOut>, \"api\"> & { api: keyof TMap & string },\n ): RouteDef<TIn, TOut> & { api: keyof TMap & string };\n\n /**\n * Build one Cloud Function per registered API and return them as a map\n * keyed by API tag — spread it directly into your `index.ts` exports.\n *\n * @param routes Pre-resolved route registry (typically the codegen output).\n * @param onRequest The `onRequest` factory imported from\n * `firebase-functions/v2/https`.\n * @param opts Optional defaults and per-API overrides for `httpsOptions`.\n */\n toFunctions(\n routes: AnyRouteDef[],\n onRequest: OnRequestFn,\n opts?: {\n defaults?: Record<string, unknown>;\n per?: Partial<Record<keyof TMap & string, Record<string, unknown>>>;\n },\n ): { [K in keyof TMap & string]: ReturnType<OnRequestFn> };\n\n /** Build the underlying {@link HonoServer} for a given API (escape hatch). */\n serverFor<K extends keyof TMap & string>(\n api: K,\n routes: AnyRouteDef[],\n ): HonoServer;\n}\n\n/**\n * Factory — declare every API tag once and get back a typed `defineRoute`\n * + `toFunctions`. See the file-level example.\n */\nexport function createApiRegistry<const TMap extends ApiConfigMap>(\n configs: TMap,\n): ApiRegistry<TMap> {\n return {\n configs,\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n defineRoute(def: any) {\n return def;\n },\n\n serverFor(api, routes) {\n const cfg = configs[api];\n if (!cfg) {\n throw new Error(\n `[ApiRegistry] unknown api \"${api}\". Registered: ${Object.keys(configs).join(\", \")}`,\n );\n }\n return new HonoServer({ ...cfg, api, routes });\n },\n\n toFunctions(routes, onRequest, opts) {\n const out = {} as { [K in keyof TMap & string]: ReturnType<OnRequestFn> };\n for (const api of Object.keys(configs) as Array<keyof TMap & string>) {\n const httpsOpts = {\n ...(opts?.defaults ?? {}),\n ...(opts?.per?.[api] ?? {}),\n };\n const server = new HonoServer({ ...configs[api], api, routes });\n out[api] = Object.keys(httpsOpts).length\n ? server.toFunction(onRequest, httpsOpts)\n : server.toFunction(onRequest);\n }\n return out;\n },\n };\n}\n","/**\n * URL path inference from filesystem layout.\n *\n * Convention: every `routes.ts` file under the configured root contributes\n * one route. The URL path is derived from its directory chain, optionally\n * skipping segments such as `useCases` so that\n *\n * domains/activities/useCases/createOrUpdateCustom/routes.ts\n *\n * becomes\n *\n * /activities/createOrUpdateCustom\n */\n\nexport interface PathDeriveOptions {\n /** Segments to drop from the derived path (case-insensitive). */\n skipSegments: string[];\n /**\n * Casing convention applied to each remaining segment.\n * - `\"preserve\"` — keep the directory name as-is (default),\n * - `\"kebab\"` — convert camelCase / PascalCase to kebab-case.\n */\n casing: \"preserve\" | \"kebab\";\n}\n\nexport const DEFAULT_DERIVE: PathDeriveOptions = {\n skipSegments: [\"useCases\", \"useCase\", \"use-cases\", \"use-case\"],\n casing: \"preserve\",\n};\n\n/**\n * @param relativeDir POSIX-style directory path of the routes file relative\n * to the codegen root (no leading slash, no `routes.ts`).\n */\nexport function derivePath(\n relativeDir: string,\n options: PathDeriveOptions = DEFAULT_DERIVE,\n): string {\n const skip = new Set(options.skipSegments.map((s) => s.toLowerCase()));\n const parts = relativeDir\n .split(\"/\")\n .filter(Boolean)\n .filter((p) => !skip.has(p.toLowerCase()))\n .map((p) => (options.casing === \"kebab\" ? kebab(p) : p));\n return \"/\" + parts.join(\"/\");\n}\n\nfunction kebab(s: string): string {\n return s\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n}\n\n/**\n * Convert an absolute filesystem path to a POSIX-style import specifier\n * relative to a directory (the generated file's directory). Always uses\n * forward slashes and prefixes with `./` or `../` as needed.\n */\nexport function toImportSpecifier(\n fromDir: string,\n toFile: string,\n ext: string,\n): string {\n // Both paths are absolute POSIX (the CLI normalises them).\n const fromParts = splitAbs(fromDir);\n const toParts = splitAbs(toFile);\n let common = 0;\n while (\n common < fromParts.length &&\n common < toParts.length &&\n fromParts[common] === toParts[common]\n ) {\n common++;\n }\n const up = fromParts.length - common;\n const down = toParts.slice(common);\n const last = down[down.length - 1] ?? \"\";\n const stripped = last.replace(/\\.[mc]?[tj]sx?$/i, \"\");\n const finalLast = ext === \"\" ? stripped : `${stripped}${ext}`;\n down[down.length - 1] = finalLast;\n const prefix = up === 0 ? \"./\" : \"../\".repeat(up);\n return prefix + down.join(\"/\");\n}\n\nfunction splitAbs(p: string): string[] {\n const norm = p.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n return norm.split(\"/\").filter((part, i) => !(i === 0 && part === \"\"));\n}\n","/**\n * Filesystem scanner — walks the configured root and yields every route file.\n * Synchronous and dependency-free (no `fast-glob` etc.) for a tiny CLI footprint.\n */\n\nimport { readdirSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\n\nexport interface ScannerOptions {\n /** Filename to look for (default: `routes.ts`). */\n routesFile: string;\n /** Glob-like exclude segments (matched against any path part). */\n excludeSegments: string[];\n}\n\nexport const DEFAULT_SCANNER: ScannerOptions = {\n routesFile: \"routes.ts\",\n excludeSegments: [\n \"node_modules\",\n \"__generated__\",\n \"tests\",\n \"__tests__\",\n \".turbo\",\n \"dist\",\n \"build\",\n \".next\",\n ],\n};\n\nexport interface ScannedRoute {\n /** Absolute path to the routes file. */\n absPath: string;\n /** Path relative to the scan root (POSIX style). */\n relPath: string;\n /** Directory portion of `relPath` (what {@link derivePath} consumes). */\n relDir: string;\n}\n\nexport function scanRoutes(\n rootAbs: string,\n options: ScannerOptions = DEFAULT_SCANNER,\n): ScannedRoute[] {\n const found: ScannedRoute[] = [];\n walk(rootAbs, rootAbs, options, found);\n // Stable, deterministic order — important for reproducible builds.\n found.sort((a, b) => a.relPath.localeCompare(b.relPath));\n return found;\n}\n\nfunction walk(\n root: string,\n dir: string,\n opts: ScannerOptions,\n out: ScannedRoute[],\n): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n if (opts.excludeSegments.includes(name)) continue;\n const abs = join(dir, name);\n let st;\n try {\n st = statSync(abs);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n walk(root, abs, opts, out);\n } else if (st.isFile() && name === opts.routesFile) {\n const relPath = relative(root, abs).split(sep).join(\"/\");\n const relDir = relPath.replace(/\\/?[^/]+$/, \"\");\n out.push({ absPath: abs, relPath, relDir });\n }\n }\n}\n","/**\n * Generator — emits `__generated__/routes.ts` from a list of {@link ScannedRoute}s.\n *\n * The generated module statically imports every `routes.ts` so that bundlers\n * (esbuild/tsup) can tree-shake unused dependencies and Cloud Functions get\n * the smallest possible cold-start footprint.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\n\nimport {\n derivePath,\n toImportSpecifier,\n type PathDeriveOptions,\n} from \"./path-utils\";\nimport type { ScannedRoute } from \"./scanner\";\n\nexport interface GeneratorOptions {\n /** Absolute path of the file to write (e.g. `…/__generated__/routes.ts`). */\n outFile: string;\n /** Path-derivation options (skipSegments, casing). */\n derive: PathDeriveOptions;\n /**\n * Import extension used in the generated file:\n * - `\".js\"` (default) — required for ESM Node.js with `\"type\": \"module\"`,\n * - `\"\"` — bundler-friendly (Vite, no extension),\n * - `\".ts\"` — for TS-ESM runners (`tsx`, `bun`).\n */\n importExtension: string;\n /** Friendly banner placed at the top of the generated file. */\n banner?: string;\n /** Generation timestamp included in the banner. Default: `new Date()`. */\n now?: Date;\n}\n\nexport const DEFAULT_GENERATOR_BANNER =\n \"/**\\n\" +\n \" * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\\n\" +\n \" * Do not edit by hand — re-run `hono:gen` after adding / removing route files.\\n\" +\n \" */\\n\";\n\nexport interface GenerationResult {\n /** Absolute path of the file written. */\n outFile: string;\n /** Number of routes captured in the manifest. */\n routeCount: number;\n /** Human-readable summary of the derived URLs. */\n derivedPaths: { source: string; url: string }[];\n}\n\nexport function generateRoutesManifest(\n routes: ScannedRoute[],\n opts: GeneratorOptions,\n): GenerationResult {\n const outDir = dirname(opts.outFile);\n mkdirSync(outDir, { recursive: true });\n\n const banner = opts.banner ?? DEFAULT_GENERATOR_BANNER;\n const now = (opts.now ?? new Date()).toISOString();\n const ext = opts.importExtension;\n\n const importLines: string[] = [];\n const entryLines: string[] = [];\n const derivedPaths: GenerationResult[\"derivedPaths\"] = [];\n\n routes.forEach((r, i) => {\n const importPath = toImportSpecifier(outDir, r.absPath, ext);\n const url = derivePath(r.relDir, opts.derive);\n importLines.push(\n `import mod${i} from ${JSON.stringify(importPath)};`,\n );\n entryLines.push(` { __derivedPath: ${JSON.stringify(url)}, mod: mod${i} },`);\n derivedPaths.push({ source: r.relPath, url });\n });\n\n const body =\n `${banner}` +\n `// Generated at ${now} — ${routes.length} route file${routes.length === 1 ? \"\" : \"s\"}.\\n` +\n `\\n` +\n `import type { AnyRouteDef, RouteModuleDefault } from \"@lpdjs/firestore-repo-service/servers/hono\";\\n` +\n `\\n` +\n importLines.join(\"\\n\") +\n (importLines.length ? \"\\n\\n\" : \"\\n\") +\n `const __defs: { __derivedPath: string; mod: RouteModuleDefault }[] = [\\n` +\n entryLines.join(\"\\n\") +\n (entryLines.length ? \"\\n\" : \"\") +\n `];\\n\\n` +\n `export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) => {\\n` +\n ` const list = Array.isArray(mod) ? mod : [mod];\\n` +\n ` return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));\\n` +\n `});\\n`;\n\n writeFileSync(opts.outFile, body, \"utf8\");\n return {\n outFile: opts.outFile,\n routeCount: routes.length,\n derivedPaths,\n };\n}\n\n/** Convenience helper used by the CLI — combines scan + generate in one call. */\nexport function generateFromRoot(\n rootAbs: string,\n outFileRel: string,\n derive: PathDeriveOptions,\n importExtension: string,\n scan: (root: string) => ScannedRoute[],\n): GenerationResult {\n const routes = scan(rootAbs);\n const outFile = join(rootAbs, outFileRel);\n return generateRoutesManifest(routes, {\n outFile,\n derive,\n importExtension,\n });\n}\n"]}
@@ -30,6 +30,10 @@ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefin
30
30
  /**
31
31
  * Logical API tag — routes sharing the same `api` are mounted on the same
32
32
  * `HonoServer` (typically one Cloud Function per `api`).
33
+ *
34
+ * To expose the **same logic** under several APIs with different
35
+ * inputs/outputs, export multiple `defineRoute({...})` from the same file
36
+ * (default + named exports are both picked up by the codegen).
33
37
  */
34
38
  api: string;
35
39
  /** HTTP method. */
@@ -294,6 +298,17 @@ interface ApiRegistry<TMap extends ApiConfigMap> {
294
298
  readonly configs: TMap;
295
299
  /**
296
300
  * Typed `defineRoute` — the `api` field is constrained to `keyof TMap`.
301
+ *
302
+ * To expose the same logical endpoint under several APIs with different
303
+ * `input` / `output` schemas, call `defineRoute` once per route and wrap
304
+ * them in an array — per-call inference is preserved:
305
+ *
306
+ * ```ts
307
+ * export default [
308
+ * defineRoute({ api: "v1", input: V1Input, handler: ({ input }) => ... }),
309
+ * defineRoute({ api: "v2", input: V2Input, handler: ({ input }) => ... }),
310
+ * ];
311
+ * ```
297
312
  */
298
313
  defineRoute<TIn extends z.ZodTypeAny | undefined = undefined, TOut extends z.ZodTypeAny | undefined = undefined>(def: Omit<RouteDef<TIn, TOut>, "api"> & {
299
314
  api: keyof TMap & string;
@@ -30,6 +30,10 @@ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefin
30
30
  /**
31
31
  * Logical API tag — routes sharing the same `api` are mounted on the same
32
32
  * `HonoServer` (typically one Cloud Function per `api`).
33
+ *
34
+ * To expose the **same logic** under several APIs with different
35
+ * inputs/outputs, export multiple `defineRoute({...})` from the same file
36
+ * (default + named exports are both picked up by the codegen).
33
37
  */
34
38
  api: string;
35
39
  /** HTTP method. */
@@ -294,6 +298,17 @@ interface ApiRegistry<TMap extends ApiConfigMap> {
294
298
  readonly configs: TMap;
295
299
  /**
296
300
  * Typed `defineRoute` — the `api` field is constrained to `keyof TMap`.
301
+ *
302
+ * To expose the same logical endpoint under several APIs with different
303
+ * `input` / `output` schemas, call `defineRoute` once per route and wrap
304
+ * them in an array — per-call inference is preserved:
305
+ *
306
+ * ```ts
307
+ * export default [
308
+ * defineRoute({ api: "v1", input: V1Input, handler: ({ input }) => ... }),
309
+ * defineRoute({ api: "v2", input: V2Input, handler: ({ input }) => ... }),
310
+ * ];
311
+ * ```
297
312
  */
298
313
  defineRoute<TIn extends z.ZodTypeAny | undefined = undefined, TOut extends z.ZodTypeAny | undefined = undefined>(def: Omit<RouteDef<TIn, TOut>, "api"> & {
299
314
  api: keyof TMap & string;
@@ -9,7 +9,7 @@ import {Hono}from'hono';import {getRequestListener}from'@hono/node-server';impor
9
9
  <script id="api-reference" data-url="${n}"></script>
10
10
  <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
11
11
  </body>
12
- </html>`}var y=class{constructor(e){this.cachedSpec=null;this.options=e,this.app=new Hono,this.mountedRoutes=L(e.routes,e.api);let n=[...e.middlewares??[],...e.globalMiddlewares??[]];for(let o of n)this.app.use("*",o);this.mountRoutes(),this.mountOpenApi(),e.notFound&&this.app.notFound(e.notFound),e.onError&&this.app.onError(e.onError);}get hono(){return this.app}get nodeHandler(){return getRequestListener(this.app.fetch,{overrideGlobalObjects:false})}toFunction(e,n){let o=this.nodeHandler;return n?e(n,o):e(o)}buildOpenApiSpec(){if(this.cachedSpec)return this.cachedSpec;if(!this.options.openapi)throw new Error("[HonoServer] openapi config not set");return this.cachedSpec=v(this.mountedRoutes,this.options.basePath??"",this.options.openapi),this.cachedSpec}mountRoutes(){let e=this.options.basePath??"",n=this.options.validateOutput??false,o=this.options.verbose??false;for(let r of this.mountedRoutes){if(!r.path)throw new Error(`[HonoServer] route "${r.method.toUpperCase()} (no path)" \u2014 missing \`path\`. Run the codegen so the path is derived from the file location, or set it explicitly.`);let i=P(e,r.path),s=r.middlewares??[],a=r.source??(r.method==="get"?"query":"json"),u=B(r,a,n,this.options.interceptor),p=r.method.toUpperCase();this.app.on(p,[i],...s,u),o&&console.log(`[HonoServer] ${r.method.toUpperCase().padEnd(6)} ${i}`);}}mountOpenApi(){let e=this.options.openapi;if(!e)return;let n=e.path??"/openapi.json",o=e.docsPath===void 0?"/docs":e.docsPath,r=P(this.options.basePath??"",n),i=o===false?null:P(this.options.basePath??"",o);if(this.app.get(r,s=>s.json(this.buildOpenApiSpec())),i){let s=U(i,r);this.app.get(i,a=>a.html(E(s,e.info.title)));}}};function L(t,e){return e?t.filter(n=>n.api===e):t.slice()}function P(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function U(t,e){let n=t.split("/").filter(Boolean),o=e.split("/").filter(Boolean);n.pop();let r=0;for(;r<n.length&&r<o.length&&n[r]===o[r];)r++;let i=n.length-r;return [...Array(i).fill(".."),...o.slice(r)].join("/")||"./"}function B(t,e,n,o){let r=t.input,i=t.output,s=t.status??200;return async a=>{let u=async()=>{let c;if(r){let l;try{l=await W(a,e,t.method);}catch(g){throw new h(g instanceof Error?g.message:String(g))}let f=r.safeParse(l);if(!f.success)throw new m(f.error,e);c=f.data;}let d=await t.handler({input:c,c:a});if(n&&i&&!(d instanceof Response)){let l=i.safeParse(d);if(!l.success)throw new R(l.error);return l.data}return d},p;if(o)p=await o({next:u,route:t,c:a});else try{p=await u();}catch(c){let d=V(a,c);if(d)return d;throw c}return p instanceof Response?p:a.json(p,s)}}var h=class extends Error{constructor(n){super(n);this.statusCode=400;this.name="BadRequestError";}},R=class extends Error{constructor(n){super("Output validation failed");this.zodError=n;this.statusCode=500;this.name="OutputValidationError";}};function V(t,e){return e instanceof m?t.json({success:false,error:"Validation failed",issues:x(e.zodError)},400):e instanceof h?t.json({success:false,error:"Bad Request",message:e.message},400):e instanceof R?t.json({success:false,error:"Output validation failed",issues:x(e.zodError)},500):null}async function W(t,e,n){switch(e){case "json":{if(n==="get")return t.req.query();let o=await t.req.text();if(!o)return {};try{return JSON.parse(o)}catch(r){throw new Error(`Invalid JSON body: ${r instanceof Error?r.message:String(r)}`)}}case "query":return t.req.query();case "form":return await t.req.parseBody();case "param":return t.req.param();default:return {}}}function x(t){return t.issues.map(e=>({path:e.path.join("."),code:e.code,message:e.message}))}function J(t){return {configs:t,defineRoute(e){return e},serverFor(e,n){let o=t[e];if(!o)throw new Error(`[ApiRegistry] unknown api "${e}". Registered: ${Object.keys(t).join(", ")}`);return new y({...o,api:e,routes:n})},toFunctions(e,n,o){let r={};for(let i of Object.keys(t)){let s={...o?.defaults??{},...o?.per?.[i]??{}},a=new y({...t[i],api:i,routes:e});r[i]=Object.keys(s).length?a.toFunction(n,s):a.toFunction(n);}return r}}}var D={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function T(t,e=D){let n=new Set(e.skipSegments.map(r=>r.toLowerCase()));return "/"+t.split("/").filter(Boolean).filter(r=>!n.has(r.toLowerCase())).map(r=>e.casing==="kebab"?K(r):r).join("/")}function K(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function A(t,e,n){let o=O(t),r=O(e),i=0;for(;i<o.length&&i<r.length&&o[i]===r[i];)i++;let s=o.length-i,a=r.slice(i),p=(a[a.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),c=n===""?p:`${p}${n}`;return a[a.length-1]=c,(s===0?"./":"../".repeat(s))+a.join("/")}function O(t){return t.replace(/\\/g,"/").replace(/\/+$/,"").split("/").filter((n,o)=>!(o===0&&n===""))}var b={routesFile:"routes.ts",excludeSegments:["node_modules","__generated__","tests","__tests__",".turbo","dist","build",".next"]};function ne(t,e=b){let n=[];return k(t,t,e,n),n.sort((o,r)=>o.relPath.localeCompare(r.relPath)),n}function k(t,e,n,o){let r;try{r=readdirSync(e);}catch{return}for(let i of r){if(n.excludeSegments.includes(i))continue;let s=join(e,i),a;try{a=statSync(s);}catch{continue}if(a.isDirectory())k(t,s,n,o);else if(a.isFile()&&i===n.routesFile){let u=relative(t,s).split(sep).join("/"),p=u.replace(/\/?[^/]+$/,"");o.push({absPath:s,relPath:u,relDir:p});}}}var M="/**\n * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\n * Do not edit by hand \u2014 re-run `hono:gen` after adding / removing route files.\n */\n";function I(t,e){let n=dirname(e.outFile);mkdirSync(n,{recursive:true});let o=e.banner??M,r=(e.now??new Date).toISOString(),i=e.importExtension,s=[],a=[],u=[];t.forEach((c,d)=>{let l=A(n,c.absPath,i),f=T(c.relDir,e.derive);s.push(`import mod${d} from ${JSON.stringify(l)};`),a.push(` { __derivedPath: ${JSON.stringify(f)}, mod: mod${d} },`),u.push({source:c.relPath,url:f});});let p=`${o}// Generated at ${r} \u2014 ${t.length} route file${t.length===1?"":"s"}.
12
+ </html>`}var y=class{constructor(e){this.cachedSpec=null;this.options=e,this.app=new Hono,this.mountedRoutes=L(e.routes,e.api);let n=[...e.middlewares??[],...e.globalMiddlewares??[]];for(let o of n)this.app.use("*",o);this.mountRoutes(),this.mountOpenApi(),e.notFound&&this.app.notFound(e.notFound),e.onError&&this.app.onError(e.onError);}get hono(){return this.app}get nodeHandler(){return getRequestListener(this.app.fetch,{overrideGlobalObjects:false})}toFunction(e,n){let o=this.nodeHandler;return n?e(n,o):e(o)}buildOpenApiSpec(){if(this.cachedSpec)return this.cachedSpec;if(!this.options.openapi)throw new Error("[HonoServer] openapi config not set");return this.cachedSpec=v(this.mountedRoutes,this.options.basePath??"",this.options.openapi),this.cachedSpec}mountRoutes(){let e=this.options.basePath??"",n=this.options.validateOutput??false,o=this.options.verbose??false;for(let r of this.mountedRoutes){if(!r.path)throw new Error(`[HonoServer] route "${r.method.toUpperCase()} (no path)" \u2014 missing \`path\`. Run the codegen so the path is derived from the file location, or set it explicitly.`);let i=A(e,r.path),s=r.middlewares??[],a=r.source??(r.method==="get"?"query":"json"),u=B(r,a,n,this.options.interceptor),p=r.method.toUpperCase();this.app.on(p,[i],...s,u),o&&console.log(`[HonoServer] ${r.method.toUpperCase().padEnd(6)} ${i}`);}}mountOpenApi(){let e=this.options.openapi;if(!e)return;let n=e.path??"/openapi.json",o=e.docsPath===void 0?"/docs":e.docsPath,r=A(this.options.basePath??"",n),i=o===false?null:A(this.options.basePath??"",o);if(this.app.get(r,s=>s.json(this.buildOpenApiSpec())),i){let s=U(i,r);this.app.get(i,a=>a.html(E(s,e.info.title)));}}};function L(t,e){return e?t.filter(n=>Array.isArray(n.api)?n.api.includes(e):n.api===e):t.slice()}function A(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function U(t,e){let n=t.split("/").filter(Boolean),o=e.split("/").filter(Boolean);n.pop();let r=0;for(;r<n.length&&r<o.length&&n[r]===o[r];)r++;let i=n.length-r;return [...Array(i).fill(".."),...o.slice(r)].join("/")||"./"}function B(t,e,n,o){let r=t.input,i=t.output,s=t.status??200;return async a=>{let u=async()=>{let c;if(r){let l;try{l=await W(a,e,t.method);}catch(g){throw new h(g instanceof Error?g.message:String(g))}let f=r.safeParse(l);if(!f.success)throw new m(f.error,e);c=f.data;}let d=await t.handler({input:c,c:a});if(n&&i&&!(d instanceof Response)){let l=i.safeParse(d);if(!l.success)throw new R(l.error);return l.data}return d},p;if(o)p=await o({next:u,route:t,c:a});else try{p=await u();}catch(c){let d=V(a,c);if(d)return d;throw c}return p instanceof Response?p:a.json(p,s)}}var h=class extends Error{constructor(n){super(n);this.statusCode=400;this.name="BadRequestError";}},R=class extends Error{constructor(n){super("Output validation failed");this.zodError=n;this.statusCode=500;this.name="OutputValidationError";}};function V(t,e){return e instanceof m?t.json({success:false,error:"Validation failed",issues:x(e.zodError)},400):e instanceof h?t.json({success:false,error:"Bad Request",message:e.message},400):e instanceof R?t.json({success:false,error:"Output validation failed",issues:x(e.zodError)},500):null}async function W(t,e,n){switch(e){case "json":{if(n==="get")return t.req.query();let o=await t.req.text();if(!o)return {};try{return JSON.parse(o)}catch(r){throw new Error(`Invalid JSON body: ${r instanceof Error?r.message:String(r)}`)}}case "query":return t.req.query();case "form":return await t.req.parseBody();case "param":return t.req.param();default:return {}}}function x(t){return t.issues.map(e=>({path:e.path.join("."),code:e.code,message:e.message}))}function J(t){return {configs:t,defineRoute(e){return e},serverFor(e,n){let o=t[e];if(!o)throw new Error(`[ApiRegistry] unknown api "${e}". Registered: ${Object.keys(t).join(", ")}`);return new y({...o,api:e,routes:n})},toFunctions(e,n,o){let r={};for(let i of Object.keys(t)){let s={...o?.defaults??{},...o?.per?.[i]??{}},a=new y({...t[i],api:i,routes:e});r[i]=Object.keys(s).length?a.toFunction(n,s):a.toFunction(n);}return r}}}var D={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function P(t,e=D){let n=new Set(e.skipSegments.map(r=>r.toLowerCase()));return "/"+t.split("/").filter(Boolean).filter(r=>!n.has(r.toLowerCase())).map(r=>e.casing==="kebab"?K(r):r).join("/")}function K(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function T(t,e,n){let o=O(t),r=O(e),i=0;for(;i<o.length&&i<r.length&&o[i]===r[i];)i++;let s=o.length-i,a=r.slice(i),p=(a[a.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),c=n===""?p:`${p}${n}`;return a[a.length-1]=c,(s===0?"./":"../".repeat(s))+a.join("/")}function O(t){return t.replace(/\\/g,"/").replace(/\/+$/,"").split("/").filter((n,o)=>!(o===0&&n===""))}var b={routesFile:"routes.ts",excludeSegments:["node_modules","__generated__","tests","__tests__",".turbo","dist","build",".next"]};function ne(t,e=b){let n=[];return k(t,t,e,n),n.sort((o,r)=>o.relPath.localeCompare(r.relPath)),n}function k(t,e,n,o){let r;try{r=readdirSync(e);}catch{return}for(let i of r){if(n.excludeSegments.includes(i))continue;let s=join(e,i),a;try{a=statSync(s);}catch{continue}if(a.isDirectory())k(t,s,n,o);else if(a.isFile()&&i===n.routesFile){let u=relative(t,s).split(sep).join("/"),p=u.replace(/\/?[^/]+$/,"");o.push({absPath:s,relPath:u,relDir:p});}}}var M="/**\n * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\n * Do not edit by hand \u2014 re-run `hono:gen` after adding / removing route files.\n */\n";function I(t,e){let n=dirname(e.outFile);mkdirSync(n,{recursive:true});let o=e.banner??M,r=(e.now??new Date).toISOString(),i=e.importExtension,s=[],a=[],u=[];t.forEach((c,d)=>{let l=T(n,c.absPath,i),f=P(c.relDir,e.derive);s.push(`import mod${d} from ${JSON.stringify(l)};`),a.push(` { __derivedPath: ${JSON.stringify(f)}, mod: mod${d} },`),u.push({source:c.relPath,url:f});});let p=`${o}// Generated at ${r} \u2014 ${t.length} route file${t.length===1?"":"s"}.
13
13
 
14
14
  import type { AnyRouteDef, RouteModuleDefault } from "@lpdjs/firestore-repo-service/servers/hono";
15
15
 
@@ -26,5 +26,5 @@ export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) =>
26
26
  const list = Array.isArray(mod) ? mod : [mod];
27
27
  return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));
28
28
  });
29
- `;return writeFileSync(e.outFile,p,"utf8"),{outFile:e.outFile,routeCount:t.length,derivedPaths:u}}function ae(t,e,n,o,r){let i=r(t),s=join(t,e);return I(i,{outFile:s,derive:n,importExtension:o})}export{D as DEFAULT_DERIVE,M as DEFAULT_GENERATOR_BANNER,b as DEFAULT_SCANNER,y as HonoServer,m as ValidationError,v as buildOpenApiDocument,J as createApiRegistry,T as derivePath,ae as generateFromRoot,I as generateRoutesManifest,E as renderDocsHtml,ne as scanRoutes,A as toImportSpecifier};//# sourceMappingURL=index.js.map
29
+ `;return writeFileSync(e.outFile,p,"utf8"),{outFile:e.outFile,routeCount:t.length,derivedPaths:u}}function ae(t,e,n,o,r){let i=r(t),s=join(t,e);return I(i,{outFile:s,derive:n,importExtension:o})}export{D as DEFAULT_DERIVE,M as DEFAULT_GENERATOR_BANNER,b as DEFAULT_SCANNER,y as HonoServer,m as ValidationError,v as buildOpenApiDocument,J as createApiRegistry,P as derivePath,ae as generateFromRoot,I as generateRoutesManifest,E as renderDocsHtml,ne as scanRoutes,T as toImportSpecifier};//# sourceMappingURL=index.js.map
30
30
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/servers/hono/types.ts","../../../src/servers/hono/openapi.ts","../../../src/servers/hono/server.ts","../../../src/servers/hono/api-registry.ts","../../../src/servers/hono/codegen/path-utils.ts","../../../src/servers/hono/codegen/scanner.ts","../../../src/servers/hono/codegen/generator.ts"],"names":["ValidationError","zodError","source","extendZodWithOpenApi","z","DEFAULT_RESPONSE_DESCRIPTION","defaultSource","method","buildOpenApiDocument","routes","basePath","config","registry","OpenAPIRegistry","name","scheme","route","fullPath","joinPath","status","requestBody","buildRequestBody","requestQuery","buildQueryOrParam","requestParams","operationId","makeOperationId","convertExpressPathToOpenApi","OpenApiGeneratorV31","schema","target","path","base","left","right","merged","cleaned","renderDocsHtml","specUrl","title","safeUrl","HonoServer","options","Hono","filterRoutes","globalMws","mw","getRequestListener","onRequest","httpsOptions","handler","validateOutput","verbose","middlewares","makeRouteHandler","httpMethod","cfg","specPath","docsPath","fullSpecPath","fullDocsPath","c","relativeSpecUrl","relativeUrlFromTo","api","r","from","to","fromSegs","toSegs","common","ups","interceptor","inputSchema","outputSchema","callNext","payload","raw","readPayload","err","BadRequestError","parsed","result","checked","OutputValidationError","handled","defaultErrorResponse","message","formatZodIssues","text","error","i","createApiRegistry","configs","def","opts","out","httpsOpts","server","DEFAULT_DERIVE","derivePath","relativeDir","skip","s","p","kebab","toImportSpecifier","fromDir","toFile","ext","fromParts","splitAbs","toParts","up","down","stripped","finalLast","part","DEFAULT_SCANNER","scanRoutes","rootAbs","found","walk","a","b","root","dir","entries","readdirSync","abs","join","st","statSync","relPath","relative","sep","relDir","DEFAULT_GENERATOR_BANNER","generateRoutesManifest","outDir","dirname","mkdirSync","banner","now","importLines","entryLines","derivedPaths","importPath","url","body","writeFileSync","generateFromRoot","outFileRel","derive","importExtension","scan","outFile"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Public types for the Hono file-based API server.\n *\n * Designed to be:\n * - **fully typed** end-to-end (Zod input/output → handler payload type),\n * - **runtime-safe** via Zod parsing on every request,\n * - **bundle-friendly** for Firebase Cloud Functions v2 cold-start (codegen\n * emits static imports — no `fs`/`import()` at runtime).\n */\n\nimport type { z, ZodError } from \"zod\";\nimport type { Context, Env, MiddlewareHandler } from \"hono\";\n\nexport type HttpMethod = \"get\" | \"post\" | \"put\" | \"patch\" | \"delete\";\n\n/** Where the validated payload comes from. */\nexport type PayloadSource = \"json\" | \"query\" | \"form\" | \"param\";\n\n/** Handler signature — receives a single typed context object. */\nexport type RouteHandler<TIn, TOut, TEnv extends Env = Env> = (ctx: {\n /** Validated (and typed) request payload. `void` when no `input` schema is defined. */\n input: TIn;\n /** Raw Hono `Context` for headers, set status, redirect, etc. */\n c: Context<TEnv>;\n}) => Promise<TOut | Response> | TOut | Response;\n\n/**\n * One route declaration. Default-exported by every `routes.ts` file inside the\n * domain tree. Use {@link defineRoute} for full type inference.\n */\nexport interface RouteDef<\n TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined,\n TOut extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined,\n TEnv extends Env = Env,\n> {\n /**\n * Logical API tag — routes sharing the same `api` are mounted on the same\n * `HonoServer` (typically one Cloud Function per `api`).\n */\n api: string;\n\n /** HTTP method. */\n method: HttpMethod;\n\n /**\n * URL path appended to the server `basePath`. If omitted, the codegen will\n * derive it from the file location (e.g. `domains/activities/useCases/createCustom/routes.ts`\n * → `/activities/createCustom`).\n */\n path?: string;\n\n /**\n * Where the request payload comes from.\n * Default: `\"json\"` for body methods (POST/PUT/PATCH/DELETE), `\"query\"` for GET.\n */\n source?: PayloadSource;\n\n /** Zod schema validating the payload. Failures yield a 400 response. */\n input?: TIn;\n\n /**\n * Zod schema for the success response. Used to populate the OpenAPI spec\n * and (when `validateOutput` is enabled on the server) to assert the\n * runtime payload returned by the handler.\n */\n output?: TOut;\n\n /** Status code for the success response. Default: 200. */\n status?: number;\n\n /** Hono middlewares applied to this route only (after global middlewares). */\n middlewares?: MiddlewareHandler<TEnv>[];\n\n // ── OpenAPI metadata ─────────────────────────────────────────────────\n summary?: string;\n description?: string;\n tags?: string[];\n /** Mark the operation as deprecated in the generated spec. */\n deprecated?: boolean;\n /** Security requirements (operationId-level override). */\n security?: Array<Record<string, string[]>>;\n\n /** The request handler. */\n handler: RouteHandler<\n TIn extends z.ZodTypeAny ? z.infer<TIn> : void,\n TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown,\n TEnv\n >;\n}\n\n/** Erased `RouteDef` used by registry/codegen — handler signature is opaque. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyRouteDef = RouteDef<any, any>;\n\n/**\n * What `routes.ts` can default-export:\n * - a single {@link RouteDef} (most common case),\n * - or an array of {@link RouteDef} (e.g. expose the same useCase on\n * multiple `api` tags or under multiple paths).\n */\nexport type RouteModuleDefault = AnyRouteDef | AnyRouteDef[];\n\n/**\n * Thrown by the server when the incoming request fails Zod validation.\n * Caught by the {@link RouteInterceptor} (if any) so users can shape the\n * response envelope however they like; otherwise yields a default 400.\n */\nexport class ValidationError extends Error {\n readonly statusCode = 400 as const;\n constructor(\n /** Original Zod error — `error.issues` to enumerate field-level problems. */\n readonly zodError: ZodError,\n /** Where the offending payload came from. */\n readonly source: PayloadSource,\n ) {\n super(\"Request validation failed\");\n this.name = \"ValidationError\";\n }\n}\n\n/**\n * Cross-cutting interceptor applied around every handler.\n * Use it for response envelopes, business-error → HTTP mapping, structured\n * logging, tracing spans, etc.\n *\n * Wrap `next()` in `try/catch` to intercept BOTH Zod {@link ValidationError}s\n * (thrown before the handler runs) AND business errors thrown by the handler.\n *\n * @example\n * ```ts\n * interceptor: async ({ next, route, c }) => {\n * try {\n * const data = await next();\n * return c.json({ success: true, data, error: null });\n * } catch (err) {\n * if (err instanceof ValidationError) {\n * return c.json({ success: false, error: \"validation\", issues: err.zodError.issues }, 400);\n * }\n * if (err instanceof DomainError) {\n * return c.json({ success: false, error: err.code }, err.statusCode);\n * }\n * throw err; // → falls back to onError or Hono's default 500\n * }\n * }\n * ```\n */\nexport type RouteInterceptor<TEnv extends Env = Env> = (ctx: {\n /**\n * Calls validation + handler and returns the raw value.\n * Throws {@link ValidationError} on Zod failure or any error thrown by the handler.\n */\n next: () => Promise<unknown>;\n /** Route metadata (read-only). */\n route: AnyRouteDef;\n /** Hono request context. */\n c: Context<TEnv>;\n}) => Promise<Response | unknown> | Response | unknown;\n\n/** OpenAPI document info (subset of the spec used by the helper). */\nexport interface OpenAPIInfo {\n title: string;\n version: string;\n description?: string;\n}\n\n/** OpenAPI configuration on the server. */\nexport interface OpenAPIConfig {\n /** Path served by the JSON spec (e.g. `/openapi.json`). Default: `/openapi.json`. */\n path?: string;\n /** Path serving the documentation UI. Set to `false` to disable. Default: `/docs`. */\n docsPath?: string | false;\n /** OpenAPI document info. */\n info: OpenAPIInfo;\n /** Optional servers list for the spec. */\n servers?: { url: string; description?: string }[];\n /** Optional security schemes (e.g. bearer auth). */\n securitySchemes?: Record<string, unknown>;\n /** Default security requirement applied to every operation. */\n security?: Array<Record<string, string[]>>;\n}\n\n/** Options consumed by the {@link HonoServer} constructor. */\nexport interface HonoServerOptions<TEnv extends Env = Env> {\n /**\n * API tag — only routes whose `api` matches this value are mounted.\n * If omitted, every route in the registry is mounted.\n */\n api?: string;\n\n /** Pre-resolved route registry (typically the codegen output). */\n routes: AnyRouteDef[];\n\n /** URL prefix mounted before every route path. Default: `\"\"`. */\n basePath?: string;\n\n /** Hono middlewares applied to every route (after the built-ins). */\n middlewares?: MiddlewareHandler<TEnv>[];\n\n /**\n * Alias for `middlewares` — global middlewares applied to every route.\n * If both are provided, `globalMiddlewares` is appended after `middlewares`.\n */\n globalMiddlewares?: MiddlewareHandler<TEnv>[];\n\n /**\n * If `true`, the server validates the value returned by every handler\n * against the route's `output` schema and rejects mismatches with a 500\n * response. Useful in dev / staging. Default: `false`.\n */\n validateOutput?: boolean;\n\n /** Enable verbose logging of mounted routes at startup. Default: `false`. */\n verbose?: boolean;\n\n /** OpenAPI configuration. Omit to disable. */\n openapi?: OpenAPIConfig;\n\n /** Custom 404 handler. */\n notFound?: (c: Context<TEnv>) => Response | Promise<Response>;\n\n /** Custom error handler. */\n onError?: (err: unknown, c: Context<TEnv>) => Response | Promise<Response>;\n\n /**\n * Cross-cutting interceptor wrapping every handler call.\n * Ideal for response envelopes, business-error mapping, tracing.\n * See {@link RouteInterceptor}.\n */\n interceptor?: RouteInterceptor<TEnv>;\n}\n","/**\n * OpenAPI 3.1 spec generator from {@link RouteDef} entries.\n *\n * Uses `@asteasolutions/zod-to-openapi` directly so users keep importing the\n * vanilla `zod` package (no opinionated `z` re-export required).\n */\n\nimport {\n OpenAPIRegistry,\n OpenApiGeneratorV31,\n extendZodWithOpenApi,\n} from \"@asteasolutions/zod-to-openapi\";\nimport { z } from \"zod\";\nimport type {\n AnyRouteDef,\n HttpMethod,\n OpenAPIConfig,\n PayloadSource,\n} from \"./types\";\n\n// Patches Zod prototype with `.openapi()` and enables schema → OpenAPI\n// conversion for vanilla zod schemas. Idempotent — safe to call multiple times.\nextendZodWithOpenApi(z);\n\nconst DEFAULT_RESPONSE_DESCRIPTION = \"Successful response\";\n\nfunction defaultSource(method: HttpMethod): PayloadSource {\n return method === \"get\" ? \"query\" : \"json\";\n}\n\n/** Build the OpenAPI document from the mounted route registry. */\nexport function buildOpenApiDocument(\n routes: AnyRouteDef[],\n basePath: string,\n config: OpenAPIConfig,\n): Record<string, unknown> {\n const registry = new OpenAPIRegistry();\n\n if (config.securitySchemes) {\n for (const [name, scheme] of Object.entries(config.securitySchemes)) {\n // The registry's runtime accepts any spec-shaped object; cast through\n // `unknown` to satisfy zod-to-openapi's stricter typings.\n registry.registerComponent(\n \"securitySchemes\",\n name,\n scheme as unknown as Parameters<\n typeof registry.registerComponent\n >[2],\n );\n }\n }\n\n for (const route of routes) {\n const method = route.method;\n const source = route.source ?? defaultSource(method);\n const fullPath = joinPath(basePath, route.path ?? \"/\");\n const status = route.status ?? 200;\n\n const requestBody = buildRequestBody(method, source, route.input);\n const requestQuery = buildQueryOrParam(source, route.input, \"query\");\n const requestParams = buildQueryOrParam(source, route.input, \"param\");\n const operationId = makeOperationId(method, fullPath);\n\n registry.registerPath({\n method,\n path: convertExpressPathToOpenApi(fullPath),\n operationId,\n summary: route.summary,\n description: route.description,\n tags: route.tags,\n deprecated: route.deprecated,\n security: route.security,\n // Cast: registerPath types narrow query/params to ZodObject — we accept\n // any ZodTypeAny at runtime and let users pass plain objects via z.object.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n request: {\n ...(requestQuery ? { query: requestQuery } : {}),\n ...(requestParams ? { params: requestParams } : {}),\n ...(requestBody ? { body: requestBody } : {}),\n } as any,\n responses: route.output\n ? {\n [status]: {\n description: DEFAULT_RESPONSE_DESCRIPTION,\n content: {\n \"application/json\": { schema: route.output },\n },\n },\n }\n : {\n [status]: { description: DEFAULT_RESPONSE_DESCRIPTION },\n },\n });\n }\n\n const generator = new OpenApiGeneratorV31(registry.definitions);\n const document = generator.generateDocument({\n openapi: \"3.1.0\",\n // OpenAPIInfo is structurally compatible; cast to satisfy the `x-*` index.\n info: config.info as Parameters<\n typeof generator.generateDocument\n >[0][\"info\"],\n servers: config.servers,\n security: config.security,\n });\n return document as unknown as Record<string, unknown>;\n}\n\nfunction buildRequestBody(\n method: HttpMethod,\n source: PayloadSource,\n schema: z.ZodTypeAny | undefined,\n): { content: Record<string, { schema: z.ZodTypeAny }> } | null {\n if (!schema) return null;\n if (method === \"get\") return null;\n if (source === \"json\") {\n return { content: { \"application/json\": { schema } } };\n }\n if (source === \"form\") {\n return {\n content: { \"application/x-www-form-urlencoded\": { schema } },\n };\n }\n return null;\n}\n\nfunction buildQueryOrParam(\n source: PayloadSource,\n schema: z.ZodTypeAny | undefined,\n target: \"query\" | \"param\",\n): z.ZodTypeAny | undefined {\n if (!schema) return undefined;\n if (target === \"query\" && source === \"query\") return schema;\n if (target === \"param\" && source === \"param\") return schema;\n return undefined;\n}\n\n/** Convert `:foo` style express params to `{foo}` OpenAPI placeholders. */\nfunction convertExpressPathToOpenApi(path: string): string {\n return path.replace(/:([A-Za-z0-9_]+)/g, \"{$1}\");\n}\n\nfunction joinPath(base: string, path: string): string {\n const left = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const right = path.startsWith(\"/\") ? path : `/${path}`;\n const merged = `${left}${right}`;\n return merged === \"\" ? \"/\" : merged;\n}\n\nfunction makeOperationId(method: HttpMethod, path: string): string {\n const cleaned = path\n .replace(/[{}]/g, \"\")\n .replace(/\\/+/g, \"_\")\n .replace(/[^A-Za-z0-9_]/g, \"\")\n .replace(/^_+|_+$/g, \"\");\n return `${method}_${cleaned || \"root\"}`;\n}\n\n/**\n * Render a self-contained Scalar API Reference HTML page that points to the\n * generated spec. Loaded from CDN — no build step required.\n */\nexport function renderDocsHtml(specUrl: string, title: string): string {\n const safeUrl = specUrl.replace(/\"/g, \"&quot;\");\n const safeTitle = title\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<title>${safeTitle}</title>\n</head>\n<body>\n<script id=\"api-reference\" data-url=\"${safeUrl}\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"></script>\n</body>\n</html>`;\n}\n","/**\n * `HonoServer` — high-performance, fully-typed file-based API server for\n * Firebase Cloud Functions v2 (`onRequest`).\n *\n * Designed to:\n * - rely on **prebuild codegen** (`hono:gen` CLI) for static imports → zero\n * runtime filesystem scan, optimal cold-start;\n * - expose handlers receiving a Zod-parsed payload typed end-to-end;\n * - generate the OpenAPI 3.1 spec automatically from the same Zod schemas;\n * - bridge Hono's Web Fetch API to Cloud Functions' Express-style\n * `(req, res)` via `@hono/node-server`'s request listener.\n */\n\nimport { Hono } from \"hono\";\nimport { getRequestListener } from \"@hono/node-server\";\nimport { z, ZodError } from \"zod\";\nimport type { Env } from \"hono\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nimport type {\n AnyRouteDef,\n HonoServerOptions,\n HttpMethod,\n PayloadSource,\n RouteInterceptor,\n} from \"./types\";\nimport { ValidationError } from \"./types\";\nimport { buildOpenApiDocument, renderDocsHtml } from \"./openapi\";\n\n/**\n * Minimal shape of `firebase-functions/v2/https` `onRequest` so the package\n * stays decoupled from a specific firebase-functions version. We import the\n * real type only when users pass `onRequest` to `toFunction(...)`.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype OnRequestFn = (...args: any[]) => any;\n\nexport class HonoServer<TEnv extends Env = Env> {\n private readonly app: Hono<TEnv>;\n private readonly options: HonoServerOptions<TEnv>;\n private readonly mountedRoutes: AnyRouteDef[];\n private cachedSpec: Record<string, unknown> | null = null;\n\n constructor(options: HonoServerOptions<TEnv>) {\n this.options = options;\n this.app = new Hono<TEnv>();\n this.mountedRoutes = filterRoutes(options.routes, options.api);\n\n const globalMws = [\n ...(options.middlewares ?? []),\n ...(options.globalMiddlewares ?? []),\n ];\n for (const mw of globalMws) this.app.use(\"*\", mw);\n\n this.mountRoutes();\n this.mountOpenApi();\n\n if (options.notFound) this.app.notFound(options.notFound);\n if (options.onError) this.app.onError(options.onError);\n }\n\n /** Underlying Hono instance — useful for advanced composition / tests. */\n get hono(): Hono<TEnv> {\n return this.app;\n }\n\n /** Raw `(req, res)` handler suitable for `onRequest()` / `http.createServer`. */\n get nodeHandler(): (req: IncomingMessage, res: ServerResponse) => void {\n return getRequestListener(this.app.fetch, {\n overrideGlobalObjects: false,\n });\n }\n\n /**\n * Wrap the server as a Cloud Functions v2 HTTP function.\n *\n * @param onRequest The `onRequest` factory imported from\n * `firebase-functions/v2/https` (or `firebase-functions/https`).\n * @param httpsOptions Options forwarded as the first argument to\n * `onRequest()` (region, memory, invoker, etc.).\n */\n toFunction(onRequest: OnRequestFn, httpsOptions?: Record<string, unknown>) {\n const handler = this.nodeHandler;\n if (httpsOptions) {\n return onRequest(httpsOptions, handler);\n }\n return onRequest(handler);\n }\n\n /** Generate (and cache) the OpenAPI 3.1 spec for the mounted routes. */\n buildOpenApiSpec(): Record<string, unknown> {\n if (this.cachedSpec) return this.cachedSpec;\n if (!this.options.openapi) {\n throw new Error(\"[HonoServer] openapi config not set\");\n }\n this.cachedSpec = buildOpenApiDocument(\n this.mountedRoutes,\n this.options.basePath ?? \"\",\n this.options.openapi,\n );\n return this.cachedSpec;\n }\n\n // ── Internals ─────────────────────────────────────────────────────────\n\n private mountRoutes(): void {\n const basePath = this.options.basePath ?? \"\";\n const validateOutput = this.options.validateOutput ?? false;\n const verbose = this.options.verbose ?? false;\n\n for (const route of this.mountedRoutes) {\n if (!route.path) {\n throw new Error(\n `[HonoServer] route \"${route.method.toUpperCase()} (no path)\" — missing \\`path\\`. ` +\n \"Run the codegen so the path is derived from the file location, or set it explicitly.\",\n );\n }\n\n const fullPath = joinPath(basePath, route.path);\n const middlewares = route.middlewares ?? [];\n const source: PayloadSource =\n route.source ?? (route.method === \"get\" ? \"query\" : \"json\");\n\n const handler = makeRouteHandler(\n route,\n source,\n validateOutput,\n this.options.interceptor as RouteInterceptor | undefined,\n );\n const httpMethod = route.method.toUpperCase() as\n | \"GET\"\n | \"POST\"\n | \"PUT\"\n | \"PATCH\"\n | \"DELETE\";\n // `app.on(method, path, handlers[])` accepts a variadic array of\n // handlers/middlewares — the typed `.get/.post/...` overloads don't\n // accept a spread of generic `MiddlewareHandler[]`.\n this.app.on(\n httpMethod,\n [fullPath],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...([...middlewares, handler] as any[]),\n );\n\n if (verbose) {\n // eslint-disable-next-line no-console\n console.log(\n `[HonoServer] ${route.method.toUpperCase().padEnd(6)} ${fullPath}`,\n );\n }\n }\n }\n\n private mountOpenApi(): void {\n const cfg = this.options.openapi;\n if (!cfg) return;\n const specPath = cfg.path ?? \"/openapi.json\";\n const docsPath = cfg.docsPath === undefined ? \"/docs\" : cfg.docsPath;\n const fullSpecPath = joinPath(this.options.basePath ?? \"\", specPath);\n const fullDocsPath =\n docsPath === false ? null : joinPath(this.options.basePath ?? \"\", docsPath);\n\n this.app.get(fullSpecPath, (c) => c.json(this.buildOpenApiSpec()));\n\n if (fullDocsPath) {\n // Resolve the spec URL relative to the docs page so it works whether the\n // server is mounted at `/`, behind a Firebase Functions prefix\n // (`/<project>/<region>/<funcName>/...`), or behind any reverse proxy.\n const relativeSpecUrl = relativeUrlFromTo(fullDocsPath, fullSpecPath);\n this.app.get(fullDocsPath, (c) =>\n c.html(renderDocsHtml(relativeSpecUrl, cfg.info.title)),\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction filterRoutes(\n routes: AnyRouteDef[],\n api: string | undefined,\n): AnyRouteDef[] {\n if (!api) return routes.slice();\n return routes.filter((r) => r.api === api);\n}\n\nfunction joinPath(base: string, path: string): string {\n const left = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const right = path.startsWith(\"/\") ? path : `/${path}`;\n const merged = `${left}${right}`;\n return merged === \"\" ? \"/\" : merged;\n}\n\n/**\n * Compute a URL relative to `from` that points to `to`, both being absolute\n * pathnames (e.g. `/v1/docs` → `/v1/openapi.json` becomes `openapi.json`).\n * Lets the OpenAPI UI fetch the spec without knowing the upstream prefix\n * added by Firebase Functions / reverse proxies.\n */\nfunction relativeUrlFromTo(from: string, to: string): string {\n const fromSegs = from.split(\"/\").filter(Boolean);\n const toSegs = to.split(\"/\").filter(Boolean);\n // Drop the docs page filename so we resolve relative to its directory.\n fromSegs.pop();\n let common = 0;\n while (\n common < fromSegs.length &&\n common < toSegs.length &&\n fromSegs[common] === toSegs[common]\n ) {\n common++;\n }\n const ups = fromSegs.length - common;\n const rel = [\n ...Array(ups).fill(\"..\"),\n ...toSegs.slice(common),\n ].join(\"/\");\n return rel || \"./\";\n}\n\n/**\n * Build the actual Hono handler with input validation, output validation\n * (optional), and error normalisation.\n */\nfunction makeRouteHandler(\n route: AnyRouteDef,\n source: PayloadSource,\n validateOutput: boolean,\n interceptor: RouteInterceptor | undefined,\n) {\n const inputSchema = route.input as z.ZodTypeAny | undefined;\n const outputSchema = route.output as z.ZodTypeAny | undefined;\n const status = route.status ?? 200;\n\n return async (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any,\n ): Promise<Response> => {\n // `next()` runs validation + handler. Any Zod failure throws\n // `ValidationError` so the interceptor (or default catcher) can shape it.\n const callNext = async (): Promise<unknown> => {\n let payload: unknown = undefined;\n\n if (inputSchema) {\n let raw: unknown;\n try {\n raw = await readPayload(c, source, route.method);\n } catch (err) {\n // Body parse failure → wrap as a generic Error so the interceptor\n // can decide. Use a 400-shaped Error subclass.\n throw new BadRequestError(\n err instanceof Error ? err.message : String(err),\n );\n }\n const parsed = inputSchema.safeParse(raw);\n if (!parsed.success) {\n throw new ValidationError(parsed.error, source);\n }\n payload = parsed.data;\n }\n\n const result = await (route.handler as (ctx: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n input: any;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any;\n }) => unknown)({ input: payload, c });\n\n if (validateOutput && outputSchema && !(result instanceof Response)) {\n const checked = outputSchema.safeParse(result);\n if (!checked.success) {\n throw new OutputValidationError(checked.error);\n }\n return checked.data;\n }\n return result;\n };\n\n let result: unknown;\n if (interceptor) {\n // Interceptor owns the response shape — including validation errors.\n result = await interceptor({ next: callNext, route, c });\n } else {\n // Default behaviour — handles ValidationError / BadRequestError with\n // a JSON envelope, lets unknown errors bubble to onError / Hono.\n try {\n result = await callNext();\n } catch (err) {\n const handled = defaultErrorResponse(c, err);\n if (handled) return handled;\n throw err;\n }\n }\n\n if (result instanceof Response) return result;\n return c.json(result, status);\n };\n}\n\nclass BadRequestError extends Error {\n readonly statusCode = 400 as const;\n constructor(message: string) {\n super(message);\n this.name = \"BadRequestError\";\n }\n}\n\nclass OutputValidationError extends Error {\n readonly statusCode = 500 as const;\n constructor(readonly zodError: ZodError) {\n super(\"Output validation failed\");\n this.name = \"OutputValidationError\";\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction defaultErrorResponse(c: any, err: unknown): Response | null {\n if (err instanceof ValidationError) {\n return c.json(\n {\n success: false,\n error: \"Validation failed\",\n issues: formatZodIssues(err.zodError),\n },\n 400,\n );\n }\n if (err instanceof BadRequestError) {\n return c.json(\n { success: false, error: \"Bad Request\", message: err.message },\n 400,\n );\n }\n if (err instanceof OutputValidationError) {\n return c.json(\n {\n success: false,\n error: \"Output validation failed\",\n issues: formatZodIssues(err.zodError),\n },\n 500,\n );\n }\n return null;\n}\n\nasync function readPayload(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any,\n source: PayloadSource,\n method: HttpMethod,\n): Promise<unknown> {\n switch (source) {\n case \"json\": {\n if (method === \"get\") return c.req.query();\n const text = await c.req.text();\n if (!text) return {};\n try {\n return JSON.parse(text);\n } catch (err) {\n throw new Error(\n `Invalid JSON body: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n case \"query\":\n return c.req.query();\n case \"form\": {\n const form = await c.req.parseBody();\n return form;\n }\n case \"param\":\n return c.req.param();\n default:\n return {};\n }\n}\n\nfunction formatZodIssues(error: ZodError): unknown {\n return error.issues.map((i) => ({\n path: i.path.join(\".\"),\n code: i.code,\n message: i.message,\n }));\n}\n","/**\n * Typed multi-API registry.\n *\n * Lets you declare every API tag (= every Cloud Function) in **one place**,\n * with full TypeScript safety: the `api` field of {@link defineRoute} is\n * narrowed to the registered tags, and {@link toFunctions} returns one\n * `onRequest` Cloud Function per tag, named after its key.\n *\n * @example\n * ```ts\n * // apis.ts\n * import { createApiRegistry } from \"@lpdjs/firestore-repo-service/servers/hono\";\n * import { enrichUser } from \"./middlewares/enrich-user.js\";\n *\n * export const apis = createApiRegistry({\n * v1: {\n * basePath: \"/v1\",\n * middlewares: [enrichUser],\n * openapi: { info: { title: \"Public API\", version: \"1.0.0\" } },\n * },\n * webhooks: {\n * basePath: \"/hooks\",\n * openapi: { info: { title: \"Webhooks\", version: \"1.0.0\" } },\n * },\n * });\n *\n * // Use in routes — `api` is now typed \"v1\" | \"webhooks\".\n * export const defineRoute = apis.defineRoute;\n *\n * // index.ts (Cloud Functions entrypoint)\n * import { onRequest } from \"firebase-functions/v2/https\";\n * import { apis } from \"./apis.js\";\n * import { routes } from \"./domains/__generated__/routes.js\";\n *\n * export const { v1, webhooks } = apis.toFunctions(routes, onRequest, {\n * defaults: { region: \"us-central1\", invoker: \"public\" },\n * per: { v1: { memory: \"512MiB\" } },\n * });\n * // → URLs: https://<region>-<project>.cloudfunctions.net/v1/posts\n * // https://<region>-<project>.cloudfunctions.net/webhooks/...\n * ```\n */\n\nimport type { Env } from \"hono\";\nimport type { z } from \"zod\";\n\nimport type {\n AnyRouteDef,\n HonoServerOptions,\n RouteDef,\n} from \"./types\";\nimport { HonoServer } from \"./server\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype OnRequestFn = (...args: any[]) => any;\n\n/**\n * Per-API configuration. Same shape as {@link HonoServerOptions} minus the\n * `routes` (resolved by the registry) and `api` (the registry key).\n */\nexport type ApiConfig<TEnv extends Env = Env> = Omit<\n HonoServerOptions<TEnv>,\n \"routes\" | \"api\"\n>;\n\n/** Map of API tag → its config. */\nexport type ApiConfigMap = Record<string, ApiConfig>;\n\nexport interface ApiRegistry<TMap extends ApiConfigMap> {\n /** The registered configs (read-only). */\n readonly configs: TMap;\n\n /**\n * Typed `defineRoute` — the `api` field is constrained to `keyof TMap`.\n */\n defineRoute<\n TIn extends z.ZodTypeAny | undefined = undefined,\n TOut extends z.ZodTypeAny | undefined = undefined,\n >(\n def: Omit<RouteDef<TIn, TOut>, \"api\"> & { api: keyof TMap & string },\n ): RouteDef<TIn, TOut> & { api: keyof TMap & string };\n\n /**\n * Build one Cloud Function per registered API and return them as a map\n * keyed by API tag — spread it directly into your `index.ts` exports.\n *\n * @param routes Pre-resolved route registry (typically the codegen output).\n * @param onRequest The `onRequest` factory imported from\n * `firebase-functions/v2/https`.\n * @param opts Optional defaults and per-API overrides for `httpsOptions`.\n */\n toFunctions(\n routes: AnyRouteDef[],\n onRequest: OnRequestFn,\n opts?: {\n defaults?: Record<string, unknown>;\n per?: Partial<Record<keyof TMap & string, Record<string, unknown>>>;\n },\n ): { [K in keyof TMap & string]: ReturnType<OnRequestFn> };\n\n /** Build the underlying {@link HonoServer} for a given API (escape hatch). */\n serverFor<K extends keyof TMap & string>(\n api: K,\n routes: AnyRouteDef[],\n ): HonoServer;\n}\n\n/**\n * Factory — declare every API tag once and get back a typed `defineRoute`\n * + `toFunctions`. See the file-level example.\n */\nexport function createApiRegistry<const TMap extends ApiConfigMap>(\n configs: TMap,\n): ApiRegistry<TMap> {\n return {\n configs,\n\n defineRoute(def) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return def as any;\n },\n\n serverFor(api, routes) {\n const cfg = configs[api];\n if (!cfg) {\n throw new Error(\n `[ApiRegistry] unknown api \"${api}\". Registered: ${Object.keys(configs).join(\", \")}`,\n );\n }\n return new HonoServer({ ...cfg, api, routes });\n },\n\n toFunctions(routes, onRequest, opts) {\n const out = {} as { [K in keyof TMap & string]: ReturnType<OnRequestFn> };\n for (const api of Object.keys(configs) as Array<keyof TMap & string>) {\n const httpsOpts = {\n ...(opts?.defaults ?? {}),\n ...(opts?.per?.[api] ?? {}),\n };\n const server = new HonoServer({ ...configs[api], api, routes });\n out[api] = Object.keys(httpsOpts).length\n ? server.toFunction(onRequest, httpsOpts)\n : server.toFunction(onRequest);\n }\n return out;\n },\n };\n}\n","/**\n * URL path inference from filesystem layout.\n *\n * Convention: every `routes.ts` file under the configured root contributes\n * one route. The URL path is derived from its directory chain, optionally\n * skipping segments such as `useCases` so that\n *\n * domains/activities/useCases/createOrUpdateCustom/routes.ts\n *\n * becomes\n *\n * /activities/createOrUpdateCustom\n */\n\nexport interface PathDeriveOptions {\n /** Segments to drop from the derived path (case-insensitive). */\n skipSegments: string[];\n /**\n * Casing convention applied to each remaining segment.\n * - `\"preserve\"` — keep the directory name as-is (default),\n * - `\"kebab\"` — convert camelCase / PascalCase to kebab-case.\n */\n casing: \"preserve\" | \"kebab\";\n}\n\nexport const DEFAULT_DERIVE: PathDeriveOptions = {\n skipSegments: [\"useCases\", \"useCase\", \"use-cases\", \"use-case\"],\n casing: \"preserve\",\n};\n\n/**\n * @param relativeDir POSIX-style directory path of the routes file relative\n * to the codegen root (no leading slash, no `routes.ts`).\n */\nexport function derivePath(\n relativeDir: string,\n options: PathDeriveOptions = DEFAULT_DERIVE,\n): string {\n const skip = new Set(options.skipSegments.map((s) => s.toLowerCase()));\n const parts = relativeDir\n .split(\"/\")\n .filter(Boolean)\n .filter((p) => !skip.has(p.toLowerCase()))\n .map((p) => (options.casing === \"kebab\" ? kebab(p) : p));\n return \"/\" + parts.join(\"/\");\n}\n\nfunction kebab(s: string): string {\n return s\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n}\n\n/**\n * Convert an absolute filesystem path to a POSIX-style import specifier\n * relative to a directory (the generated file's directory). Always uses\n * forward slashes and prefixes with `./` or `../` as needed.\n */\nexport function toImportSpecifier(\n fromDir: string,\n toFile: string,\n ext: string,\n): string {\n // Both paths are absolute POSIX (the CLI normalises them).\n const fromParts = splitAbs(fromDir);\n const toParts = splitAbs(toFile);\n let common = 0;\n while (\n common < fromParts.length &&\n common < toParts.length &&\n fromParts[common] === toParts[common]\n ) {\n common++;\n }\n const up = fromParts.length - common;\n const down = toParts.slice(common);\n const last = down[down.length - 1] ?? \"\";\n const stripped = last.replace(/\\.[mc]?[tj]sx?$/i, \"\");\n const finalLast = ext === \"\" ? stripped : `${stripped}${ext}`;\n down[down.length - 1] = finalLast;\n const prefix = up === 0 ? \"./\" : \"../\".repeat(up);\n return prefix + down.join(\"/\");\n}\n\nfunction splitAbs(p: string): string[] {\n const norm = p.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n return norm.split(\"/\").filter((part, i) => !(i === 0 && part === \"\"));\n}\n","/**\n * Filesystem scanner — walks the configured root and yields every route file.\n * Synchronous and dependency-free (no `fast-glob` etc.) for a tiny CLI footprint.\n */\n\nimport { readdirSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\n\nexport interface ScannerOptions {\n /** Filename to look for (default: `routes.ts`). */\n routesFile: string;\n /** Glob-like exclude segments (matched against any path part). */\n excludeSegments: string[];\n}\n\nexport const DEFAULT_SCANNER: ScannerOptions = {\n routesFile: \"routes.ts\",\n excludeSegments: [\n \"node_modules\",\n \"__generated__\",\n \"tests\",\n \"__tests__\",\n \".turbo\",\n \"dist\",\n \"build\",\n \".next\",\n ],\n};\n\nexport interface ScannedRoute {\n /** Absolute path to the routes file. */\n absPath: string;\n /** Path relative to the scan root (POSIX style). */\n relPath: string;\n /** Directory portion of `relPath` (what {@link derivePath} consumes). */\n relDir: string;\n}\n\nexport function scanRoutes(\n rootAbs: string,\n options: ScannerOptions = DEFAULT_SCANNER,\n): ScannedRoute[] {\n const found: ScannedRoute[] = [];\n walk(rootAbs, rootAbs, options, found);\n // Stable, deterministic order — important for reproducible builds.\n found.sort((a, b) => a.relPath.localeCompare(b.relPath));\n return found;\n}\n\nfunction walk(\n root: string,\n dir: string,\n opts: ScannerOptions,\n out: ScannedRoute[],\n): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n if (opts.excludeSegments.includes(name)) continue;\n const abs = join(dir, name);\n let st;\n try {\n st = statSync(abs);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n walk(root, abs, opts, out);\n } else if (st.isFile() && name === opts.routesFile) {\n const relPath = relative(root, abs).split(sep).join(\"/\");\n const relDir = relPath.replace(/\\/?[^/]+$/, \"\");\n out.push({ absPath: abs, relPath, relDir });\n }\n }\n}\n","/**\n * Generator — emits `__generated__/routes.ts` from a list of {@link ScannedRoute}s.\n *\n * The generated module statically imports every `routes.ts` so that bundlers\n * (esbuild/tsup) can tree-shake unused dependencies and Cloud Functions get\n * the smallest possible cold-start footprint.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\n\nimport {\n derivePath,\n toImportSpecifier,\n type PathDeriveOptions,\n} from \"./path-utils\";\nimport type { ScannedRoute } from \"./scanner\";\n\nexport interface GeneratorOptions {\n /** Absolute path of the file to write (e.g. `…/__generated__/routes.ts`). */\n outFile: string;\n /** Path-derivation options (skipSegments, casing). */\n derive: PathDeriveOptions;\n /**\n * Import extension used in the generated file:\n * - `\".js\"` (default) — required for ESM Node.js with `\"type\": \"module\"`,\n * - `\"\"` — bundler-friendly (Vite, no extension),\n * - `\".ts\"` — for TS-ESM runners (`tsx`, `bun`).\n */\n importExtension: string;\n /** Friendly banner placed at the top of the generated file. */\n banner?: string;\n /** Generation timestamp included in the banner. Default: `new Date()`. */\n now?: Date;\n}\n\nexport const DEFAULT_GENERATOR_BANNER =\n \"/**\\n\" +\n \" * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\\n\" +\n \" * Do not edit by hand — re-run `hono:gen` after adding / removing route files.\\n\" +\n \" */\\n\";\n\nexport interface GenerationResult {\n /** Absolute path of the file written. */\n outFile: string;\n /** Number of routes captured in the manifest. */\n routeCount: number;\n /** Human-readable summary of the derived URLs. */\n derivedPaths: { source: string; url: string }[];\n}\n\nexport function generateRoutesManifest(\n routes: ScannedRoute[],\n opts: GeneratorOptions,\n): GenerationResult {\n const outDir = dirname(opts.outFile);\n mkdirSync(outDir, { recursive: true });\n\n const banner = opts.banner ?? DEFAULT_GENERATOR_BANNER;\n const now = (opts.now ?? new Date()).toISOString();\n const ext = opts.importExtension;\n\n const importLines: string[] = [];\n const entryLines: string[] = [];\n const derivedPaths: GenerationResult[\"derivedPaths\"] = [];\n\n routes.forEach((r, i) => {\n const importPath = toImportSpecifier(outDir, r.absPath, ext);\n const url = derivePath(r.relDir, opts.derive);\n importLines.push(\n `import mod${i} from ${JSON.stringify(importPath)};`,\n );\n entryLines.push(` { __derivedPath: ${JSON.stringify(url)}, mod: mod${i} },`);\n derivedPaths.push({ source: r.relPath, url });\n });\n\n const body =\n `${banner}` +\n `// Generated at ${now} — ${routes.length} route file${routes.length === 1 ? \"\" : \"s\"}.\\n` +\n `\\n` +\n `import type { AnyRouteDef, RouteModuleDefault } from \"@lpdjs/firestore-repo-service/servers/hono\";\\n` +\n `\\n` +\n importLines.join(\"\\n\") +\n (importLines.length ? \"\\n\\n\" : \"\\n\") +\n `const __defs: { __derivedPath: string; mod: RouteModuleDefault }[] = [\\n` +\n entryLines.join(\"\\n\") +\n (entryLines.length ? \"\\n\" : \"\") +\n `];\\n\\n` +\n `export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) => {\\n` +\n ` const list = Array.isArray(mod) ? mod : [mod];\\n` +\n ` return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));\\n` +\n `});\\n`;\n\n writeFileSync(opts.outFile, body, \"utf8\");\n return {\n outFile: opts.outFile,\n routeCount: routes.length,\n derivedPaths,\n };\n}\n\n/** Convenience helper used by the CLI — combines scan + generate in one call. */\nexport function generateFromRoot(\n rootAbs: string,\n outFileRel: string,\n derive: PathDeriveOptions,\n importExtension: string,\n scan: (root: string) => ScannedRoute[],\n): GenerationResult {\n const routes = scan(rootAbs);\n const outFile = join(rootAbs, outFileRel);\n return generateRoutesManifest(routes, {\n outFile,\n derive,\n importExtension,\n });\n}\n"]}
1
+ {"version":3,"sources":["../../../src/servers/hono/types.ts","../../../src/servers/hono/openapi.ts","../../../src/servers/hono/server.ts","../../../src/servers/hono/api-registry.ts","../../../src/servers/hono/codegen/path-utils.ts","../../../src/servers/hono/codegen/scanner.ts","../../../src/servers/hono/codegen/generator.ts"],"names":["ValidationError","zodError","source","extendZodWithOpenApi","z","DEFAULT_RESPONSE_DESCRIPTION","defaultSource","method","buildOpenApiDocument","routes","basePath","config","registry","OpenAPIRegistry","name","scheme","route","fullPath","joinPath","status","requestBody","buildRequestBody","requestQuery","buildQueryOrParam","requestParams","operationId","makeOperationId","convertExpressPathToOpenApi","OpenApiGeneratorV31","schema","target","path","base","left","right","merged","cleaned","renderDocsHtml","specUrl","title","safeUrl","HonoServer","options","Hono","filterRoutes","globalMws","mw","getRequestListener","onRequest","httpsOptions","handler","validateOutput","verbose","middlewares","makeRouteHandler","httpMethod","cfg","specPath","docsPath","fullSpecPath","fullDocsPath","c","relativeSpecUrl","relativeUrlFromTo","api","r","from","to","fromSegs","toSegs","common","ups","interceptor","inputSchema","outputSchema","callNext","payload","raw","readPayload","err","BadRequestError","parsed","result","checked","OutputValidationError","handled","defaultErrorResponse","message","formatZodIssues","text","error","i","createApiRegistry","configs","def","opts","out","httpsOpts","server","DEFAULT_DERIVE","derivePath","relativeDir","skip","s","p","kebab","toImportSpecifier","fromDir","toFile","ext","fromParts","splitAbs","toParts","up","down","stripped","finalLast","part","DEFAULT_SCANNER","scanRoutes","rootAbs","found","walk","a","b","root","dir","entries","readdirSync","abs","join","st","statSync","relPath","relative","sep","relDir","DEFAULT_GENERATOR_BANNER","generateRoutesManifest","outDir","dirname","mkdirSync","banner","now","importLines","entryLines","derivedPaths","importPath","url","body","writeFileSync","generateFromRoot","outFileRel","derive","importExtension","scan","outFile"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Public types for the Hono file-based API server.\n *\n * Designed to be:\n * - **fully typed** end-to-end (Zod input/output → handler payload type),\n * - **runtime-safe** via Zod parsing on every request,\n * - **bundle-friendly** for Firebase Cloud Functions v2 cold-start (codegen\n * emits static imports — no `fs`/`import()` at runtime).\n */\n\nimport type { z, ZodError } from \"zod\";\nimport type { Context, Env, MiddlewareHandler } from \"hono\";\n\nexport type HttpMethod = \"get\" | \"post\" | \"put\" | \"patch\" | \"delete\";\n\n/** Where the validated payload comes from. */\nexport type PayloadSource = \"json\" | \"query\" | \"form\" | \"param\";\n\n/** Handler signature — receives a single typed context object. */\nexport type RouteHandler<TIn, TOut, TEnv extends Env = Env> = (ctx: {\n /** Validated (and typed) request payload. `void` when no `input` schema is defined. */\n input: TIn;\n /** Raw Hono `Context` for headers, set status, redirect, etc. */\n c: Context<TEnv>;\n}) => Promise<TOut | Response> | TOut | Response;\n\n/**\n * One route declaration. Default-exported by every `routes.ts` file inside the\n * domain tree. Use {@link defineRoute} for full type inference.\n */\nexport interface RouteDef<\n TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined,\n TOut extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined,\n TEnv extends Env = Env,\n> {\n /**\n * Logical API tag — routes sharing the same `api` are mounted on the same\n * `HonoServer` (typically one Cloud Function per `api`).\n *\n * To expose the **same logic** under several APIs with different\n * inputs/outputs, export multiple `defineRoute({...})` from the same file\n * (default + named exports are both picked up by the codegen).\n */\n api: string;\n\n /** HTTP method. */\n method: HttpMethod;\n\n /**\n * URL path appended to the server `basePath`. If omitted, the codegen will\n * derive it from the file location (e.g. `domains/activities/useCases/createCustom/routes.ts`\n * → `/activities/createCustom`).\n */\n path?: string;\n\n /**\n * Where the request payload comes from.\n * Default: `\"json\"` for body methods (POST/PUT/PATCH/DELETE), `\"query\"` for GET.\n */\n source?: PayloadSource;\n\n /** Zod schema validating the payload. Failures yield a 400 response. */\n input?: TIn;\n\n /**\n * Zod schema for the success response. Used to populate the OpenAPI spec\n * and (when `validateOutput` is enabled on the server) to assert the\n * runtime payload returned by the handler.\n */\n output?: TOut;\n\n /** Status code for the success response. Default: 200. */\n status?: number;\n\n /** Hono middlewares applied to this route only (after global middlewares). */\n middlewares?: MiddlewareHandler<TEnv>[];\n\n // ── OpenAPI metadata ─────────────────────────────────────────────────\n summary?: string;\n description?: string;\n tags?: string[];\n /** Mark the operation as deprecated in the generated spec. */\n deprecated?: boolean;\n /** Security requirements (operationId-level override). */\n security?: Array<Record<string, string[]>>;\n\n /** The request handler. */\n handler: RouteHandler<\n TIn extends z.ZodTypeAny ? z.infer<TIn> : void,\n TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown,\n TEnv\n >;\n}\n\n/** Erased `RouteDef` used by registry/codegen — handler signature is opaque. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyRouteDef = RouteDef<any, any>;\n\n/**\n * What `routes.ts` can default-export:\n * - a single {@link RouteDef} (most common case),\n * - or an array of {@link RouteDef} (e.g. expose the same useCase on\n * multiple `api` tags or under multiple paths).\n */\nexport type RouteModuleDefault = AnyRouteDef | AnyRouteDef[];\n\n/**\n * Thrown by the server when the incoming request fails Zod validation.\n * Caught by the {@link RouteInterceptor} (if any) so users can shape the\n * response envelope however they like; otherwise yields a default 400.\n */\nexport class ValidationError extends Error {\n readonly statusCode = 400 as const;\n constructor(\n /** Original Zod error — `error.issues` to enumerate field-level problems. */\n readonly zodError: ZodError,\n /** Where the offending payload came from. */\n readonly source: PayloadSource,\n ) {\n super(\"Request validation failed\");\n this.name = \"ValidationError\";\n }\n}\n\n/**\n * Cross-cutting interceptor applied around every handler.\n * Use it for response envelopes, business-error → HTTP mapping, structured\n * logging, tracing spans, etc.\n *\n * Wrap `next()` in `try/catch` to intercept BOTH Zod {@link ValidationError}s\n * (thrown before the handler runs) AND business errors thrown by the handler.\n *\n * @example\n * ```ts\n * interceptor: async ({ next, route, c }) => {\n * try {\n * const data = await next();\n * return c.json({ success: true, data, error: null });\n * } catch (err) {\n * if (err instanceof ValidationError) {\n * return c.json({ success: false, error: \"validation\", issues: err.zodError.issues }, 400);\n * }\n * if (err instanceof DomainError) {\n * return c.json({ success: false, error: err.code }, err.statusCode);\n * }\n * throw err; // → falls back to onError or Hono's default 500\n * }\n * }\n * ```\n */\nexport type RouteInterceptor<TEnv extends Env = Env> = (ctx: {\n /**\n * Calls validation + handler and returns the raw value.\n * Throws {@link ValidationError} on Zod failure or any error thrown by the handler.\n */\n next: () => Promise<unknown>;\n /** Route metadata (read-only). */\n route: AnyRouteDef;\n /** Hono request context. */\n c: Context<TEnv>;\n}) => Promise<Response | unknown> | Response | unknown;\n\n/** OpenAPI document info (subset of the spec used by the helper). */\nexport interface OpenAPIInfo {\n title: string;\n version: string;\n description?: string;\n}\n\n/** OpenAPI configuration on the server. */\nexport interface OpenAPIConfig {\n /** Path served by the JSON spec (e.g. `/openapi.json`). Default: `/openapi.json`. */\n path?: string;\n /** Path serving the documentation UI. Set to `false` to disable. Default: `/docs`. */\n docsPath?: string | false;\n /** OpenAPI document info. */\n info: OpenAPIInfo;\n /** Optional servers list for the spec. */\n servers?: { url: string; description?: string }[];\n /** Optional security schemes (e.g. bearer auth). */\n securitySchemes?: Record<string, unknown>;\n /** Default security requirement applied to every operation. */\n security?: Array<Record<string, string[]>>;\n}\n\n/** Options consumed by the {@link HonoServer} constructor. */\nexport interface HonoServerOptions<TEnv extends Env = Env> {\n /**\n * API tag — only routes whose `api` matches this value are mounted.\n * If omitted, every route in the registry is mounted.\n */\n api?: string;\n\n /** Pre-resolved route registry (typically the codegen output). */\n routes: AnyRouteDef[];\n\n /** URL prefix mounted before every route path. Default: `\"\"`. */\n basePath?: string;\n\n /** Hono middlewares applied to every route (after the built-ins). */\n middlewares?: MiddlewareHandler<TEnv>[];\n\n /**\n * Alias for `middlewares` — global middlewares applied to every route.\n * If both are provided, `globalMiddlewares` is appended after `middlewares`.\n */\n globalMiddlewares?: MiddlewareHandler<TEnv>[];\n\n /**\n * If `true`, the server validates the value returned by every handler\n * against the route's `output` schema and rejects mismatches with a 500\n * response. Useful in dev / staging. Default: `false`.\n */\n validateOutput?: boolean;\n\n /** Enable verbose logging of mounted routes at startup. Default: `false`. */\n verbose?: boolean;\n\n /** OpenAPI configuration. Omit to disable. */\n openapi?: OpenAPIConfig;\n\n /** Custom 404 handler. */\n notFound?: (c: Context<TEnv>) => Response | Promise<Response>;\n\n /** Custom error handler. */\n onError?: (err: unknown, c: Context<TEnv>) => Response | Promise<Response>;\n\n /**\n * Cross-cutting interceptor wrapping every handler call.\n * Ideal for response envelopes, business-error mapping, tracing.\n * See {@link RouteInterceptor}.\n */\n interceptor?: RouteInterceptor<TEnv>;\n}\n","/**\n * OpenAPI 3.1 spec generator from {@link RouteDef} entries.\n *\n * Uses `@asteasolutions/zod-to-openapi` directly so users keep importing the\n * vanilla `zod` package (no opinionated `z` re-export required).\n */\n\nimport {\n OpenAPIRegistry,\n OpenApiGeneratorV31,\n extendZodWithOpenApi,\n} from \"@asteasolutions/zod-to-openapi\";\nimport { z } from \"zod\";\nimport type {\n AnyRouteDef,\n HttpMethod,\n OpenAPIConfig,\n PayloadSource,\n} from \"./types\";\n\n// Patches Zod prototype with `.openapi()` and enables schema → OpenAPI\n// conversion for vanilla zod schemas. Idempotent — safe to call multiple times.\nextendZodWithOpenApi(z);\n\nconst DEFAULT_RESPONSE_DESCRIPTION = \"Successful response\";\n\nfunction defaultSource(method: HttpMethod): PayloadSource {\n return method === \"get\" ? \"query\" : \"json\";\n}\n\n/** Build the OpenAPI document from the mounted route registry. */\nexport function buildOpenApiDocument(\n routes: AnyRouteDef[],\n basePath: string,\n config: OpenAPIConfig,\n): Record<string, unknown> {\n const registry = new OpenAPIRegistry();\n\n if (config.securitySchemes) {\n for (const [name, scheme] of Object.entries(config.securitySchemes)) {\n // The registry's runtime accepts any spec-shaped object; cast through\n // `unknown` to satisfy zod-to-openapi's stricter typings.\n registry.registerComponent(\n \"securitySchemes\",\n name,\n scheme as unknown as Parameters<\n typeof registry.registerComponent\n >[2],\n );\n }\n }\n\n for (const route of routes) {\n const method = route.method;\n const source = route.source ?? defaultSource(method);\n const fullPath = joinPath(basePath, route.path ?? \"/\");\n const status = route.status ?? 200;\n\n const requestBody = buildRequestBody(method, source, route.input);\n const requestQuery = buildQueryOrParam(source, route.input, \"query\");\n const requestParams = buildQueryOrParam(source, route.input, \"param\");\n const operationId = makeOperationId(method, fullPath);\n\n registry.registerPath({\n method,\n path: convertExpressPathToOpenApi(fullPath),\n operationId,\n summary: route.summary,\n description: route.description,\n tags: route.tags,\n deprecated: route.deprecated,\n security: route.security,\n // Cast: registerPath types narrow query/params to ZodObject — we accept\n // any ZodTypeAny at runtime and let users pass plain objects via z.object.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n request: {\n ...(requestQuery ? { query: requestQuery } : {}),\n ...(requestParams ? { params: requestParams } : {}),\n ...(requestBody ? { body: requestBody } : {}),\n } as any,\n responses: route.output\n ? {\n [status]: {\n description: DEFAULT_RESPONSE_DESCRIPTION,\n content: {\n \"application/json\": { schema: route.output },\n },\n },\n }\n : {\n [status]: { description: DEFAULT_RESPONSE_DESCRIPTION },\n },\n });\n }\n\n const generator = new OpenApiGeneratorV31(registry.definitions);\n const document = generator.generateDocument({\n openapi: \"3.1.0\",\n // OpenAPIInfo is structurally compatible; cast to satisfy the `x-*` index.\n info: config.info as Parameters<\n typeof generator.generateDocument\n >[0][\"info\"],\n servers: config.servers,\n security: config.security,\n });\n return document as unknown as Record<string, unknown>;\n}\n\nfunction buildRequestBody(\n method: HttpMethod,\n source: PayloadSource,\n schema: z.ZodTypeAny | undefined,\n): { content: Record<string, { schema: z.ZodTypeAny }> } | null {\n if (!schema) return null;\n if (method === \"get\") return null;\n if (source === \"json\") {\n return { content: { \"application/json\": { schema } } };\n }\n if (source === \"form\") {\n return {\n content: { \"application/x-www-form-urlencoded\": { schema } },\n };\n }\n return null;\n}\n\nfunction buildQueryOrParam(\n source: PayloadSource,\n schema: z.ZodTypeAny | undefined,\n target: \"query\" | \"param\",\n): z.ZodTypeAny | undefined {\n if (!schema) return undefined;\n if (target === \"query\" && source === \"query\") return schema;\n if (target === \"param\" && source === \"param\") return schema;\n return undefined;\n}\n\n/** Convert `:foo` style express params to `{foo}` OpenAPI placeholders. */\nfunction convertExpressPathToOpenApi(path: string): string {\n return path.replace(/:([A-Za-z0-9_]+)/g, \"{$1}\");\n}\n\nfunction joinPath(base: string, path: string): string {\n const left = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const right = path.startsWith(\"/\") ? path : `/${path}`;\n const merged = `${left}${right}`;\n return merged === \"\" ? \"/\" : merged;\n}\n\nfunction makeOperationId(method: HttpMethod, path: string): string {\n const cleaned = path\n .replace(/[{}]/g, \"\")\n .replace(/\\/+/g, \"_\")\n .replace(/[^A-Za-z0-9_]/g, \"\")\n .replace(/^_+|_+$/g, \"\");\n return `${method}_${cleaned || \"root\"}`;\n}\n\n/**\n * Render a self-contained Scalar API Reference HTML page that points to the\n * generated spec. Loaded from CDN — no build step required.\n */\nexport function renderDocsHtml(specUrl: string, title: string): string {\n const safeUrl = specUrl.replace(/\"/g, \"&quot;\");\n const safeTitle = title\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<title>${safeTitle}</title>\n</head>\n<body>\n<script id=\"api-reference\" data-url=\"${safeUrl}\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"></script>\n</body>\n</html>`;\n}\n","/**\n * `HonoServer` — high-performance, fully-typed file-based API server for\n * Firebase Cloud Functions v2 (`onRequest`).\n *\n * Designed to:\n * - rely on **prebuild codegen** (`hono:gen` CLI) for static imports → zero\n * runtime filesystem scan, optimal cold-start;\n * - expose handlers receiving a Zod-parsed payload typed end-to-end;\n * - generate the OpenAPI 3.1 spec automatically from the same Zod schemas;\n * - bridge Hono's Web Fetch API to Cloud Functions' Express-style\n * `(req, res)` via `@hono/node-server`'s request listener.\n */\n\nimport { Hono } from \"hono\";\nimport { getRequestListener } from \"@hono/node-server\";\nimport { z, ZodError } from \"zod\";\nimport type { Env } from \"hono\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nimport type {\n AnyRouteDef,\n HonoServerOptions,\n HttpMethod,\n PayloadSource,\n RouteInterceptor,\n} from \"./types\";\nimport { ValidationError } from \"./types\";\nimport { buildOpenApiDocument, renderDocsHtml } from \"./openapi\";\n\n/**\n * Minimal shape of `firebase-functions/v2/https` `onRequest` so the package\n * stays decoupled from a specific firebase-functions version. We import the\n * real type only when users pass `onRequest` to `toFunction(...)`.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype OnRequestFn = (...args: any[]) => any;\n\nexport class HonoServer<TEnv extends Env = Env> {\n private readonly app: Hono<TEnv>;\n private readonly options: HonoServerOptions<TEnv>;\n private readonly mountedRoutes: AnyRouteDef[];\n private cachedSpec: Record<string, unknown> | null = null;\n\n constructor(options: HonoServerOptions<TEnv>) {\n this.options = options;\n this.app = new Hono<TEnv>();\n this.mountedRoutes = filterRoutes(options.routes, options.api);\n\n const globalMws = [\n ...(options.middlewares ?? []),\n ...(options.globalMiddlewares ?? []),\n ];\n for (const mw of globalMws) this.app.use(\"*\", mw);\n\n this.mountRoutes();\n this.mountOpenApi();\n\n if (options.notFound) this.app.notFound(options.notFound);\n if (options.onError) this.app.onError(options.onError);\n }\n\n /** Underlying Hono instance — useful for advanced composition / tests. */\n get hono(): Hono<TEnv> {\n return this.app;\n }\n\n /** Raw `(req, res)` handler suitable for `onRequest()` / `http.createServer`. */\n get nodeHandler(): (req: IncomingMessage, res: ServerResponse) => void {\n return getRequestListener(this.app.fetch, {\n overrideGlobalObjects: false,\n });\n }\n\n /**\n * Wrap the server as a Cloud Functions v2 HTTP function.\n *\n * @param onRequest The `onRequest` factory imported from\n * `firebase-functions/v2/https` (or `firebase-functions/https`).\n * @param httpsOptions Options forwarded as the first argument to\n * `onRequest()` (region, memory, invoker, etc.).\n */\n toFunction(onRequest: OnRequestFn, httpsOptions?: Record<string, unknown>) {\n const handler = this.nodeHandler;\n if (httpsOptions) {\n return onRequest(httpsOptions, handler);\n }\n return onRequest(handler);\n }\n\n /** Generate (and cache) the OpenAPI 3.1 spec for the mounted routes. */\n buildOpenApiSpec(): Record<string, unknown> {\n if (this.cachedSpec) return this.cachedSpec;\n if (!this.options.openapi) {\n throw new Error(\"[HonoServer] openapi config not set\");\n }\n this.cachedSpec = buildOpenApiDocument(\n this.mountedRoutes,\n this.options.basePath ?? \"\",\n this.options.openapi,\n );\n return this.cachedSpec;\n }\n\n // ── Internals ─────────────────────────────────────────────────────────\n\n private mountRoutes(): void {\n const basePath = this.options.basePath ?? \"\";\n const validateOutput = this.options.validateOutput ?? false;\n const verbose = this.options.verbose ?? false;\n\n for (const route of this.mountedRoutes) {\n if (!route.path) {\n throw new Error(\n `[HonoServer] route \"${route.method.toUpperCase()} (no path)\" — missing \\`path\\`. ` +\n \"Run the codegen so the path is derived from the file location, or set it explicitly.\",\n );\n }\n\n const fullPath = joinPath(basePath, route.path);\n const middlewares = route.middlewares ?? [];\n const source: PayloadSource =\n route.source ?? (route.method === \"get\" ? \"query\" : \"json\");\n\n const handler = makeRouteHandler(\n route,\n source,\n validateOutput,\n this.options.interceptor as RouteInterceptor | undefined,\n );\n const httpMethod = route.method.toUpperCase() as\n | \"GET\"\n | \"POST\"\n | \"PUT\"\n | \"PATCH\"\n | \"DELETE\";\n // `app.on(method, path, handlers[])` accepts a variadic array of\n // handlers/middlewares — the typed `.get/.post/...` overloads don't\n // accept a spread of generic `MiddlewareHandler[]`.\n this.app.on(\n httpMethod,\n [fullPath],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...([...middlewares, handler] as any[]),\n );\n\n if (verbose) {\n // eslint-disable-next-line no-console\n console.log(\n `[HonoServer] ${route.method.toUpperCase().padEnd(6)} ${fullPath}`,\n );\n }\n }\n }\n\n private mountOpenApi(): void {\n const cfg = this.options.openapi;\n if (!cfg) return;\n const specPath = cfg.path ?? \"/openapi.json\";\n const docsPath = cfg.docsPath === undefined ? \"/docs\" : cfg.docsPath;\n const fullSpecPath = joinPath(this.options.basePath ?? \"\", specPath);\n const fullDocsPath =\n docsPath === false ? null : joinPath(this.options.basePath ?? \"\", docsPath);\n\n this.app.get(fullSpecPath, (c) => c.json(this.buildOpenApiSpec()));\n\n if (fullDocsPath) {\n // Resolve the spec URL relative to the docs page so it works whether the\n // server is mounted at `/`, behind a Firebase Functions prefix\n // (`/<project>/<region>/<funcName>/...`), or behind any reverse proxy.\n const relativeSpecUrl = relativeUrlFromTo(fullDocsPath, fullSpecPath);\n this.app.get(fullDocsPath, (c) =>\n c.html(renderDocsHtml(relativeSpecUrl, cfg.info.title)),\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction filterRoutes(\n routes: AnyRouteDef[],\n api: string | undefined,\n): AnyRouteDef[] {\n if (!api) return routes.slice();\n return routes.filter((r) =>\n Array.isArray(r.api) ? r.api.includes(api) : r.api === api,\n );\n}\n\nfunction joinPath(base: string, path: string): string {\n const left = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const right = path.startsWith(\"/\") ? path : `/${path}`;\n const merged = `${left}${right}`;\n return merged === \"\" ? \"/\" : merged;\n}\n\n/**\n * Compute a URL relative to `from` that points to `to`, both being absolute\n * pathnames (e.g. `/v1/docs` → `/v1/openapi.json` becomes `openapi.json`).\n * Lets the OpenAPI UI fetch the spec without knowing the upstream prefix\n * added by Firebase Functions / reverse proxies.\n */\nfunction relativeUrlFromTo(from: string, to: string): string {\n const fromSegs = from.split(\"/\").filter(Boolean);\n const toSegs = to.split(\"/\").filter(Boolean);\n // Drop the docs page filename so we resolve relative to its directory.\n fromSegs.pop();\n let common = 0;\n while (\n common < fromSegs.length &&\n common < toSegs.length &&\n fromSegs[common] === toSegs[common]\n ) {\n common++;\n }\n const ups = fromSegs.length - common;\n const rel = [\n ...Array(ups).fill(\"..\"),\n ...toSegs.slice(common),\n ].join(\"/\");\n return rel || \"./\";\n}\n\n/**\n * Build the actual Hono handler with input validation, output validation\n * (optional), and error normalisation.\n */\nfunction makeRouteHandler(\n route: AnyRouteDef,\n source: PayloadSource,\n validateOutput: boolean,\n interceptor: RouteInterceptor | undefined,\n) {\n const inputSchema = route.input as z.ZodTypeAny | undefined;\n const outputSchema = route.output as z.ZodTypeAny | undefined;\n const status = route.status ?? 200;\n\n return async (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any,\n ): Promise<Response> => {\n // `next()` runs validation + handler. Any Zod failure throws\n // `ValidationError` so the interceptor (or default catcher) can shape it.\n const callNext = async (): Promise<unknown> => {\n let payload: unknown = undefined;\n\n if (inputSchema) {\n let raw: unknown;\n try {\n raw = await readPayload(c, source, route.method);\n } catch (err) {\n // Body parse failure → wrap as a generic Error so the interceptor\n // can decide. Use a 400-shaped Error subclass.\n throw new BadRequestError(\n err instanceof Error ? err.message : String(err),\n );\n }\n const parsed = inputSchema.safeParse(raw);\n if (!parsed.success) {\n throw new ValidationError(parsed.error, source);\n }\n payload = parsed.data;\n }\n\n const result = await (route.handler as (ctx: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n input: any;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any;\n }) => unknown)({ input: payload, c });\n\n if (validateOutput && outputSchema && !(result instanceof Response)) {\n const checked = outputSchema.safeParse(result);\n if (!checked.success) {\n throw new OutputValidationError(checked.error);\n }\n return checked.data;\n }\n return result;\n };\n\n let result: unknown;\n if (interceptor) {\n // Interceptor owns the response shape — including validation errors.\n result = await interceptor({ next: callNext, route, c });\n } else {\n // Default behaviour — handles ValidationError / BadRequestError with\n // a JSON envelope, lets unknown errors bubble to onError / Hono.\n try {\n result = await callNext();\n } catch (err) {\n const handled = defaultErrorResponse(c, err);\n if (handled) return handled;\n throw err;\n }\n }\n\n if (result instanceof Response) return result;\n return c.json(result, status);\n };\n}\n\nclass BadRequestError extends Error {\n readonly statusCode = 400 as const;\n constructor(message: string) {\n super(message);\n this.name = \"BadRequestError\";\n }\n}\n\nclass OutputValidationError extends Error {\n readonly statusCode = 500 as const;\n constructor(readonly zodError: ZodError) {\n super(\"Output validation failed\");\n this.name = \"OutputValidationError\";\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction defaultErrorResponse(c: any, err: unknown): Response | null {\n if (err instanceof ValidationError) {\n return c.json(\n {\n success: false,\n error: \"Validation failed\",\n issues: formatZodIssues(err.zodError),\n },\n 400,\n );\n }\n if (err instanceof BadRequestError) {\n return c.json(\n { success: false, error: \"Bad Request\", message: err.message },\n 400,\n );\n }\n if (err instanceof OutputValidationError) {\n return c.json(\n {\n success: false,\n error: \"Output validation failed\",\n issues: formatZodIssues(err.zodError),\n },\n 500,\n );\n }\n return null;\n}\n\nasync function readPayload(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: any,\n source: PayloadSource,\n method: HttpMethod,\n): Promise<unknown> {\n switch (source) {\n case \"json\": {\n if (method === \"get\") return c.req.query();\n const text = await c.req.text();\n if (!text) return {};\n try {\n return JSON.parse(text);\n } catch (err) {\n throw new Error(\n `Invalid JSON body: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n case \"query\":\n return c.req.query();\n case \"form\": {\n const form = await c.req.parseBody();\n return form;\n }\n case \"param\":\n return c.req.param();\n default:\n return {};\n }\n}\n\nfunction formatZodIssues(error: ZodError): unknown {\n return error.issues.map((i) => ({\n path: i.path.join(\".\"),\n code: i.code,\n message: i.message,\n }));\n}\n","/**\n * Typed multi-API registry.\n *\n * Lets you declare every API tag (= every Cloud Function) in **one place**,\n * with full TypeScript safety: the `api` field of {@link defineRoute} is\n * narrowed to the registered tags, and {@link toFunctions} returns one\n * `onRequest` Cloud Function per tag, named after its key.\n *\n * @example\n * ```ts\n * // apis.ts\n * import { createApiRegistry } from \"@lpdjs/firestore-repo-service/servers/hono\";\n * import { enrichUser } from \"./middlewares/enrich-user.js\";\n *\n * export const apis = createApiRegistry({\n * v1: {\n * basePath: \"/v1\",\n * middlewares: [enrichUser],\n * openapi: { info: { title: \"Public API\", version: \"1.0.0\" } },\n * },\n * webhooks: {\n * basePath: \"/hooks\",\n * openapi: { info: { title: \"Webhooks\", version: \"1.0.0\" } },\n * },\n * });\n *\n * // Use in routes — `api` is now typed \"v1\" | \"webhooks\".\n * export const defineRoute = apis.defineRoute;\n *\n * // index.ts (Cloud Functions entrypoint)\n * import { onRequest } from \"firebase-functions/v2/https\";\n * import { apis } from \"./apis.js\";\n * import { routes } from \"./domains/__generated__/routes.js\";\n *\n * export const { v1, webhooks } = apis.toFunctions(routes, onRequest, {\n * defaults: { region: \"us-central1\", invoker: \"public\" },\n * per: { v1: { memory: \"512MiB\" } },\n * });\n * // → URLs: https://<region>-<project>.cloudfunctions.net/v1/posts\n * // https://<region>-<project>.cloudfunctions.net/webhooks/...\n * ```\n */\n\nimport type { Env } from \"hono\";\nimport type { z } from \"zod\";\n\nimport type {\n AnyRouteDef,\n HonoServerOptions,\n RouteDef,\n} from \"./types\";\nimport { HonoServer } from \"./server\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype OnRequestFn = (...args: any[]) => any;\n\n/**\n * Per-API configuration. Same shape as {@link HonoServerOptions} minus the\n * `routes` (resolved by the registry) and `api` (the registry key).\n */\nexport type ApiConfig<TEnv extends Env = Env> = Omit<\n HonoServerOptions<TEnv>,\n \"routes\" | \"api\"\n>;\n\n/** Map of API tag → its config. */\nexport type ApiConfigMap = Record<string, ApiConfig>;\n\nexport interface ApiRegistry<TMap extends ApiConfigMap> {\n /** The registered configs (read-only). */\n readonly configs: TMap;\n\n /**\n * Typed `defineRoute` — the `api` field is constrained to `keyof TMap`.\n *\n * To expose the same logical endpoint under several APIs with different\n * `input` / `output` schemas, call `defineRoute` once per route and wrap\n * them in an array — per-call inference is preserved:\n *\n * ```ts\n * export default [\n * defineRoute({ api: \"v1\", input: V1Input, handler: ({ input }) => ... }),\n * defineRoute({ api: \"v2\", input: V2Input, handler: ({ input }) => ... }),\n * ];\n * ```\n */\n defineRoute<\n TIn extends z.ZodTypeAny | undefined = undefined,\n TOut extends z.ZodTypeAny | undefined = undefined,\n >(\n def: Omit<RouteDef<TIn, TOut>, \"api\"> & { api: keyof TMap & string },\n ): RouteDef<TIn, TOut> & { api: keyof TMap & string };\n\n /**\n * Build one Cloud Function per registered API and return them as a map\n * keyed by API tag — spread it directly into your `index.ts` exports.\n *\n * @param routes Pre-resolved route registry (typically the codegen output).\n * @param onRequest The `onRequest` factory imported from\n * `firebase-functions/v2/https`.\n * @param opts Optional defaults and per-API overrides for `httpsOptions`.\n */\n toFunctions(\n routes: AnyRouteDef[],\n onRequest: OnRequestFn,\n opts?: {\n defaults?: Record<string, unknown>;\n per?: Partial<Record<keyof TMap & string, Record<string, unknown>>>;\n },\n ): { [K in keyof TMap & string]: ReturnType<OnRequestFn> };\n\n /** Build the underlying {@link HonoServer} for a given API (escape hatch). */\n serverFor<K extends keyof TMap & string>(\n api: K,\n routes: AnyRouteDef[],\n ): HonoServer;\n}\n\n/**\n * Factory — declare every API tag once and get back a typed `defineRoute`\n * + `toFunctions`. See the file-level example.\n */\nexport function createApiRegistry<const TMap extends ApiConfigMap>(\n configs: TMap,\n): ApiRegistry<TMap> {\n return {\n configs,\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n defineRoute(def: any) {\n return def;\n },\n\n serverFor(api, routes) {\n const cfg = configs[api];\n if (!cfg) {\n throw new Error(\n `[ApiRegistry] unknown api \"${api}\". Registered: ${Object.keys(configs).join(\", \")}`,\n );\n }\n return new HonoServer({ ...cfg, api, routes });\n },\n\n toFunctions(routes, onRequest, opts) {\n const out = {} as { [K in keyof TMap & string]: ReturnType<OnRequestFn> };\n for (const api of Object.keys(configs) as Array<keyof TMap & string>) {\n const httpsOpts = {\n ...(opts?.defaults ?? {}),\n ...(opts?.per?.[api] ?? {}),\n };\n const server = new HonoServer({ ...configs[api], api, routes });\n out[api] = Object.keys(httpsOpts).length\n ? server.toFunction(onRequest, httpsOpts)\n : server.toFunction(onRequest);\n }\n return out;\n },\n };\n}\n","/**\n * URL path inference from filesystem layout.\n *\n * Convention: every `routes.ts` file under the configured root contributes\n * one route. The URL path is derived from its directory chain, optionally\n * skipping segments such as `useCases` so that\n *\n * domains/activities/useCases/createOrUpdateCustom/routes.ts\n *\n * becomes\n *\n * /activities/createOrUpdateCustom\n */\n\nexport interface PathDeriveOptions {\n /** Segments to drop from the derived path (case-insensitive). */\n skipSegments: string[];\n /**\n * Casing convention applied to each remaining segment.\n * - `\"preserve\"` — keep the directory name as-is (default),\n * - `\"kebab\"` — convert camelCase / PascalCase to kebab-case.\n */\n casing: \"preserve\" | \"kebab\";\n}\n\nexport const DEFAULT_DERIVE: PathDeriveOptions = {\n skipSegments: [\"useCases\", \"useCase\", \"use-cases\", \"use-case\"],\n casing: \"preserve\",\n};\n\n/**\n * @param relativeDir POSIX-style directory path of the routes file relative\n * to the codegen root (no leading slash, no `routes.ts`).\n */\nexport function derivePath(\n relativeDir: string,\n options: PathDeriveOptions = DEFAULT_DERIVE,\n): string {\n const skip = new Set(options.skipSegments.map((s) => s.toLowerCase()));\n const parts = relativeDir\n .split(\"/\")\n .filter(Boolean)\n .filter((p) => !skip.has(p.toLowerCase()))\n .map((p) => (options.casing === \"kebab\" ? kebab(p) : p));\n return \"/\" + parts.join(\"/\");\n}\n\nfunction kebab(s: string): string {\n return s\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n}\n\n/**\n * Convert an absolute filesystem path to a POSIX-style import specifier\n * relative to a directory (the generated file's directory). Always uses\n * forward slashes and prefixes with `./` or `../` as needed.\n */\nexport function toImportSpecifier(\n fromDir: string,\n toFile: string,\n ext: string,\n): string {\n // Both paths are absolute POSIX (the CLI normalises them).\n const fromParts = splitAbs(fromDir);\n const toParts = splitAbs(toFile);\n let common = 0;\n while (\n common < fromParts.length &&\n common < toParts.length &&\n fromParts[common] === toParts[common]\n ) {\n common++;\n }\n const up = fromParts.length - common;\n const down = toParts.slice(common);\n const last = down[down.length - 1] ?? \"\";\n const stripped = last.replace(/\\.[mc]?[tj]sx?$/i, \"\");\n const finalLast = ext === \"\" ? stripped : `${stripped}${ext}`;\n down[down.length - 1] = finalLast;\n const prefix = up === 0 ? \"./\" : \"../\".repeat(up);\n return prefix + down.join(\"/\");\n}\n\nfunction splitAbs(p: string): string[] {\n const norm = p.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n return norm.split(\"/\").filter((part, i) => !(i === 0 && part === \"\"));\n}\n","/**\n * Filesystem scanner — walks the configured root and yields every route file.\n * Synchronous and dependency-free (no `fast-glob` etc.) for a tiny CLI footprint.\n */\n\nimport { readdirSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\n\nexport interface ScannerOptions {\n /** Filename to look for (default: `routes.ts`). */\n routesFile: string;\n /** Glob-like exclude segments (matched against any path part). */\n excludeSegments: string[];\n}\n\nexport const DEFAULT_SCANNER: ScannerOptions = {\n routesFile: \"routes.ts\",\n excludeSegments: [\n \"node_modules\",\n \"__generated__\",\n \"tests\",\n \"__tests__\",\n \".turbo\",\n \"dist\",\n \"build\",\n \".next\",\n ],\n};\n\nexport interface ScannedRoute {\n /** Absolute path to the routes file. */\n absPath: string;\n /** Path relative to the scan root (POSIX style). */\n relPath: string;\n /** Directory portion of `relPath` (what {@link derivePath} consumes). */\n relDir: string;\n}\n\nexport function scanRoutes(\n rootAbs: string,\n options: ScannerOptions = DEFAULT_SCANNER,\n): ScannedRoute[] {\n const found: ScannedRoute[] = [];\n walk(rootAbs, rootAbs, options, found);\n // Stable, deterministic order — important for reproducible builds.\n found.sort((a, b) => a.relPath.localeCompare(b.relPath));\n return found;\n}\n\nfunction walk(\n root: string,\n dir: string,\n opts: ScannerOptions,\n out: ScannedRoute[],\n): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n if (opts.excludeSegments.includes(name)) continue;\n const abs = join(dir, name);\n let st;\n try {\n st = statSync(abs);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n walk(root, abs, opts, out);\n } else if (st.isFile() && name === opts.routesFile) {\n const relPath = relative(root, abs).split(sep).join(\"/\");\n const relDir = relPath.replace(/\\/?[^/]+$/, \"\");\n out.push({ absPath: abs, relPath, relDir });\n }\n }\n}\n","/**\n * Generator — emits `__generated__/routes.ts` from a list of {@link ScannedRoute}s.\n *\n * The generated module statically imports every `routes.ts` so that bundlers\n * (esbuild/tsup) can tree-shake unused dependencies and Cloud Functions get\n * the smallest possible cold-start footprint.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\n\nimport {\n derivePath,\n toImportSpecifier,\n type PathDeriveOptions,\n} from \"./path-utils\";\nimport type { ScannedRoute } from \"./scanner\";\n\nexport interface GeneratorOptions {\n /** Absolute path of the file to write (e.g. `…/__generated__/routes.ts`). */\n outFile: string;\n /** Path-derivation options (skipSegments, casing). */\n derive: PathDeriveOptions;\n /**\n * Import extension used in the generated file:\n * - `\".js\"` (default) — required for ESM Node.js with `\"type\": \"module\"`,\n * - `\"\"` — bundler-friendly (Vite, no extension),\n * - `\".ts\"` — for TS-ESM runners (`tsx`, `bun`).\n */\n importExtension: string;\n /** Friendly banner placed at the top of the generated file. */\n banner?: string;\n /** Generation timestamp included in the banner. Default: `new Date()`. */\n now?: Date;\n}\n\nexport const DEFAULT_GENERATOR_BANNER =\n \"/**\\n\" +\n \" * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\\n\" +\n \" * Do not edit by hand — re-run `hono:gen` after adding / removing route files.\\n\" +\n \" */\\n\";\n\nexport interface GenerationResult {\n /** Absolute path of the file written. */\n outFile: string;\n /** Number of routes captured in the manifest. */\n routeCount: number;\n /** Human-readable summary of the derived URLs. */\n derivedPaths: { source: string; url: string }[];\n}\n\nexport function generateRoutesManifest(\n routes: ScannedRoute[],\n opts: GeneratorOptions,\n): GenerationResult {\n const outDir = dirname(opts.outFile);\n mkdirSync(outDir, { recursive: true });\n\n const banner = opts.banner ?? DEFAULT_GENERATOR_BANNER;\n const now = (opts.now ?? new Date()).toISOString();\n const ext = opts.importExtension;\n\n const importLines: string[] = [];\n const entryLines: string[] = [];\n const derivedPaths: GenerationResult[\"derivedPaths\"] = [];\n\n routes.forEach((r, i) => {\n const importPath = toImportSpecifier(outDir, r.absPath, ext);\n const url = derivePath(r.relDir, opts.derive);\n importLines.push(\n `import mod${i} from ${JSON.stringify(importPath)};`,\n );\n entryLines.push(` { __derivedPath: ${JSON.stringify(url)}, mod: mod${i} },`);\n derivedPaths.push({ source: r.relPath, url });\n });\n\n const body =\n `${banner}` +\n `// Generated at ${now} — ${routes.length} route file${routes.length === 1 ? \"\" : \"s\"}.\\n` +\n `\\n` +\n `import type { AnyRouteDef, RouteModuleDefault } from \"@lpdjs/firestore-repo-service/servers/hono\";\\n` +\n `\\n` +\n importLines.join(\"\\n\") +\n (importLines.length ? \"\\n\\n\" : \"\\n\") +\n `const __defs: { __derivedPath: string; mod: RouteModuleDefault }[] = [\\n` +\n entryLines.join(\"\\n\") +\n (entryLines.length ? \"\\n\" : \"\") +\n `];\\n\\n` +\n `export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) => {\\n` +\n ` const list = Array.isArray(mod) ? mod : [mod];\\n` +\n ` return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));\\n` +\n `});\\n`;\n\n writeFileSync(opts.outFile, body, \"utf8\");\n return {\n outFile: opts.outFile,\n routeCount: routes.length,\n derivedPaths,\n };\n}\n\n/** Convenience helper used by the CLI — combines scan + generate in one call. */\nexport function generateFromRoot(\n rootAbs: string,\n outFileRel: string,\n derive: PathDeriveOptions,\n importExtension: string,\n scan: (root: string) => ScannedRoute[],\n): GenerationResult {\n const routes = scan(rootAbs);\n const outFile = join(rootAbs, outFileRel);\n return generateRoutesManifest(routes, {\n outFile,\n derive,\n importExtension,\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lpdjs/firestore-repo-service",
3
- "version": "2.2.9-beta.13",
3
+ "version": "2.2.9-beta.14",
4
4
  "workspaces": [
5
5
  "test/functions"
6
6
  ],