@lpdjs/firestore-repo-service 2.2.9-beta.8 → 2.2.9-beta.9

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.
@@ -1,4 +1,4 @@
1
- 'use strict';var hono=require('hono'),nodeServer=require('@hono/node-server'),zodToOpenapi=require('@asteasolutions/zod-to-openapi'),fs=require('fs'),path=require('path');var m=class extends Error{constructor(n,o){super("Request validation failed");this.zodError=n;this.source=o;this.statusCode=400;this.name="ValidationError";}};var O="Successful response";function z(t){return t==="get"?"query":"json"}function R(t,e,n){let o=new zodToOpenapi.OpenAPIRegistry;if(n.securitySchemes)for(let[s,i]of Object.entries(n.securitySchemes))o.registerComponent("securitySchemes",s,i);for(let s of t){let i=s.method,p=s.source??z(i),u=F(e,s.path??"/"),c=s.status??200,d=C(i,p,s.input),l=v(p,s.input,"query"),f=v(p,s.input,"param"),h=q(i,u);o.registerPath({method:i,path:j(u),operationId:h,summary:s.summary,description:s.description,tags:s.tags,deprecated:s.deprecated,security:s.security,request:{...l?{query:l}:{},...f?{params:f}:{},...d?{body:d}:{}},responses:s.output?{[c]:{description:O,content:{"application/json":{schema:s.output}}}}:{[c]:{description:O}}});}return new zodToOpenapi.OpenApiGeneratorV31(o.definitions).generateDocument({openapi:"3.1.0",info:n.info,servers:n.servers,security:n.security})}function C(t,e,n){return !n||t==="get"?null:e==="json"?{content:{"application/json":{schema:n}}}:e==="form"?{content:{"application/x-www-form-urlencoded":{schema:n}}}:null}function v(t,e,n){if(e&&(n==="query"&&t==="query"||n==="param"&&t==="param"))return e}function j(t){return t.replace(/:([A-Za-z0-9_]+)/g,"{$1}")}function F(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function q(t,e){let n=e.replace(/[{}]/g,"").replace(/\/+/g,"_").replace(/[^A-Za-z0-9_]/g,"").replace(/^_+|_+$/g,"");return `${t}_${n||"root"}`}function P(t,e){let n=t.replace(/"/g,"&quot;");return `<!doctype html>
1
+ 'use strict';var hono=require('hono'),nodeServer=require('@hono/node-server'),zodToOpenapi=require('@asteasolutions/zod-to-openapi'),fs=require('fs'),path=require('path');var m=class extends Error{constructor(n,o){super("Request validation failed");this.zodError=n;this.source=o;this.statusCode=400;this.name="ValidationError";}};var S="Successful response";function $(t){return t==="get"?"query":"json"}function R(t,e,n){let o=new zodToOpenapi.OpenAPIRegistry;if(n.securitySchemes)for(let[s,i]of Object.entries(n.securitySchemes))o.registerComponent("securitySchemes",s,i);for(let s of t){let i=s.method,p=s.source??$(i),u=F(e,s.path??"/"),c=s.status??200,d=C(i,p,s.input),l=w(p,s.input,"query"),f=w(p,s.input,"param"),h=q(i,u);o.registerPath({method:i,path:j(u),operationId:h,summary:s.summary,description:s.description,tags:s.tags,deprecated:s.deprecated,security:s.security,request:{...l?{query:l}:{},...f?{params:f}:{},...d?{body:d}:{}},responses:s.output?{[c]:{description:S,content:{"application/json":{schema:s.output}}}}:{[c]:{description:S}}});}return new zodToOpenapi.OpenApiGeneratorV31(o.definitions).generateDocument({openapi:"3.1.0",info:n.info,servers:n.servers,security:n.security})}function C(t,e,n){return !n||t==="get"?null:e==="json"?{content:{"application/json":{schema:n}}}:e==="form"?{content:{"application/x-www-form-urlencoded":{schema:n}}}:null}function w(t,e,n){if(e&&(n==="query"&&t==="query"||n==="param"&&t==="param"))return e}function j(t){return t.replace(/:([A-Za-z0-9_]+)/g,"{$1}")}function F(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function q(t,e){let n=e.replace(/[{}]/g,"").replace(/\/+/g,"_").replace(/[^A-Za-z0-9_]/g,"").replace(/^_+|_+$/g,"");return `${t}_${n||"root"}`}function v(t,e){let n=t.replace(/"/g,"&quot;");return `<!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8" />
@@ -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 w=class{constructor(e){this.cachedSpec=null;this.options=e,this.app=new hono.Hono,this.mountedRoutes=N(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=R(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 a=S(e,r.path),s=r.middlewares??[],i=r.source??(r.method==="get"?"query":"json"),p=G(r,i,n,this.options.interceptor),u=r.method.toUpperCase();this.app.on(u,[a],...s,p),o&&console.log(`[HonoServer] ${r.method.toUpperCase().padEnd(6)} ${a}`);}}mountOpenApi(){let e=this.options.openapi;if(!e)return;let n=e.path??"/openapi.json",o=e.docsPath===void 0?"/docs":e.docsPath,r=S(this.options.basePath??"",n),a=o===false?null:S(this.options.basePath??"",o);this.app.get(r,s=>s.json(this.buildOpenApiSpec())),a&&this.app.get(a,s=>s.html(P(r,e.info.title)));}};function N(t,e){return e?t.filter(n=>n.api===e):t.slice()}function S(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function G(t,e,n,o){let r=t.input,a=t.output,s=t.status??200;return async i=>{let p=async()=>{let c;if(r){let l;try{l=await U(i,e,t.method);}catch(h){throw new g(h instanceof Error?h.message:String(h))}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:i});if(n&&a&&!(d instanceof Response)){let l=a.safeParse(d);if(!l.success)throw new y(l.error);return l.data}return d},u;if(o)u=await o({next:p,route:t,c:i});else try{u=await p();}catch(c){let d=L(i,c);if(d)return d;throw c}return u instanceof Response?u:i.json(u,s)}}var g=class extends Error{constructor(n){super(n);this.statusCode=400;this.name="BadRequestError";}},y=class extends Error{constructor(n){super("Output validation failed");this.zodError=n;this.statusCode=500;this.name="OutputValidationError";}};function L(t,e){return e instanceof m?t.json({success:false,error:"Validation failed",issues:D(e.zodError)},400):e instanceof g?t.json({success:false,error:"Bad Request",message:e.message},400):e instanceof y?t.json({success:false,error:"Output validation failed",issues:D(e.zodError)},500):null}async function U(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 D(t){return t.issues.map(e=>({path:e.path.join("."),code:e.code,message:e.message}))}function B(t){return t}var T={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function x(t,e=T){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"?V(r):r).join("/")}function V(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function A(t,e,n){let o=E(t),r=E(e),a=0;for(;a<o.length&&a<r.length&&o[a]===r[a];)a++;let s=o.length-a,i=r.slice(a),u=(i[i.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),c=n===""?u:`${u}${n}`;return i[i.length-1]=c,(s===0?"./":"../".repeat(s))+i.join("/")}function E(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 Y(t,e=b){let n=[];return I(t,t,e,n),n.sort((o,r)=>o.relPath.localeCompare(r.relPath)),n}function I(t,e,n,o){let r;try{r=fs.readdirSync(e);}catch{return}for(let a of r){if(n.excludeSegments.includes(a))continue;let s=path.join(e,a),i;try{i=fs.statSync(s);}catch{continue}if(i.isDirectory())I(t,s,n,o);else if(i.isFile()&&a===n.routesFile){let p=path.relative(t,s).split(path.sep).join("/"),u=p.replace(/\/?[^/]+$/,"");o.push({absPath:s,relPath:p,relDir:u});}}}var _="/**\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 H(t,e){let n=path.dirname(e.outFile);fs.mkdirSync(n,{recursive:true});let o=e.banner??_,r=(e.now??new Date).toISOString(),a=e.importExtension,s=[],i=[],p=[];t.forEach((c,d)=>{let l=A(n,c.absPath,a),f=x(c.relDir,e.derive);s.push(`import mod${d} from ${JSON.stringify(l)};`),i.push(` { __derivedPath: ${JSON.stringify(f)}, mod: mod${d} },`),p.push({source:c.relPath,url:f});});let u=`${o}// Generated at ${r} \u2014 ${t.length} route file${t.length===1?"":"s"}.
12
+ </html>`}var P=class{constructor(e){this.cachedSpec=null;this.options=e,this.app=new hono.Hono,this.mountedRoutes=N(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=R(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 a=E(e,r.path),s=r.middlewares??[],i=r.source??(r.method==="get"?"query":"json"),p=G(r,i,n,this.options.interceptor),u=r.method.toUpperCase();this.app.on(u,[a],...s,p),o&&console.log(`[HonoServer] ${r.method.toUpperCase().padEnd(6)} ${a}`);}}mountOpenApi(){let e=this.options.openapi;if(!e)return;let n=e.path??"/openapi.json",o=e.docsPath===void 0?"/docs":e.docsPath,r=E(this.options.basePath??"",n),a=o===false?null:E(this.options.basePath??"",o);this.app.get(r,s=>s.json(this.buildOpenApiSpec())),a&&this.app.get(a,s=>s.html(v(r,e.info.title)));}};function N(t,e){return e?t.filter(n=>n.api===e):t.slice()}function E(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function G(t,e,n,o){let r=t.input,a=t.output,s=t.status??200;return async i=>{let p=async()=>{let c;if(r){let l;try{l=await U(i,e,t.method);}catch(h){throw new y(h instanceof Error?h.message:String(h))}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:i});if(n&&a&&!(d instanceof Response)){let l=a.safeParse(d);if(!l.success)throw new g(l.error);return l.data}return d},u;if(o)u=await o({next:p,route:t,c:i});else try{u=await p();}catch(c){let d=L(i,c);if(d)return d;throw c}return u instanceof Response?u:i.json(u,s)}}var y=class extends Error{constructor(n){super(n);this.statusCode=400;this.name="BadRequestError";}},g=class extends Error{constructor(n){super("Output validation failed");this.zodError=n;this.statusCode=500;this.name="OutputValidationError";}};function L(t,e){return e instanceof m?t.json({success:false,error:"Validation failed",issues:O(e.zodError)},400):e instanceof y?t.json({success:false,error:"Bad Request",message:e.message},400):e instanceof g?t.json({success:false,error:"Output validation failed",issues:O(e.zodError)},500):null}async function U(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 O(t){return t.issues.map(e=>({path:e.path.join("."),code:e.code,message:e.message}))}function B(t){return t}function V(){return function(e){return e}}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"?J(r):r).join("/")}function J(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function x(t,e,n){let o=A(t),r=A(e),a=0;for(;a<o.length&&a<r.length&&o[a]===r[a];)a++;let s=o.length-a,i=r.slice(a),u=(i[i.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),c=n===""?u:`${u}${n}`;return i[i.length-1]=c,(s===0?"./":"../".repeat(s))+i.join("/")}function A(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 ee(t,e=b){let n=[];return I(t,t,e,n),n.sort((o,r)=>o.relPath.localeCompare(r.relPath)),n}function I(t,e,n,o){let r;try{r=fs.readdirSync(e);}catch{return}for(let a of r){if(n.excludeSegments.includes(a))continue;let s=path.join(e,a),i;try{i=fs.statSync(s);}catch{continue}if(i.isDirectory())I(t,s,n,o);else if(i.isFile()&&a===n.routesFile){let p=path.relative(t,s).split(path.sep).join("/"),u=p.replace(/\/?[^/]+$/,"");o.push({absPath:s,relPath:p,relDir:u});}}}var _="/**\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 H(t,e){let n=path.dirname(e.outFile);fs.mkdirSync(n,{recursive:true});let o=e.banner??_,r=(e.now??new Date).toISOString(),a=e.importExtension,s=[],i=[],p=[];t.forEach((c,d)=>{let l=x(n,c.absPath,a),f=T(c.relDir,e.derive);s.push(`import mod${d} from ${JSON.stringify(l)};`),i.push(` { __derivedPath: ${JSON.stringify(f)}, mod: mod${d} },`),p.push({source:c.relPath,url:f});});let u=`${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,u,"utf8"),{outFile:e.outFile,routeCount:t.length,derivedPaths:p}}function oe(t,e,n,o,r){let a=r(t),s=path.join(t,e);return H(a,{outFile:s,derive:n,importExtension:o})}exports.DEFAULT_DERIVE=T;exports.DEFAULT_GENERATOR_BANNER=_;exports.DEFAULT_SCANNER=b;exports.HonoServer=w;exports.ValidationError=m;exports.buildOpenApiDocument=R;exports.defineRoute=B;exports.derivePath=x;exports.generateFromRoot=oe;exports.generateRoutesManifest=H;exports.renderDocsHtml=P;exports.scanRoutes=Y;exports.toImportSpecifier=A;//# sourceMappingURL=index.cjs.map
29
+ `;return fs.writeFileSync(e.outFile,u,"utf8"),{outFile:e.outFile,routeCount:t.length,derivedPaths:p}}function se(t,e,n,o,r){let a=r(t),s=path.join(t,e);return H(a,{outFile:s,derive:n,importExtension:o})}exports.DEFAULT_DERIVE=D;exports.DEFAULT_GENERATOR_BANNER=_;exports.DEFAULT_SCANNER=b;exports.HonoServer=P;exports.ValidationError=m;exports.buildOpenApiDocument=R;exports.createDefineRoute=V;exports.defineRoute=B;exports.derivePath=T;exports.generateFromRoot=se;exports.generateRoutesManifest=H;exports.renderDocsHtml=v;exports.scanRoutes=ee;exports.toImportSpecifier=x;//# 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/define-route.ts","../../../src/servers/hono/codegen/path-utils.ts","../../../src/servers/hono/codegen/scanner.ts","../../../src/servers/hono/codegen/generator.ts"],"names":["ValidationError","zodError","source","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","api","r","interceptor","inputSchema","outputSchema","callNext","payload","raw","readPayload","err","BadRequestError","parsed","result","checked","OutputValidationError","handled","defaultErrorResponse","message","formatZodIssues","text","error","i","defineRoute","def","DEFAULT_DERIVE","derivePath","relativeDir","skip","s","p","kebab","toImportSpecifier","fromDir","toFile","ext","fromParts","splitAbs","toParts","common","up","down","stripped","finalLast","part","DEFAULT_SCANNER","scanRoutes","rootAbs","found","walk","a","b","root","dir","opts","out","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, 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> = (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;\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> {\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[];\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 >;\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 = (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;\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 {\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[];\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[];\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) => Response | Promise<Response>;\n\n /** Custom error handler. */\n onError?: (err: unknown, c: Context) => 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;\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} from \"@asteasolutions/zod-to-openapi\";\nimport type { z } from \"zod\";\nimport type {\n AnyRouteDef,\n HttpMethod,\n OpenAPIConfig,\n PayloadSource,\n} from \"./types\";\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 { 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 {\n private readonly app: Hono;\n private readonly options: HonoServerOptions;\n private readonly mountedRoutes: AnyRouteDef[];\n private cachedSpec: Record<string, unknown> | null = null;\n\n constructor(options: HonoServerOptions) {\n this.options = options;\n this.app = new Hono();\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 {\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,\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 this.app.get(fullDocsPath, (c) =>\n c.html(renderDocsHtml(fullSpecPath, 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 * 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 * Type-safe route factory. Use as the **default export** of every\n * `routes.ts` file under your `domains/` tree.\n *\n * @example\n * ```ts\n * import { defineRoute } from \"@lpdjs/firestore-repo-service/servers/hono\";\n * import { execute as input } from \"./input.js\";\n * import { execute as output } from \"./output.js\";\n *\n * export default defineRoute({\n * api: \"v1\",\n * method: \"post\",\n * input,\n * output,\n * summary: \"Create or update a custom activity\",\n * tags: [\"activities\"],\n * handler: async (_c, payload) => {\n * // payload is typed as z.infer<typeof input>\n * const useCase = new ActivitiesCreateOrUpdateCustomUseCase(new RepositoryActivities());\n * return useCase.execute(payload);\n * },\n * });\n * ```\n */\n\nimport type { z } from \"zod\";\nimport type { RouteDef } from \"./types\";\n\nexport function defineRoute<\n TIn extends z.ZodTypeAny | undefined = undefined,\n TOut extends z.ZodTypeAny | undefined = undefined,\n>(def: RouteDef<TIn, TOut>): RouteDef<TIn, TOut> {\n return def;\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/define-route.ts","../../../src/servers/hono/codegen/path-utils.ts","../../../src/servers/hono/codegen/scanner.ts","../../../src/servers/hono/codegen/generator.ts"],"names":["ValidationError","zodError","source","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","api","r","interceptor","inputSchema","outputSchema","callNext","payload","raw","readPayload","err","BadRequestError","parsed","result","checked","OutputValidationError","handled","defaultErrorResponse","message","formatZodIssues","text","error","i","defineRoute","def","createDefineRoute","DEFAULT_DERIVE","derivePath","relativeDir","skip","s","p","kebab","toImportSpecifier","fromDir","toFile","ext","fromParts","splitAbs","toParts","common","up","down","stripped","finalLast","part","DEFAULT_SCANNER","scanRoutes","rootAbs","found","walk","a","b","root","dir","opts","out","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} from \"@asteasolutions/zod-to-openapi\";\nimport type { z } from \"zod\";\nimport type {\n AnyRouteDef,\n HttpMethod,\n OpenAPIConfig,\n PayloadSource,\n} from \"./types\";\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 this.app.get(fullDocsPath, (c) =>\n c.html(renderDocsHtml(fullSpecPath, 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 * 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 * Type-safe route factory. Use as the **default export** of every\n * `routes.ts` file under your `domains/` tree.\n *\n * @example\n * ```ts\n * import { defineRoute } from \"@lpdjs/firestore-repo-service/servers/hono\";\n * import { execute as input } from \"./input.js\";\n * import { execute as output } from \"./output.js\";\n *\n * export default defineRoute({\n * api: \"v1\",\n * method: \"post\",\n * input,\n * output,\n * summary: \"Create or update a custom activity\",\n * tags: [\"activities\"],\n * handler: async (_c, payload) => {\n * // payload is typed as z.infer<typeof input>\n * const useCase = new ActivitiesCreateOrUpdateCustomUseCase(new RepositoryActivities());\n * return useCase.execute(payload);\n * },\n * });\n * ```\n */\n\nimport type { z } from \"zod\";\nimport type { Env } from \"hono\";\nimport type { RouteDef } from \"./types\";\n\nexport function defineRoute<\n TIn extends z.ZodTypeAny | undefined = undefined,\n TOut extends z.ZodTypeAny | undefined = undefined,\n TEnv extends Env = Env,\n>(def: RouteDef<TIn, TOut, TEnv>): RouteDef<TIn, TOut, TEnv> {\n return def;\n}\n\n/**\n * Create a typed `defineRoute` bound to your app's Hono `Env` (Variables /\n * Bindings). Define your env type **once**, then import the typed factory\n * everywhere — `c` and middlewares get full inference (e.g. `c.get(\"user\")`\n * is typed without casts).\n *\n * @example\n * ```ts\n * // app-env.ts\n * import { createDefineRoute } from \"@lpdjs/firestore-repo-service/servers/hono\";\n *\n * export type AppEnv = {\n * Variables: {\n * user: { uid: string; role: \"admin\" | \"user\"; email: string };\n * };\n * };\n *\n * export const defineRoute = createDefineRoute<AppEnv>();\n *\n * // routes.ts\n * import { defineRoute } from \"../../app-env.js\";\n *\n * export default defineRoute({\n * api: \"v1\",\n * method: \"post\",\n * input: z.object({ title: z.string() }),\n * handler: async ({ input, c }) => {\n * const user = c.get(\"user\"); // ← typed { uid; role; email }\n * return new CreatePostUseCase().execute(input, user);\n * },\n * });\n * ```\n */\nexport function createDefineRoute<TEnv extends Env>() {\n return function defineRoute<\n TIn extends z.ZodTypeAny | undefined = undefined,\n TOut extends z.ZodTypeAny | undefined = undefined,\n >(def: RouteDef<TIn, TOut, TEnv>): RouteDef<TIn, TOut, TEnv> {\n return def;\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,4 +1,4 @@
1
- import { MiddlewareHandler, Context, Hono } from 'hono';
1
+ import { Env, MiddlewareHandler, Context, Hono } from 'hono';
2
2
  import { IncomingMessage, ServerResponse } from 'node:http';
3
3
  import { z, ZodError } from 'zod';
4
4
 
@@ -16,17 +16,17 @@ type HttpMethod = "get" | "post" | "put" | "patch" | "delete";
16
16
  /** Where the validated payload comes from. */
17
17
  type PayloadSource = "json" | "query" | "form" | "param";
18
18
  /** Handler signature — receives a single typed context object. */
19
- type RouteHandler<TIn, TOut> = (ctx: {
19
+ type RouteHandler<TIn, TOut, TEnv extends Env = Env> = (ctx: {
20
20
  /** Validated (and typed) request payload. `void` when no `input` schema is defined. */
21
21
  input: TIn;
22
22
  /** Raw Hono `Context` for headers, set status, redirect, etc. */
23
- c: Context;
23
+ c: Context<TEnv>;
24
24
  }) => Promise<TOut | Response> | TOut | Response;
25
25
  /**
26
26
  * One route declaration. Default-exported by every `routes.ts` file inside the
27
27
  * domain tree. Use {@link defineRoute} for full type inference.
28
28
  */
29
- interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined, TOut extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined> {
29
+ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined, TOut extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined, TEnv extends Env = Env> {
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`).
@@ -56,7 +56,7 @@ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefin
56
56
  /** Status code for the success response. Default: 200. */
57
57
  status?: number;
58
58
  /** Hono middlewares applied to this route only (after global middlewares). */
59
- middlewares?: MiddlewareHandler[];
59
+ middlewares?: MiddlewareHandler<TEnv>[];
60
60
  summary?: string;
61
61
  description?: string;
62
62
  tags?: string[];
@@ -65,7 +65,7 @@ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefin
65
65
  /** Security requirements (operationId-level override). */
66
66
  security?: Array<Record<string, string[]>>;
67
67
  /** The request handler. */
68
- handler: RouteHandler<TIn extends z.ZodTypeAny ? z.infer<TIn> : void, TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown>;
68
+ handler: RouteHandler<TIn extends z.ZodTypeAny ? z.infer<TIn> : void, TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown, TEnv>;
69
69
  }
70
70
  /** Erased `RouteDef` used by registry/codegen — handler signature is opaque. */
71
71
  type AnyRouteDef = RouteDef<any, any>;
@@ -119,7 +119,7 @@ declare class ValidationError extends Error {
119
119
  * }
120
120
  * ```
121
121
  */
122
- type RouteInterceptor = (ctx: {
122
+ type RouteInterceptor<TEnv extends Env = Env> = (ctx: {
123
123
  /**
124
124
  * Calls validation + handler and returns the raw value.
125
125
  * Throws {@link ValidationError} on Zod failure or any error thrown by the handler.
@@ -128,7 +128,7 @@ type RouteInterceptor = (ctx: {
128
128
  /** Route metadata (read-only). */
129
129
  route: AnyRouteDef;
130
130
  /** Hono request context. */
131
- c: Context;
131
+ c: Context<TEnv>;
132
132
  }) => Promise<Response | unknown> | Response | unknown;
133
133
  /** OpenAPI document info (subset of the spec used by the helper). */
134
134
  interface OpenAPIInfo {
@@ -155,7 +155,7 @@ interface OpenAPIConfig {
155
155
  security?: Array<Record<string, string[]>>;
156
156
  }
157
157
  /** Options consumed by the {@link HonoServer} constructor. */
158
- interface HonoServerOptions {
158
+ interface HonoServerOptions<TEnv extends Env = Env> {
159
159
  /**
160
160
  * API tag — only routes whose `api` matches this value are mounted.
161
161
  * If omitted, every route in the registry is mounted.
@@ -166,12 +166,12 @@ interface HonoServerOptions {
166
166
  /** URL prefix mounted before every route path. Default: `""`. */
167
167
  basePath?: string;
168
168
  /** Hono middlewares applied to every route (after the built-ins). */
169
- middlewares?: MiddlewareHandler[];
169
+ middlewares?: MiddlewareHandler<TEnv>[];
170
170
  /**
171
171
  * Alias for `middlewares` — global middlewares applied to every route.
172
172
  * If both are provided, `globalMiddlewares` is appended after `middlewares`.
173
173
  */
174
- globalMiddlewares?: MiddlewareHandler[];
174
+ globalMiddlewares?: MiddlewareHandler<TEnv>[];
175
175
  /**
176
176
  * If `true`, the server validates the value returned by every handler
177
177
  * against the route's `output` schema and rejects mismatches with a 500
@@ -183,15 +183,15 @@ interface HonoServerOptions {
183
183
  /** OpenAPI configuration. Omit to disable. */
184
184
  openapi?: OpenAPIConfig;
185
185
  /** Custom 404 handler. */
186
- notFound?: (c: Context) => Response | Promise<Response>;
186
+ notFound?: (c: Context<TEnv>) => Response | Promise<Response>;
187
187
  /** Custom error handler. */
188
- onError?: (err: unknown, c: Context) => Response | Promise<Response>;
188
+ onError?: (err: unknown, c: Context<TEnv>) => Response | Promise<Response>;
189
189
  /**
190
190
  * Cross-cutting interceptor wrapping every handler call.
191
191
  * Ideal for response envelopes, business-error mapping, tracing.
192
192
  * See {@link RouteInterceptor}.
193
193
  */
194
- interceptor?: RouteInterceptor;
194
+ interceptor?: RouteInterceptor<TEnv>;
195
195
  }
196
196
 
197
197
  /**
@@ -213,14 +213,14 @@ interface HonoServerOptions {
213
213
  * real type only when users pass `onRequest` to `toFunction(...)`.
214
214
  */
215
215
  type OnRequestFn = (...args: any[]) => any;
216
- declare class HonoServer {
216
+ declare class HonoServer<TEnv extends Env = Env> {
217
217
  private readonly app;
218
218
  private readonly options;
219
219
  private readonly mountedRoutes;
220
220
  private cachedSpec;
221
- constructor(options: HonoServerOptions);
221
+ constructor(options: HonoServerOptions<TEnv>);
222
222
  /** Underlying Hono instance — useful for advanced composition / tests. */
223
- get hono(): Hono;
223
+ get hono(): Hono<TEnv>;
224
224
  /** Raw `(req, res)` handler suitable for `onRequest()` / `http.createServer`. */
225
225
  get nodeHandler(): (req: IncomingMessage, res: ServerResponse) => void;
226
226
  /**
@@ -264,7 +264,41 @@ declare class HonoServer {
264
264
  * ```
265
265
  */
266
266
 
267
- declare function defineRoute<TIn extends z.ZodTypeAny | undefined = undefined, TOut extends z.ZodTypeAny | undefined = undefined>(def: RouteDef<TIn, TOut>): RouteDef<TIn, TOut>;
267
+ declare function defineRoute<TIn extends z.ZodTypeAny | undefined = undefined, TOut extends z.ZodTypeAny | undefined = undefined, TEnv extends Env = Env>(def: RouteDef<TIn, TOut, TEnv>): RouteDef<TIn, TOut, TEnv>;
268
+ /**
269
+ * Create a typed `defineRoute` bound to your app's Hono `Env` (Variables /
270
+ * Bindings). Define your env type **once**, then import the typed factory
271
+ * everywhere — `c` and middlewares get full inference (e.g. `c.get("user")`
272
+ * is typed without casts).
273
+ *
274
+ * @example
275
+ * ```ts
276
+ * // app-env.ts
277
+ * import { createDefineRoute } from "@lpdjs/firestore-repo-service/servers/hono";
278
+ *
279
+ * export type AppEnv = {
280
+ * Variables: {
281
+ * user: { uid: string; role: "admin" | "user"; email: string };
282
+ * };
283
+ * };
284
+ *
285
+ * export const defineRoute = createDefineRoute<AppEnv>();
286
+ *
287
+ * // routes.ts
288
+ * import { defineRoute } from "../../app-env.js";
289
+ *
290
+ * export default defineRoute({
291
+ * api: "v1",
292
+ * method: "post",
293
+ * input: z.object({ title: z.string() }),
294
+ * handler: async ({ input, c }) => {
295
+ * const user = c.get("user"); // ← typed { uid; role; email }
296
+ * return new CreatePostUseCase().execute(input, user);
297
+ * },
298
+ * });
299
+ * ```
300
+ */
301
+ declare function createDefineRoute<TEnv extends Env>(): <TIn extends z.ZodTypeAny | undefined = undefined, TOut extends z.ZodTypeAny | undefined = undefined>(def: RouteDef<TIn, TOut, TEnv>) => RouteDef<TIn, TOut, TEnv>;
268
302
 
269
303
  /**
270
304
  * OpenAPI 3.1 spec generator from {@link RouteDef} entries.
@@ -379,4 +413,4 @@ declare function generateRoutesManifest(routes: ScannedRoute[], opts: GeneratorO
379
413
  /** Convenience helper used by the CLI — combines scan + generate in one call. */
380
414
  declare function generateFromRoot(rootAbs: string, outFileRel: string, derive: PathDeriveOptions, importExtension: string, scan: (root: string) => ScannedRoute[]): GenerationResult;
381
415
 
382
- export { type AnyRouteDef, DEFAULT_DERIVE, DEFAULT_GENERATOR_BANNER, DEFAULT_SCANNER, type GenerationResult, type GeneratorOptions, HonoServer, type HonoServerOptions, type HttpMethod, type OpenAPIConfig, type OpenAPIInfo, type PathDeriveOptions, type PayloadSource, type RouteDef, type RouteHandler, type RouteInterceptor, type RouteModuleDefault, type ScannedRoute, type ScannerOptions, ValidationError, buildOpenApiDocument, defineRoute, derivePath, generateFromRoot, generateRoutesManifest, renderDocsHtml, scanRoutes, toImportSpecifier };
416
+ export { type AnyRouteDef, DEFAULT_DERIVE, DEFAULT_GENERATOR_BANNER, DEFAULT_SCANNER, type GenerationResult, type GeneratorOptions, HonoServer, type HonoServerOptions, type HttpMethod, type OpenAPIConfig, type OpenAPIInfo, type PathDeriveOptions, type PayloadSource, type RouteDef, type RouteHandler, type RouteInterceptor, type RouteModuleDefault, type ScannedRoute, type ScannerOptions, ValidationError, buildOpenApiDocument, createDefineRoute, defineRoute, derivePath, generateFromRoot, generateRoutesManifest, renderDocsHtml, scanRoutes, toImportSpecifier };
@@ -1,4 +1,4 @@
1
- import { MiddlewareHandler, Context, Hono } from 'hono';
1
+ import { Env, MiddlewareHandler, Context, Hono } from 'hono';
2
2
  import { IncomingMessage, ServerResponse } from 'node:http';
3
3
  import { z, ZodError } from 'zod';
4
4
 
@@ -16,17 +16,17 @@ type HttpMethod = "get" | "post" | "put" | "patch" | "delete";
16
16
  /** Where the validated payload comes from. */
17
17
  type PayloadSource = "json" | "query" | "form" | "param";
18
18
  /** Handler signature — receives a single typed context object. */
19
- type RouteHandler<TIn, TOut> = (ctx: {
19
+ type RouteHandler<TIn, TOut, TEnv extends Env = Env> = (ctx: {
20
20
  /** Validated (and typed) request payload. `void` when no `input` schema is defined. */
21
21
  input: TIn;
22
22
  /** Raw Hono `Context` for headers, set status, redirect, etc. */
23
- c: Context;
23
+ c: Context<TEnv>;
24
24
  }) => Promise<TOut | Response> | TOut | Response;
25
25
  /**
26
26
  * One route declaration. Default-exported by every `routes.ts` file inside the
27
27
  * domain tree. Use {@link defineRoute} for full type inference.
28
28
  */
29
- interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined, TOut extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined> {
29
+ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined, TOut extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefined, TEnv extends Env = Env> {
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`).
@@ -56,7 +56,7 @@ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefin
56
56
  /** Status code for the success response. Default: 200. */
57
57
  status?: number;
58
58
  /** Hono middlewares applied to this route only (after global middlewares). */
59
- middlewares?: MiddlewareHandler[];
59
+ middlewares?: MiddlewareHandler<TEnv>[];
60
60
  summary?: string;
61
61
  description?: string;
62
62
  tags?: string[];
@@ -65,7 +65,7 @@ interface RouteDef<TIn extends z.ZodTypeAny | undefined = z.ZodTypeAny | undefin
65
65
  /** Security requirements (operationId-level override). */
66
66
  security?: Array<Record<string, string[]>>;
67
67
  /** The request handler. */
68
- handler: RouteHandler<TIn extends z.ZodTypeAny ? z.infer<TIn> : void, TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown>;
68
+ handler: RouteHandler<TIn extends z.ZodTypeAny ? z.infer<TIn> : void, TOut extends z.ZodTypeAny ? z.infer<TOut> : unknown, TEnv>;
69
69
  }
70
70
  /** Erased `RouteDef` used by registry/codegen — handler signature is opaque. */
71
71
  type AnyRouteDef = RouteDef<any, any>;
@@ -119,7 +119,7 @@ declare class ValidationError extends Error {
119
119
  * }
120
120
  * ```
121
121
  */
122
- type RouteInterceptor = (ctx: {
122
+ type RouteInterceptor<TEnv extends Env = Env> = (ctx: {
123
123
  /**
124
124
  * Calls validation + handler and returns the raw value.
125
125
  * Throws {@link ValidationError} on Zod failure or any error thrown by the handler.
@@ -128,7 +128,7 @@ type RouteInterceptor = (ctx: {
128
128
  /** Route metadata (read-only). */
129
129
  route: AnyRouteDef;
130
130
  /** Hono request context. */
131
- c: Context;
131
+ c: Context<TEnv>;
132
132
  }) => Promise<Response | unknown> | Response | unknown;
133
133
  /** OpenAPI document info (subset of the spec used by the helper). */
134
134
  interface OpenAPIInfo {
@@ -155,7 +155,7 @@ interface OpenAPIConfig {
155
155
  security?: Array<Record<string, string[]>>;
156
156
  }
157
157
  /** Options consumed by the {@link HonoServer} constructor. */
158
- interface HonoServerOptions {
158
+ interface HonoServerOptions<TEnv extends Env = Env> {
159
159
  /**
160
160
  * API tag — only routes whose `api` matches this value are mounted.
161
161
  * If omitted, every route in the registry is mounted.
@@ -166,12 +166,12 @@ interface HonoServerOptions {
166
166
  /** URL prefix mounted before every route path. Default: `""`. */
167
167
  basePath?: string;
168
168
  /** Hono middlewares applied to every route (after the built-ins). */
169
- middlewares?: MiddlewareHandler[];
169
+ middlewares?: MiddlewareHandler<TEnv>[];
170
170
  /**
171
171
  * Alias for `middlewares` — global middlewares applied to every route.
172
172
  * If both are provided, `globalMiddlewares` is appended after `middlewares`.
173
173
  */
174
- globalMiddlewares?: MiddlewareHandler[];
174
+ globalMiddlewares?: MiddlewareHandler<TEnv>[];
175
175
  /**
176
176
  * If `true`, the server validates the value returned by every handler
177
177
  * against the route's `output` schema and rejects mismatches with a 500
@@ -183,15 +183,15 @@ interface HonoServerOptions {
183
183
  /** OpenAPI configuration. Omit to disable. */
184
184
  openapi?: OpenAPIConfig;
185
185
  /** Custom 404 handler. */
186
- notFound?: (c: Context) => Response | Promise<Response>;
186
+ notFound?: (c: Context<TEnv>) => Response | Promise<Response>;
187
187
  /** Custom error handler. */
188
- onError?: (err: unknown, c: Context) => Response | Promise<Response>;
188
+ onError?: (err: unknown, c: Context<TEnv>) => Response | Promise<Response>;
189
189
  /**
190
190
  * Cross-cutting interceptor wrapping every handler call.
191
191
  * Ideal for response envelopes, business-error mapping, tracing.
192
192
  * See {@link RouteInterceptor}.
193
193
  */
194
- interceptor?: RouteInterceptor;
194
+ interceptor?: RouteInterceptor<TEnv>;
195
195
  }
196
196
 
197
197
  /**
@@ -213,14 +213,14 @@ interface HonoServerOptions {
213
213
  * real type only when users pass `onRequest` to `toFunction(...)`.
214
214
  */
215
215
  type OnRequestFn = (...args: any[]) => any;
216
- declare class HonoServer {
216
+ declare class HonoServer<TEnv extends Env = Env> {
217
217
  private readonly app;
218
218
  private readonly options;
219
219
  private readonly mountedRoutes;
220
220
  private cachedSpec;
221
- constructor(options: HonoServerOptions);
221
+ constructor(options: HonoServerOptions<TEnv>);
222
222
  /** Underlying Hono instance — useful for advanced composition / tests. */
223
- get hono(): Hono;
223
+ get hono(): Hono<TEnv>;
224
224
  /** Raw `(req, res)` handler suitable for `onRequest()` / `http.createServer`. */
225
225
  get nodeHandler(): (req: IncomingMessage, res: ServerResponse) => void;
226
226
  /**
@@ -264,7 +264,41 @@ declare class HonoServer {
264
264
  * ```
265
265
  */
266
266
 
267
- declare function defineRoute<TIn extends z.ZodTypeAny | undefined = undefined, TOut extends z.ZodTypeAny | undefined = undefined>(def: RouteDef<TIn, TOut>): RouteDef<TIn, TOut>;
267
+ declare function defineRoute<TIn extends z.ZodTypeAny | undefined = undefined, TOut extends z.ZodTypeAny | undefined = undefined, TEnv extends Env = Env>(def: RouteDef<TIn, TOut, TEnv>): RouteDef<TIn, TOut, TEnv>;
268
+ /**
269
+ * Create a typed `defineRoute` bound to your app's Hono `Env` (Variables /
270
+ * Bindings). Define your env type **once**, then import the typed factory
271
+ * everywhere — `c` and middlewares get full inference (e.g. `c.get("user")`
272
+ * is typed without casts).
273
+ *
274
+ * @example
275
+ * ```ts
276
+ * // app-env.ts
277
+ * import { createDefineRoute } from "@lpdjs/firestore-repo-service/servers/hono";
278
+ *
279
+ * export type AppEnv = {
280
+ * Variables: {
281
+ * user: { uid: string; role: "admin" | "user"; email: string };
282
+ * };
283
+ * };
284
+ *
285
+ * export const defineRoute = createDefineRoute<AppEnv>();
286
+ *
287
+ * // routes.ts
288
+ * import { defineRoute } from "../../app-env.js";
289
+ *
290
+ * export default defineRoute({
291
+ * api: "v1",
292
+ * method: "post",
293
+ * input: z.object({ title: z.string() }),
294
+ * handler: async ({ input, c }) => {
295
+ * const user = c.get("user"); // ← typed { uid; role; email }
296
+ * return new CreatePostUseCase().execute(input, user);
297
+ * },
298
+ * });
299
+ * ```
300
+ */
301
+ declare function createDefineRoute<TEnv extends Env>(): <TIn extends z.ZodTypeAny | undefined = undefined, TOut extends z.ZodTypeAny | undefined = undefined>(def: RouteDef<TIn, TOut, TEnv>) => RouteDef<TIn, TOut, TEnv>;
268
302
 
269
303
  /**
270
304
  * OpenAPI 3.1 spec generator from {@link RouteDef} entries.
@@ -379,4 +413,4 @@ declare function generateRoutesManifest(routes: ScannedRoute[], opts: GeneratorO
379
413
  /** Convenience helper used by the CLI — combines scan + generate in one call. */
380
414
  declare function generateFromRoot(rootAbs: string, outFileRel: string, derive: PathDeriveOptions, importExtension: string, scan: (root: string) => ScannedRoute[]): GenerationResult;
381
415
 
382
- export { type AnyRouteDef, DEFAULT_DERIVE, DEFAULT_GENERATOR_BANNER, DEFAULT_SCANNER, type GenerationResult, type GeneratorOptions, HonoServer, type HonoServerOptions, type HttpMethod, type OpenAPIConfig, type OpenAPIInfo, type PathDeriveOptions, type PayloadSource, type RouteDef, type RouteHandler, type RouteInterceptor, type RouteModuleDefault, type ScannedRoute, type ScannerOptions, ValidationError, buildOpenApiDocument, defineRoute, derivePath, generateFromRoot, generateRoutesManifest, renderDocsHtml, scanRoutes, toImportSpecifier };
416
+ export { type AnyRouteDef, DEFAULT_DERIVE, DEFAULT_GENERATOR_BANNER, DEFAULT_SCANNER, type GenerationResult, type GeneratorOptions, HonoServer, type HonoServerOptions, type HttpMethod, type OpenAPIConfig, type OpenAPIInfo, type PathDeriveOptions, type PayloadSource, type RouteDef, type RouteHandler, type RouteInterceptor, type RouteModuleDefault, type ScannedRoute, type ScannerOptions, ValidationError, buildOpenApiDocument, createDefineRoute, defineRoute, derivePath, generateFromRoot, generateRoutesManifest, renderDocsHtml, scanRoutes, toImportSpecifier };
@@ -1,4 +1,4 @@
1
- import {Hono}from'hono';import {getRequestListener}from'@hono/node-server';import {OpenAPIRegistry,OpenApiGeneratorV31}from'@asteasolutions/zod-to-openapi';import {readdirSync,statSync,mkdirSync,writeFileSync}from'fs';import {join,relative,sep,dirname}from'path';var m=class extends Error{constructor(n,o){super("Request validation failed");this.zodError=n;this.source=o;this.statusCode=400;this.name="ValidationError";}};var O="Successful response";function z(t){return t==="get"?"query":"json"}function R(t,e,n){let o=new OpenAPIRegistry;if(n.securitySchemes)for(let[s,i]of Object.entries(n.securitySchemes))o.registerComponent("securitySchemes",s,i);for(let s of t){let i=s.method,p=s.source??z(i),u=F(e,s.path??"/"),c=s.status??200,d=C(i,p,s.input),l=v(p,s.input,"query"),f=v(p,s.input,"param"),h=q(i,u);o.registerPath({method:i,path:j(u),operationId:h,summary:s.summary,description:s.description,tags:s.tags,deprecated:s.deprecated,security:s.security,request:{...l?{query:l}:{},...f?{params:f}:{},...d?{body:d}:{}},responses:s.output?{[c]:{description:O,content:{"application/json":{schema:s.output}}}}:{[c]:{description:O}}});}return new OpenApiGeneratorV31(o.definitions).generateDocument({openapi:"3.1.0",info:n.info,servers:n.servers,security:n.security})}function C(t,e,n){return !n||t==="get"?null:e==="json"?{content:{"application/json":{schema:n}}}:e==="form"?{content:{"application/x-www-form-urlencoded":{schema:n}}}:null}function v(t,e,n){if(e&&(n==="query"&&t==="query"||n==="param"&&t==="param"))return e}function j(t){return t.replace(/:([A-Za-z0-9_]+)/g,"{$1}")}function F(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function q(t,e){let n=e.replace(/[{}]/g,"").replace(/\/+/g,"_").replace(/[^A-Za-z0-9_]/g,"").replace(/^_+|_+$/g,"");return `${t}_${n||"root"}`}function P(t,e){let n=t.replace(/"/g,"&quot;");return `<!doctype html>
1
+ import {Hono}from'hono';import {getRequestListener}from'@hono/node-server';import {OpenAPIRegistry,OpenApiGeneratorV31}from'@asteasolutions/zod-to-openapi';import {readdirSync,statSync,mkdirSync,writeFileSync}from'fs';import {join,relative,sep,dirname}from'path';var m=class extends Error{constructor(n,o){super("Request validation failed");this.zodError=n;this.source=o;this.statusCode=400;this.name="ValidationError";}};var S="Successful response";function $(t){return t==="get"?"query":"json"}function R(t,e,n){let o=new OpenAPIRegistry;if(n.securitySchemes)for(let[s,i]of Object.entries(n.securitySchemes))o.registerComponent("securitySchemes",s,i);for(let s of t){let i=s.method,p=s.source??$(i),u=F(e,s.path??"/"),c=s.status??200,d=C(i,p,s.input),l=w(p,s.input,"query"),f=w(p,s.input,"param"),h=q(i,u);o.registerPath({method:i,path:j(u),operationId:h,summary:s.summary,description:s.description,tags:s.tags,deprecated:s.deprecated,security:s.security,request:{...l?{query:l}:{},...f?{params:f}:{},...d?{body:d}:{}},responses:s.output?{[c]:{description:S,content:{"application/json":{schema:s.output}}}}:{[c]:{description:S}}});}return new OpenApiGeneratorV31(o.definitions).generateDocument({openapi:"3.1.0",info:n.info,servers:n.servers,security:n.security})}function C(t,e,n){return !n||t==="get"?null:e==="json"?{content:{"application/json":{schema:n}}}:e==="form"?{content:{"application/x-www-form-urlencoded":{schema:n}}}:null}function w(t,e,n){if(e&&(n==="query"&&t==="query"||n==="param"&&t==="param"))return e}function j(t){return t.replace(/:([A-Za-z0-9_]+)/g,"{$1}")}function F(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function q(t,e){let n=e.replace(/[{}]/g,"").replace(/\/+/g,"_").replace(/[^A-Za-z0-9_]/g,"").replace(/^_+|_+$/g,"");return `${t}_${n||"root"}`}function v(t,e){let n=t.replace(/"/g,"&quot;");return `<!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8" />
@@ -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 w=class{constructor(e){this.cachedSpec=null;this.options=e,this.app=new Hono,this.mountedRoutes=N(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=R(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 a=S(e,r.path),s=r.middlewares??[],i=r.source??(r.method==="get"?"query":"json"),p=G(r,i,n,this.options.interceptor),u=r.method.toUpperCase();this.app.on(u,[a],...s,p),o&&console.log(`[HonoServer] ${r.method.toUpperCase().padEnd(6)} ${a}`);}}mountOpenApi(){let e=this.options.openapi;if(!e)return;let n=e.path??"/openapi.json",o=e.docsPath===void 0?"/docs":e.docsPath,r=S(this.options.basePath??"",n),a=o===false?null:S(this.options.basePath??"",o);this.app.get(r,s=>s.json(this.buildOpenApiSpec())),a&&this.app.get(a,s=>s.html(P(r,e.info.title)));}};function N(t,e){return e?t.filter(n=>n.api===e):t.slice()}function S(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function G(t,e,n,o){let r=t.input,a=t.output,s=t.status??200;return async i=>{let p=async()=>{let c;if(r){let l;try{l=await U(i,e,t.method);}catch(h){throw new g(h instanceof Error?h.message:String(h))}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:i});if(n&&a&&!(d instanceof Response)){let l=a.safeParse(d);if(!l.success)throw new y(l.error);return l.data}return d},u;if(o)u=await o({next:p,route:t,c:i});else try{u=await p();}catch(c){let d=L(i,c);if(d)return d;throw c}return u instanceof Response?u:i.json(u,s)}}var g=class extends Error{constructor(n){super(n);this.statusCode=400;this.name="BadRequestError";}},y=class extends Error{constructor(n){super("Output validation failed");this.zodError=n;this.statusCode=500;this.name="OutputValidationError";}};function L(t,e){return e instanceof m?t.json({success:false,error:"Validation failed",issues:D(e.zodError)},400):e instanceof g?t.json({success:false,error:"Bad Request",message:e.message},400):e instanceof y?t.json({success:false,error:"Output validation failed",issues:D(e.zodError)},500):null}async function U(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 D(t){return t.issues.map(e=>({path:e.path.join("."),code:e.code,message:e.message}))}function B(t){return t}var T={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function x(t,e=T){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"?V(r):r).join("/")}function V(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function A(t,e,n){let o=E(t),r=E(e),a=0;for(;a<o.length&&a<r.length&&o[a]===r[a];)a++;let s=o.length-a,i=r.slice(a),u=(i[i.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),c=n===""?u:`${u}${n}`;return i[i.length-1]=c,(s===0?"./":"../".repeat(s))+i.join("/")}function E(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 Y(t,e=b){let n=[];return I(t,t,e,n),n.sort((o,r)=>o.relPath.localeCompare(r.relPath)),n}function I(t,e,n,o){let r;try{r=readdirSync(e);}catch{return}for(let a of r){if(n.excludeSegments.includes(a))continue;let s=join(e,a),i;try{i=statSync(s);}catch{continue}if(i.isDirectory())I(t,s,n,o);else if(i.isFile()&&a===n.routesFile){let p=relative(t,s).split(sep).join("/"),u=p.replace(/\/?[^/]+$/,"");o.push({absPath:s,relPath:p,relDir:u});}}}var _="/**\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 H(t,e){let n=dirname(e.outFile);mkdirSync(n,{recursive:true});let o=e.banner??_,r=(e.now??new Date).toISOString(),a=e.importExtension,s=[],i=[],p=[];t.forEach((c,d)=>{let l=A(n,c.absPath,a),f=x(c.relDir,e.derive);s.push(`import mod${d} from ${JSON.stringify(l)};`),i.push(` { __derivedPath: ${JSON.stringify(f)}, mod: mod${d} },`),p.push({source:c.relPath,url:f});});let u=`${o}// Generated at ${r} \u2014 ${t.length} route file${t.length===1?"":"s"}.
12
+ </html>`}var P=class{constructor(e){this.cachedSpec=null;this.options=e,this.app=new Hono,this.mountedRoutes=N(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=R(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 a=E(e,r.path),s=r.middlewares??[],i=r.source??(r.method==="get"?"query":"json"),p=G(r,i,n,this.options.interceptor),u=r.method.toUpperCase();this.app.on(u,[a],...s,p),o&&console.log(`[HonoServer] ${r.method.toUpperCase().padEnd(6)} ${a}`);}}mountOpenApi(){let e=this.options.openapi;if(!e)return;let n=e.path??"/openapi.json",o=e.docsPath===void 0?"/docs":e.docsPath,r=E(this.options.basePath??"",n),a=o===false?null:E(this.options.basePath??"",o);this.app.get(r,s=>s.json(this.buildOpenApiSpec())),a&&this.app.get(a,s=>s.html(v(r,e.info.title)));}};function N(t,e){return e?t.filter(n=>n.api===e):t.slice()}function E(t,e){let n=t.endsWith("/")?t.slice(0,-1):t,o=e.startsWith("/")?e:`/${e}`,r=`${n}${o}`;return r===""?"/":r}function G(t,e,n,o){let r=t.input,a=t.output,s=t.status??200;return async i=>{let p=async()=>{let c;if(r){let l;try{l=await U(i,e,t.method);}catch(h){throw new y(h instanceof Error?h.message:String(h))}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:i});if(n&&a&&!(d instanceof Response)){let l=a.safeParse(d);if(!l.success)throw new g(l.error);return l.data}return d},u;if(o)u=await o({next:p,route:t,c:i});else try{u=await p();}catch(c){let d=L(i,c);if(d)return d;throw c}return u instanceof Response?u:i.json(u,s)}}var y=class extends Error{constructor(n){super(n);this.statusCode=400;this.name="BadRequestError";}},g=class extends Error{constructor(n){super("Output validation failed");this.zodError=n;this.statusCode=500;this.name="OutputValidationError";}};function L(t,e){return e instanceof m?t.json({success:false,error:"Validation failed",issues:O(e.zodError)},400):e instanceof y?t.json({success:false,error:"Bad Request",message:e.message},400):e instanceof g?t.json({success:false,error:"Output validation failed",issues:O(e.zodError)},500):null}async function U(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 O(t){return t.issues.map(e=>({path:e.path.join("."),code:e.code,message:e.message}))}function B(t){return t}function V(){return function(e){return e}}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"?J(r):r).join("/")}function J(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function x(t,e,n){let o=A(t),r=A(e),a=0;for(;a<o.length&&a<r.length&&o[a]===r[a];)a++;let s=o.length-a,i=r.slice(a),u=(i[i.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),c=n===""?u:`${u}${n}`;return i[i.length-1]=c,(s===0?"./":"../".repeat(s))+i.join("/")}function A(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 ee(t,e=b){let n=[];return I(t,t,e,n),n.sort((o,r)=>o.relPath.localeCompare(r.relPath)),n}function I(t,e,n,o){let r;try{r=readdirSync(e);}catch{return}for(let a of r){if(n.excludeSegments.includes(a))continue;let s=join(e,a),i;try{i=statSync(s);}catch{continue}if(i.isDirectory())I(t,s,n,o);else if(i.isFile()&&a===n.routesFile){let p=relative(t,s).split(sep).join("/"),u=p.replace(/\/?[^/]+$/,"");o.push({absPath:s,relPath:p,relDir:u});}}}var _="/**\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 H(t,e){let n=dirname(e.outFile);mkdirSync(n,{recursive:true});let o=e.banner??_,r=(e.now??new Date).toISOString(),a=e.importExtension,s=[],i=[],p=[];t.forEach((c,d)=>{let l=x(n,c.absPath,a),f=T(c.relDir,e.derive);s.push(`import mod${d} from ${JSON.stringify(l)};`),i.push(` { __derivedPath: ${JSON.stringify(f)}, mod: mod${d} },`),p.push({source:c.relPath,url:f});});let u=`${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,u,"utf8"),{outFile:e.outFile,routeCount:t.length,derivedPaths:p}}function oe(t,e,n,o,r){let a=r(t),s=join(t,e);return H(a,{outFile:s,derive:n,importExtension:o})}export{T as DEFAULT_DERIVE,_ as DEFAULT_GENERATOR_BANNER,b as DEFAULT_SCANNER,w as HonoServer,m as ValidationError,R as buildOpenApiDocument,B as defineRoute,x as derivePath,oe as generateFromRoot,H as generateRoutesManifest,P as renderDocsHtml,Y as scanRoutes,A as toImportSpecifier};//# sourceMappingURL=index.js.map
29
+ `;return writeFileSync(e.outFile,u,"utf8"),{outFile:e.outFile,routeCount:t.length,derivedPaths:p}}function se(t,e,n,o,r){let a=r(t),s=join(t,e);return H(a,{outFile:s,derive:n,importExtension:o})}export{D as DEFAULT_DERIVE,_ as DEFAULT_GENERATOR_BANNER,b as DEFAULT_SCANNER,P as HonoServer,m as ValidationError,R as buildOpenApiDocument,V as createDefineRoute,B as defineRoute,T as derivePath,se as generateFromRoot,H as generateRoutesManifest,v as renderDocsHtml,ee as scanRoutes,x 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/define-route.ts","../../../src/servers/hono/codegen/path-utils.ts","../../../src/servers/hono/codegen/scanner.ts","../../../src/servers/hono/codegen/generator.ts"],"names":["ValidationError","zodError","source","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","api","r","interceptor","inputSchema","outputSchema","callNext","payload","raw","readPayload","err","BadRequestError","parsed","result","checked","OutputValidationError","handled","defaultErrorResponse","message","formatZodIssues","text","error","i","defineRoute","def","DEFAULT_DERIVE","derivePath","relativeDir","skip","s","p","kebab","toImportSpecifier","fromDir","toFile","ext","fromParts","splitAbs","toParts","common","up","down","stripped","finalLast","part","DEFAULT_SCANNER","scanRoutes","rootAbs","found","walk","a","b","root","dir","opts","out","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, 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> = (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;\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> {\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[];\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 >;\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 = (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;\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 {\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[];\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[];\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) => Response | Promise<Response>;\n\n /** Custom error handler. */\n onError?: (err: unknown, c: Context) => 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;\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} from \"@asteasolutions/zod-to-openapi\";\nimport type { z } from \"zod\";\nimport type {\n AnyRouteDef,\n HttpMethod,\n OpenAPIConfig,\n PayloadSource,\n} from \"./types\";\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 { 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 {\n private readonly app: Hono;\n private readonly options: HonoServerOptions;\n private readonly mountedRoutes: AnyRouteDef[];\n private cachedSpec: Record<string, unknown> | null = null;\n\n constructor(options: HonoServerOptions) {\n this.options = options;\n this.app = new Hono();\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 {\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,\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 this.app.get(fullDocsPath, (c) =>\n c.html(renderDocsHtml(fullSpecPath, 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 * 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 * Type-safe route factory. Use as the **default export** of every\n * `routes.ts` file under your `domains/` tree.\n *\n * @example\n * ```ts\n * import { defineRoute } from \"@lpdjs/firestore-repo-service/servers/hono\";\n * import { execute as input } from \"./input.js\";\n * import { execute as output } from \"./output.js\";\n *\n * export default defineRoute({\n * api: \"v1\",\n * method: \"post\",\n * input,\n * output,\n * summary: \"Create or update a custom activity\",\n * tags: [\"activities\"],\n * handler: async (_c, payload) => {\n * // payload is typed as z.infer<typeof input>\n * const useCase = new ActivitiesCreateOrUpdateCustomUseCase(new RepositoryActivities());\n * return useCase.execute(payload);\n * },\n * });\n * ```\n */\n\nimport type { z } from \"zod\";\nimport type { RouteDef } from \"./types\";\n\nexport function defineRoute<\n TIn extends z.ZodTypeAny | undefined = undefined,\n TOut extends z.ZodTypeAny | undefined = undefined,\n>(def: RouteDef<TIn, TOut>): RouteDef<TIn, TOut> {\n return def;\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/define-route.ts","../../../src/servers/hono/codegen/path-utils.ts","../../../src/servers/hono/codegen/scanner.ts","../../../src/servers/hono/codegen/generator.ts"],"names":["ValidationError","zodError","source","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","api","r","interceptor","inputSchema","outputSchema","callNext","payload","raw","readPayload","err","BadRequestError","parsed","result","checked","OutputValidationError","handled","defaultErrorResponse","message","formatZodIssues","text","error","i","defineRoute","def","createDefineRoute","DEFAULT_DERIVE","derivePath","relativeDir","skip","s","p","kebab","toImportSpecifier","fromDir","toFile","ext","fromParts","splitAbs","toParts","common","up","down","stripped","finalLast","part","DEFAULT_SCANNER","scanRoutes","rootAbs","found","walk","a","b","root","dir","opts","out","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} from \"@asteasolutions/zod-to-openapi\";\nimport type { z } from \"zod\";\nimport type {\n AnyRouteDef,\n HttpMethod,\n OpenAPIConfig,\n PayloadSource,\n} from \"./types\";\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 this.app.get(fullDocsPath, (c) =>\n c.html(renderDocsHtml(fullSpecPath, 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 * 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 * Type-safe route factory. Use as the **default export** of every\n * `routes.ts` file under your `domains/` tree.\n *\n * @example\n * ```ts\n * import { defineRoute } from \"@lpdjs/firestore-repo-service/servers/hono\";\n * import { execute as input } from \"./input.js\";\n * import { execute as output } from \"./output.js\";\n *\n * export default defineRoute({\n * api: \"v1\",\n * method: \"post\",\n * input,\n * output,\n * summary: \"Create or update a custom activity\",\n * tags: [\"activities\"],\n * handler: async (_c, payload) => {\n * // payload is typed as z.infer<typeof input>\n * const useCase = new ActivitiesCreateOrUpdateCustomUseCase(new RepositoryActivities());\n * return useCase.execute(payload);\n * },\n * });\n * ```\n */\n\nimport type { z } from \"zod\";\nimport type { Env } from \"hono\";\nimport type { RouteDef } from \"./types\";\n\nexport function defineRoute<\n TIn extends z.ZodTypeAny | undefined = undefined,\n TOut extends z.ZodTypeAny | undefined = undefined,\n TEnv extends Env = Env,\n>(def: RouteDef<TIn, TOut, TEnv>): RouteDef<TIn, TOut, TEnv> {\n return def;\n}\n\n/**\n * Create a typed `defineRoute` bound to your app's Hono `Env` (Variables /\n * Bindings). Define your env type **once**, then import the typed factory\n * everywhere — `c` and middlewares get full inference (e.g. `c.get(\"user\")`\n * is typed without casts).\n *\n * @example\n * ```ts\n * // app-env.ts\n * import { createDefineRoute } from \"@lpdjs/firestore-repo-service/servers/hono\";\n *\n * export type AppEnv = {\n * Variables: {\n * user: { uid: string; role: \"admin\" | \"user\"; email: string };\n * };\n * };\n *\n * export const defineRoute = createDefineRoute<AppEnv>();\n *\n * // routes.ts\n * import { defineRoute } from \"../../app-env.js\";\n *\n * export default defineRoute({\n * api: \"v1\",\n * method: \"post\",\n * input: z.object({ title: z.string() }),\n * handler: async ({ input, c }) => {\n * const user = c.get(\"user\"); // ← typed { uid; role; email }\n * return new CreatePostUseCase().execute(input, user);\n * },\n * });\n * ```\n */\nexport function createDefineRoute<TEnv extends Env>() {\n return function defineRoute<\n TIn extends z.ZodTypeAny | undefined = undefined,\n TOut extends z.ZodTypeAny | undefined = undefined,\n >(def: RouteDef<TIn, TOut, TEnv>): RouteDef<TIn, TOut, TEnv> {\n return def;\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.8",
3
+ "version": "2.2.9-beta.9",
4
4
  "workspaces": [
5
5
  "test/functions"
6
6
  ],