@geekmidas/constructs 10.0.0-alpha.101 → 10.0.0-alpha.103

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.
Files changed (100) hide show
  1. package/dist/{Endpoint-BG4UJ9oA.mjs → Endpoint-BW-nFdL_.mjs} +67 -3
  2. package/dist/Endpoint-BW-nFdL_.mjs.map +1 -0
  3. package/dist/{Endpoint-aCvBMPoH.d.cts → Endpoint-BWyKCa2x.d.cts} +47 -9
  4. package/dist/Endpoint-BWyKCa2x.d.cts.map +1 -0
  5. package/dist/{Endpoint-BoUg8ocM.cjs → Endpoint-B_UHlLxv.cjs} +78 -2
  6. package/dist/Endpoint-B_UHlLxv.cjs.map +1 -0
  7. package/dist/{Endpoint-Du-NwPX2.d.mts → Endpoint-C3r2OzOD.d.mts} +47 -9
  8. package/dist/Endpoint-C3r2OzOD.d.mts.map +1 -0
  9. package/dist/{EndpointFactory-BDtsrEvJ.mjs → EndpointFactory-Bbmx4vXc.mjs} +7 -2
  10. package/dist/EndpointFactory-Bbmx4vXc.mjs.map +1 -0
  11. package/dist/{EndpointFactory-I7s1co3Y.d.cts → EndpointFactory-C1-IBwm4.d.cts} +9 -4
  12. package/dist/EndpointFactory-C1-IBwm4.d.cts.map +1 -0
  13. package/dist/{EndpointFactory-DMYdJCOj.d.mts → EndpointFactory-C9eVhDKF.d.mts} +9 -4
  14. package/dist/EndpointFactory-C9eVhDKF.d.mts.map +1 -0
  15. package/dist/{EndpointFactory-DJ7gXLpR.cjs → EndpointFactory-DeZTvDjU.cjs} +7 -2
  16. package/dist/{EndpointFactory-BDtsrEvJ.mjs.map → EndpointFactory-DeZTvDjU.cjs.map} +1 -1
  17. package/dist/{HonoEndpointAdaptor-PtPlbeKY.mjs → HonoEndpointAdaptor-RSvRnXSl.mjs} +38 -14
  18. package/dist/HonoEndpointAdaptor-RSvRnXSl.mjs.map +1 -0
  19. package/dist/{HonoEndpointAdaptor-zVzVLJEO.cjs → HonoEndpointAdaptor-eCKyEOl_.cjs} +41 -17
  20. package/dist/HonoEndpointAdaptor-eCKyEOl_.cjs.map +1 -0
  21. package/dist/adaptors/aws.cjs +66 -27
  22. package/dist/adaptors/aws.cjs.map +1 -1
  23. package/dist/adaptors/aws.d.cts +23 -10
  24. package/dist/adaptors/aws.d.cts.map +1 -1
  25. package/dist/adaptors/aws.d.mts +23 -10
  26. package/dist/adaptors/aws.d.mts.map +1 -1
  27. package/dist/adaptors/aws.mjs +63 -24
  28. package/dist/adaptors/aws.mjs.map +1 -1
  29. package/dist/adaptors/hono.cjs +1 -1
  30. package/dist/adaptors/hono.d.cts +1 -1
  31. package/dist/adaptors/hono.d.cts.map +1 -1
  32. package/dist/adaptors/hono.d.mts +1 -1
  33. package/dist/adaptors/hono.d.mts.map +1 -1
  34. package/dist/adaptors/hono.mjs +1 -1
  35. package/dist/adaptors/testing.cjs +26 -13
  36. package/dist/adaptors/testing.cjs.map +1 -1
  37. package/dist/adaptors/testing.d.cts +2 -2
  38. package/dist/adaptors/testing.d.cts.map +1 -1
  39. package/dist/adaptors/testing.d.mts +2 -2
  40. package/dist/adaptors/testing.d.mts.map +1 -1
  41. package/dist/adaptors/testing.mjs +24 -11
  42. package/dist/adaptors/testing.mjs.map +1 -1
  43. package/dist/credential-DYymbp_7.cjs +257 -0
  44. package/dist/credential-DYymbp_7.cjs.map +1 -0
  45. package/dist/credential-DfSAOOdY.mjs +222 -0
  46. package/dist/credential-DfSAOOdY.mjs.map +1 -0
  47. package/dist/credential.cjs +7 -155
  48. package/dist/credential.d.cts +2 -86
  49. package/dist/credential.d.mts +2 -86
  50. package/dist/credential.mjs +2 -152
  51. package/dist/endpoints/index.cjs +15 -9
  52. package/dist/endpoints/index.d.cts +57 -3
  53. package/dist/endpoints/index.d.cts.map +1 -1
  54. package/dist/endpoints/index.d.mts +57 -3
  55. package/dist/endpoints/index.d.mts.map +1 -1
  56. package/dist/endpoints/index.mjs +4 -4
  57. package/dist/external-api.cjs +3 -39
  58. package/dist/external-api.cjs.map +1 -1
  59. package/dist/external-api.d.cts +2 -2
  60. package/dist/external-api.d.mts +2 -2
  61. package/dist/external-api.mjs +1 -37
  62. package/dist/external-api.mjs.map +1 -1
  63. package/dist/fake-BcHTPz7N.d.mts +201 -0
  64. package/dist/fake-BcHTPz7N.d.mts.map +1 -0
  65. package/dist/fake-Dvion_Fh.d.cts +201 -0
  66. package/dist/fake-Dvion_Fh.d.cts.map +1 -0
  67. package/dist/{processAudits-DasOZjPl.cjs → rateLimit-BwHcMECR.cjs} +26 -5
  68. package/dist/rateLimit-BwHcMECR.cjs.map +1 -0
  69. package/dist/{processAudits-BRRVVHUB.mjs → rateLimit-DbOTsP01.mjs} +23 -2
  70. package/dist/rateLimit-DbOTsP01.mjs.map +1 -0
  71. package/dist/{lazyAccessors-CFanua0O.mjs → readRequestBody-BTIl3k-D.mjs} +99 -2
  72. package/dist/readRequestBody-BTIl3k-D.mjs.map +1 -0
  73. package/dist/{lazyAccessors-hJKGv8Ay.cjs → readRequestBody-Dej_L3c4.cjs} +122 -1
  74. package/dist/readRequestBody-Dej_L3c4.cjs.map +1 -0
  75. package/dist/rest-api.cjs +1 -1
  76. package/dist/rest-api.d.cts +2 -2
  77. package/dist/rest-api.d.mts +2 -2
  78. package/dist/rest-api.mjs +1 -1
  79. package/package.json +31 -31
  80. package/dist/Endpoint-BG4UJ9oA.mjs.map +0 -1
  81. package/dist/Endpoint-BoUg8ocM.cjs.map +0 -1
  82. package/dist/Endpoint-Du-NwPX2.d.mts.map +0 -1
  83. package/dist/Endpoint-aCvBMPoH.d.cts.map +0 -1
  84. package/dist/EndpointFactory-DJ7gXLpR.cjs.map +0 -1
  85. package/dist/EndpointFactory-DMYdJCOj.d.mts.map +0 -1
  86. package/dist/EndpointFactory-I7s1co3Y.d.cts.map +0 -1
  87. package/dist/HonoEndpointAdaptor-PtPlbeKY.mjs.map +0 -1
  88. package/dist/HonoEndpointAdaptor-zVzVLJEO.cjs.map +0 -1
  89. package/dist/credential.cjs.map +0 -1
  90. package/dist/credential.d.cts.map +0 -1
  91. package/dist/credential.d.mts.map +0 -1
  92. package/dist/credential.mjs.map +0 -1
  93. package/dist/external-api-MJ8id87V.d.mts +0 -105
  94. package/dist/external-api-MJ8id87V.d.mts.map +0 -1
  95. package/dist/external-api-O107yzZJ.d.cts +0 -105
  96. package/dist/external-api-O107yzZJ.d.cts.map +0 -1
  97. package/dist/lazyAccessors-CFanua0O.mjs.map +0 -1
  98. package/dist/lazyAccessors-hJKGv8Ay.cjs.map +0 -1
  99. package/dist/processAudits-BRRVVHUB.mjs.map +0 -1
  100. package/dist/processAudits-DasOZjPl.cjs.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { i as publishConstructEvents } from "./functions-BFizikJU.mjs";
2
- import { n as ResponseBuilder, t as Endpoint } from "./Endpoint-BG4UJ9oA.mjs";
3
- import { a as createHonoHeaders, i as createHonoCookies } from "./lazyAccessors-CFanua0O.mjs";
4
- import { i as loadRls, n as executeWithAuditTransaction, r as loadRateLimit, t as createAuditContext } from "./processAudits-BRRVVHUB.mjs";
5
- import { wrapError } from "@geekmidas/errors";
2
+ import { r as ResponseBuilder, t as Endpoint } from "./Endpoint-BW-nFdL_.mjs";
3
+ import { c as createHonoCookies, i as readRequestBody, l as createHonoHeaders } from "./readRequestBody-BTIl3k-D.mjs";
4
+ import { i as loadRls, n as createAuditContext, r as executeWithAuditTransaction, t as enforceRateLimit } from "./rateLimit-DbOTsP01.mjs";
5
+ import { isHttpError, wrapError } from "@geekmidas/errors";
6
6
  import { ServiceDiscovery, runWithRequestContext } from "@geekmidas/services";
7
7
  import { setCookie } from "hono/cookie";
8
8
  import { Hono } from "hono";
@@ -144,7 +144,20 @@ var HonoEndpoint = class HonoEndpoint {
144
144
  const method = endpoint.method.toLowerCase();
145
145
  const features = analyzeEndpointFeatures(endpoint);
146
146
  const validators = [];
147
- if (features.hasBodyValidation) validators.push(validator("json", (value, c) => HonoEndpoint.validate(c, value, endpoint.input?.body)));
147
+ if (features.hasBodyValidation) validators.push(async (c, next) => {
148
+ let raw;
149
+ try {
150
+ raw = await readRequestBody(c.req, c.req.header("content-type"), { untyped: "empty" });
151
+ } catch (error) {
152
+ if (!isHttpError(error)) throw error;
153
+ setErrorHeaders(c, error);
154
+ return c.json(errorBody(error), error.statusCode);
155
+ }
156
+ const body = await HonoEndpoint.validate(c, raw, endpoint.input?.body);
157
+ if (body instanceof Response) return body;
158
+ c.req.addValidatedData("json", body);
159
+ await next();
160
+ });
148
161
  if (features.hasQueryValidation) validators.push(validator("query", (_, c) => {
149
162
  const parsedQuery = parseHonoQuery(c);
150
163
  return HonoEndpoint.validate(c, parsedQuery, endpoint.input?.query);
@@ -195,16 +208,15 @@ var HonoEndpoint = class HonoEndpoint {
195
208
  return c.json({ error: "Forbidden" }, 403);
196
209
  }
197
210
  if (features.hasRateLimit) {
198
- const { checkRateLimit, getRateLimitHeaders } = await loadRateLimit();
199
- const rateLimitHeaders = getRateLimitHeaders(await checkRateLimit(endpoint.rateLimit, {
211
+ const rateLimitHeaders = await enforceRateLimit(endpoint.rateLimit, {
200
212
  header,
201
213
  services,
202
214
  logger,
203
215
  session,
204
216
  path: c.req.path,
205
217
  method: endpoint.method
206
- }), endpoint.rateLimit);
207
- for (const [key, value] of Object.entries(rateLimitHeaders)) if (value) c.header(key, value);
218
+ });
219
+ for (const [key, value] of Object.entries(rateLimitHeaders)) c.header(key, value);
208
220
  }
209
221
  const auditContext = features.hasAudits ? await createAuditContext(endpoint, serviceDiscovery, logger, {
210
222
  session,
@@ -221,7 +233,7 @@ var HonoEndpoint = class HonoEndpoint {
221
233
  cookie,
222
234
  logger
223
235
  }) : void 0;
224
- const { output, metadata } = await executeWithAuditTransaction(auditContext, async (auditor) => {
236
+ const { output, metadata, redirect } = await executeWithAuditTransaction(auditContext, async (auditor) => {
225
237
  const baseDb = auditContext?.storage?.databaseServiceName && auditContext.storage.databaseServiceName === endpoint.databaseService?.serviceName ? auditor?.getTransaction?.() ?? rawDb : rawDb;
226
238
  const executeHandler = async (db) => {
227
239
  const responseBuilder = new ResponseBuilder();
@@ -243,10 +255,12 @@ var HonoEndpoint = class HonoEndpoint {
243
255
  data = response.data;
244
256
  metadata = response.metadata;
245
257
  }
258
+ const redirect = Endpoint.isRedirectStatus(metadata.status ?? endpoint.status);
246
259
  return {
247
- output: endpoint.outputSchema ? await endpoint.parseOutput(data) : void 0,
260
+ output: endpoint.outputSchema && !redirect ? await endpoint.parseOutput(data) : void 0,
248
261
  metadata,
249
- responseBuilder
262
+ responseBuilder,
263
+ redirect
250
264
  };
251
265
  };
252
266
  if (features.hasRls && rlsContext && baseDb) {
@@ -255,7 +269,7 @@ var HonoEndpoint = class HonoEndpoint {
255
269
  }
256
270
  return executeHandler(baseDb);
257
271
  }, async (result, auditor) => {
258
- if (!audits?.length) return;
272
+ if (!audits?.length || result.redirect) return;
259
273
  for (const audit of audits) {
260
274
  if (audit.when && !audit.when(result.output)) continue;
261
275
  const payload = audit.payload(result.output);
@@ -276,12 +290,14 @@ var HonoEndpoint = class HonoEndpoint {
276
290
  status,
277
291
  body: output
278
292
  }, "Outgoing response");
293
+ if (redirect) return c.body(null, status);
279
294
  if (endpoint.responseType === "application/json") return c.json(output, status);
280
295
  if (!(metadata.headers && hasContentTypeHeader(metadata.headers))) c.header("content-type", endpoint.responseType);
281
296
  return c.body(output, status);
282
297
  } catch (validationError) {
283
298
  logger.error({ err: validationError }, "Output validation failed");
284
299
  const error = wrapError(validationError, 422, "Response validation failed");
300
+ setErrorHeaders(c, error);
285
301
  const body = errorBody(error);
286
302
  if (HonoEndpoint.isDev) logger.info({
287
303
  status: error.statusCode,
@@ -293,6 +309,7 @@ var HonoEndpoint = class HonoEndpoint {
293
309
  logger.error({ err: e }, "Error processing endpoint request");
294
310
  const error = wrapError(e, 500, "Internal Server Error");
295
311
  if (error.statusCode >= 500 && e instanceof Error) c.error = e;
312
+ setErrorHeaders(c, error);
296
313
  const body = errorBody(error);
297
314
  if (HonoEndpoint.isDev) logger.info({
298
315
  status: error.statusCode,
@@ -335,7 +352,14 @@ function errorBody(error) {
335
352
  ...HonoEndpoint.isDev ? { stack: error.stack } : {}
336
353
  };
337
354
  }
355
+ /**
356
+ * The headers an error says its response carries — a 429's `Retry-After` and
357
+ * `X-RateLimit-*` — on the response that reports it.
358
+ */
359
+ function setErrorHeaders(c, error) {
360
+ for (const [key, value] of Object.entries(error.headers ?? {})) c.header(key, value);
361
+ }
338
362
  //#endregion
339
363
  export { HonoEndpoint as t };
340
364
 
341
- //# sourceMappingURL=HonoEndpointAdaptor-PtPlbeKY.mjs.map
365
+ //# sourceMappingURL=HonoEndpointAdaptor-RSvRnXSl.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HonoEndpointAdaptor-RSvRnXSl.mjs","names":["honoLogger"],"sources":["../src/endpoints/helpers.ts","../src/endpoints/parseHonoQuery.ts","../src/endpoints/HonoEndpointAdaptor.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Service } from '@geekmidas/services';\nimport fg from 'fast-glob';\nimport type { HttpMethod } from '../types';\nimport { Endpoint } from './Endpoint';\n\n// Re-export utility functions\n\n/**\n * Recursively finds the project root directory by looking for lock files.\n * Traverses up the directory tree until it finds a package manager lock file.\n *\n * @param cwd - The current working directory to start searching from\n * @returns Promise resolving to the absolute path of the project root\n *\n * @example\n * ```typescript\n * const projectRoot = await getProjectRoot(process.cwd());\n * console.log(`Project root: ${projectRoot}`);\n * // Output: Project root: /Users/user/my-project\n * ```\n */\nexport async function getProjectRoot(cwd: string): Promise<string> {\n\tif (cwd === '/') {\n\t\treturn cwd;\n\t}\n\n\tconst stream = fg.stream(\n\t\t['yarn.lock', 'pnpm-lock.yaml', 'package-lock.json', 'deno.lock'],\n\t\t{ dot: true, cwd },\n\t);\n\n\tlet isRoot = false;\n\n\tfor await (const _ of stream) {\n\t\tisRoot = true;\n\t\tbreak;\n\t}\n\n\tif (isRoot) {\n\t\treturn cwd;\n\t}\n\n\treturn getProjectRoot(path.resolve(cwd, '..'));\n}\n\n/**\n * Discovers and imports all Endpoint instances from the specified route patterns.\n * Uses fast-glob to find files matching the patterns and extracts exported Endpoints.\n *\n * @template TServices - Array of service types used by the endpoints\n * @param routes - Array of glob patterns to match route files (e.g., ['src/routes/*.ts'])\n * @param cwd - The current working directory to resolve paths from\n * @returns Promise resolving to an array of Endpoint instances found in the matched files\n *\n * @example\n * ```typescript\n * // Find all endpoints in the routes directory\n * const endpoints = await getEndpointsFromRoutes(\n * ['src/routes/**\\/*.ts'],\n * process.cwd()\n * );\n *\n * // Register endpoints with your server\n * for (const endpoint of endpoints) {\n * server.register(endpoint);\n * }\n * ```\n *\n * @remarks\n * - Only exports that are valid Endpoint instances are included\n * - Files are imported dynamically, so they must be valid ES modules\n * - The function filters out non-Endpoint exports automatically\n */\nexport async function getEndpointsFromRoutes<TServices extends Service[]>(\n\troutes: string[],\n\tcwd: string,\n): Promise<Endpoint<string, HttpMethod, any, any, TServices>[]> {\n\tconst stream = fg.stream(routes, { cwd });\n\n\tconst endpoints: Endpoint<string, HttpMethod, any, any, TServices>[] = [];\n\n\tfor await (const f of stream) {\n\t\t// Resolve the absolute path for the route file\n\t\tconst routePath = path.resolve(cwd, f.toString());\n\t\t// Dynamically import the route module\n\t\tconst route = await import(routePath);\n\n\t\t// Filter exported values to find only Endpoint instances\n\t\tconst handlers = Object.values(route).filter((value) => {\n\t\t\treturn Endpoint.isEndpoint(value);\n\t\t}) as unknown as Endpoint<string, HttpMethod, any, any, TServices>[];\n\n\t\tendpoints.push(...handlers);\n\t}\n\n\treturn endpoints;\n}\n","import type { Context } from 'hono';\nimport qs from 'qs';\n\n/**\n * Parse Hono query parameters using qs bracket notation\n * Supports nested objects, arrays, and deep nesting via standard bracket syntax\n */\nexport function parseHonoQuery(c: Context): Record<string, any> {\n\tconst url = c.req.url;\n\tconst queryIndex = url.indexOf('?');\n\tif (queryIndex === -1) {\n\t\treturn {};\n\t}\n\tconst queryString = url.slice(queryIndex + 1);\n\treturn qs.parse(queryString) as Record<string, any>;\n}\n","import type { AuditableAction, AuditStorage } from '@geekmidas/audit';\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport { type HttpError, isHttpError, wrapError } from '@geekmidas/errors';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport {\n\trunWithRequestContext,\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { type Context, Hono } from 'hono';\nimport { setCookie } from 'hono/cookie';\nimport { logger as honoLogger } from 'hono/logger';\nimport { timing } from 'hono/timing';\nimport type {\n\tContentfulStatusCode,\n\tRedirectStatusCode,\n} from 'hono/utils/http-status';\nimport { validator } from 'hono/validator';\nimport { publishConstructEvents } from '../publisher';\nimport type { HttpMethod, LowerHttpMethod } from '../types';\nimport type { MappedAudit } from './audit';\nimport {\n\tEndpoint,\n\ttype EndpointContext,\n\ttype EndpointSchemas,\n\tResponseBuilder,\n} from './Endpoint';\nimport { getEndpointsFromRoutes } from './helpers';\nimport { createHonoCookies, createHonoHeaders } from './lazyAccessors';\nimport { loadRls } from './optionalPeers';\nimport { parseHonoQuery } from './parseHonoQuery';\nimport {\n\tcreateAuditContext,\n\texecuteWithAuditTransaction,\n} from './processAudits';\nimport { enforceRateLimit } from './rateLimit';\nimport { readRequestBody } from './readRequestBody';\n\nexport interface HonoEndpointOptions {\n\t/**\n\t * Path where OpenAPI documentation will be served.\n\t * Set to false to disable docs route.\n\t * @default '/docs'\n\t */\n\tdocsPath?: string | false;\n\t/**\n\t * OpenAPI schema options\n\t */\n\topenApiOptions?: {\n\t\ttitle?: string;\n\t\tversion?: string;\n\t\tdescription?: string;\n\t};\n}\n\n/**\n * Feature flags for an endpoint, analyzed once at route registration time.\n * This avoids per-request feature detection overhead.\n */\ninterface EndpointFeatures {\n\thasServices: boolean;\n\thasDatabase: boolean;\n\thasBodyValidation: boolean;\n\thasQueryValidation: boolean;\n\thasParamValidation: boolean;\n\thasAudits: boolean;\n\thasEvents: boolean;\n\thasRateLimit: boolean;\n\thasRls: boolean;\n}\n\n/**\n * Case-insensitive check for a `content-type` header in a metadata headers map.\n */\nfunction hasContentTypeHeader(headers: Record<string, string>): boolean {\n\tfor (const key of Object.keys(headers)) {\n\t\tif (key.toLowerCase() === 'content-type') return true;\n\t}\n\treturn false;\n}\n\n/**\n * Analyze endpoint features at registration time (not per-request)\n */\nfunction analyzeEndpointFeatures(\n\tendpoint: Endpoint<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany\n\t>,\n): EndpointFeatures {\n\treturn {\n\t\thasServices: endpoint.services.length > 0,\n\t\thasDatabase: !!endpoint.databaseService,\n\t\thasBodyValidation: !!endpoint.input?.body,\n\t\thasQueryValidation: !!endpoint.input?.query,\n\t\thasParamValidation: !!endpoint.input?.params,\n\t\t// Audit context needed if declarative audits OR auditor storage service configured (for manual audits)\n\t\thasAudits:\n\t\t\t(endpoint.audits?.length ?? 0) > 0 || !!endpoint.auditorStorageService,\n\t\thasEvents: (endpoint.events?.length ?? 0) > 0,\n\t\thasRateLimit: !!endpoint.rateLimit,\n\t\thasRls: !!endpoint.rlsConfig && !endpoint.rlsBypass,\n\t};\n}\n\nexport class HonoEndpoint<\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tconstructor(\n\t\tprivate readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {}\n\n\tstatic isDev = process.env.NODE_ENV === 'development';\n\n\tstatic async validate<T extends StandardSchemaV1>(\n\t\tc: Context<any, string, {}>,\n\t\tdata: unknown,\n\t\tschema?: T,\n\t) {\n\t\tif (!schema) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst parsed = await Endpoint.validate(schema, data);\n\n\t\tif (parsed.issues) {\n\t\t\treturn c.json(parsed.issues, 422);\n\t\t}\n\n\t\treturn parsed.value;\n\t}\n\taddRoute(\n\t\tserviceDiscovery: ServiceDiscovery<ServiceRecord<TServices>>,\n\t\tapp: Hono,\n\t): void {\n\t\tHonoEndpoint.addRoute(this.endpoint, serviceDiscovery, app);\n\t}\n\n\t/**\n\t * @deprecated Global event middleware is no longer used.\n\t * Events are now published per-route only for endpoints that have events configured.\n\t * This method is kept for backward compatibility but does nothing.\n\t */\n\tstatic applyEventMiddleware(\n\t\t_app: Hono,\n\t\t_serviceDiscovery: ServiceDiscovery<any>,\n\t) {\n\t\t// No-op: Event publishing is now handled per-route in addRoute\n\t\t// This avoids running middleware on every request including 404s\n\t}\n\n\tstatic async fromRoutes<TLogger extends Logger, TServices extends Service[]>(\n\t\troutes: string[],\n\t\tenvParser: EnvironmentParser<{}>,\n\t\tapp = new Hono(),\n\t\t_logger: TLogger,\n\t\tcwd = process.cwd(),\n\t\toptions?: HonoEndpointOptions,\n\t): Promise<Hono> {\n\t\tconst endpoints = await getEndpointsFromRoutes<TServices>(routes, cwd);\n\t\tconst serviceDiscovery =\n\t\t\tServiceDiscovery.getInstance<ServiceRecord<TServices>>(envParser);\n\n\t\tHonoEndpoint.addRoutes(endpoints, serviceDiscovery, app, options);\n\n\t\treturn app;\n\t}\n\n\tstatic addRoutes<\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t>(\n\t\tendpoints: Endpoint<string, HttpMethod, any, any, TServices, TLogger>[],\n\t\tserviceDiscovery: ServiceDiscovery<ServiceRecord<TServices>>,\n\t\tapp: Hono,\n\t\toptions?: HonoEndpointOptions,\n\t): void {\n\t\t// Add timing middleware (always enabled)\n\t\tapp.use('*', timing());\n\n\t\t// Add logger middleware in development mode\n\n\t\tif (HonoEndpoint.isDev) {\n\t\t\tapp.use('*', honoLogger());\n\t\t}\n\n\t\t// Add docs route if not disabled\n\t\tconst docsPath =\n\t\t\toptions?.docsPath !== false ? options?.docsPath || '/docs' : null;\n\t\tif (docsPath) {\n\t\t\tHonoEndpoint.addDocsRoute(\n\t\t\t\tendpoints,\n\t\t\t\tapp,\n\t\t\t\tdocsPath,\n\t\t\t\toptions?.openApiOptions,\n\t\t\t);\n\t\t}\n\n\t\t// Sort endpoints to ensure static routes come before dynamic ones\n\t\tconst sortedEndpoints = endpoints.sort((a, b) => {\n\t\t\tconst aSegments = a.route.split('/');\n\t\t\tconst bSegments = b.route.split('/');\n\n\t\t\t// Compare each segment\n\t\t\tfor (let i = 0; i < Math.max(aSegments.length, bSegments.length); i++) {\n\t\t\t\tconst aSegment = aSegments[i] || '';\n\t\t\t\tconst bSegment = bSegments[i] || '';\n\n\t\t\t\t// If one is dynamic and the other is not, static comes first\n\t\t\t\tconst aIsDynamic = aSegment.startsWith(':');\n\t\t\t\tconst bIsDynamic = bSegment.startsWith(':');\n\n\t\t\t\tif (!aIsDynamic && bIsDynamic) return -1;\n\t\t\t\tif (aIsDynamic && !bIsDynamic) return 1;\n\n\t\t\t\t// If both are the same type, compare alphabetically\n\t\t\t\tif (aSegment !== bSegment) {\n\t\t\t\t\treturn aSegment.localeCompare(bSegment);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t});\n\n\t\t// Note: Global event middleware removed for performance\n\t\t// Events are now published per-route only for endpoints with events configured\n\t\tfor (const endpoint of sortedEndpoints) {\n\t\t\tHonoEndpoint.addRoute(endpoint, serviceDiscovery, app);\n\t\t}\n\t}\n\n\t// biome-ignore lint/suspicious/useAdjacentOverloadSignatures: not an overload — the instance `addRoute` above registers this adaptor's own endpoint, this one registers any endpoint on a given app. They share a name because they do the same thing to different subjects.\n\tstatic addRoute<\n\t\tTRoute extends string,\n\t\tTMethod extends HttpMethod,\n\t\tTInput extends EndpointSchemas = {},\n\t\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t\tTSession = unknown,\n\t\tTAuditStorage extends AuditStorage | undefined = undefined,\n\t\tTAuditStorageServiceName extends string = string,\n\t\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t\tTDatabase = undefined,\n\t\tTDatabaseServiceName extends string = string,\n\t>(\n\t\tendpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t\tserviceDiscovery: ServiceDiscovery<ServiceRecord<TServices>>,\n\t\tapp: Hono,\n\t): void {\n\t\tconst { route } = endpoint;\n\t\tconst method = endpoint.method.toLowerCase() as LowerHttpMethod<TMethod>;\n\n\t\t// Analyze endpoint features once at registration time (not per-request)\n\t\tconst features = analyzeEndpointFeatures(endpoint);\n\n\t\t// Build validators array - only add validators for schemas that exist\n\t\tconst validators: any[] = [];\n\n\t\tif (features.hasBodyValidation) {\n\t\t\t// Read by its Content-Type — JSON, an HTML form, text — and kept\n\t\t\t// where a JSON validator keeps it, so the handler reads it as before.\n\t\t\tvalidators.push(async (c: Context, next: () => Promise<void>) => {\n\t\t\t\tlet raw: unknown;\n\t\t\t\ttry {\n\t\t\t\t\traw = await readRequestBody(c.req, c.req.header('content-type'), {\n\t\t\t\t\t\tuntyped: 'empty',\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isHttpError(error)) throw error;\n\t\t\t\t\tsetErrorHeaders(c, error);\n\t\t\t\t\treturn c.json(\n\t\t\t\t\t\terrorBody(error),\n\t\t\t\t\t\terror.statusCode as ContentfulStatusCode,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst body = await HonoEndpoint.validate(c, raw, endpoint.input?.body);\n\t\t\t\tif (body instanceof Response) return body;\n\t\t\t\tc.req.addValidatedData('json', body as {});\n\t\t\t\tawait next();\n\t\t\t});\n\t\t}\n\n\t\tif (features.hasQueryValidation) {\n\t\t\tvalidators.push(\n\t\t\t\tvalidator('query', (_, c) => {\n\t\t\t\t\tconst parsedQuery = parseHonoQuery(c);\n\t\t\t\t\treturn HonoEndpoint.validate(c, parsedQuery, endpoint.input?.query);\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (features.hasParamValidation) {\n\t\t\tvalidators.push(\n\t\t\t\tvalidator('param', (params, c) =>\n\t\t\t\t\tHonoEndpoint.validate(c, params, endpoint.input?.params),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// Main handler\n\t\tconst handler = async (c: Context) => {\n\t\t\t// Request context setup\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = c.req.header('X-Request-ID') ?? crypto.randomUUID();\n\n\t\t\tconst logger = endpoint.logger.child({\n\t\t\t\trequestId,\n\t\t\t\tendpoint: endpoint.fullPath,\n\t\t\t\troute: endpoint.route,\n\t\t\t\thost: c.req.header('host'),\n\t\t\t\tmethod: endpoint.method,\n\t\t\t\tpath: c.req.path,\n\t\t\t}) as TLogger;\n\n\t\t\t// Set response header\n\t\t\tc.header('X-Request-ID', requestId);\n\n\t\t\t// Wrap entire handler in request context for services to access\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{\n\t\t\t\t\tlogger,\n\t\t\t\t\trequestId,\n\t\t\t\t\tstartTime,\n\t\t\t\t\toperation: `${endpoint.method} ${endpoint.route}`,\n\t\t\t\t},\n\t\t\t\tasync () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Lazy accessors - no upfront parsing, use native Hono methods\n\t\t\t\t\t\tconst header = createHonoHeaders(c);\n\t\t\t\t\t\tconst cookie = createHonoCookies(c);\n\n\t\t\t\t\t\t// Only register services if endpoint has any\n\t\t\t\t\t\tconst services = features.hasServices\n\t\t\t\t\t\t\t? await serviceDiscovery.register(endpoint.services)\n\t\t\t\t\t\t\t: ({} as ServiceRecord<TServices>);\n\n\t\t\t\t\t\t// Resolve database service only if configured\n\t\t\t\t\t\tconst rawDb = features.hasDatabase\n\t\t\t\t\t\t\t? await serviceDiscovery\n\t\t\t\t\t\t\t\t\t.register([endpoint.databaseService!])\n\t\t\t\t\t\t\t\t\t.then(\n\t\t\t\t\t\t\t\t\t\t(s) =>\n\t\t\t\t\t\t\t\t\t\t\ts[\n\t\t\t\t\t\t\t\t\t\t\t\tendpoint.databaseService?.serviceName as keyof typeof s\n\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\t// Extract session (defaults to empty object)\n\t\t\t\t\t\tconst session = await endpoint.getSession({\n\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t...(rawDb !== undefined && { db: rawDb }),\n\t\t\t\t\t\t} as any);\n\n\t\t\t\t\t\t// Check authorization (defaults to true)\n\t\t\t\t\t\tconst isAuthorized = await endpoint.authorize({\n\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t...(rawDb !== undefined && { db: rawDb }),\n\t\t\t\t\t\t\tbody: features.hasBodyValidation\n\t\t\t\t\t\t\t\t? (c.req.valid as any)('json')\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\tquery: features.hasQueryValidation\n\t\t\t\t\t\t\t\t? (c.req.valid as any)('query')\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\tparams: features.hasParamValidation\n\t\t\t\t\t\t\t\t? (c.req.valid as any)('param')\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t} as any);\n\n\t\t\t\t\t\tif (!isAuthorized) {\n\t\t\t\t\t\t\tlogger.warn('Forbidden access attempt');\n\t\t\t\t\t\t\treturn c.json({ error: 'Forbidden' }, 403);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Check rate limit only if configured\n\t\t\t\t\t\tif (features.hasRateLimit) {\n\t\t\t\t\t\t\tconst rateLimitHeaders = await enforceRateLimit(\n\t\t\t\t\t\t\t\tendpoint.rateLimit!,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t\t\tpath: c.req.path,\n\t\t\t\t\t\t\t\t\tmethod: endpoint.method,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tfor (const [key, value] of Object.entries(rateLimitHeaders)) {\n\t\t\t\t\t\t\t\tc.header(key, value);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Create audit context only if audits are configured\n\t\t\t\t\t\tconst auditContext = features.hasAudits\n\t\t\t\t\t\t\t? await createAuditContext(\n\t\t\t\t\t\t\t\t\tendpoint as any,\n\t\t\t\t\t\t\t\t\tserviceDiscovery,\n\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t\t\t\tservices: services as Record<string, unknown>,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\tconst audits = features.hasAudits\n\t\t\t\t\t\t\t? (endpoint.audits as MappedAudit<TAuditAction, TOutSchema>[])\n\t\t\t\t\t\t\t: [];\n\n\t\t\t\t\t\t// Warn if declarative audits are configured but no audit storage\n\t\t\t\t\t\tif (features.hasAudits && !auditContext) {\n\t\t\t\t\t\t\tlogger.warn('No auditor storage service available');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Extract RLS context only if configured and not bypassed\n\t\t\t\t\t\tconst rlsContext =\n\t\t\t\t\t\t\tfeatures.hasRls && rawDb !== undefined\n\t\t\t\t\t\t\t\t? await endpoint.rlsConfig?.extractor({\n\t\t\t\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\t\t\t\tsession: session as TSession,\n\t\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\t// Execute handler with automatic audit transaction support\n\t\t\t\t\t\tconst result = await executeWithAuditTransaction(\n\t\t\t\t\t\t\tauditContext,\n\t\t\t\t\t\t\tasync (auditor) => {\n\t\t\t\t\t\t\t\tconst sameDatabase =\n\t\t\t\t\t\t\t\t\tauditContext?.storage?.databaseServiceName &&\n\t\t\t\t\t\t\t\t\tauditContext.storage.databaseServiceName ===\n\t\t\t\t\t\t\t\t\t\tendpoint.databaseService?.serviceName;\n\t\t\t\t\t\t\t\tconst baseDb = sameDatabase\n\t\t\t\t\t\t\t\t\t? (auditor?.getTransaction?.() ?? rawDb)\n\t\t\t\t\t\t\t\t\t: rawDb;\n\n\t\t\t\t\t\t\t\tconst executeHandler = async (db: TDatabase | undefined) => {\n\t\t\t\t\t\t\t\t\tconst responseBuilder = new ResponseBuilder();\n\t\t\t\t\t\t\t\t\tconst response = await endpoint.handler(\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\t\t\tbody: features.hasBodyValidation\n\t\t\t\t\t\t\t\t\t\t\t\t? (c.req.valid as any)('json')\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t\t\tquery: features.hasQueryValidation\n\t\t\t\t\t\t\t\t\t\t\t\t? (c.req.valid as any)('query')\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t\t\tparams: features.hasParamValidation\n\t\t\t\t\t\t\t\t\t\t\t\t? (c.req.valid as any)('param')\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t\t\t\t\tauditor,\n\t\t\t\t\t\t\t\t\t\t\tdb,\n\t\t\t\t\t\t\t\t\t\t} as unknown as EndpointContext<\n\t\t\t\t\t\t\t\t\t\t\tTInput,\n\t\t\t\t\t\t\t\t\t\t\tTServices,\n\t\t\t\t\t\t\t\t\t\t\tTLogger,\n\t\t\t\t\t\t\t\t\t\t\tTSession,\n\t\t\t\t\t\t\t\t\t\t\tTAuditAction,\n\t\t\t\t\t\t\t\t\t\t\tTDatabase,\n\t\t\t\t\t\t\t\t\t\t\tTAuditStorage\n\t\t\t\t\t\t\t\t\t\t>,\n\t\t\t\t\t\t\t\t\t\tresponseBuilder,\n\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\tlet data = response;\n\t\t\t\t\t\t\t\t\tlet metadata = responseBuilder.getMetadata();\n\n\t\t\t\t\t\t\t\t\tif (Endpoint.hasMetadata(response)) {\n\t\t\t\t\t\t\t\t\t\tdata = response.data;\n\t\t\t\t\t\t\t\t\t\tmetadata = response.metadata;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// A redirect has no body to check against the schema.\n\t\t\t\t\t\t\t\t\tconst redirect = Endpoint.isRedirectStatus(\n\t\t\t\t\t\t\t\t\t\tmetadata.status ?? endpoint.status,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tconst output =\n\t\t\t\t\t\t\t\t\t\tendpoint.outputSchema && !redirect\n\t\t\t\t\t\t\t\t\t\t\t? await endpoint.parseOutput(data)\n\t\t\t\t\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\t\t\t\treturn { output, metadata, responseBuilder, redirect };\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tif (features.hasRls && rlsContext && baseDb) {\n\t\t\t\t\t\t\t\t\tconst { withRlsContext } = await loadRls();\n\t\t\t\t\t\t\t\t\treturn withRlsContext(\n\t\t\t\t\t\t\t\t\t\tbaseDb as any,\n\t\t\t\t\t\t\t\t\t\trlsContext,\n\t\t\t\t\t\t\t\t\t\tasync (trx) => executeHandler(trx as TDatabase),\n\t\t\t\t\t\t\t\t\t\t{ prefix: endpoint.rlsConfig?.prefix },\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn executeHandler(baseDb as TDatabase | undefined);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tasync (result, auditor) => {\n\t\t\t\t\t\t\t\tif (!audits?.length || result.redirect) return;\n\n\t\t\t\t\t\t\t\tfor (const audit of audits) {\n\t\t\t\t\t\t\t\t\tif (audit.when && !audit.when(result.output as any)) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tconst payload = audit.payload(result.output as any);\n\t\t\t\t\t\t\t\t\tconst entityId = audit.entityId?.(result.output as any);\n\t\t\t\t\t\t\t\t\tauditor.audit(audit.type as any, payload as any, {\n\t\t\t\t\t\t\t\t\t\ttable: audit.table,\n\t\t\t\t\t\t\t\t\t\tentityId,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ db: rawDb },\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst { output, metadata, redirect } = result;\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tlet status = endpoint.status as ContentfulStatusCode;\n\n\t\t\t\t\t\t\tif (metadata.status) {\n\t\t\t\t\t\t\t\tstatus = metadata.status as ContentfulStatusCode;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (metadata.headers) {\n\t\t\t\t\t\t\t\tfor (const [key, value] of Object.entries(metadata.headers)) {\n\t\t\t\t\t\t\t\t\tc.header(key, value);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (metadata.cookies) {\n\t\t\t\t\t\t\t\tfor (const [name, { value, options }] of metadata.cookies) {\n\t\t\t\t\t\t\t\t\tsetCookie(c, name, value, options);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Only publish events if configured (no global middleware overhead)\n\t\t\t\t\t\t\tif (features.hasEvents && Endpoint.isSuccessStatus(status)) {\n\t\t\t\t\t\t\t\tawait publishConstructEvents<any>(\n\t\t\t\t\t\t\t\t\tendpoint as any,\n\t\t\t\t\t\t\t\t\toutput,\n\t\t\t\t\t\t\t\t\tserviceDiscovery,\n\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (HonoEndpoint.isDev) {\n\t\t\t\t\t\t\t\tlogger.info({ status, body: output }, 'Outgoing response');\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (redirect) {\n\t\t\t\t\t\t\t\treturn c.body(null, status as RedirectStatusCode);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Respect the endpoint's declared responseType. For JSON (default)\n\t\t\t\t\t\t\t// we use c.json() which sets Content-Type and serializes. For any\n\t\t\t\t\t\t\t// other type, emit the body as-is and set the header explicitly —\n\t\t\t\t\t\t\t// metadata.headers (from r.header()) applied above still wins.\n\t\t\t\t\t\t\tif (endpoint.responseType === 'application/json') {\n\t\t\t\t\t\t\t\t// Hono 4.13 types `c.json`'s status well enough that the\n\t\t\t\t\t\t\t\t// suppression here is now itself an error.\n\t\t\t\t\t\t\t\treturn c.json(output, status);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!(metadata.headers && hasContentTypeHeader(metadata.headers))\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tc.header('content-type', endpoint.responseType);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn c.body(output as any, status);\n\t\t\t\t\t\t} catch (validationError: any) {\n\t\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t\t{ err: validationError },\n\t\t\t\t\t\t\t\t'Output validation failed',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconst error = wrapError(\n\t\t\t\t\t\t\t\tvalidationError,\n\t\t\t\t\t\t\t\t422,\n\t\t\t\t\t\t\t\t'Response validation failed',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tsetErrorHeaders(c, error);\n\t\t\t\t\t\t\tconst body = errorBody(error);\n\t\t\t\t\t\t\tif (HonoEndpoint.isDev) {\n\t\t\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t\t\t{ status: error.statusCode, body },\n\t\t\t\t\t\t\t\t\t'Outgoing response',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn c.json(body, error.statusCode as ContentfulStatusCode);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t// Under `err`, which every pino serializes as an Error — passed bare,\n\t\t\t\t\t\t// one pino does not recognise (a ZodError) is spread into the line\n\t\t\t\t\t\t// with no stack.\n\t\t\t\t\t\tlogger.error({ err: e }, 'Error processing endpoint request');\n\t\t\t\t\t\tconst error = wrapError(e, 500, 'Internal Server Error');\n\t\t\t\t\t\t// Answered here rather than rethrown, so Hono never sets it:\n\t\t\t\t\t\t// a server failure is left on the context the way Hono\n\t\t\t\t\t\t// leaves an uncaught one, for middleware — a request span\n\t\t\t\t\t\t// records it as the exception. A 4xx is the client's.\n\t\t\t\t\t\tif (error.statusCode >= 500 && e instanceof Error) c.error = e;\n\t\t\t\t\t\tsetErrorHeaders(c, error);\n\t\t\t\t\t\tconst body = errorBody(error);\n\t\t\t\t\t\tif (HonoEndpoint.isDev) {\n\t\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t\t{ status: error.statusCode, body },\n\t\t\t\t\t\t\t\t'Outgoing response',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn c.json(body, error.statusCode as ContentfulStatusCode);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t};\n\n\t\t// Register route with conditional validators.\n\t\t//\n\t\t// `route` is a generic `TRoute`, and Hono 4.13 overloads `app.get` on\n\t\t// whether the first argument is a path or a handler. A generic that is\n\t\t// only *constrained* to string does not pick the path overload, so it\n\t\t// gets matched against the handler one and reported there. Saying it is a\n\t\t// string chooses the overload that was always intended.\n\t\t//\n\t\t// The spread is why a cast is needed rather than a better type: with\n\t\t// `...validators` of unknown length, the arity is not decidable, so the\n\t\t// first overload is chosen whatever the path argument says. Naming the\n\t\t// shape we are calling states the intent the overloads cannot infer.\n\t\t(\n\t\t\tapp[method] as unknown as (\n\t\t\t\tpath: string,\n\t\t\t\t...handlers: unknown[]\n\t\t\t) => unknown\n\t\t)(route, ...validators, handler);\n\t}\n\n\tstatic addDocsRoute<\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t>(\n\t\tendpoints: Endpoint<string, HttpMethod, any, any, TServices, TLogger>[],\n\t\tapp: Hono,\n\t\tdocsPath: string,\n\t\topenApiOptions?: HonoEndpointOptions['openApiOptions'],\n\t): void {\n\t\tapp.get(docsPath, async (c) => {\n\t\t\ttry {\n\t\t\t\tconst openApiSchema = await Endpoint.buildOpenApiSchema(\n\t\t\t\t\tendpoints,\n\t\t\t\t\topenApiOptions,\n\t\t\t\t);\n\n\t\t\t\treturn c.json(openApiSchema);\n\t\t\t} catch {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{ error: 'Failed to generate OpenAPI documentation' },\n\t\t\t\t\t500,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n}\n\n/**\n * What a client is told about a failed request: what went wrong, and the stack\n * only under `gkm dev`. A server's stack is its file layout and its\n * dependencies' versions — for the person debugging it, not for whoever sent\n * the request. A server failure's details are the error it wrapped (an\n * environment variable it could not read, a query that failed), so they go to\n * the log and not the response either; a 4xx's details are the client's.\n */\nfunction errorBody(error: HttpError) {\n\tconst internal = error.statusCode >= 500 && !HonoEndpoint.isDev;\n\treturn {\n\t\tname: error.name,\n\t\tmessage: error.message,\n\t\tstatusCode: error.statusCode,\n\t\tstatusMessage: error.statusMessage,\n\t\tcode: error.code,\n\t\t...(internal ? {} : { details: error.details }),\n\t\t...(HonoEndpoint.isDev ? { stack: error.stack } : {}),\n\t};\n}\n\n/**\n * The headers an error says its response carries — a 429's `Retry-After` and\n * `X-RateLimit-*` — on the response that reports it.\n */\nfunction setErrorHeaders(c: Context, error: HttpError): void {\n\tfor (const [key, value] of Object.entries(error.headers ?? {})) {\n\t\tc.header(key, value);\n\t}\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-y5ARvAq5.cjs");
2
2
  const require_functions = require("./functions-CfzJG9hK.cjs");
3
- const require_Endpoint = require("./Endpoint-BoUg8ocM.cjs");
4
- const require_lazyAccessors = require("./lazyAccessors-hJKGv8Ay.cjs");
5
- const require_processAudits = require("./processAudits-DasOZjPl.cjs");
3
+ const require_Endpoint = require("./Endpoint-B_UHlLxv.cjs");
4
+ const require_readRequestBody = require("./readRequestBody-Dej_L3c4.cjs");
5
+ const require_rateLimit = require("./rateLimit-BwHcMECR.cjs");
6
6
  let _geekmidas_errors = require("@geekmidas/errors");
7
7
  let _geekmidas_services = require("@geekmidas/services");
8
8
  let hono_cookie = require("hono/cookie");
@@ -148,7 +148,20 @@ var HonoEndpoint = class HonoEndpoint {
148
148
  const method = endpoint.method.toLowerCase();
149
149
  const features = analyzeEndpointFeatures(endpoint);
150
150
  const validators = [];
151
- if (features.hasBodyValidation) validators.push((0, hono_validator.validator)("json", (value, c) => HonoEndpoint.validate(c, value, endpoint.input?.body)));
151
+ if (features.hasBodyValidation) validators.push(async (c, next) => {
152
+ let raw;
153
+ try {
154
+ raw = await require_readRequestBody.readRequestBody(c.req, c.req.header("content-type"), { untyped: "empty" });
155
+ } catch (error) {
156
+ if (!(0, _geekmidas_errors.isHttpError)(error)) throw error;
157
+ setErrorHeaders(c, error);
158
+ return c.json(errorBody(error), error.statusCode);
159
+ }
160
+ const body = await HonoEndpoint.validate(c, raw, endpoint.input?.body);
161
+ if (body instanceof Response) return body;
162
+ c.req.addValidatedData("json", body);
163
+ await next();
164
+ });
152
165
  if (features.hasQueryValidation) validators.push((0, hono_validator.validator)("query", (_, c) => {
153
166
  const parsedQuery = parseHonoQuery(c);
154
167
  return HonoEndpoint.validate(c, parsedQuery, endpoint.input?.query);
@@ -173,8 +186,8 @@ var HonoEndpoint = class HonoEndpoint {
173
186
  operation: `${endpoint.method} ${endpoint.route}`
174
187
  }, async () => {
175
188
  try {
176
- const header = require_lazyAccessors.createHonoHeaders(c);
177
- const cookie = require_lazyAccessors.createHonoCookies(c);
189
+ const header = require_readRequestBody.createHonoHeaders(c);
190
+ const cookie = require_readRequestBody.createHonoCookies(c);
178
191
  const services = features.hasServices ? await serviceDiscovery.register(endpoint.services) : {};
179
192
  const rawDb = features.hasDatabase ? await serviceDiscovery.register([endpoint.databaseService]).then((s) => s[endpoint.databaseService?.serviceName]) : void 0;
180
193
  const session = await endpoint.getSession({
@@ -199,18 +212,17 @@ var HonoEndpoint = class HonoEndpoint {
199
212
  return c.json({ error: "Forbidden" }, 403);
200
213
  }
201
214
  if (features.hasRateLimit) {
202
- const { checkRateLimit, getRateLimitHeaders } = await require_processAudits.loadRateLimit();
203
- const rateLimitHeaders = getRateLimitHeaders(await checkRateLimit(endpoint.rateLimit, {
215
+ const rateLimitHeaders = await require_rateLimit.enforceRateLimit(endpoint.rateLimit, {
204
216
  header,
205
217
  services,
206
218
  logger,
207
219
  session,
208
220
  path: c.req.path,
209
221
  method: endpoint.method
210
- }), endpoint.rateLimit);
211
- for (const [key, value] of Object.entries(rateLimitHeaders)) if (value) c.header(key, value);
222
+ });
223
+ for (const [key, value] of Object.entries(rateLimitHeaders)) c.header(key, value);
212
224
  }
213
- const auditContext = features.hasAudits ? await require_processAudits.createAuditContext(endpoint, serviceDiscovery, logger, {
225
+ const auditContext = features.hasAudits ? await require_rateLimit.createAuditContext(endpoint, serviceDiscovery, logger, {
214
226
  session,
215
227
  header,
216
228
  cookie,
@@ -225,7 +237,7 @@ var HonoEndpoint = class HonoEndpoint {
225
237
  cookie,
226
238
  logger
227
239
  }) : void 0;
228
- const { output, metadata } = await require_processAudits.executeWithAuditTransaction(auditContext, async (auditor) => {
240
+ const { output, metadata, redirect } = await require_rateLimit.executeWithAuditTransaction(auditContext, async (auditor) => {
229
241
  const baseDb = auditContext?.storage?.databaseServiceName && auditContext.storage.databaseServiceName === endpoint.databaseService?.serviceName ? auditor?.getTransaction?.() ?? rawDb : rawDb;
230
242
  const executeHandler = async (db) => {
231
243
  const responseBuilder = new require_Endpoint.ResponseBuilder();
@@ -247,19 +259,21 @@ var HonoEndpoint = class HonoEndpoint {
247
259
  data = response.data;
248
260
  metadata = response.metadata;
249
261
  }
262
+ const redirect = require_Endpoint.Endpoint.isRedirectStatus(metadata.status ?? endpoint.status);
250
263
  return {
251
- output: endpoint.outputSchema ? await endpoint.parseOutput(data) : void 0,
264
+ output: endpoint.outputSchema && !redirect ? await endpoint.parseOutput(data) : void 0,
252
265
  metadata,
253
- responseBuilder
266
+ responseBuilder,
267
+ redirect
254
268
  };
255
269
  };
256
270
  if (features.hasRls && rlsContext && baseDb) {
257
- const { withRlsContext } = await require_processAudits.loadRls();
271
+ const { withRlsContext } = await require_rateLimit.loadRls();
258
272
  return withRlsContext(baseDb, rlsContext, async (trx) => executeHandler(trx), { prefix: endpoint.rlsConfig?.prefix });
259
273
  }
260
274
  return executeHandler(baseDb);
261
275
  }, async (result, auditor) => {
262
- if (!audits?.length) return;
276
+ if (!audits?.length || result.redirect) return;
263
277
  for (const audit of audits) {
264
278
  if (audit.when && !audit.when(result.output)) continue;
265
279
  const payload = audit.payload(result.output);
@@ -280,12 +294,14 @@ var HonoEndpoint = class HonoEndpoint {
280
294
  status,
281
295
  body: output
282
296
  }, "Outgoing response");
297
+ if (redirect) return c.body(null, status);
283
298
  if (endpoint.responseType === "application/json") return c.json(output, status);
284
299
  if (!(metadata.headers && hasContentTypeHeader(metadata.headers))) c.header("content-type", endpoint.responseType);
285
300
  return c.body(output, status);
286
301
  } catch (validationError) {
287
302
  logger.error({ err: validationError }, "Output validation failed");
288
303
  const error = (0, _geekmidas_errors.wrapError)(validationError, 422, "Response validation failed");
304
+ setErrorHeaders(c, error);
289
305
  const body = errorBody(error);
290
306
  if (HonoEndpoint.isDev) logger.info({
291
307
  status: error.statusCode,
@@ -297,6 +313,7 @@ var HonoEndpoint = class HonoEndpoint {
297
313
  logger.error({ err: e }, "Error processing endpoint request");
298
314
  const error = (0, _geekmidas_errors.wrapError)(e, 500, "Internal Server Error");
299
315
  if (error.statusCode >= 500 && e instanceof Error) c.error = e;
316
+ setErrorHeaders(c, error);
300
317
  const body = errorBody(error);
301
318
  if (HonoEndpoint.isDev) logger.info({
302
319
  status: error.statusCode,
@@ -339,6 +356,13 @@ function errorBody(error) {
339
356
  ...HonoEndpoint.isDev ? { stack: error.stack } : {}
340
357
  };
341
358
  }
359
+ /**
360
+ * The headers an error says its response carries — a 429's `Retry-After` and
361
+ * `X-RateLimit-*` — on the response that reports it.
362
+ */
363
+ function setErrorHeaders(c, error) {
364
+ for (const [key, value] of Object.entries(error.headers ?? {})) c.header(key, value);
365
+ }
342
366
  //#endregion
343
367
  Object.defineProperty(exports, "HonoEndpoint", {
344
368
  enumerable: true,
@@ -347,4 +371,4 @@ Object.defineProperty(exports, "HonoEndpoint", {
347
371
  }
348
372
  });
349
373
 
350
- //# sourceMappingURL=HonoEndpointAdaptor-zVzVLJEO.cjs.map
374
+ //# sourceMappingURL=HonoEndpointAdaptor-eCKyEOl_.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HonoEndpointAdaptor-eCKyEOl_.cjs","names":["fg","path","Endpoint","Endpoint","Hono","ServiceDiscovery","timing","honoLogger","readRequestBody","isHttpError","validator","runWithRequestContext","createHonoHeaders","createHonoCookies","enforceRateLimit","createAuditContext","executeWithAuditTransaction","ResponseBuilder","loadRls","publishConstructEvents","wrapError"],"sources":["../src/endpoints/helpers.ts","../src/endpoints/parseHonoQuery.ts","../src/endpoints/HonoEndpointAdaptor.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Service } from '@geekmidas/services';\nimport fg from 'fast-glob';\nimport type { HttpMethod } from '../types';\nimport { Endpoint } from './Endpoint';\n\n// Re-export utility functions\n\n/**\n * Recursively finds the project root directory by looking for lock files.\n * Traverses up the directory tree until it finds a package manager lock file.\n *\n * @param cwd - The current working directory to start searching from\n * @returns Promise resolving to the absolute path of the project root\n *\n * @example\n * ```typescript\n * const projectRoot = await getProjectRoot(process.cwd());\n * console.log(`Project root: ${projectRoot}`);\n * // Output: Project root: /Users/user/my-project\n * ```\n */\nexport async function getProjectRoot(cwd: string): Promise<string> {\n\tif (cwd === '/') {\n\t\treturn cwd;\n\t}\n\n\tconst stream = fg.stream(\n\t\t['yarn.lock', 'pnpm-lock.yaml', 'package-lock.json', 'deno.lock'],\n\t\t{ dot: true, cwd },\n\t);\n\n\tlet isRoot = false;\n\n\tfor await (const _ of stream) {\n\t\tisRoot = true;\n\t\tbreak;\n\t}\n\n\tif (isRoot) {\n\t\treturn cwd;\n\t}\n\n\treturn getProjectRoot(path.resolve(cwd, '..'));\n}\n\n/**\n * Discovers and imports all Endpoint instances from the specified route patterns.\n * Uses fast-glob to find files matching the patterns and extracts exported Endpoints.\n *\n * @template TServices - Array of service types used by the endpoints\n * @param routes - Array of glob patterns to match route files (e.g., ['src/routes/*.ts'])\n * @param cwd - The current working directory to resolve paths from\n * @returns Promise resolving to an array of Endpoint instances found in the matched files\n *\n * @example\n * ```typescript\n * // Find all endpoints in the routes directory\n * const endpoints = await getEndpointsFromRoutes(\n * ['src/routes/**\\/*.ts'],\n * process.cwd()\n * );\n *\n * // Register endpoints with your server\n * for (const endpoint of endpoints) {\n * server.register(endpoint);\n * }\n * ```\n *\n * @remarks\n * - Only exports that are valid Endpoint instances are included\n * - Files are imported dynamically, so they must be valid ES modules\n * - The function filters out non-Endpoint exports automatically\n */\nexport async function getEndpointsFromRoutes<TServices extends Service[]>(\n\troutes: string[],\n\tcwd: string,\n): Promise<Endpoint<string, HttpMethod, any, any, TServices>[]> {\n\tconst stream = fg.stream(routes, { cwd });\n\n\tconst endpoints: Endpoint<string, HttpMethod, any, any, TServices>[] = [];\n\n\tfor await (const f of stream) {\n\t\t// Resolve the absolute path for the route file\n\t\tconst routePath = path.resolve(cwd, f.toString());\n\t\t// Dynamically import the route module\n\t\tconst route = await import(routePath);\n\n\t\t// Filter exported values to find only Endpoint instances\n\t\tconst handlers = Object.values(route).filter((value) => {\n\t\t\treturn Endpoint.isEndpoint(value);\n\t\t}) as unknown as Endpoint<string, HttpMethod, any, any, TServices>[];\n\n\t\tendpoints.push(...handlers);\n\t}\n\n\treturn endpoints;\n}\n","import type { Context } from 'hono';\nimport qs from 'qs';\n\n/**\n * Parse Hono query parameters using qs bracket notation\n * Supports nested objects, arrays, and deep nesting via standard bracket syntax\n */\nexport function parseHonoQuery(c: Context): Record<string, any> {\n\tconst url = c.req.url;\n\tconst queryIndex = url.indexOf('?');\n\tif (queryIndex === -1) {\n\t\treturn {};\n\t}\n\tconst queryString = url.slice(queryIndex + 1);\n\treturn qs.parse(queryString) as Record<string, any>;\n}\n","import type { AuditableAction, AuditStorage } from '@geekmidas/audit';\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport { type HttpError, isHttpError, wrapError } from '@geekmidas/errors';\nimport type { EventPublisher } from '@geekmidas/events';\nimport type { Logger } from '@geekmidas/logger';\nimport {\n\trunWithRequestContext,\n\ttype Service,\n\tServiceDiscovery,\n\ttype ServiceRecord,\n} from '@geekmidas/services';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { type Context, Hono } from 'hono';\nimport { setCookie } from 'hono/cookie';\nimport { logger as honoLogger } from 'hono/logger';\nimport { timing } from 'hono/timing';\nimport type {\n\tContentfulStatusCode,\n\tRedirectStatusCode,\n} from 'hono/utils/http-status';\nimport { validator } from 'hono/validator';\nimport { publishConstructEvents } from '../publisher';\nimport type { HttpMethod, LowerHttpMethod } from '../types';\nimport type { MappedAudit } from './audit';\nimport {\n\tEndpoint,\n\ttype EndpointContext,\n\ttype EndpointSchemas,\n\tResponseBuilder,\n} from './Endpoint';\nimport { getEndpointsFromRoutes } from './helpers';\nimport { createHonoCookies, createHonoHeaders } from './lazyAccessors';\nimport { loadRls } from './optionalPeers';\nimport { parseHonoQuery } from './parseHonoQuery';\nimport {\n\tcreateAuditContext,\n\texecuteWithAuditTransaction,\n} from './processAudits';\nimport { enforceRateLimit } from './rateLimit';\nimport { readRequestBody } from './readRequestBody';\n\nexport interface HonoEndpointOptions {\n\t/**\n\t * Path where OpenAPI documentation will be served.\n\t * Set to false to disable docs route.\n\t * @default '/docs'\n\t */\n\tdocsPath?: string | false;\n\t/**\n\t * OpenAPI schema options\n\t */\n\topenApiOptions?: {\n\t\ttitle?: string;\n\t\tversion?: string;\n\t\tdescription?: string;\n\t};\n}\n\n/**\n * Feature flags for an endpoint, analyzed once at route registration time.\n * This avoids per-request feature detection overhead.\n */\ninterface EndpointFeatures {\n\thasServices: boolean;\n\thasDatabase: boolean;\n\thasBodyValidation: boolean;\n\thasQueryValidation: boolean;\n\thasParamValidation: boolean;\n\thasAudits: boolean;\n\thasEvents: boolean;\n\thasRateLimit: boolean;\n\thasRls: boolean;\n}\n\n/**\n * Case-insensitive check for a `content-type` header in a metadata headers map.\n */\nfunction hasContentTypeHeader(headers: Record<string, string>): boolean {\n\tfor (const key of Object.keys(headers)) {\n\t\tif (key.toLowerCase() === 'content-type') return true;\n\t}\n\treturn false;\n}\n\n/**\n * Analyze endpoint features at registration time (not per-request)\n */\nfunction analyzeEndpointFeatures(\n\tendpoint: Endpoint<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany\n\t>,\n): EndpointFeatures {\n\treturn {\n\t\thasServices: endpoint.services.length > 0,\n\t\thasDatabase: !!endpoint.databaseService,\n\t\thasBodyValidation: !!endpoint.input?.body,\n\t\thasQueryValidation: !!endpoint.input?.query,\n\t\thasParamValidation: !!endpoint.input?.params,\n\t\t// Audit context needed if declarative audits OR auditor storage service configured (for manual audits)\n\t\thasAudits:\n\t\t\t(endpoint.audits?.length ?? 0) > 0 || !!endpoint.auditorStorageService,\n\t\thasEvents: (endpoint.events?.length ?? 0) > 0,\n\t\thasRateLimit: !!endpoint.rateLimit,\n\t\thasRls: !!endpoint.rlsConfig && !endpoint.rlsBypass,\n\t};\n}\n\nexport class HonoEndpoint<\n\tTRoute extends string,\n\tTMethod extends HttpMethod,\n\tTInput extends EndpointSchemas = {},\n\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\tTServices extends Service[] = [],\n\tTLogger extends Logger = Logger,\n\tTSession = unknown,\n\tTAuditStorage extends AuditStorage | undefined = undefined,\n\tTAuditStorageServiceName extends string = string,\n\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\tstring,\n\t\tunknown\n\t>,\n\tTDatabase = undefined,\n\tTDatabaseServiceName extends string = string,\n> {\n\tconstructor(\n\t\tprivate readonly endpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t) {}\n\n\tstatic isDev = process.env.NODE_ENV === 'development';\n\n\tstatic async validate<T extends StandardSchemaV1>(\n\t\tc: Context<any, string, {}>,\n\t\tdata: unknown,\n\t\tschema?: T,\n\t) {\n\t\tif (!schema) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst parsed = await Endpoint.validate(schema, data);\n\n\t\tif (parsed.issues) {\n\t\t\treturn c.json(parsed.issues, 422);\n\t\t}\n\n\t\treturn parsed.value;\n\t}\n\taddRoute(\n\t\tserviceDiscovery: ServiceDiscovery<ServiceRecord<TServices>>,\n\t\tapp: Hono,\n\t): void {\n\t\tHonoEndpoint.addRoute(this.endpoint, serviceDiscovery, app);\n\t}\n\n\t/**\n\t * @deprecated Global event middleware is no longer used.\n\t * Events are now published per-route only for endpoints that have events configured.\n\t * This method is kept for backward compatibility but does nothing.\n\t */\n\tstatic applyEventMiddleware(\n\t\t_app: Hono,\n\t\t_serviceDiscovery: ServiceDiscovery<any>,\n\t) {\n\t\t// No-op: Event publishing is now handled per-route in addRoute\n\t\t// This avoids running middleware on every request including 404s\n\t}\n\n\tstatic async fromRoutes<TLogger extends Logger, TServices extends Service[]>(\n\t\troutes: string[],\n\t\tenvParser: EnvironmentParser<{}>,\n\t\tapp = new Hono(),\n\t\t_logger: TLogger,\n\t\tcwd = process.cwd(),\n\t\toptions?: HonoEndpointOptions,\n\t): Promise<Hono> {\n\t\tconst endpoints = await getEndpointsFromRoutes<TServices>(routes, cwd);\n\t\tconst serviceDiscovery =\n\t\t\tServiceDiscovery.getInstance<ServiceRecord<TServices>>(envParser);\n\n\t\tHonoEndpoint.addRoutes(endpoints, serviceDiscovery, app, options);\n\n\t\treturn app;\n\t}\n\n\tstatic addRoutes<\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t>(\n\t\tendpoints: Endpoint<string, HttpMethod, any, any, TServices, TLogger>[],\n\t\tserviceDiscovery: ServiceDiscovery<ServiceRecord<TServices>>,\n\t\tapp: Hono,\n\t\toptions?: HonoEndpointOptions,\n\t): void {\n\t\t// Add timing middleware (always enabled)\n\t\tapp.use('*', timing());\n\n\t\t// Add logger middleware in development mode\n\n\t\tif (HonoEndpoint.isDev) {\n\t\t\tapp.use('*', honoLogger());\n\t\t}\n\n\t\t// Add docs route if not disabled\n\t\tconst docsPath =\n\t\t\toptions?.docsPath !== false ? options?.docsPath || '/docs' : null;\n\t\tif (docsPath) {\n\t\t\tHonoEndpoint.addDocsRoute(\n\t\t\t\tendpoints,\n\t\t\t\tapp,\n\t\t\t\tdocsPath,\n\t\t\t\toptions?.openApiOptions,\n\t\t\t);\n\t\t}\n\n\t\t// Sort endpoints to ensure static routes come before dynamic ones\n\t\tconst sortedEndpoints = endpoints.sort((a, b) => {\n\t\t\tconst aSegments = a.route.split('/');\n\t\t\tconst bSegments = b.route.split('/');\n\n\t\t\t// Compare each segment\n\t\t\tfor (let i = 0; i < Math.max(aSegments.length, bSegments.length); i++) {\n\t\t\t\tconst aSegment = aSegments[i] || '';\n\t\t\t\tconst bSegment = bSegments[i] || '';\n\n\t\t\t\t// If one is dynamic and the other is not, static comes first\n\t\t\t\tconst aIsDynamic = aSegment.startsWith(':');\n\t\t\t\tconst bIsDynamic = bSegment.startsWith(':');\n\n\t\t\t\tif (!aIsDynamic && bIsDynamic) return -1;\n\t\t\t\tif (aIsDynamic && !bIsDynamic) return 1;\n\n\t\t\t\t// If both are the same type, compare alphabetically\n\t\t\t\tif (aSegment !== bSegment) {\n\t\t\t\t\treturn aSegment.localeCompare(bSegment);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t});\n\n\t\t// Note: Global event middleware removed for performance\n\t\t// Events are now published per-route only for endpoints with events configured\n\t\tfor (const endpoint of sortedEndpoints) {\n\t\t\tHonoEndpoint.addRoute(endpoint, serviceDiscovery, app);\n\t\t}\n\t}\n\n\t// biome-ignore lint/suspicious/useAdjacentOverloadSignatures: not an overload — the instance `addRoute` above registers this adaptor's own endpoint, this one registers any endpoint on a given app. They share a name because they do the same thing to different subjects.\n\tstatic addRoute<\n\t\tTRoute extends string,\n\t\tTMethod extends HttpMethod,\n\t\tTInput extends EndpointSchemas = {},\n\t\tTOutSchema extends StandardSchemaV1 | undefined = undefined,\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t\tTSession = unknown,\n\t\tTAuditStorage extends AuditStorage | undefined = undefined,\n\t\tTAuditStorageServiceName extends string = string,\n\t\tTAuditAction extends AuditableAction<string, unknown> = AuditableAction<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>,\n\t\tTDatabase = undefined,\n\t\tTDatabaseServiceName extends string = string,\n\t>(\n\t\tendpoint: Endpoint<\n\t\t\tTRoute,\n\t\t\tTMethod,\n\t\t\tTInput,\n\t\t\tTOutSchema,\n\t\t\tTServices,\n\t\t\tTLogger,\n\t\t\tTSession,\n\t\t\tTAuditStorage,\n\t\t\tTAuditStorageServiceName,\n\t\t\tTAuditAction,\n\t\t\tTDatabase,\n\t\t\tTDatabaseServiceName\n\t\t>,\n\t\tserviceDiscovery: ServiceDiscovery<ServiceRecord<TServices>>,\n\t\tapp: Hono,\n\t): void {\n\t\tconst { route } = endpoint;\n\t\tconst method = endpoint.method.toLowerCase() as LowerHttpMethod<TMethod>;\n\n\t\t// Analyze endpoint features once at registration time (not per-request)\n\t\tconst features = analyzeEndpointFeatures(endpoint);\n\n\t\t// Build validators array - only add validators for schemas that exist\n\t\tconst validators: any[] = [];\n\n\t\tif (features.hasBodyValidation) {\n\t\t\t// Read by its Content-Type — JSON, an HTML form, text — and kept\n\t\t\t// where a JSON validator keeps it, so the handler reads it as before.\n\t\t\tvalidators.push(async (c: Context, next: () => Promise<void>) => {\n\t\t\t\tlet raw: unknown;\n\t\t\t\ttry {\n\t\t\t\t\traw = await readRequestBody(c.req, c.req.header('content-type'), {\n\t\t\t\t\t\tuntyped: 'empty',\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isHttpError(error)) throw error;\n\t\t\t\t\tsetErrorHeaders(c, error);\n\t\t\t\t\treturn c.json(\n\t\t\t\t\t\terrorBody(error),\n\t\t\t\t\t\terror.statusCode as ContentfulStatusCode,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst body = await HonoEndpoint.validate(c, raw, endpoint.input?.body);\n\t\t\t\tif (body instanceof Response) return body;\n\t\t\t\tc.req.addValidatedData('json', body as {});\n\t\t\t\tawait next();\n\t\t\t});\n\t\t}\n\n\t\tif (features.hasQueryValidation) {\n\t\t\tvalidators.push(\n\t\t\t\tvalidator('query', (_, c) => {\n\t\t\t\t\tconst parsedQuery = parseHonoQuery(c);\n\t\t\t\t\treturn HonoEndpoint.validate(c, parsedQuery, endpoint.input?.query);\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tif (features.hasParamValidation) {\n\t\t\tvalidators.push(\n\t\t\t\tvalidator('param', (params, c) =>\n\t\t\t\t\tHonoEndpoint.validate(c, params, endpoint.input?.params),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// Main handler\n\t\tconst handler = async (c: Context) => {\n\t\t\t// Request context setup\n\t\t\tconst startTime = Date.now();\n\t\t\tconst requestId = c.req.header('X-Request-ID') ?? crypto.randomUUID();\n\n\t\t\tconst logger = endpoint.logger.child({\n\t\t\t\trequestId,\n\t\t\t\tendpoint: endpoint.fullPath,\n\t\t\t\troute: endpoint.route,\n\t\t\t\thost: c.req.header('host'),\n\t\t\t\tmethod: endpoint.method,\n\t\t\t\tpath: c.req.path,\n\t\t\t}) as TLogger;\n\n\t\t\t// Set response header\n\t\t\tc.header('X-Request-ID', requestId);\n\n\t\t\t// Wrap entire handler in request context for services to access\n\t\t\treturn runWithRequestContext(\n\t\t\t\t{\n\t\t\t\t\tlogger,\n\t\t\t\t\trequestId,\n\t\t\t\t\tstartTime,\n\t\t\t\t\toperation: `${endpoint.method} ${endpoint.route}`,\n\t\t\t\t},\n\t\t\t\tasync () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Lazy accessors - no upfront parsing, use native Hono methods\n\t\t\t\t\t\tconst header = createHonoHeaders(c);\n\t\t\t\t\t\tconst cookie = createHonoCookies(c);\n\n\t\t\t\t\t\t// Only register services if endpoint has any\n\t\t\t\t\t\tconst services = features.hasServices\n\t\t\t\t\t\t\t? await serviceDiscovery.register(endpoint.services)\n\t\t\t\t\t\t\t: ({} as ServiceRecord<TServices>);\n\n\t\t\t\t\t\t// Resolve database service only if configured\n\t\t\t\t\t\tconst rawDb = features.hasDatabase\n\t\t\t\t\t\t\t? await serviceDiscovery\n\t\t\t\t\t\t\t\t\t.register([endpoint.databaseService!])\n\t\t\t\t\t\t\t\t\t.then(\n\t\t\t\t\t\t\t\t\t\t(s) =>\n\t\t\t\t\t\t\t\t\t\t\ts[\n\t\t\t\t\t\t\t\t\t\t\t\tendpoint.databaseService?.serviceName as keyof typeof s\n\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\t// Extract session (defaults to empty object)\n\t\t\t\t\t\tconst session = await endpoint.getSession({\n\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t...(rawDb !== undefined && { db: rawDb }),\n\t\t\t\t\t\t} as any);\n\n\t\t\t\t\t\t// Check authorization (defaults to true)\n\t\t\t\t\t\tconst isAuthorized = await endpoint.authorize({\n\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t...(rawDb !== undefined && { db: rawDb }),\n\t\t\t\t\t\t\tbody: features.hasBodyValidation\n\t\t\t\t\t\t\t\t? (c.req.valid as any)('json')\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\tquery: features.hasQueryValidation\n\t\t\t\t\t\t\t\t? (c.req.valid as any)('query')\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\tparams: features.hasParamValidation\n\t\t\t\t\t\t\t\t? (c.req.valid as any)('param')\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t} as any);\n\n\t\t\t\t\t\tif (!isAuthorized) {\n\t\t\t\t\t\t\tlogger.warn('Forbidden access attempt');\n\t\t\t\t\t\t\treturn c.json({ error: 'Forbidden' }, 403);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Check rate limit only if configured\n\t\t\t\t\t\tif (features.hasRateLimit) {\n\t\t\t\t\t\t\tconst rateLimitHeaders = await enforceRateLimit(\n\t\t\t\t\t\t\t\tendpoint.rateLimit!,\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t\t\tpath: c.req.path,\n\t\t\t\t\t\t\t\t\tmethod: endpoint.method,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tfor (const [key, value] of Object.entries(rateLimitHeaders)) {\n\t\t\t\t\t\t\t\tc.header(key, value);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Create audit context only if audits are configured\n\t\t\t\t\t\tconst auditContext = features.hasAudits\n\t\t\t\t\t\t\t? await createAuditContext(\n\t\t\t\t\t\t\t\t\tendpoint as any,\n\t\t\t\t\t\t\t\t\tserviceDiscovery,\n\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t\t\t\tservices: services as Record<string, unknown>,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\tconst audits = features.hasAudits\n\t\t\t\t\t\t\t? (endpoint.audits as MappedAudit<TAuditAction, TOutSchema>[])\n\t\t\t\t\t\t\t: [];\n\n\t\t\t\t\t\t// Warn if declarative audits are configured but no audit storage\n\t\t\t\t\t\tif (features.hasAudits && !auditContext) {\n\t\t\t\t\t\t\tlogger.warn('No auditor storage service available');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Extract RLS context only if configured and not bypassed\n\t\t\t\t\t\tconst rlsContext =\n\t\t\t\t\t\t\tfeatures.hasRls && rawDb !== undefined\n\t\t\t\t\t\t\t\t? await endpoint.rlsConfig?.extractor({\n\t\t\t\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\t\t\t\tsession: session as TSession,\n\t\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\t// Execute handler with automatic audit transaction support\n\t\t\t\t\t\tconst result = await executeWithAuditTransaction(\n\t\t\t\t\t\t\tauditContext,\n\t\t\t\t\t\t\tasync (auditor) => {\n\t\t\t\t\t\t\t\tconst sameDatabase =\n\t\t\t\t\t\t\t\t\tauditContext?.storage?.databaseServiceName &&\n\t\t\t\t\t\t\t\t\tauditContext.storage.databaseServiceName ===\n\t\t\t\t\t\t\t\t\t\tendpoint.databaseService?.serviceName;\n\t\t\t\t\t\t\t\tconst baseDb = sameDatabase\n\t\t\t\t\t\t\t\t\t? (auditor?.getTransaction?.() ?? rawDb)\n\t\t\t\t\t\t\t\t\t: rawDb;\n\n\t\t\t\t\t\t\t\tconst executeHandler = async (db: TDatabase | undefined) => {\n\t\t\t\t\t\t\t\t\tconst responseBuilder = new ResponseBuilder();\n\t\t\t\t\t\t\t\t\tconst response = await endpoint.handler(\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tservices,\n\t\t\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t\t\t\tbody: features.hasBodyValidation\n\t\t\t\t\t\t\t\t\t\t\t\t? (c.req.valid as any)('json')\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t\t\tquery: features.hasQueryValidation\n\t\t\t\t\t\t\t\t\t\t\t\t? (c.req.valid as any)('query')\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t\t\tparams: features.hasParamValidation\n\t\t\t\t\t\t\t\t\t\t\t\t? (c.req.valid as any)('param')\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t\t\t\t\theader,\n\t\t\t\t\t\t\t\t\t\t\tcookie,\n\t\t\t\t\t\t\t\t\t\t\tauditor,\n\t\t\t\t\t\t\t\t\t\t\tdb,\n\t\t\t\t\t\t\t\t\t\t} as unknown as EndpointContext<\n\t\t\t\t\t\t\t\t\t\t\tTInput,\n\t\t\t\t\t\t\t\t\t\t\tTServices,\n\t\t\t\t\t\t\t\t\t\t\tTLogger,\n\t\t\t\t\t\t\t\t\t\t\tTSession,\n\t\t\t\t\t\t\t\t\t\t\tTAuditAction,\n\t\t\t\t\t\t\t\t\t\t\tTDatabase,\n\t\t\t\t\t\t\t\t\t\t\tTAuditStorage\n\t\t\t\t\t\t\t\t\t\t>,\n\t\t\t\t\t\t\t\t\t\tresponseBuilder,\n\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\tlet data = response;\n\t\t\t\t\t\t\t\t\tlet metadata = responseBuilder.getMetadata();\n\n\t\t\t\t\t\t\t\t\tif (Endpoint.hasMetadata(response)) {\n\t\t\t\t\t\t\t\t\t\tdata = response.data;\n\t\t\t\t\t\t\t\t\t\tmetadata = response.metadata;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// A redirect has no body to check against the schema.\n\t\t\t\t\t\t\t\t\tconst redirect = Endpoint.isRedirectStatus(\n\t\t\t\t\t\t\t\t\t\tmetadata.status ?? endpoint.status,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tconst output =\n\t\t\t\t\t\t\t\t\t\tendpoint.outputSchema && !redirect\n\t\t\t\t\t\t\t\t\t\t\t? await endpoint.parseOutput(data)\n\t\t\t\t\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t\t\t\t\treturn { output, metadata, responseBuilder, redirect };\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\tif (features.hasRls && rlsContext && baseDb) {\n\t\t\t\t\t\t\t\t\tconst { withRlsContext } = await loadRls();\n\t\t\t\t\t\t\t\t\treturn withRlsContext(\n\t\t\t\t\t\t\t\t\t\tbaseDb as any,\n\t\t\t\t\t\t\t\t\t\trlsContext,\n\t\t\t\t\t\t\t\t\t\tasync (trx) => executeHandler(trx as TDatabase),\n\t\t\t\t\t\t\t\t\t\t{ prefix: endpoint.rlsConfig?.prefix },\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn executeHandler(baseDb as TDatabase | undefined);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tasync (result, auditor) => {\n\t\t\t\t\t\t\t\tif (!audits?.length || result.redirect) return;\n\n\t\t\t\t\t\t\t\tfor (const audit of audits) {\n\t\t\t\t\t\t\t\t\tif (audit.when && !audit.when(result.output as any)) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tconst payload = audit.payload(result.output as any);\n\t\t\t\t\t\t\t\t\tconst entityId = audit.entityId?.(result.output as any);\n\t\t\t\t\t\t\t\t\tauditor.audit(audit.type as any, payload as any, {\n\t\t\t\t\t\t\t\t\t\ttable: audit.table,\n\t\t\t\t\t\t\t\t\t\tentityId,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ db: rawDb },\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst { output, metadata, redirect } = result;\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tlet status = endpoint.status as ContentfulStatusCode;\n\n\t\t\t\t\t\t\tif (metadata.status) {\n\t\t\t\t\t\t\t\tstatus = metadata.status as ContentfulStatusCode;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (metadata.headers) {\n\t\t\t\t\t\t\t\tfor (const [key, value] of Object.entries(metadata.headers)) {\n\t\t\t\t\t\t\t\t\tc.header(key, value);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (metadata.cookies) {\n\t\t\t\t\t\t\t\tfor (const [name, { value, options }] of metadata.cookies) {\n\t\t\t\t\t\t\t\t\tsetCookie(c, name, value, options);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Only publish events if configured (no global middleware overhead)\n\t\t\t\t\t\t\tif (features.hasEvents && Endpoint.isSuccessStatus(status)) {\n\t\t\t\t\t\t\t\tawait publishConstructEvents<any>(\n\t\t\t\t\t\t\t\t\tendpoint as any,\n\t\t\t\t\t\t\t\t\toutput,\n\t\t\t\t\t\t\t\t\tserviceDiscovery,\n\t\t\t\t\t\t\t\t\tlogger,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (HonoEndpoint.isDev) {\n\t\t\t\t\t\t\t\tlogger.info({ status, body: output }, 'Outgoing response');\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (redirect) {\n\t\t\t\t\t\t\t\treturn c.body(null, status as RedirectStatusCode);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Respect the endpoint's declared responseType. For JSON (default)\n\t\t\t\t\t\t\t// we use c.json() which sets Content-Type and serializes. For any\n\t\t\t\t\t\t\t// other type, emit the body as-is and set the header explicitly —\n\t\t\t\t\t\t\t// metadata.headers (from r.header()) applied above still wins.\n\t\t\t\t\t\t\tif (endpoint.responseType === 'application/json') {\n\t\t\t\t\t\t\t\t// Hono 4.13 types `c.json`'s status well enough that the\n\t\t\t\t\t\t\t\t// suppression here is now itself an error.\n\t\t\t\t\t\t\t\treturn c.json(output, status);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t!(metadata.headers && hasContentTypeHeader(metadata.headers))\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tc.header('content-type', endpoint.responseType);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn c.body(output as any, status);\n\t\t\t\t\t\t} catch (validationError: any) {\n\t\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t\t{ err: validationError },\n\t\t\t\t\t\t\t\t'Output validation failed',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconst error = wrapError(\n\t\t\t\t\t\t\t\tvalidationError,\n\t\t\t\t\t\t\t\t422,\n\t\t\t\t\t\t\t\t'Response validation failed',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tsetErrorHeaders(c, error);\n\t\t\t\t\t\t\tconst body = errorBody(error);\n\t\t\t\t\t\t\tif (HonoEndpoint.isDev) {\n\t\t\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t\t\t{ status: error.statusCode, body },\n\t\t\t\t\t\t\t\t\t'Outgoing response',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn c.json(body, error.statusCode as ContentfulStatusCode);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e: any) {\n\t\t\t\t\t\t// Under `err`, which every pino serializes as an Error — passed bare,\n\t\t\t\t\t\t// one pino does not recognise (a ZodError) is spread into the line\n\t\t\t\t\t\t// with no stack.\n\t\t\t\t\t\tlogger.error({ err: e }, 'Error processing endpoint request');\n\t\t\t\t\t\tconst error = wrapError(e, 500, 'Internal Server Error');\n\t\t\t\t\t\t// Answered here rather than rethrown, so Hono never sets it:\n\t\t\t\t\t\t// a server failure is left on the context the way Hono\n\t\t\t\t\t\t// leaves an uncaught one, for middleware — a request span\n\t\t\t\t\t\t// records it as the exception. A 4xx is the client's.\n\t\t\t\t\t\tif (error.statusCode >= 500 && e instanceof Error) c.error = e;\n\t\t\t\t\t\tsetErrorHeaders(c, error);\n\t\t\t\t\t\tconst body = errorBody(error);\n\t\t\t\t\t\tif (HonoEndpoint.isDev) {\n\t\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t\t{ status: error.statusCode, body },\n\t\t\t\t\t\t\t\t'Outgoing response',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn c.json(body, error.statusCode as ContentfulStatusCode);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t};\n\n\t\t// Register route with conditional validators.\n\t\t//\n\t\t// `route` is a generic `TRoute`, and Hono 4.13 overloads `app.get` on\n\t\t// whether the first argument is a path or a handler. A generic that is\n\t\t// only *constrained* to string does not pick the path overload, so it\n\t\t// gets matched against the handler one and reported there. Saying it is a\n\t\t// string chooses the overload that was always intended.\n\t\t//\n\t\t// The spread is why a cast is needed rather than a better type: with\n\t\t// `...validators` of unknown length, the arity is not decidable, so the\n\t\t// first overload is chosen whatever the path argument says. Naming the\n\t\t// shape we are calling states the intent the overloads cannot infer.\n\t\t(\n\t\t\tapp[method] as unknown as (\n\t\t\t\tpath: string,\n\t\t\t\t...handlers: unknown[]\n\t\t\t) => unknown\n\t\t)(route, ...validators, handler);\n\t}\n\n\tstatic addDocsRoute<\n\t\tTServices extends Service[] = [],\n\t\tTLogger extends Logger = Logger,\n\t>(\n\t\tendpoints: Endpoint<string, HttpMethod, any, any, TServices, TLogger>[],\n\t\tapp: Hono,\n\t\tdocsPath: string,\n\t\topenApiOptions?: HonoEndpointOptions['openApiOptions'],\n\t): void {\n\t\tapp.get(docsPath, async (c) => {\n\t\t\ttry {\n\t\t\t\tconst openApiSchema = await Endpoint.buildOpenApiSchema(\n\t\t\t\t\tendpoints,\n\t\t\t\t\topenApiOptions,\n\t\t\t\t);\n\n\t\t\t\treturn c.json(openApiSchema);\n\t\t\t} catch {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{ error: 'Failed to generate OpenAPI documentation' },\n\t\t\t\t\t500,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n}\n\n/**\n * What a client is told about a failed request: what went wrong, and the stack\n * only under `gkm dev`. A server's stack is its file layout and its\n * dependencies' versions — for the person debugging it, not for whoever sent\n * the request. A server failure's details are the error it wrapped (an\n * environment variable it could not read, a query that failed), so they go to\n * the log and not the response either; a 4xx's details are the client's.\n */\nfunction errorBody(error: HttpError) {\n\tconst internal = error.statusCode >= 500 && !HonoEndpoint.isDev;\n\treturn {\n\t\tname: error.name,\n\t\tmessage: error.message,\n\t\tstatusCode: error.statusCode,\n\t\tstatusMessage: error.statusMessage,\n\t\tcode: error.code,\n\t\t...(internal ? {} : { details: error.details }),\n\t\t...(HonoEndpoint.isDev ? { stack: error.stack } : {}),\n\t};\n}\n\n/**\n * The headers an error says its response carries — a 429's `Retry-After` and\n * `X-RateLimit-*` — on the response that reports it.\n */\nfunction setErrorHeaders(c: Context, error: HttpError): void {\n\tfor (const [key, value] of Object.entries(error.headers ?? {})) {\n\t\tc.header(key, value);\n\t}\n}\n"],"mappings":"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"}