@forinda/kickjs 9.2.0 → 10.1.0

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 (164) hide show
  1. package/dist/{adapter-DjfRMob8.d.mts → adapter-ClEh-Oos.d.mts} +24 -2
  2. package/dist/adapter-ClEh-Oos.d.mts.map +1 -0
  3. package/dist/application-Dv514EeR.mjs +12 -0
  4. package/dist/application-Dv514EeR.mjs.map +1 -0
  5. package/dist/{application-CGxZCYl1.d.mts → application-oIPzgjfk.d.mts} +38 -11
  6. package/dist/application-oIPzgjfk.d.mts.map +1 -0
  7. package/dist/bootstrap-BdyBlBMe.mjs +12 -0
  8. package/dist/bootstrap-BdyBlBMe.mjs.map +1 -0
  9. package/dist/{client-ip-pcINkkPr.mjs → client-ip-BbLDp03S.mjs} +2 -2
  10. package/dist/{client-ip-pcINkkPr.mjs.map → client-ip-BbLDp03S.mjs.map} +1 -1
  11. package/dist/config/index.mjs +2 -2
  12. package/dist/{config-DmGiAIuH.mjs → config-CJiGeOca.mjs} +3 -3
  13. package/dist/{config-DmGiAIuH.mjs.map → config-CJiGeOca.mjs.map} +1 -1
  14. package/dist/{container-CAPN7pBG.mjs → container-CS9v9UFU.mjs} +4 -4
  15. package/dist/{container-CAPN7pBG.mjs.map → container-CS9v9UFU.mjs.map} +1 -1
  16. package/dist/context-CtL9RxXp.mjs +12 -0
  17. package/dist/context-CtL9RxXp.mjs.map +1 -0
  18. package/dist/{context-errors-DYJjhalQ.mjs → context-errors-7C5TBYn7.mjs} +2 -2
  19. package/dist/{context-errors-DYJjhalQ.mjs.map → context-errors-7C5TBYn7.mjs.map} +1 -1
  20. package/dist/contributor-runner-DBpRR5dl.mjs +12 -0
  21. package/dist/{contributor-runner-vk9XlOED.mjs.map → contributor-runner-DBpRR5dl.mjs.map} +1 -1
  22. package/dist/core/adapter.d.mts +1 -1
  23. package/dist/core/adapter.mjs +1 -1
  24. package/dist/core/app-module.mjs +1 -1
  25. package/dist/core/cache.mjs +2 -2
  26. package/dist/core/cache.mjs.map +1 -1
  27. package/dist/core/container.mjs +2 -2
  28. package/dist/core/cron.mjs +2 -2
  29. package/dist/core/cron.mjs.map +1 -1
  30. package/dist/core/decorators.mjs +2 -2
  31. package/dist/core/decorators.mjs.map +1 -1
  32. package/dist/core/errors.mjs +1 -1
  33. package/dist/core/interfaces.mjs +2 -2
  34. package/dist/core/interfaces.mjs.map +1 -1
  35. package/dist/core/logger.mjs +2 -2
  36. package/dist/core/logger.mjs.map +1 -1
  37. package/dist/core/path.mjs +1 -1
  38. package/dist/core/plugin.d.mts +1 -1
  39. package/dist/core/plugin.mjs +1 -1
  40. package/dist/core/reactivity.mjs +2 -2
  41. package/dist/core/reactivity.mjs.map +1 -1
  42. package/dist/core-B0JmdBvX.mjs +12 -0
  43. package/dist/{core-DKrKVLfj.mjs.map → core-B0JmdBvX.mjs.map} +1 -1
  44. package/dist/{csrf-CxJItZVa.mjs → csrf--1t4qDov.mjs} +3 -3
  45. package/dist/{csrf-CxJItZVa.mjs.map → csrf--1t4qDov.mjs.map} +1 -1
  46. package/dist/{decorate-DfH1NIqV.mjs → decorate-BHkdkV4l.mjs} +1 -1
  47. package/dist/{define-adapter-C6lFhCL-.mjs → define-adapter-BLKlUbSL.mjs} +2 -2
  48. package/dist/{define-adapter-C6lFhCL-.mjs.map → define-adapter-BLKlUbSL.mjs.map} +1 -1
  49. package/dist/disposables-CuY2S3bw.mjs +12 -0
  50. package/dist/{disposables-NlW6Mibk.mjs.map → disposables-CuY2S3bw.mjs.map} +1 -1
  51. package/dist/env-BwzF9MkZ.mjs +13 -0
  52. package/dist/env-BwzF9MkZ.mjs.map +1 -0
  53. package/dist/{error-handler-itG65lj0.d.mts → error-handler-BB9wAElm.d.mts} +2 -2
  54. package/dist/{error-handler-itG65lj0.d.mts.map → error-handler-BB9wAElm.d.mts.map} +1 -1
  55. package/dist/handler-zKfIgNo4.mjs +13 -0
  56. package/dist/handler-zKfIgNo4.mjs.map +1 -0
  57. package/dist/http/application.d.mts +1 -1
  58. package/dist/http/application.mjs +2 -2
  59. package/dist/http/bootstrap.d.mts +1 -1
  60. package/dist/http/bootstrap.mjs +2 -2
  61. package/dist/http/context.d.mts +1 -1
  62. package/dist/http/context.mjs +2 -2
  63. package/dist/http/middleware/cors.mjs +2 -2
  64. package/dist/http/middleware/cors.mjs.map +1 -1
  65. package/dist/http/middleware/csrf.mjs +2 -2
  66. package/dist/http/middleware/error-handler.d.mts +1 -1
  67. package/dist/http/middleware/error-handler.mjs +2 -2
  68. package/dist/http/middleware/helmet.mjs +1 -1
  69. package/dist/http/middleware/rate-limit.mjs +2 -2
  70. package/dist/http/middleware/rate-limit.mjs.map +1 -1
  71. package/dist/http/middleware/request-id.mjs +1 -1
  72. package/dist/http/middleware/request-logger.mjs +2 -2
  73. package/dist/http/middleware/session.d.mts +1 -1
  74. package/dist/http/middleware/session.mjs +2 -2
  75. package/dist/http/middleware/session.mjs.map +1 -1
  76. package/dist/http/middleware/spa.d.mts +1 -1
  77. package/dist/http/middleware/spa.mjs +2 -2
  78. package/dist/http/middleware/spa.mjs.map +1 -1
  79. package/dist/http/middleware/upload.d.mts +1 -1
  80. package/dist/http/middleware/upload.mjs +2 -2
  81. package/dist/http/middleware/validate.mjs +1 -1
  82. package/dist/http/middleware/views.d.mts +1 -1
  83. package/dist/http/middleware/views.mjs +2 -2
  84. package/dist/http/middleware/views.mjs.map +1 -1
  85. package/dist/http/query/index.mjs +2 -2
  86. package/dist/http/router-builder.d.mts +1 -1
  87. package/dist/http/router-builder.mjs +2 -2
  88. package/dist/http/runtimes/fastify.d.mts +2 -1
  89. package/dist/http/runtimes/fastify.d.mts.map +1 -1
  90. package/dist/http/runtimes/fastify.mjs +3 -3
  91. package/dist/http/runtimes/fastify.mjs.map +1 -1
  92. package/dist/http/runtimes/h3-web.d.mts +2 -49
  93. package/dist/http/runtimes/h3-web.mjs +2 -3
  94. package/dist/http/runtimes/h3.d.mts +48 -18
  95. package/dist/http/runtimes/h3.d.mts.map +1 -1
  96. package/dist/http/runtimes/h3.mjs +2 -2
  97. package/dist/http/runtimes/h3.mjs.map +1 -1
  98. package/dist/index.d.mts +8 -8
  99. package/dist/index.mjs +2 -2
  100. package/dist/index.mjs.map +1 -1
  101. package/dist/jobs-BQ4t3TAb.mjs +12 -0
  102. package/dist/{jobs-BmpeFvi0.mjs.map → jobs-BQ4t3TAb.mjs.map} +1 -1
  103. package/dist/{kv-stores-BZTe23Tw.d.mts → kv-stores-Co5k2PYA.d.mts} +21 -6
  104. package/dist/kv-stores-Co5k2PYA.d.mts.map +1 -0
  105. package/dist/kv-stores-DsfVy2fk.mjs +12 -0
  106. package/dist/{kv-stores-DJwxntyw.mjs.map → kv-stores-DsfVy2fk.mjs.map} +1 -1
  107. package/dist/{metadata-CzzRUnAx.mjs → metadata-zvd6AWkE.mjs} +3 -3
  108. package/dist/{metadata-CzzRUnAx.mjs.map → metadata-zvd6AWkE.mjs.map} +1 -1
  109. package/dist/{module-registry-BnmRNy-3.mjs → module-registry-Daf_P2oD.mjs} +2 -2
  110. package/dist/{module-registry-BnmRNy-3.mjs.map → module-registry-Daf_P2oD.mjs.map} +1 -1
  111. package/dist/{observers-COlQfinD.mjs → observers-DZgs48UM.mjs} +2 -2
  112. package/dist/{observers-COlQfinD.mjs.map → observers-DZgs48UM.mjs.map} +1 -1
  113. package/dist/query-B_k9qn0R.mjs +12 -0
  114. package/dist/{query-B5DTq_4W.mjs.map → query-B_k9qn0R.mjs.map} +1 -1
  115. package/dist/reply-BDK9xt7Z.mjs +12 -0
  116. package/dist/{reply-CbTYIj_e.mjs.map → reply-BDK9xt7Z.mjs.map} +1 -1
  117. package/dist/{respond-wbMSqHYo.mjs → respond-DFRj2-7q.mjs} +3 -3
  118. package/dist/{respond-wbMSqHYo.mjs.map → respond-DFRj2-7q.mjs.map} +1 -1
  119. package/dist/route-flag-BWuvJsGZ.mjs +12 -0
  120. package/dist/{route-flag-Bb7n9Mnp.mjs.map → route-flag-BWuvJsGZ.mjs.map} +1 -1
  121. package/dist/route-policy-B3vM6bSf.mjs +12 -0
  122. package/dist/{route-policy-C_Wsc-b1.mjs.map → route-policy-B3vM6bSf.mjs.map} +1 -1
  123. package/dist/router-builder-JvMTp50k.mjs +12 -0
  124. package/dist/{router-builder-DawnRzlp.mjs.map → router-builder-JvMTp50k.mjs.map} +1 -1
  125. package/dist/{router-builder-CDkW88KQ.d.mts → router-builder-zWuVIOmd.d.mts} +2 -2
  126. package/dist/{router-builder-CDkW88KQ.d.mts.map → router-builder-zWuVIOmd.d.mts.map} +1 -1
  127. package/dist/{session-4Tb6q0Ny.d.mts → session-B4hOZj90.d.mts} +13 -4
  128. package/dist/session-B4hOZj90.d.mts.map +1 -0
  129. package/dist/{upload-Cy7qQy7M.d.mts → upload-CiMtdXWD.d.mts} +2 -2
  130. package/dist/upload-CiMtdXWD.d.mts.map +1 -0
  131. package/dist/{upload-config-D01Ty40f.mjs → upload-config-Dz2INCaa.mjs} +2 -2
  132. package/dist/{upload-config-D01Ty40f.mjs.map → upload-config-Dz2INCaa.mjs.map} +1 -1
  133. package/dist/web.d.mts +9 -7
  134. package/dist/web.d.mts.map +1 -1
  135. package/dist/web.mjs +2 -2
  136. package/dist/web.mjs.map +1 -1
  137. package/package.json +7 -8
  138. package/dist/adapter-DjfRMob8.d.mts.map +0 -1
  139. package/dist/application-BSJpaiSY.mjs +0 -12
  140. package/dist/application-BSJpaiSY.mjs.map +0 -1
  141. package/dist/application-CGxZCYl1.d.mts.map +0 -1
  142. package/dist/bootstrap-CbTF6xvv.mjs +0 -12
  143. package/dist/bootstrap-CbTF6xvv.mjs.map +0 -1
  144. package/dist/context-gpvcMlG5.mjs +0 -12
  145. package/dist/context-gpvcMlG5.mjs.map +0 -1
  146. package/dist/contributor-runner-vk9XlOED.mjs +0 -12
  147. package/dist/core-DKrKVLfj.mjs +0 -12
  148. package/dist/disposables-NlW6Mibk.mjs +0 -12
  149. package/dist/env-__fzPsHD.mjs +0 -13
  150. package/dist/env-__fzPsHD.mjs.map +0 -1
  151. package/dist/handler-DnZbcleS.mjs +0 -13
  152. package/dist/handler-DnZbcleS.mjs.map +0 -1
  153. package/dist/http/runtimes/h3-web.d.mts.map +0 -1
  154. package/dist/http/runtimes/h3-web.mjs.map +0 -1
  155. package/dist/jobs-BmpeFvi0.mjs +0 -12
  156. package/dist/kv-stores-BZTe23Tw.d.mts.map +0 -1
  157. package/dist/kv-stores-DJwxntyw.mjs +0 -12
  158. package/dist/query-B5DTq_4W.mjs +0 -12
  159. package/dist/reply-CbTYIj_e.mjs +0 -12
  160. package/dist/route-flag-Bb7n9Mnp.mjs +0 -12
  161. package/dist/route-policy-C_Wsc-b1.mjs +0 -12
  162. package/dist/router-builder-DawnRzlp.mjs +0 -12
  163. package/dist/session-4Tb6q0Ny.d.mts.map +0 -1
  164. package/dist/upload-Cy7qQy7M.d.mts.map +0 -1
@@ -6,7 +6,7 @@ import { p as RouteFlags } from "./route-flag-B1lQht6v.mjs";
6
6
  import { C as MetaValue, S as ExecutionContext, m as ContributorRegistrations, o as AnyContributorRegistration, r as AppModuleEntry, t as AppModule, x as ContextMetaKey } from "./app-module-DCA65GZZ.mjs";
7
7
  import "./cache-C0qL0NSD.mjs";
8
8
  import { i as ProblemDetails } from "./errors-By6UueCm.mjs";
9
- import { t as Session } from "./session-4Tb6q0Ny.mjs";
9
+ import { t as Session } from "./session-B4hOZj90.mjs";
10
10
  import { S as QueryFieldConfig, T as TypedParsedQuery, t as ParseQueryOptions, v as PaginatedResponse } from "./index-BfT5KZ-M.mjs";
11
11
  import "reflect-metadata";
12
12
  import http, { IncomingMessage, ServerResponse } from "node:http";
@@ -1403,6 +1403,21 @@ TKeys extends ContextMetaKey = ContextMetaKey> implements ExecutionContext<TKeys
1403
1403
  */
1404
1404
  redirect(url: string, status?: number): unknown;
1405
1405
  html(content: string, status?: number): RuntimeResponse;
1406
+ /**
1407
+ * Send a body with its content type — XML, plain text, CSV, an image.
1408
+ * Runtime-neutral, like `json()` and `html()`. `type` defaults to
1409
+ * `text/plain; charset=utf-8`.
1410
+ *
1411
+ * @example
1412
+ * ```ts
1413
+ * ctx.send(sitemapXml, { type: 'application/xml' })
1414
+ * ctx.send(csv, { type: 'text/csv; charset=utf-8', status: 200 })
1415
+ * ```
1416
+ */
1417
+ send(body: string | Uint8Array, options?: {
1418
+ type?: string;
1419
+ status?: number;
1420
+ }): RuntimeResponse;
1406
1421
  /**
1407
1422
  * Send `buffer` as a file download named `filename`. The name is written
1408
1423
  * per RFC 6266 — an ASCII fallback plus `filename*` in UTF-8 — so a name
@@ -1737,6 +1752,13 @@ interface RuntimeAppOptions {
1737
1752
  * shapes (or normalize X-Forwarded-For themselves — see spec §10 Q1).
1738
1753
  */
1739
1754
  trustProxy?: boolean | string | number | string[] | ((ip: string, hopIndex: number) => boolean);
1755
+ /**
1756
+ * When the app is mounted inside another handler (`kick dev`'s Vite server,
1757
+ * `app.handle(req, res, next)` in a host server), hand requests no route
1758
+ * matched back to it — `true`, the default — or answer them here with the
1759
+ * app's not-found handling — `false`. Express always answers.
1760
+ */
1761
+ notFoundFallthrough?: boolean;
1740
1762
  }
1741
1763
  /** Where a connect middleware sits and what it scopes to. */
1742
1764
  interface UseConnectOptions {
@@ -2109,4 +2131,4 @@ interface AppAdapter {
2109
2131
  type AppAdapterClass = new () => AppAdapter;
2110
2132
  //#endregion
2111
2133
  export { setInMetaRecord as $, contentDisposition as A, SourcedRegistration as B, RuntimeTypeMap as C, reportResponse as Ct, RequestContext as D, DeepReadonly as E, RunContributorsOptions as F, getMetaRecord as G, getClassMeta as H, runContributors as I, hasClassMeta as J, getMethodMeta as K, BuildPipelineOptions as L, AdapterFactory as M, DefineAdapterOptions as N, RouteShape as O, defineAdapter as P, setInMetaMap as Q, ContributorPipeline as R, RuntimeResponse as S, reportError as St, Ctx as T, getClassMetaOrUndefined as U, buildPipeline as V, getMetaMap as W, pushMethodMeta as X, pushClassMeta as Y, setClassMeta as Z, RouteMeta as _, IntrospectionKind as _t, KickServer as a, defineModule as at, RuntimeAppOptions as b, ErrorSource as bt, ActiveRuntime as c, PluginAsyncOptions as ct, CtxHandler as d, KickPlugin as dt, setMethodMeta as et, ExpressRuntimeTypes as f, ModuleRegistry as ft, RouteEntry as g, IntrospectFn as gt, MatchedRoute as h, KickJsPluginRegistry as ht, AppAdapterClass as i, ModuleFactory as it, AdapterAsyncOptions as j, SseHandler as k, AdapterHttp as l, PluginFactory as lt, KickRuntimeRegister as m, KickJsPluginName as mt, AdapterMiddleware as n, defineModules as nt, MiddlewarePath as o, BuildContext as ot, HttpRuntime as p, MutableModuleRegistry as pt, getMethodMetaOrUndefined as q, AppAdapter as r, DefineModuleOptions as rt, MiddlewarePhase as s, DefinePluginOptions as st, AdapterContext as t, ModuleList as tt, ConnectMiddleware as u, definePlugin as ut, RouteMethod as v, IntrospectionSnapshot as vt, UseConnectOptions as w, RuntimeCapabilities as x, ResponseInfo as xt, RouteTable as y, ErrorInfo as yt, ContributorSource as z };
2112
- //# sourceMappingURL=adapter-DjfRMob8.d.mts.map
2134
+ //# sourceMappingURL=adapter-ClEh-Oos.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-ClEh-Oos.d.mts","names":["Request","ExpressRequest","ExpressResponse","Express"],"sources":["../src/core/observers.ts","../src/core/introspect.ts","../src/core/augmentation.ts","../src/core/module-registry.ts","../src/core/plugin.ts","../src/core/define-plugin.ts","../src/core/define-module.ts","../src/core/define-modules.ts","../src/core/metadata.ts","../src/core/contributor-pipeline.ts","../src/core/contributor-runner.ts","../src/core/define-adapter.ts","../src/http/context.ts","../src/http/runtime.ts","../src/core/adapter.ts"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @forinda/kickjs v10.1.0
3
+ *
4
+ * Copyright (c) Felix Orinda
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ *
9
+ * @license MIT
10
+ */
11
+ import{METADATA}from"./core/interfaces.mjs";import{c as http2UnsupportedRuntimeError,i as createToken,l as moduleRouteMissingControllerError,n as tokenName,s as http2RequiresTlsError,t as Container,u as tlsCredentialsError}from"./container-CS9v9UFU.mjs";import{Logger,createLogger}from"./core/logger.mjs";import{t as getClassMeta}from"./metadata-zvd6AWkE.mjs";import{Controller,Get}from"./core/decorators.mjs";import{t as MutableModuleRegistry}from"./module-registry-Daf_P2oD.mjs";import{a as setObserverPublisher,i as reportResponse,n as releaseObservers,o as setObservers,r as reportError,t as hasResponseObservers}from"./observers-DZgs48UM.mjs";import{runCronJobs}from"./core/cron.mjs";import{i as runDisposables,n as drainDisposables,r as registerDisposable,t as disposeAll}from"./disposables-CuY2S3bw.mjs";import{HttpException}from"./core/errors.mjs";import{buildMountPath,joinPaths}from"./core/path.mjs";import{t as ROUTE_SLOT}from"./route-flag-BWuvJsGZ.mjs";import"./core-B0JmdBvX.mjs";import{n as assertRouteUnique,r as buildRouteTable,t as _setExternalContributorSources}from"./router-builder-JvMTp50k.mjs";import{r as offerRoutePolicy,t as RoutePolicyTable}from"./route-policy-B3vM6bSf.mjs";import{a as reply,c as isRequestScopeMiddleware,f as rejectUnsupportedBody,l as requestScopeMiddleware,n as applyHandlerResult,u as EXPRESS_JSON_TYPES}from"./reply-BDK9xt7Z.mjs";import{requestId}from"./http/middleware/request-id.mjs";import{d as requestStore,o as pendingBackgroundWork,r as publishMatchedRoute,s as settleBackgroundWork,t as RequestContext}from"./context-CtL9RxXp.mjs";import{errorHandler,notFoundHandler}from"./http/middleware/error-handler.mjs";import{t as __decorate}from"./decorate-BHkdkV4l.mjs";import{validate}from"./http/middleware/validate.mjs";import{buildUploadMiddleware}from"./http/middleware/upload.mjs";import http from"node:http";import{randomBytes,timingSafeEqual}from"node:crypto";import https from"node:https";import http2 from"node:http2";import tls from"node:tls";import express,{Router}from"express";import diagnostics from"node:diagnostics_channel";const mergeConfig=(defaults={},overrides={})=>({...defaults,...overrides}),composeName=(base,scope)=>`${base}:${scope}`;function defineModule(options){if(typeof options!=`object`||!options)throw TypeError(`defineModule: options must be an object literal. See architecture.md §21 for the spec shape.`);if(typeof options.name!=`string`||options.name.length===0){let got=typeof options.name==`string`?`""`:typeof options.name;throw TypeError(`defineModule: options.name must be a non-empty string (got ${got}). Module names appear in diagnostics and \`.scoped()\` namespacing — anonymous modules make cycle / missing-dep errors hard to localize.`)}if(typeof options.build!=`function`)throw TypeError(`defineModule(${options.name}): options.build is required and must be a function (got ${typeof options.build}). The build function returns the AppModule shape — register() / routes() / contributors() — wired with the resolved config.`);let buildSync=(instanceName,scoped,overrides)=>{let config=mergeConfig(options.defaults,overrides),built=options.build(config,{name:instanceName,scoped});return Object.defineProperty(built,"__moduleName",{value:instanceName,enumerable:!1,writable:!1,configurable:!1}),built},factory=(config=>buildSync(options.name,!1,config));return factory.scoped=(scopeName,config)=>buildSync(composeName(options.name,scopeName),!0,config),Object.defineProperty(factory,"definition",{value:Object.freeze({...options}),writable:!1,enumerable:!0,configurable:!1}),factory}var DuplicateMountNameError=class extends Error{kind;mountName;constructor(kind,mountName){super(`Duplicate ${kind} name '${mountName}' — every ${kind} must declare a unique name`),this.name=`DuplicateMountNameError`,this.kind=kind,this.mountName=mountName}},MissingMountDepError=class extends Error{kind;missing;dependent;constructor(kind,missing,dependent){super(`Missing ${kind} dependency '${missing}' required by '${dependent}' — is the ${kind} installed and named correctly?`),this.name=`MissingMountDepError`,this.kind=kind,this.missing=missing,this.dependent=dependent}},MountCycleError=class extends Error{kind;cycle;constructor(kind,cycle){super(`Cycle in ${kind} dependsOn declarations: ${cycle.join(` → `)}`),this.name=`MountCycleError`,this.kind=kind,this.cycle=Object.freeze([...cycle])}};function mountSort(items,kind){let byName=/* @__PURE__ */ new Map;for(let item of items){if(byName.has(item.name))throw new DuplicateMountNameError(kind,item.name);byName.set(item.name,item)}let indegree=/* @__PURE__ */ new Map,dependents=/* @__PURE__ */ new Map;for(let item of items)indegree.set(item.name,item.dependsOn?.length??0);for(let item of items)for(let dep of item.dependsOn??[]){if(!byName.has(dep))throw new MissingMountDepError(kind,dep,item.name);let list=dependents.get(dep)??[];list.push(item),dependents.set(dep,list)}let queue=[];for(let item of items)indegree.get(item.name)===0&&queue.push(item);let sorted=[];for(;queue.length>0;){let node=queue.shift();sorted.push(node);for(let dependent of dependents.get(node.name)??[]){let next=(indegree.get(dependent.name)??0)-1;indegree.set(dependent.name,next),next===0&&queue.push(dependent)}}if(sorted.length!==items.length)throw new MountCycleError(kind,reconstructCycle(items.filter(it=>(indegree.get(it.name)??0)>0),byName));return sorted}function reconstructCycle(residual,byName){let residualNames=new Set(residual.map(r=>r.name)),start=residual[0].name,path=[start],seen=/* @__PURE__ */ new Set([start]),cursor=start;for(;;){let nextDep=byName.get(cursor).dependsOn?.find(d=>residualNames.has(d));if(nextDep===void 0)break;if(seen.has(nextDep)){let cycleStart=path.indexOf(nextDep);return[...path.slice(cycleStart),nextDep]}path.push(nextDep),seen.add(nextDep),cursor=nextDep}return path}const HANDLER_CHANNEL=`kickjs:handler`,ERROR_CHANNEL=`kickjs:error`,RESPONSE_CHANNEL=`kickjs:response`,handlerChannel=diagnostics.tracingChannel(HANDLER_CHANNEL),errorChannel=diagnostics.channel(ERROR_CHANNEL),responseChannel=diagnostics.channel(RESPONSE_CHANNEL);function installChannelPublisher(){setObserverPublisher({name:`diagnostics_channel`,onError(error,info){errorChannel.hasSubscribers&&errorChannel.publish({error,...info})},onResponse(info){responseChannel.hasSubscribers&&responseChannel.publish(info)}},()=>responseChannel.hasSubscribers)}function traceHandler(handler,meta){return ctx=>handlerChannel.hasSubscribers?handlerChannel.tracePromise(async()=>handler(ctx),{...meta,requestId:ctx.requestId,ctx}):handler(ctx)}function __decorateMetadata(k,v){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(k,v)}const HEALTH_PROBE=createToken(`kick/Health/probe`);let HealthController=class{probe;constructor(probe){this.probe=probe}live(){return this.probe.isDraining()?reply(503,{status:`draining`,uptime:process.uptime()}):reply(200,{status:`ok`,uptime:process.uptime()})}async ready(){if(this.probe.isDraining())return reply(503,{status:`draining`,checks:[]});let checks=await this.probe.checks(),healthy=checks.every(c=>c.status===`up`);return reply(healthy?200:503,{status:healthy?`ready`:`degraded`,checks})}};__decorate([Get(`/live`),__decorateMetadata(`design:type`,Function),__decorateMetadata(`design:paramtypes`,[]),__decorateMetadata(`design:returntype`,void 0)],HealthController.prototype,`live`,null),__decorate([Get(`/ready`),__decorateMetadata(`design:type`,Function),__decorateMetadata(`design:paramtypes`,[]),__decorateMetadata(`design:returntype`,Promise)],HealthController.prototype,`ready`,null),HealthController=__decorate([Controller(),__decorateMetadata(`design:paramtypes`,[Object])],HealthController);const healthModule=defineModule({name:`HealthModule`,build:config=>({register(container){container.registerFactory(HealthController,()=>{let probe=container.resolve(HEALTH_PROBE);return new HealthController(probe)})},routes(){return{path:`/health`,controller:HealthController,version:!1,prefix:!1,flags:config.flags}}})});function materializeRouter(entries){let router=Router();for(let entry of entries){let method=entry.method.toLowerCase(),handlers=[];handlers.push((req,_res,next)=>{publishMatchedRoute(req,entry),next()});let contributorStep=run=>async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{await run(ctx),res.headersSent||next()}catch(err){next(err)}};entry.earlyContributorRunner&&handlers.push(contributorStep(entry.earlyContributorRunner)),entry.meta.validation&&handlers.push(validate(entry.meta.validation)),entry.meta.upload&&handlers.push(buildUploadMiddleware(entry.meta.upload));for(let mw of entry.middlewares)handlers.push((req,res,next)=>{let ctx=new RequestContext(req,res,next);Promise.resolve(mw(ctx,next)).catch(next)});entry.contributorRunner&&handlers.push(contributorStep(entry.contributorRunner)),handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{let result=await entry.handler(ctx);res.headersSent||await applyHandlerResult(ctx,result)}catch(err){next(err)}}),router[method](entry.path,...handlers)}return router}function buildRoutes(controllerClass,options={}){return materializeRouter(buildRouteTable(controllerClass,options))}function expressRuntime(){return{name:`express`,createApp(options={}){let app=express();return app.disable(`x-powered-by`),app.set(`trust proxy`,options.trustProxy??!1),app},nodeHandler(app){return(req,res,next)=>{next?app(req,res,next):app(req,res)}},mountRoutes(app,table){for(let{mountPath,routes}of table)app.use(mountPath,materializeRouter(routes))},useConnect(app,mw,opts={}){opts.path===void 0?app.use(mw):app.use(opts.path,mw)},serveStatic(app,prefix,dir){app.use(prefix,express.static(dir))},setNotFound(app,mw){app.use(mw)},setErrorHandler(app,mw){app.use(mw)},capabilities:{render:!0,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!1,http2:!1}}}const HOP_BY_HOP_REQUEST=[`connection`,`keep-alive`,`proxy-connection`,`transfer-encoding`,`upgrade`,`te`,`trailer`,`host`,`content-length`],HOP_BY_HOP_RESPONSE=[`connection`,`keep-alive`,`proxy-connection`,`transfer-encoding`,`upgrade`,`trailer`,`content-encoding`,`content-length`],LOOPBACK_HOST=`x-kick-loopback-host`,LOOPBACK_SECRET=`x-kick-loopback-secret`;async function streamedBody(request){if(!request.body||request.method===`GET`||request.method===`HEAD`)return;let reader=request.body.getReader(),first=await reader.read();if(!first.done)return new ReadableStream({start(controller){controller.enqueue(first.value)},async pull(controller){let{done,value}=await reader.read();done?controller.close():controller.enqueue(value)},cancel(reason){return reader.cancel(reason)}})}function createLoopback(handle){let started,secret=randomBytes(24).toString(`hex`),server=()=>(started??=new Promise((resolve,reject)=>{let instance=http.createServer((req,res)=>{let host=req.headers[LOOPBACK_HOST];req.headers[LOOPBACK_SECRET]===secret&&typeof host==`string`&&(req.headers.host=host),delete req.headers[LOOPBACK_HOST],delete req.headers[LOOPBACK_SECRET],handle(req,res)});instance.once(`error`,reject),instance.listen(0,`127.0.0.1`,()=>{instance.unref(),resolve({origin:`http://127.0.0.1:${instance.address().port}`,server:instance})})}).catch(err=>{throw started=void 0,err}),started);return{async fetch(request){let{origin}=await server(),url=new URL(request.url),headers=new Headers(request.headers);for(let name of HOP_BY_HOP_REQUEST)headers.delete(name);headers.set(`x-forwarded-host`,url.host),headers.set(`x-forwarded-proto`,url.protocol.slice(0,-1)),headers.set(LOOPBACK_HOST,url.host),headers.set(LOOPBACK_SECRET,secret);let body=await streamedBody(request),upstream=await fetch(origin+url.pathname+url.search,{method:request.method,headers,body,...body?{duplex:`half`}:{},redirect:`manual`,signal:request.signal}),responseHeaders=new Headers(upstream.headers),named=responseHeaders.get(`connection`)?.split(`,`)??[];for(let name of[...HOP_BY_HOP_RESPONSE,...named])responseHeaders.delete(name.trim());return new Response(upstream.body,{status:upstream.status,statusText:upstream.statusText,headers:responseHeaders})},async close(){let running=await started?.catch(()=>void 0);if(started=void 0,!running)return;let closed=new Promise(resolve=>running.server.close(()=>resolve()));running.server.closeAllConnections(),await closed}}}const log=createLogger(`Application`);function assertUsableTls(options){let{key,cert,pfx,SNICallback,pskCallback}=options;if(!pfx&&!SNICallback&&!pskCallback){if(!key&&!cert)throw tlsCredentialsError(`no key/cert, pfx, SNICallback, or pskCallback`);if(!key)throw tlsCredentialsError(`cert is set but key is missing`);if(!cert)throw tlsCredentialsError(`key is set but cert is missing`)}try{tls.createSecureContext(options)}catch(err){throw tlsCredentialsError(err.message)}}function isModuleFactory(entry){return typeof entry==`function`&&`definition`in entry&&typeof entry.scoped==`function`}function isConfigurable(factory){return factory.definition?.defaults!=null}function toAppModule(entry){if(typeof entry!=`function`)return entry;if(isModuleFactory(entry)){if(isConfigurable(entry)){let name=entry.definition?.name||entry.name||`<anonymous>`;throw TypeError(`bootstrap: module \`${name}\` takes configuration, so it must be invoked.\n Write \`${name}()\` for its defaults, or \`${name}({ … })\` to configure it.\n Passing it bare would have silently selected the defaults.`)}return entry()}let name=entry.name||`<anonymous>`;if(!isConstructible(entry))throw TypeError(`bootstrap: module entry \`${name}\` is a function, but not a module class and not a defineModule() factory (no \`definition\`).\n A defineModule() module is passed as \`${name}\` or \`${name}()\`; a class must implement AppModule.`);let mod=new entry;if(typeof mod?.routes!=`function`)throw TypeError(`bootstrap: module entry \`${name}\` constructed, but the result does not implement AppModule — \`routes()\` is missing.\n A module class needs \`routes()\`; a defineModule() module is passed as \`${name}\` or \`${name}()\`.`);return mod}function isConstructible(fn){try{return Reflect.construct(String,[],fn),!0}catch{return!1}}var Application=class{options;app;runtime;container;httpServer=null;handlerMode=!1;observers=[];http2Sessions=/* @__PURE__ */ new Set;loopback;adapters;routePolicy=new RoutePolicyTable;plugins;_moduleContributors=[];_inFlightRequests=0;_draining=!1;_shutdownInitiated=!1;_drainResolvers=[];constructor(options){this.options=options,this.runtime=options.runtime??expressRuntime(),this.app=this.runtime.createApp({trustProxy:options.trustProxy,notFoundFallthrough:options.notFoundFallthrough}),this.container=Container.getInstance(),this.plugins=mountSort(options.plugins??[],`plugin`);let allAdapters=[...this.plugins.flatMap(p=>p.adapters?.()??[]),...options.adapters??[]],anonAdapterCount=0,namedAdapters=allAdapters.map(adapter=>{if(!adapter.name){let ctorName=adapter.constructor?.name,fallback=ctorName&&ctorName!==`Object`?ctorName:`AnonymousAdapter#${anonAdapterCount++}`;return Object.assign(adapter,{name:fallback})}return adapter});this.adapters=mountSort(namedAdapters,`adapter`),this.observers=setObservers([...this.adapters,...this.plugins]),installChannelPublisher(),Container._requestStoreProvider=()=>requestStore.getStore()??null}get isDraining(){return this._draining}get inFlightRequests(){return this._inFlightRequests}getContainer(){return this.container}fetch(request){let runtimeApp=this.app;return typeof runtimeApp?.fetch==`function`?Promise.resolve(runtimeApp.fetch(request)):(this.loopback??=createLoopback((req,res)=>this.handle(req,res)),this.loopback.fetch(request))}handle(req,res,next){let handler=this.runtime.nodeHandler(this.app);next?handler(req,res,next):handler(req,res)}adapterCtx(server){let env=process.env.NODE_ENV??`development`;return{http:this.adapterHttp(),app:this.app,fetch:request=>this.fetch(request),container:this.container,server,env,isProduction:env===`production`}}adapterHttp(){return{route:(method,path,handler)=>{let entry={method,path,middlewares:[],contributorRunner:null,handler,meta:{}};this.runtime.mountRoutes(this.app,[{mountPath:`/`,routes:[entry]}])},mount:(prefix,routes)=>{this.runtime.mountRoutes(this.app,[{mountPath:prefix,routes}])},serveStatic:(prefix,dir)=>{this.runtime.serveStatic(this.app,prefix,dir)},use:(mw,opts)=>{this.runtime.useConnect(this.app,mw,opts)}}}async callHook(hook,ctx,adapterName=`adapter`,phase=`setup`){if(hook)try{await hook(ctx)}catch(err){if(phase===`setup`)throw err;log.error(err,`${adapterName} afterStart hook failed`)}}async setup(){log.debug(`Bootstrapping application...`),this._moduleContributors=[];let adapterMw=this.collectAdapterMiddleware();this.app.__kickApp=this;let ctx=this.adapterCtx();for(let adapter of this.adapters)await this.callHook(adapter.beforeMount?.bind(adapter),ctx,adapter.name);this.handlerMode&&this.mountCronTrigger(),this.runtime.useConnect(this.app,this.requestTrackingMiddleware()),this.shouldAutoMountRequestScope()&&this.runtime.useConnect(this.app,requestScopeMiddleware()),this.mountMiddlewareList(adapterMw.beforeGlobal);for(let plugin of this.plugins)plugin.register?.(this.container);for(let plugin of this.plugins)try{let mw=plugin.middleware?.()??[];for(let handler of mw)this.runtime.useConnect(this.app,handler)}catch(err){log.error(err,`Plugin middleware failed: ${plugin.name??`unknown`}`)}let declaredHelmet=(this.options.middlewares??[]).some(entry=>typeof entry==`function`&&Symbol.for(`kick/http/helmet`)in entry);if(this.options.security?.helmet!==!1&&!declaredHelmet)try{let{helmet:helmetFn}=await import(`./http/middleware/helmet.mjs`);this.runtime.useConnect(this.app,helmetFn())}catch{}let userMiddlewares=this.options.middlewares;if(userMiddlewares)for(let entry of userMiddlewares)this.mountMiddlewareEntry(entry);else if(this.runtime.useConnect(this.app,requestId()),!this.runtime.capabilities.nativeBodyParsing){let limit=this.options.jsonLimit??`1mb`;this.runtime.useConnect(this.app,express.json({limit,type:[...EXPRESS_JSON_TYPES]})),this.runtime.useConnect(this.app,express.urlencoded({extended:!0,limit})),this.runtime.useConnect(this.app,express.text({limit,type:`text/*`})),this.runtime.useConnect(this.app,rejectUnsupportedBody())}this.mountMiddlewareList(adapterMw.afterGlobal);let moduleRegistry=new MutableModuleRegistry;for(let plugin of this.plugins){for(let m of plugin.modules?.()??[])moduleRegistry.mount(m);plugin.setup?.(moduleRegistry)}for(let m of this.options.modules)moduleRegistry.mount(m);this.options.setup?.(moduleRegistry),this.options.health!==!1&&(this.container.registerInstance(HEALTH_PROBE,{isDraining:()=>this._draining,checks:async()=>{let withCheck=this.adapters.filter(a=>a.onHealthCheck);return(await Promise.allSettled(withCheck.map(a=>a.onHealthCheck()))).map((c,i)=>c.status===`fulfilled`?c.value:{name:withCheck[i].name??`unknown`,status:`down`})}}),moduleRegistry.mount(healthModule(this.options.health===!0?{}:this.options.health)));let modules=moduleRegistry.entries.map(entry=>{let mod=toAppModule(entry);return mod.register?.(this.container),mod});this.container.bootstrap(),this.container.has(Logger)||this.container.registerInstance(Logger,new Logger),this.mountMiddlewareList(adapterMw.beforeRoutes);let apiPrefix=this.options.apiPrefix??`/api`,defaultVersion=this.options.defaultVersion??1,shouldLogRoutes=this.options.logRouteTable??this.options.logRoutesTable??!1,mountedRoutes=[],adapterSources=[];for(let adapter of this.adapters){let adapterContribs=adapter.contributors?.()??[],adapterLabel=adapter.name??adapter.constructor.name??`adapter`;for(let registration of adapterContribs)adapterSources.push({source:`adapter`,registration,label:adapterLabel})}for(let plugin of this.plugins){let pluginContribs=plugin.contributors?.()??[],pluginLabel=plugin.name??plugin.constructor?.name??`plugin`;for(let registration of pluginContribs)adapterSources.push({source:`adapter`,registration,label:pluginLabel})}let globalSources=(this.options.contributors??[]).map(registration=>({source:`global`,registration,label:`bootstrap`}));this._moduleContributors=[];let seenRoutes=/* @__PURE__ */ new Map,mountedPaths=[];for(let mod of modules){let declaredName=mod.name,ctorName=mod.constructor?.name,moduleLabel=typeof declaredName==`string`&&declaredName.length>0?declaredName:ctorName&&ctorName!==`Object`?ctorName:`module`,moduleSources=(mod.contributors?.()??[]).map(registration=>({source:`module`,registration,label:moduleLabel}));for(let src of moduleSources)this._moduleContributors.push({registration:src.registration,label:moduleLabel});_setExternalContributorSources([...moduleSources,...adapterSources,...globalSources]);try{let result=mod.routes();if(!result)continue;let routeSets=Array.isArray(result)?result:[result];for(let route of routeSets){let version=route.version??defaultVersion,mountPath=buildMountPath(route.prefix===!1?``:apiPrefix,version,route.path);if(route.router)this.runtime.useConnect(this.app,route.router,{path:mountPath});else if(route.controller){let routeTable=buildRouteTable(route.controller,{mountFlags:route.flags}),ctrl=route.controller.name??`controller`;for(let entry of routeTable){let owner=`${ctrl}.${String(entry.meta.handlerName??`?`)}`,fullPath=joinPaths(mountPath,entry.path);assertRouteUnique(seenRoutes,entry.method,fullPath,owner),mountedPaths.push({method:entry.method,path:fullPath}),this.routePolicy.add(entry.method,fullPath,entry.meta.flags)}let traced=routeTable.map(entry=>{let pattern=joinPaths(mountPath,entry.path);return{...entry,handler:traceHandler(entry.handler,{method:entry.method,route:pattern,controller:route.controller?.name,handler:entry.meta.handlerName}),meta:{...entry.meta,pattern}}});this.runtime.mountRoutes(this.app,[{mountPath,routes:traced}])}else throw moduleRouteMissingControllerError(mountPath);if(route.controller){for(let adapter of this.adapters)try{adapter.onRouteMount?.(route.controller,mountPath)}catch(err){log.error(err,`adapter.onRouteMount() failed for ${mountPath}`)}shouldLogRoutes&&mountedRoutes.push({controller:route.controller,mountPath})}}}finally{_setExternalContributorSources([])}}if(shouldLogRoutes&&mountedRoutes.length>0){let methodOrder=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`HEAD`,`OPTIONS`],methodRank=m=>{let i=methodOrder.indexOf(m);return i===-1?99:i},totalRoutes=0;log.info(`Routes:`);for(let{controller,mountPath}of mountedRoutes){let defs=getClassMeta(METADATA.ROUTES,controller,[]);if(defs.length===0)continue;let counts={};for(let def of defs){let m=def.method.toUpperCase();counts[m]=(counts[m]||0)+1}totalRoutes+=defs.length;let methods=Object.entries(counts).toSorted(([a],[b])=>methodRank(a)-methodRank(b)).map(([m,n])=>`${n} ${m}`).join(`, `),name=controller.name||`Controller`;log.info(` ${name.padEnd(30)} ${mountPath.padEnd(25)} ${defs.length} routes (${methods})`)}log.info(` Total: ${totalRoutes} routes`)}this.mountMiddlewareList(adapterMw.afterRoutes);for(let adapter of this.adapters)await this.callHook(adapter.beforeStart?.bind(adapter),ctx,adapter.name);for(let entry of this.options.middlewares??[])offerRoutePolicy(typeof entry==`function`?entry:entry.handler,this.routePolicy);for(let adapter of this.adapters)for(let mw of adapter.middleware?.()??[])offerRoutePolicy(mw.handler,this.routePolicy);let builtInNotFound=notFoundHandler(mountedPaths),customNotFound=this.options.onNotFound;this.runtime.setNotFound(this.app,customNotFound?(req,res,next)=>customNotFound(req,res,err=>err===void 0?builtInNotFound(req,res,next):next(err)):builtInNotFound),this.runtime.setErrorHandler(this.app,observeErrors(this.options.onError??errorHandler()))}async registerOnly(){await this.setup()}async startWithoutServer(){this.handlerMode=!0,await this.setup();let needServer=this.adapters.filter(adapter=>adapter.afterStart);needServer.length>0&&log.warn(`No HTTP server in handler mode, so afterStart does not run for: ${needServer.map(adapter=>adapter.name).join(`, `)}. Anything they attach to the server (WebSockets, upgrade handlers) is unavailable.`);for(let plugin of this.plugins)await plugin.onReady?.(this.container)}async start(){let g=globalThis,serverOptions=this.options.server;if(serverOptions&&g.__kickjs_httpServer)log.warn(`server options (tls / http2) are ignored in dev mode — Vite owns the server`);else if(serverOptions){if(serverOptions.http2&&!serverOptions.tls)throw http2RequiresTlsError();if(serverOptions.http2&&!this.runtime.capabilities.http2)throw http2UnsupportedRuntimeError(this.runtime.name);serverOptions.tls&&assertUsableTls(serverOptions.tls)}if(await this.setup(),g.__kickjs_httpServer){this.httpServer=g.__kickjs_httpServer,log.debug(`Attached to Vite dev server`),g.__kickjs_app_shutdown=()=>this.shutdown();for(let adapter of this.adapters){let ctx=this.adapterCtx(this.httpServer);await this.callHook(adapter.afterStart?.bind(adapter),ctx,adapter.name,`afterStart`)}for(let plugin of this.plugins)await plugin.onReady?.(this.container);return}let port=this.options.port??parseInt(process.env.PORT||`3000`,10);this.httpServer=this.createNodeServer(),this.httpServer.on(`error`,err=>{throw err.code===`EADDRINUSE`&&(log.error(`Port ${port} is already in use. Kill the existing process or use a different port:\n PORT=${port+1} kick dev\n lsof -i :${port} # find what's using it\n kill <PID> # stop it`),process.exit(1)),err}),await new Promise((resolve,reject)=>{this.httpServer.listen(port,async()=>{try{let scheme=serverOptions?.tls?`https`:`http`,protocol=serverOptions?.http2?` (HTTP/2)`:``;log.info(`Server running on ${scheme}://localhost:${port}${protocol}`);for(let adapter of this.adapters){let afterCtx=this.adapterCtx(this.httpServer);await this.callHook(adapter.afterStart?.bind(adapter),afterCtx,adapter.name,`afterStart`)}for(let plugin of this.plugins)await plugin.onReady?.(this.container);resolve()}catch(err){reject(err)}})})}createNodeServer(){let handler=this.runtime.nodeHandler(this.app),{tls,http2:useHttp2}=this.options.server??{};if(useHttp2){let server=http2.createSecureServer({...tls,allowHTTP1:!0},handler);return server.on(`session`,session=>{this.http2Sessions.add(session),session.once(`close`,()=>this.http2Sessions.delete(session))}),server}return tls?https.createServer(tls,handler):http.createServer(handler)}async rebuild(){let prevApp=this.app,prevContainer=this.container,staleDisposables=drainDisposables();try{Container.reset(),this.container=Container.getInstance(),this.app=this.runtime.createApp({trustProxy:this.options.trustProxy,notFoundFallthrough:this.options.notFoundFallthrough}),await this.setup()}catch(err){log.error(err,`HMR rebuild failed, keeping previous app`),await runDisposables(drainDisposables());for(let d of staleDisposables)registerDisposable(d);this.app=prevApp,this.container=prevContainer;return}await runDisposables(staleDisposables),this.httpServer&&(this.httpServer.removeAllListeners(`request`),this.httpServer.on(`request`,this.runtime.nodeHandler(this.app)),log.debug(`HMR: app rebuilt and swapped`))}async shutdown(options={}){let closeServer=options.closeServer??!0;if(this._shutdownInitiated){log.debug(`Shutdown already in progress, skipping duplicate call`);return}this._shutdownInitiated=!0,this._draining=!0,log.debug(`Shutting down — draining in-flight requests...`);let timeoutMs=this.options.shutdownTimeout??3e4,timer,forceExitPromise=timeoutMs>0?new Promise(resolve=>{timer=setTimeout(()=>resolve(`timeout`),timeoutMs),timer.unref()}):new Promise(()=>{});try{if(closeServer&&this.httpServer){this.httpServer.close(()=>{});for(let session of this.http2Sessions)session.close()}if(closeServer&&this._inFlightRequests>0){log.debug(`Waiting for ${this._inFlightRequests} in-flight request(s) to complete...`);let drainPromise=new Promise(resolve=>{this._drainResolvers.push(()=>resolve(`drained`))});if(await Promise.race([drainPromise,forceExitPromise])===`timeout`){log.warn(`Shutdown timeout (${timeoutMs}ms) reached with ${this._inFlightRequests} request(s) still in-flight, forcing shutdown`);for(let session of this.http2Sessions)session.destroy()}else log.debug(`All in-flight requests completed`)}closeServer&&pendingBackgroundWork()>0&&(log.debug(`Waiting for ${pendingBackgroundWork()} waitUntil() task(s) to settle...`),await Promise.race([settleBackgroundWork().then(()=>`settled`),forceExitPromise])===`timeout`&&log.warn(`Shutdown timeout (${timeoutMs}ms) reached with ${pendingBackgroundWork()} waitUntil() task(s) still running`));let hookTimeoutMs=closeServer?timeoutMs:Math.min(timeoutMs,5e3),timeBoxed=(label,run)=>{let hook=(async()=>run())();return hookTimeoutMs<=0?hook:new Promise((resolve,reject)=>{let t=setTimeout(()=>{log.warn(`${label} did not finish shutting down within ${hookTimeoutMs}ms — continuing without it. Its resources may still be held.`),resolve(void 0)},hookTimeoutMs);t.unref(),hook.then(value=>{clearTimeout(t),resolve(value)},err=>{clearTimeout(t),reject(err)})})},wasListening=this.httpServer?.listening===!0,results=await Promise.allSettled([...this.plugins.map(plugin=>timeBoxed(`Plugin '${plugin.name}'`,()=>plugin.shutdown?.())),...this.adapters.map(adapter=>timeBoxed(`Adapter '${adapter.name}'`,()=>adapter.shutdown?.()))]);!closeServer&&wasListening&&this.httpServer?.listening===!1&&log.error("An adapter or plugin closed the shared HTTP server during an HMR reload. The dev server is now unreachable and will not rebind until you restart it. Close only what you own: socket.io's `io.close()` closes the HTTP server passed to its constructor — on a reload, disconnect the sockets instead.");for(let result of results)result.status===`rejected`&&log.error({err:result.reason},`Adapter shutdown failed`);await disposeAll(),this.container.flushChanges(),await this.loopback?.close(),this.loopback=void 0,closeServer&&releaseObservers(this.observers)}finally{timer&&clearTimeout(timer)}}getRuntimeApp(){return this.app}getActiveRuntime(){return{name:this.runtime.name,capabilities:this.runtime.capabilities}}getExpressApp(){return this.app}getAdapters(){return this.adapters}getPlugins(){return this.plugins}getContributors(){let out=[],ingest=(list,source,label)=>{if(list)for(let reg of list){let r=reg;if(r.key===void 0||r.key===null)continue;let key=typeof r.key==`string`?r.key:tokenName(r.key);out.push({key,source,label,dependsOn:Array.isArray(r.dependsOn)?r.dependsOn:[]})}};for(let adapter of this.adapters)ingest(adapter.contributors?.(),`adapter`,adapter.name??`adapter`);for(let plugin of this.plugins)ingest(plugin.contributors?.(),`plugin`,plugin.name??`plugin`);for(let{registration,label}of this._moduleContributors)ingest([registration],`module`,label);return ingest(this.options.contributors,`global`,`bootstrap`),out}getHttpServer(){return this.httpServer}shouldAutoMountRequestScope(){if(this.options.contextStore===`manual`)return!1;let userEntries=this.options.middlewares??[];for(let entry of userEntries){let handler=typeof entry==`function`?entry:entry.handler;if(isRequestScopeMiddleware(handler))return!1}return!0}collectAdapterMiddleware(){let result={beforeGlobal:[],afterGlobal:[],beforeRoutes:[],afterRoutes:[]};for(let adapter of this.adapters){let entries=adapter.middleware?.()??[];for(let entry of entries)result[entry.phase??`afterGlobal`].push(entry)}return result}mountMiddlewareList(entries){for(let entry of entries)entry.path===void 0?this.runtime.useConnect(this.app,entry.handler):this.runtime.useConnect(this.app,entry.handler,{path:entry.path})}mountMiddlewareEntry(entry){typeof entry==`function`?this.runtime.useConnect(this.app,entry):this.runtime.useConnect(this.app,entry.handler,{path:entry.path})}mountCronTrigger(){let secret=process.env.CRON_SECRET;secret&&this.adapterHttp().route(`GET`,`/_kick/cron/:scheduleId`,async ctx=>{if(!matchesBearer(ctx.headers.authorization,secret))throw new HttpException(401,`Missing or invalid CRON_SECRET bearer token`);let result=await runCronJobs(this.container,{scheduleId:ctx.params.scheduleId},`http`);ctx.json(result,result.failed>0?500:200)})}requestTrackingMiddleware(){return(req,res,next)=>{this._inFlightRequests++;let started=hasResponseObservers()?performance.now():void 0,onFinish=()=>settle(!0),onClose=()=>settle(!1),settle=finished=>{if(res.removeListener(`finish`,onFinish),res.removeListener(`close`,onClose),finished&&started!==void 0){let matched=req[ROUTE_SLOT];reportResponse({method:req.method,path:requestPath(req),route:matched?.pattern,status:res.statusCode,durationMs:performance.now()-started,requestId:requestIdOf(req)})}if(this._inFlightRequests--,this._draining&&this._inFlightRequests===0){for(let resolve of this._drainResolvers)resolve();this._drainResolvers=[]}};res.on(`finish`,onFinish),res.on(`close`,onClose),next()}}};function requestPath(req){return(req.originalUrl??req.url??``).split(`?`)[0]}function requestIdOf(req){let header=req.headers?.[`x-request-id`];return req.requestId??(typeof header==`string`?header:void 0)}function errorStatus(err){return typeof err?.problem?.status==`number`?err.problem.status:typeof err?.status==`number`?err.status:typeof err?.statusCode==`number`?err.statusCode:err?.name===`ZodError`?422:500}function observeErrors(handler){return((err,req,res,next)=>{let matched=req?.[ROUTE_SLOT];return reportError(err,{source:`request`,requestId:requestIdOf(req??{}),method:req?.method,path:requestPath(req??{}),route:matched?.pattern,status:errorStatus(err)}),handler(err,req,res,next)})}function matchesBearer(header,secret){if(typeof header!=`string`)return!1;let given=Buffer.from(header),expected=Buffer.from(`Bearer ${secret}`);return given.length===expected.length&&timingSafeEqual(given,expected)}export{HEALTH_PROBE as a,ERROR_CHANNEL as c,DuplicateMountNameError as d,MissingMountDepError as f,defineModule as h,materializeRouter as i,HANDLER_CHANNEL as l,mountSort as m,buildRoutes as n,HealthController as o,MountCycleError as p,expressRuntime as r,healthModule as s,Application as t,RESPONSE_CHANNEL as u};
12
+ //# sourceMappingURL=application-Dv514EeR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-Dv514EeR.mjs","names":[],"sources":["../src/core/define-module.ts","../src/core/mount-sort.ts","../src/http/tracing.ts","../src/http/health-module.ts","../src/http/runtimes/express.ts","../src/http/loopback.ts","../src/http/application.ts"],"sourcesContent":["import type { AppModule } from './app-module'\nimport type { BuildContext } from './define-plugin'\n\n/**\n * Options passed to {@link defineModule}. Mirrors {@link DefineAdapterOptions}\n * and {@link DefinePluginOptions} so adopters learn one mental model\n * across all three primitives.\n *\n * The optional `TExtra` generic lets a module ship public methods\n * beyond the standard {@link AppModule} contract — they're preserved\n * on the returned module instance so external callers (tests, sibling\n * modules) can invoke them directly.\n */\nexport interface DefineModuleOptions<TConfig, TExtra = unknown> {\n /**\n * Stable identity surfaced in diagnostics, route logs, and\n * `.scoped()` namespacing. Required because boot-time error\n * messages reference modules by name — anonymous modules make\n * cycles + missing-dep errors hard to localize.\n */\n name: string\n /** Optional version string surfaced in diagnostics. */\n version?: string\n /**\n * Optional kickjs version constraint. Enforced by the framework\n * when implemented; today it lives on the definition for\n * documentation and forward compatibility.\n */\n requires?: { kickjs?: string }\n /**\n * Default config merged with overrides at instantiation time\n * (`MyModule({ field: x })`). Call-site overrides win.\n */\n defaults?: Partial<TConfig>\n /**\n * Build the AppModule shape from the resolved config and context.\n * `ctx.name` is the resolved instance name (the bare definition\n * name for the default call, or `${name}:${scope}` for\n * `.scoped()`); `ctx.scoped` discriminates so the build function\n * can branch on namespacing if needed.\n *\n * Return any extension methods alongside the standard\n * {@link AppModule} surface — they're typed via `TExtra` and stay\n * accessible on the resulting instance.\n */\n build(config: TConfig, ctx: BuildContext): AppModule & TExtra\n}\n\n/**\n * Factory returned by {@link defineModule}. Mirrors {@link AdapterFactory}\n * — call (`MyModule({...})`), scope (`MyModule.scoped('public', {...})`),\n * inspect (`MyModule.definition`).\n *\n * `.async()` is intentionally **not** part of the module factory\n * surface. Module config has no async-resolution window: `register()`\n * and `routes()` both run synchronously during bootstrap, before any\n * adapter `beforeStart` hook fires. Adopters that need async-resolved\n * config push it into an adapter (which has `beforeStart`), then\n * inject the resolved value into the module via DI tokens.\n */\nexport interface ModuleFactory<TConfig, TExtra = unknown> {\n (config?: Partial<TConfig>): AppModule & TExtra\n scoped(scopeName: string, config?: Partial<TConfig>): AppModule & TExtra\n readonly definition: Readonly<DefineModuleOptions<TConfig, TExtra>>\n}\n\nconst mergeConfig = <TConfig>(\n defaults: Partial<TConfig> = {},\n overrides: Partial<TConfig> = {},\n): TConfig => ({ ...defaults, ...overrides }) as TConfig\n\nconst composeName = (base: string, scope: string): string => `${base}:${scope}`\n\n/**\n * Build an {@link AppModule} factory from a typed definition. Pairs\n * with {@link defineAdapter}, {@link definePlugin}, and\n * {@link defineContextDecorator} as the fourth `define*` primitive\n * — adopters write modules the same way they write adapters and\n * plugins.\n *\n * The returned factory produces real {@link AppModule} instances\n * that drop into `bootstrap({ modules })` directly. The legacy\n * class form (`class TasksModule implements AppModule { ... }`) keeps\n * working — `bootstrap` accepts either shape.\n *\n * @example\n * ```ts\n * interface TasksConfig {\n * scope: 'public' | 'admin'\n * }\n *\n * const TasksModule = defineModule<TasksConfig>({\n * name: 'TasksModule',\n * defaults: { scope: 'public' },\n * build: (config, { name }) => ({\n * register(container) {\n * container.registerInstance(`tasks:scope:${name}`, config.scope)\n * },\n * routes() {\n * return { path: `/${config.scope}/tasks`, router: buildTasksRouter() }\n * },\n * contributors() {\n * return [LoadTenant.registration]\n * },\n * }),\n * })\n *\n * bootstrap({\n * modules: [\n * TasksModule(), // public scope (defaults)\n * TasksModule.scoped('admin', { scope: 'admin' }),\n * ],\n * })\n * ```\n */\nexport function defineModule<TConfig = Record<string, unknown>, TExtra = unknown>(\n options: DefineModuleOptions<TConfig, TExtra>,\n): ModuleFactory<TConfig, TExtra> {\n if (options === null || typeof options !== 'object') {\n throw new TypeError(\n 'defineModule: options must be an object literal. ' +\n 'See architecture.md §21 for the spec shape.',\n )\n }\n if (typeof options.name !== 'string' || options.name.length === 0) {\n const got = typeof options.name === 'string' ? '\"\"' : typeof options.name\n throw new TypeError(\n `defineModule: options.name must be a non-empty string (got ${got}). ` +\n 'Module names appear in diagnostics and `.scoped()` namespacing — anonymous ' +\n 'modules make cycle / missing-dep errors hard to localize.',\n )\n }\n if (typeof options.build !== 'function') {\n throw new TypeError(\n `defineModule(${options.name}): options.build is required and must be a function ` +\n `(got ${typeof options.build}). The build function returns the AppModule shape — ` +\n 'register() / routes() / contributors() — wired with the resolved config.',\n )\n }\n\n const buildSync = (\n instanceName: string,\n scoped: boolean,\n overrides?: Partial<TConfig>,\n ): AppModule & TExtra => {\n const config = mergeConfig(options.defaults, overrides)\n // Mutate-in-place rather than spread: spread strips prototype methods\n // when build() returns a class instance, and the existing defineAdapter\n // pattern uses the same approach. `TExtra` properties survive because\n // they're own properties on the build result.\n const built = options.build(config, { name: instanceName, scoped })\n // AppModule has no `name` field today, but stamping the resolved\n // instance name onto the result lets diagnostics surface it without\n // adopter wiring. The cast keeps the public AppModule type\n // unchanged while the data is available for any framework code\n // that wants it.\n Object.defineProperty(built, '__moduleName', {\n value: instanceName,\n enumerable: false,\n writable: false,\n configurable: false,\n })\n return built\n }\n\n const factory = ((config?: Partial<TConfig>) =>\n buildSync(options.name, false, config)) as ModuleFactory<TConfig, TExtra>\n\n factory.scoped = (scopeName: string, config?: Partial<TConfig>) =>\n buildSync(composeName(options.name, scopeName), true, config)\n\n Object.defineProperty(factory, 'definition', {\n value: Object.freeze({ ...options }),\n writable: false,\n enumerable: true,\n configurable: false,\n })\n\n return factory\n}\n","/**\n * Topological sort for plugins and adapters with `dependsOn` declarations.\n *\n * Mirrors the algorithm used by the Context Contributor pipeline\n * (`contributor-pipeline.ts:topoSort`) but operates on a name-keyed\n * `MountSortItem` shape rather than a `ContributorRegistration`. Kept\n * standalone instead of factored generic so that:\n *\n * 1. The contributor pipeline doesn't need refactoring (risk reduction).\n * 2. The error classes can carry plugin/adapter-specific diagnostic\n * fields (`kind`, `dependent`) without bending the contributor types.\n */\n\n/**\n * A plugin or adapter that participates in dependency-ordered mounting.\n * Both `KickPlugin` and `AppAdapter` satisfy this shape via their `name`\n * and `dependsOn` fields.\n */\nexport interface MountSortItem {\n /** Unique mount name within the plugin/adapter set being sorted. */\n name: string\n /**\n * Other plugin/adapter names that must mount before this one. Cycles\n * and missing names fail at boot — same fail-fast contract as the\n * Context Contributor pipeline (architecture.md §20.4).\n */\n dependsOn?: readonly string[]\n}\n\n/** What kind of mount set the sort is operating on — surfaces in error messages. */\nexport type MountKind = 'plugin' | 'adapter'\n\n/**\n * Two items in the same mount set declared the same `name`. Plugin/adapter\n * names must be unique so `dependsOn` references resolve unambiguously.\n */\nexport class DuplicateMountNameError extends Error {\n readonly kind: MountKind\n readonly mountName: string\n\n constructor(kind: MountKind, mountName: string) {\n super(`Duplicate ${kind} name '${mountName}' — every ${kind} must declare a unique name`)\n this.name = 'DuplicateMountNameError'\n this.kind = kind\n this.mountName = mountName\n }\n}\n\n/**\n * A `dependsOn` entry references a plugin/adapter that isn't in the\n * current set. The dependency might not be installed, might be misspelled,\n * or might live in a different mount set (plugin referencing an adapter,\n * or vice versa — they sort independently).\n */\nexport class MissingMountDepError extends Error {\n readonly kind: MountKind\n readonly missing: string\n readonly dependent: string\n\n constructor(kind: MountKind, missing: string, dependent: string) {\n super(\n `Missing ${kind} dependency '${missing}' required by '${dependent}' — ` +\n `is the ${kind} installed and named correctly?`,\n )\n this.name = 'MissingMountDepError'\n this.kind = kind\n this.missing = missing\n this.dependent = dependent\n }\n}\n\n/**\n * The `dependsOn` declarations form a cycle. Boot fails so the cycle\n * can't poison live traffic. The `cycle` field reports one path through\n * the offending nodes for diagnostics — formatted `A → B → C → A`.\n */\nexport class MountCycleError extends Error {\n readonly kind: MountKind\n readonly cycle: readonly string[]\n\n constructor(kind: MountKind, cycle: readonly string[]) {\n super(`Cycle in ${kind} dependsOn declarations: ${cycle.join(' → ')}`)\n this.name = 'MountCycleError'\n this.kind = kind\n this.cycle = Object.freeze([...cycle])\n }\n}\n\n/**\n * Topologically sort `items` so every item with `dependsOn: ['X']` mounts\n * after `X`. Items without `dependsOn` keep their input order via Kahn's\n * algorithm with a FIFO queue seeded in input order — preserves the\n * historical \"plugins/adapters mount in declaration order\" behaviour for\n * any set that doesn't use `dependsOn`.\n *\n * @param items - mountable items, e.g. plugins or adapters\n * @param kind - 'plugin' | 'adapter', used in error messages\n * @returns a new array in valid mount order\n * @throws {DuplicateMountNameError} two items share a name\n * @throws {MissingMountDepError} a `dependsOn` references an unknown name\n * @throws {MountCycleError} the dep graph contains a cycle\n */\nexport function mountSort<T extends MountSortItem>(items: readonly T[], kind: MountKind): T[] {\n const byName = new Map<string, T>()\n for (const item of items) {\n if (byName.has(item.name)) {\n throw new DuplicateMountNameError(kind, item.name)\n }\n byName.set(item.name, item)\n }\n\n // indegree[name] = number of unresolved deps for that item\n const indegree = new Map<string, number>()\n // dependents[depName] = items that depend on depName\n const dependents = new Map<string, T[]>()\n\n for (const item of items) {\n indegree.set(item.name, item.dependsOn?.length ?? 0)\n }\n for (const item of items) {\n for (const dep of item.dependsOn ?? []) {\n if (!byName.has(dep)) {\n throw new MissingMountDepError(kind, dep, item.name)\n }\n const list = dependents.get(dep) ?? []\n list.push(item)\n dependents.set(dep, list)\n }\n }\n\n // Initial queue: items with zero indegree, preserving input order.\n const queue: T[] = []\n for (const item of items) {\n if (indegree.get(item.name) === 0) queue.push(item)\n }\n\n const sorted: T[] = []\n while (queue.length > 0) {\n const node = queue.shift()!\n sorted.push(node)\n for (const dependent of dependents.get(node.name) ?? []) {\n const next = (indegree.get(dependent.name) ?? 0) - 1\n indegree.set(dependent.name, next)\n if (next === 0) queue.push(dependent)\n }\n }\n\n if (sorted.length !== items.length) {\n const remaining = items.filter((it) => (indegree.get(it.name) ?? 0) > 0)\n throw new MountCycleError(kind, reconstructCycle(remaining, byName))\n }\n\n return sorted\n}\n\n/**\n * Walk the residual graph to find one cycle path for diagnostics.\n * Starts from the first remaining node and follows the first dep that's\n * still in the residual set until we revisit a node. Returns a path\n * starting and ending with the same name so the message reads naturally\n * (`A → B → C → A`).\n */\nfunction reconstructCycle<T extends MountSortItem>(\n residual: readonly T[],\n byName: ReadonlyMap<string, T>,\n): string[] {\n const residualNames = new Set(residual.map((r) => r.name))\n const start = residual[0].name\n const path: string[] = [start]\n const seen = new Set<string>([start])\n let cursor = start\n\n while (true) {\n const item = byName.get(cursor)!\n const nextDep = item.dependsOn?.find((d) => residualNames.has(d))\n if (nextDep === undefined) break\n if (seen.has(nextDep)) {\n const cycleStart = path.indexOf(nextDep)\n return [...path.slice(cycleStart), nextDep]\n }\n path.push(nextDep)\n seen.add(nextDep)\n cursor = nextDep\n }\n\n return path\n}\n","/**\n * `diagnostics_channel` publishing for the Node server — how APM agents,\n * OpenTelemetry instrumentations and the DevTools subscribe without being an\n * adapter or plugin. Every channel is free when nobody subscribes: each\n * publish is guarded by `hasSubscribers`.\n *\n * | Channel | Kind | Payload |\n * | --- | --- | --- |\n * | `kickjs:handler` | tracing channel (`start` / `end` / `asyncStart` / `asyncEnd` / `error`) | {@link HandlerTraceContext} |\n * | `kickjs:error` | channel | `{ error, ...ErrorInfo }` |\n * | `kickjs:response` | channel | `ResponseInfo` |\n *\n * `kickjs:handler` wraps the controller method itself, so a span started on\n * `start` covers the handler and follows its async work.\n *\n * Node-only (imported by the Application, never by the web entry).\n *\n * @module @forinda/kickjs/http/tracing\n */\nimport diagnostics from 'node:diagnostics_channel'\nimport { setObserverPublisher, type ErrorInfo, type ResponseInfo } from '../core/observers'\nimport type { CtxHandler } from './runtime'\nimport type { RequestContext } from './context'\n\nexport const HANDLER_CHANNEL = 'kickjs:handler'\nexport const ERROR_CHANNEL = 'kickjs:error'\nexport const RESPONSE_CHANNEL = 'kickjs:response'\n\n/** What subscribers to `kickjs:handler` receive. */\nexport interface HandlerTraceContext {\n method: string\n /** The route pattern, e.g. `/api/v1/users/:id` — the span name APMs want. */\n route: string\n controller?: string\n handler?: string\n requestId?: string\n ctx: RequestContext\n}\n\nconst handlerChannel = diagnostics.tracingChannel<HandlerTraceContext>(HANDLER_CHANNEL)\nconst errorChannel = diagnostics.channel(ERROR_CHANNEL)\nconst responseChannel = diagnostics.channel(RESPONSE_CHANNEL)\n\n/** Feed the error / response channels from the observer funnel. Idempotent. */\nexport function installChannelPublisher(): void {\n setObserverPublisher(\n {\n name: 'diagnostics_channel',\n onError(error: unknown, info: ErrorInfo) {\n if (errorChannel.hasSubscribers) errorChannel.publish({ error, ...info })\n },\n onResponse(info: ResponseInfo) {\n if (responseChannel.hasSubscribers) responseChannel.publish(info)\n },\n },\n () => responseChannel.hasSubscribers,\n )\n}\n\n/**\n * Wrap a route's terminal handler in the `kickjs:handler` tracing channel.\n * With no subscriber the original handler runs untouched (same return value,\n * sync or async).\n */\nexport function traceHandler(\n handler: CtxHandler,\n meta: Omit<HandlerTraceContext, 'ctx' | 'requestId'>,\n): CtxHandler {\n return (ctx) => {\n if (!handlerChannel.hasSubscribers) return handler(ctx)\n return handlerChannel.tracePromise(async () => handler(ctx), {\n ...meta,\n requestId: ctx.requestId,\n ctx,\n })\n }\n}\n","/**\n * The built-in health endpoints, as a module you can see.\n *\n * These used to be mounted straight onto the engine with `http.route()` before\n * the middleware chain. That worked, but it made them invisible in two ways\n * that matter:\n *\n * 1. **Swagger never saw them.** The OpenAPI spec is built by scanning\n * controller decorators, and a raw `http.route()` carries none — so the two\n * routes the framework itself serves were missing from every generated spec.\n * 2. **Nothing in an adopter's code said they existed.** `kick g adapter`\n * documents `onHealthCheck`, but the endpoint consuming it appeared nowhere,\n * so people wrote their own `/health/ready` beside the built-in one.\n *\n * As a module it is registered like any other, appears in the boot module list,\n * shows up in the spec, and can be replaced — pass your own module and set\n * `health: false`.\n *\n * Mounting it with the other modules also puts it INSIDE the middleware chain,\n * where it was previously ahead of it. That is deliberate: an app controls its\n * own auth, and a framework route quietly bypassing it is the surprise, not the\n * other way round. An app that wants its probes unauthenticated exempts them —\n * `health: { flags: ['auth.public'] }` puts the app's own flag on both routes,\n * and whatever already skips on that flag skips these too.\n *\n * @module @forinda/kickjs/http/health-module\n */\nimport { Controller, Get } from '../core/decorators'\nimport { createToken } from '../core/token'\nimport { defineModule } from '../core/define-module'\nimport type { Container } from '../core/container'\nimport type { RouteFlagDeclarations } from '../core/route-flag'\nimport { reply } from './reply'\n\n/** One adapter's answer to `onHealthCheck()`. */\nexport interface HealthCheckResult {\n name: string\n status: 'up' | 'down'\n}\n\n/**\n * What the health routes need from the running Application.\n *\n * A controller cannot reach `Application` internals, and should not: this is\n * the whole surface the endpoints depend on, so a replacement module can\n * satisfy it without knowing anything else.\n */\nexport interface HealthProbe {\n /** True once shutdown has begun and traffic should drain away. */\n isDraining(): boolean\n /** Aggregated `onHealthCheck()` results from every adapter that has one. */\n checks(): Promise<HealthCheckResult[]>\n}\n\nexport const HEALTH_PROBE = createToken<HealthProbe>('kick/Health/probe')\n\n/** Config for {@link healthModule}. */\nexport interface HealthModuleConfig {\n /**\n * Route flags to put on `GET /health/live` and `GET /health/ready`.\n *\n * The framework names no flags, so it cannot decide these probes are\n * \"public\" on your behalf — the name is yours, and every consumer that\n * already reads it (an auth contributor's `skipWhen`, a guard's\n * `exemptWhen`, `SwaggerAdapter({ publicFlag })`) then covers health with no\n * further wiring:\n *\n * ```ts\n * bootstrap({ health: { flags: ['auth.public'] } })\n * ```\n *\n * This replaces exempting `/health/live` and `/health/ready` by pathname,\n * which silently stops matching if the probe paths ever move.\n */\n flags?: RouteFlagDeclarations\n}\n\n@Controller()\nexport class HealthController {\n constructor(private readonly probe: HealthProbe) {}\n\n /** Liveness: is the process up and not shutting down. */\n @Get('/live')\n live() {\n return this.probe.isDraining()\n ? reply(503, { status: 'draining', uptime: process.uptime() })\n : reply(200, { status: 'ok', uptime: process.uptime() })\n }\n\n /** Readiness: is every adapter's dependency answering. */\n @Get('/ready')\n async ready() {\n if (this.probe.isDraining()) {\n return reply(503, { status: 'draining', checks: [] })\n }\n const checks = await this.probe.checks()\n const healthy = checks.every((c) => c.status === 'up')\n return reply(healthy ? 200 : 503, {\n status: healthy ? 'ready' : 'degraded',\n checks,\n })\n }\n}\n\n/**\n * `GET /health/live` and `GET /health/ready`, mounted at the root.\n *\n * A `defineModule` factory like any other module — the framework's own routes\n * should be declared the way it asks adopters to declare theirs, not through a\n * shape only the framework can produce.\n *\n * Registered automatically unless the app passes `health: false` or supplies a\n * module of its own. Pass `health: { flags: [...] }` to `bootstrap()` to flag\n * both probes — see {@link HealthModuleConfig}.\n */\nexport const healthModule = defineModule<HealthModuleConfig>({\n name: 'HealthModule',\n build: (config) => ({\n register(container: Container) {\n // Resolve through the token so a replacement probe — or a test double —\n // can be bound before this runs.\n container.registerFactory(HealthController, () => {\n const probe = container.resolve<HealthProbe>(HEALTH_PROBE)\n return new HealthController(probe)\n })\n },\n routes() {\n // Root-mounted and unversioned: the probe URL an orchestrator is\n // configured against must not move when the API prefix or version does.\n return {\n path: '/health',\n controller: HealthController,\n version: false,\n prefix: false,\n // Declared at the mount rather than on HealthController: the class is\n // the framework's, the flag names are the app's.\n flags: config.flags,\n }\n },\n }),\n})\n","// The default Express runtime — the M1 reference implementation of HttpRuntime.\n//\n// Its route materializer rebuilds the exact `(req, res, next)` handler chain\n// the old `buildRoutes()` produced, so swapping decorators → RouteTable →\n// runtime is behavior-neutral under Express. `buildRoutes()` (the public,\n// Router-returning API) now lives here as a thin shim over `buildRouteTable()`\n// + this materializer.\n\nimport express, {\n Router,\n type Express,\n type Request,\n type Response,\n type NextFunction,\n type RequestHandler,\n} from 'express'\n\nimport { buildRouteTable, type BuildRoutesOptions } from '../router-builder'\nimport { RequestContext } from '../context'\nimport { applyHandlerResult } from '../reply'\nimport { validate } from '../middleware/validate'\nimport { publishMatchedRoute } from '../runtime'\nimport { buildUploadMiddleware } from '../middleware/upload'\nimport type {\n ConnectMiddleware,\n HttpRuntime,\n RouteEntry,\n RouteTable,\n RuntimeAppOptions,\n UseConnectOptions,\n} from '../runtime'\n\n/**\n * Materialize a controller's {@link RouteEntry}[] onto a fresh `express.Router`.\n * Reproduces the legacy handler ordering precisely: validation → upload →\n * `(ctx, next)` middleware → contributor runner → terminal handler, with\n * `beforeValidation` contributors ahead of validation. Each step\n * constructs its own `RequestContext` over the same `req`/`res` — request-scoped\n * state lives in the AsyncLocalStorage store, not on the ctx instance, so the\n * per-step instances share state exactly as before.\n */\nexport function materializeRouter(entries: RouteEntry[]): Router {\n const router = Router()\n\n for (const entry of entries) {\n const method = entry.method.toLowerCase() as 'get' | 'post' | 'put' | 'delete' | 'patch'\n const handlers: RequestHandler[] = []\n\n // Publish the matched route first, so every later step — validation,\n // upload, middleware, contributors, handler — reads the same `ctx.route`.\n handlers.push((req: Request, _res: Response, next: NextFunction) => {\n publishMatchedRoute(req, entry)\n next()\n })\n\n // Context Contributor pipeline (#107) — runs, then advances the chain\n // unless a contributor already answered.\n const contributorStep =\n (run: NonNullable<RouteEntry['contributorRunner']>): RequestHandler =>\n async (req: Request, res: Response, next: NextFunction) => {\n const ctx = new RequestContext(req, res, next)\n try {\n await run(ctx)\n if (!res.headersSent) next()\n } catch (err) {\n next(err)\n }\n }\n\n // `beforeValidation` contributors (#677) — an auth rejection answers 401\n // before a malformed body could answer 422.\n if (entry.earlyContributorRunner) {\n handlers.push(contributorStep(entry.earlyContributorRunner))\n }\n\n // Validation middleware (shared with the standalone validate() export).\n if (entry.meta.validation) {\n handlers.push(validate(entry.meta.validation))\n }\n\n // @FileUpload — auto-attach upload middleware from metadata.\n if (entry.meta.upload) {\n handlers.push(buildUploadMiddleware(entry.meta.upload))\n }\n\n // Class + method middleware — wrapped as Express middleware with error catching.\n for (const mw of entry.middlewares) {\n handlers.push((req: Request, res: Response, next: NextFunction) => {\n const ctx = new RequestContext(req, res, next)\n Promise.resolve(mw(ctx, next)).catch(next)\n })\n }\n\n if (entry.contributorRunner) {\n handlers.push(contributorStep(entry.contributorRunner))\n }\n\n // Terminal handler.\n handlers.push(async (req: Request, res: Response, next: NextFunction) => {\n const ctx = new RequestContext(req, res, next)\n try {\n const result = await entry.handler(ctx)\n // Return-value handlers (reply.ts): auto-send the returned payload\n // when the handler wrote nothing. `undefined` keeps prior behavior.\n if (!res.headersSent) await applyHandlerResult(ctx, result)\n } catch (err) {\n next(err)\n }\n })\n ;(router as any)[method](entry.path, ...handlers)\n }\n\n return router\n}\n\n/**\n * Build an Express Router from a controller class decorated with @Get, @Post,\n * etc. The public, back-compatible entry point: a thin shim over\n * {@link buildRouteTable} + {@link materializeRouter}. Returns an\n * `express.Router` exactly as before — modules pass it to `app.use(path, router)`.\n *\n * @see buildRouteTable for the engine-neutral route-table form.\n */\nexport function buildRoutes(controllerClass: any, options: BuildRoutesOptions = {}): Router {\n return materializeRouter(buildRouteTable(controllerClass, options))\n}\n\n/**\n * The default HTTP runtime. Express stays a peer dependency of `@forinda/kickjs`;\n * apps that pass no `runtime` get this and behave exactly as before.\n */\nexport function expressRuntime(): HttpRuntime<Express> {\n return {\n name: 'express',\n\n createApp(options: RuntimeAppOptions = {}): Express {\n const app = express()\n // Engine hardening + config the Application used to apply directly.\n app.disable('x-powered-by')\n app.set('trust proxy', options.trustProxy ?? false)\n return app\n },\n\n nodeHandler(app) {\n return (req, res, next) => {\n if (next) {\n ;(app as any)(req, res, next)\n } else {\n ;(app as any)(req, res)\n }\n }\n },\n\n mountRoutes(app, table: RouteTable) {\n for (const { mountPath, routes } of table) {\n app.use(mountPath, materializeRouter(routes))\n }\n },\n\n useConnect(app, mw: ConnectMiddleware, opts: UseConnectOptions = {}) {\n if (opts.path !== undefined) {\n ;(app as any).use(opts.path, mw)\n } else {\n ;(app as any).use(mw)\n }\n },\n\n serveStatic(app, prefix, dir) {\n app.use(prefix, express.static(dir))\n },\n\n setNotFound(app, mw: ConnectMiddleware) {\n ;(app as any).use(mw)\n },\n\n setErrorHandler(app, mw: ConnectMiddleware) {\n ;(app as any).use(mw)\n },\n\n capabilities: {\n render: true,\n uploads: true,\n connectMiddleware: true,\n nativeBodyParsing: false,\n http2: false,\n },\n }\n}\n","/**\n * Serve web `Request`s through a Node request handler by forwarding them to a\n * server bound to `127.0.0.1` inside the same process. Used by\n * `Application.fetch()` for runtimes without a native fetch (Express and\n * Fastify): their handlers need real Node request/response objects, and Express\n * swaps the `res` prototype, which makes a direct Request→`res` bridge\n * unreliable.\n *\n * @module @forinda/kickjs/http/loopback\n */\nimport { randomBytes } from 'node:crypto'\nimport http, { type IncomingMessage, type ServerResponse } from 'node:http'\nimport type { AddressInfo } from 'node:net'\n\n/**\n * Request headers that describe one hop, not the request. Forwarding them\n * makes Node's `fetch` reject the request (\"fetch failed\") or misframe it —\n * `content-length` is recomputed from the buffered body.\n */\nconst HOP_BY_HOP_REQUEST = [\n 'connection',\n 'keep-alive',\n 'proxy-connection',\n 'transfer-encoding',\n 'upgrade',\n 'te',\n 'trailer',\n 'host',\n 'content-length',\n]\n\nconst HOP_BY_HOP_RESPONSE = [\n 'connection',\n 'keep-alive',\n 'proxy-connection',\n 'transfer-encoding',\n 'upgrade',\n 'trailer',\n 'content-encoding',\n 'content-length',\n]\n\n/** Carry the Request's host across the loopback hop; never seen by the app. */\nconst LOOPBACK_HOST = 'x-kick-loopback-host'\nconst LOOPBACK_SECRET = 'x-kick-loopback-secret'\n\nexport interface Loopback {\n /** Forward a Request to the handler; starts the server on first use. */\n fetch(request: Request): Promise<Response>\n /** Stop the server, if it started. */\n close(): Promise<void>\n}\n\n/**\n * The request body as a stream, without buffering it — so the app's body\n * parsers enforce their size limits as it arrives. A body that turns out to be\n * empty becomes `undefined`: Node's fetch rejects an empty stream on POST.\n */\nasync function streamedBody(request: Request): Promise<ReadableStream<Uint8Array> | undefined> {\n if (!request.body || request.method === 'GET' || request.method === 'HEAD') return undefined\n const reader = request.body.getReader()\n const first = await reader.read()\n if (first.done) return undefined\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(first.value)\n },\n async pull(controller) {\n const { done, value } = await reader.read()\n if (done) controller.close()\n else controller.enqueue(value)\n },\n cancel(reason) {\n return reader.cancel(reason)\n },\n })\n}\n\nexport function createLoopback(\n handle: (req: IncomingMessage, res: ServerResponse) => void,\n): Loopback {\n let started: Promise<{ origin: string; server: http.Server }> | undefined\n // Proves a request came through this loopback, not from another process\n // that found the port: only then is the host it carries believed.\n const secret = randomBytes(24).toString('hex')\n\n const server = () => {\n started ??= new Promise<{ origin: string; server: http.Server }>((resolve, reject) => {\n const instance = http.createServer((req, res) => {\n // Node's fetch won't send a Host header, so the original host travels\n // in LOOPBACK_HOST and is put back here: the app sees the Host the\n // Request was addressed to, as it would from a real client.\n const host = req.headers[LOOPBACK_HOST]\n if (req.headers[LOOPBACK_SECRET] === secret && typeof host === 'string')\n req.headers.host = host\n delete req.headers[LOOPBACK_HOST]\n delete req.headers[LOOPBACK_SECRET]\n handle(req, res)\n })\n instance.once('error', reject)\n instance.listen(0, '127.0.0.1', () => {\n // Never keep a process alive on its own account.\n instance.unref()\n resolve({\n origin: `http://127.0.0.1:${(instance.address() as AddressInfo).port}`,\n server: instance,\n })\n })\n }).catch((err) => {\n started = undefined\n throw err\n })\n return started\n }\n\n return {\n async fetch(request) {\n const { origin } = await server()\n const url = new URL(request.url)\n\n const headers = new Headers(request.headers)\n for (const name of HOP_BY_HOP_REQUEST) headers.delete(name)\n // The forwarded request arrives from 127.0.0.1. Host and protocol come\n // from the Request's URL — never from caller-supplied forwarding\n // headers, which a trusting runtime would otherwise believe.\n headers.set('x-forwarded-host', url.host)\n headers.set('x-forwarded-proto', url.protocol.slice(0, -1))\n headers.set(LOOPBACK_HOST, url.host)\n headers.set(LOOPBACK_SECRET, secret)\n\n const body = await streamedBody(request)\n const upstream = await fetch(origin + url.pathname + url.search, {\n method: request.method,\n headers,\n body,\n // Required by Node's fetch for a streamed request body.\n ...(body ? { duplex: 'half' } : {}),\n redirect: 'manual',\n signal: request.signal,\n } as RequestInit)\n\n // Node's fetch has already decoded a compressed body, so the encoding and\n // length headers no longer describe what is being returned. Connection\n // headers describe the loopback hop.\n const responseHeaders = new Headers(upstream.headers)\n const named = responseHeaders.get('connection')?.split(',') ?? []\n for (const name of [...HOP_BY_HOP_RESPONSE, ...named]) responseHeaders.delete(name.trim())\n return new Response(upstream.body, {\n status: upstream.status,\n statusText: upstream.statusText,\n headers: responseHeaders,\n })\n },\n\n async close() {\n const running = await started?.catch(() => undefined)\n started = undefined\n if (!running) return\n const closed = new Promise<void>((resolve) => running.server.close(() => resolve()))\n // Application.shutdown has already drained in-flight requests; what is\n // left is keep-alive sockets from fetch's pool, which would hold\n // close() open past the shutdown timeout.\n running.server.closeAllConnections()\n await closed\n },\n }\n}\n","import http from 'node:http'\nimport { timingSafeEqual } from 'node:crypto'\nimport { HttpException } from '../core/errors'\nimport { runCronJobs } from '../core/cron'\nimport https from 'node:https'\nimport http2 from 'node:http2'\nimport tls from 'node:tls'\nimport express, { type Express, type RequestHandler } from 'express'\nimport {\n Container,\n createLogger,\n Logger,\n joinPaths,\n buildMountPath,\n tokenName,\n METADATA,\n type AppModule,\n type AppModuleClass,\n type AppModuleEntry,\n type AppAdapter,\n type AdapterContext,\n type KickServer,\n type AdapterMiddleware,\n type ContributorRegistrations,\n type KickPlugin,\n type ModuleRegistry,\n type RouteDefinition,\n type SourcedRegistration,\n mountSort,\n MutableModuleRegistry,\n} from '../core'\nimport {\n http2RequiresTlsError,\n http2UnsupportedRuntimeError,\n moduleRouteMissingControllerError,\n tlsCredentialsError,\n} from '../core/kick-errors'\nimport { RoutePolicyTable, offerRoutePolicy } from '../core/route-policy'\nimport {\n disposeAll,\n drainDisposables,\n registerDisposable,\n runDisposables,\n} from '../core/disposables'\nimport { getClassMeta } from '../core/metadata'\nimport { EXPRESS_JSON_TYPES, rejectUnsupportedBody } from './body-policy'\nimport { requestId } from './middleware/request-id'\nimport { pendingBackgroundWork, settleBackgroundWork } from './background'\nimport { installChannelPublisher, traceHandler } from './tracing'\nimport {\n hasResponseObservers,\n releaseObservers,\n reportError,\n reportResponse,\n setObservers,\n type Observer,\n} from '../core/observers'\nimport { ROUTE_SLOT } from '../core/route-flag'\nimport type { MatchedRoute } from './runtime'\nimport { notFoundHandler, errorHandler, type MountedRoute } from './middleware/error-handler'\nimport { requestScopeMiddleware, isRequestScopeMiddleware } from './middleware/request-scope'\nimport {\n _setExternalContributorSources,\n assertRouteUnique,\n buildRouteTable,\n} from './router-builder'\nimport { HEALTH_PROBE, healthModule, type HealthModuleConfig } from './health-module'\nimport { expressRuntime } from './runtimes/express'\nimport { createLoopback, type Loopback } from './loopback'\nimport type {\n ActiveRuntime,\n AdapterHttp,\n HttpRuntime,\n RouteEntry,\n RuntimeCapabilities,\n RuntimeResponse,\n} from './runtime'\nimport { requestStore } from './request-store'\n\nconst log = createLogger('Application')\n\n/**\n * A middleware entry in the declarative pipeline.\n * Can be a bare handler or an object with path scoping.\n */\nexport type MiddlewareEntry = RequestHandler | { path: string; handler: RequestHandler }\n\n/** Options for {@link Application.shutdown}. */\nexport interface ShutdownOptions {\n /**\n * Close the HTTP server and drain in-flight requests. Default `true`.\n *\n * Set `false` for a dev HMR rebuild, where the server is shared across\n * reloads — adapters, plugins, and disposables are still torn down, which is\n * the part a reload needs.\n */\n closeServer?: boolean\n}\n\n/**\n * Throw KICK009 unless `options` can identify the server. Node creates an\n * HTTPS / HTTP/2 server without a certificate and then fails every handshake,\n * so the check has to happen here, at boot.\n */\nfunction assertUsableTls(options: tls.SecureContextOptions & tls.TlsOptions): void {\n const { key, cert, pfx, SNICallback, pskCallback } = options\n if (!pfx && !SNICallback && !pskCallback) {\n if (!key && !cert) throw tlsCredentialsError('no key/cert, pfx, SNICallback, or pskCallback')\n if (!key) throw tlsCredentialsError('cert is set but key is missing')\n if (!cert) throw tlsCredentialsError('key is set but cert is missing')\n }\n // Build the context once: surfaces unreadable PEMs, a wrong passphrase, and\n // a key that does not match its certificate before any adapter runs.\n try {\n tls.createSecureContext(options)\n } catch (err) {\n throw tlsCredentialsError((err as Error).message)\n }\n}\n\n/** See {@link ApplicationOptions.server}. */\nexport interface ServerOptions {\n /** TLS credentials and options (`key`, `cert`, `ca`, `pfx`, `passphrase`, …). */\n tls?: tls.SecureContextOptions & tls.TlsOptions\n /** Serve HTTP/2 with HTTP/1.1 fallback. Requires `tls` and an HTTP/2-capable runtime. */\n http2?: boolean\n}\n\nexport interface ApplicationOptions {\n /**\n * Feature modules to load. Accepts both class form\n * (`SomeModule extends AppModule`, instantiated via `new`) and\n * instance form (output of {@link defineModule}'s factory call,\n * e.g. `TasksModule({ scope: 'admin' })`) — the loader discriminates\n * and either `new`-s the class or uses the instance directly.\n *\n * Static path — for conditional / dynamic registration, use the\n * {@link ApplicationOptions.setup} callback instead.\n */\n modules: AppModuleEntry[]\n\n /**\n * Imperative module registration callback. Receives a\n * {@link ModuleRegistry} you call `.mount(module)` on to register\n * modules conditionally — based on env flags, runtime config, a\n * tenant list, etc. Runs after every plugin's `setup()` hook and\n * after the static `modules: [...]` array is collected.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules: [HelloModule()],\n * setup(registry) {\n * if (env.ENABLE_ADMIN) registry.mount(AdminModule())\n * },\n * })\n * ```\n */\n setup?(registry: ModuleRegistry): void\n /** Adapters that hook into the lifecycle (DB, Redis, Swagger, etc.) */\n adapters?: AppAdapter[]\n /** Server port (falls back to PORT env var, then 3000) */\n port?: number\n /**\n * The Node server `start()` creates in production. Omit for plain HTTP.\n *\n * - `{ tls }` — HTTPS on every runtime.\n * - `{ tls, http2: true }` — HTTP/2 (ALPN `h2`) with HTTP/1.1 fallback, so\n * HTTP/1.1 clients and WebSocket upgrades keep working. Fastify and h3\n * only; Express fails at boot with KICK007, and `http2` without `tls`\n * with KICK008.\n *\n * Ignored in dev mode, where Vite owns the server. Most deploys terminate\n * TLS / HTTP/2 at a proxy and need none of this.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * runtime: fastifyRuntime(),\n * server: {\n * tls: { key: readFileSync('key.pem'), cert: readFileSync('cert.pem') },\n * http2: true,\n * },\n * })\n * ```\n */\n server?: ServerOptions\n /** Global API prefix (default: '/api'). Pass `''` to mount at the root. */\n apiPrefix?: string\n /**\n * Default API version (default: 1) — routes become /{prefix}/v{version}/{path}.\n *\n * `false` opts out of URL versioning: routes mount at /{prefix}/{path}.\n * A module can still version itself by setting `version` on its\n * `ModuleRoutes` — the per-mount value always wins over this default, in\n * either direction.\n */\n defaultVersion?: number | false\n\n /**\n * Global middleware pipeline. Declared in order.\n * Replaces the hardcoded middleware stack — you control exactly what runs.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * middlewares: [\n * helmet(),\n * cors(),\n * compression(),\n * morgan('dev'),\n * express.json({ limit: '1mb' }),\n * ],\n * })\n * ```\n *\n * If omitted, a sensible default is applied:\n * requestId(), express.json({ limit: '100kb' })\n */\n middlewares?: MiddlewareEntry[]\n\n /** Plugins that bundle modules, adapters, middleware, and DI bindings */\n plugins?: KickPlugin[]\n\n /**\n * Global Context Contributors (#107) that apply to every route in the\n * application. Merge into the per-route pipeline at the `'global'`\n * precedence level — they lose to module, adapter, class, and method\n * contributors with the same key, providing app-wide defaults that any\n * narrower scope can override.\n *\n * @example\n * ```ts\n * const StartedAt = defineContextDecorator({\n * key: 'requestStartedAt',\n * resolve: () => Date.now(),\n * })\n *\n * bootstrap({\n * modules,\n * contributors: [StartedAt.registration],\n * })\n * ```\n */\n contributors?: ContributorRegistrations\n\n /**\n * Backing store strategy for {@link RequestContext} `set/get` and the\n * Context Contributor pipeline (#107).\n *\n * - `'auto'` (default) — Application mounts {@link requestScopeMiddleware}\n * automatically before any user middleware. If the user-supplied\n * `middleware` list already includes `requestScopeMiddleware()`,\n * detection skips the auto-mount so adopters can control its position.\n * - `'manual'` — Application never mounts the wrapper. The Context\n * Contributor pipeline still runs on every route — the runner is\n * inserted by `router-builder` regardless of ALS state. What\n * degrades without an ALS frame is the *backing store*:\n * REQUEST-scoped DI throws (no `requestStore.getStore()` to read\n * from) and `RequestContext.set/get` throws. Use `'manual'` only when you\n * genuinely intend to wrap requests in your own ALS frame (rare —\n * multi-tenant adapters used to do this; post-Phase 3 they share\n * the framework's frame instead).\n */\n contextStore?: 'auto' | 'manual'\n\n /**\n * Controls whether KickJS registers process-level handlers at bootstrap.\n *\n * - `'auto'` (default) — register `uncaughtException` /\n * `unhandledRejection` loggers AND `SIGINT` / `SIGTERM` →\n * `app.shutdown()` → `process.exit(0)`.\n * - `'errors-only'` — register the error loggers; skip the signal\n * handlers. Use this when you have your own shutdown choreographer\n * (OpenTelemetry SDK, Sentry, custom orchestrator) that calls\n * `app.shutdown()` itself and you don't want two listeners racing\n * to call `process.exit`.\n * - `'manual'` — skip everything. Adopter is responsible for both\n * error logging and signal-driven shutdown.\n *\n * Background: many observability SDKs install their own SIGTERM\n * handler to flush spans/metrics before the process exits. Two\n * separate handlers calling `process.exit(0)` race each other and\n * can truncate the in-flight flush. Setting this to `'errors-only'`\n * or `'manual'` lets the SDK own the lifecycle while still letting\n * KickJS surface uncaught exceptions / rejections via the logger.\n *\n * The dev mode auto-detect (`__kickjs_httpServer` set by the Vite\n * plugin) suppresses signal registration regardless of this option,\n * since Vite owns the lifecycle in development.\n */\n processHooks?: 'auto' | 'errors-only' | 'manual'\n\n /**\n * The HTTP engine driver. Defaults to {@link expressRuntime} — Express stays\n * the zero-config default, so existing apps are unaffected.\n *\n * Any `HttpRuntime` is accepted — pass `fastifyRuntime()` (from\n * `@forinda/kickjs/fastify`) or a custom engine driver. Defaults to\n * {@link expressRuntime}. The engine-native escape hatches\n * (`getRuntimeApp()`, `AdapterContext.app`) follow the active runtime via the\n * registry (`ActiveRuntime`, spec §4.3b) — Express by default, or the augmented\n * engine once the `kick/runtime` typegen output is present.\n */\n /**\n * Mount the built-in `GET /health/live` and `GET /health/ready`.\n *\n * On by default. Set `false` to take them over completely — supply your own\n * module at `/health` and nothing collides. The built-in is registered last,\n * so an app module claiming the same path already wins without this.\n *\n * They mount as an ordinary module, which means they sit inside the\n * middleware chain: whatever guards the rest of the app guards these too.\n * Pass `{ flags: [...] }` to put your own route flags on both probes — the\n * way to make them reachable unauthenticated without exempting a pathname\n * that can move:\n *\n * ```ts\n * bootstrap({ health: { flags: ['auth.public'] } })\n * ```\n */\n health?: boolean | HealthModuleConfig\n\n runtime?: HttpRuntime\n\n /**\n * Requests no route matched, when the app runs inside another handler —\n * `kick dev`'s Vite server, or `app.handle(req, res, next)` in a host server.\n *\n * - `true` (default): Fastify and h3 hand them back to that handler.\n * - `false`: they are answered here — `onNotFound`, or the built-in 404 /\n * 405 — the same as in production. Express always answers.\n *\n * Set `false` when the app owns its URLs, e.g. pages with an error page,\n * so dev answers unknown URLs the way production does.\n */\n notFoundFallthrough?: boolean\n\n /** Express `trust proxy` setting */\n trustProxy?: boolean | number | string | ((ip: string, hopIndex: number) => boolean)\n /** Maximum JSON body size (only used when middleware is not provided) */\n jsonLimit?: string | number\n /**\n * Print the route table on startup. Default: `false`. Set to `true`\n * to log a per-controller summary (method counts + mount paths) once\n * the app has mounted. Emitted at `info` level, so it is still subject\n * to `LOG_LEVEL` filtering (visible at the default `info`; hidden at\n * `warn`/`error`/`silent`).\n */\n logRouteTable?: boolean\n /**\n * @deprecated Use {@link ApplicationOptions.logRouteTable}. Kept as an\n * alias for back-compat; `logRouteTable` wins when both are set.\n */\n logRoutesTable?: boolean\n /**\n * Maximum time (ms) to wait for graceful shutdown before forcing exit.\n * Default: 30000 (30 seconds). Set to 0 to disable forced exit.\n */\n shutdownTimeout?: number\n\n /**\n * Enable cluster mode for multi-core utilization.\n *\n * When enabled, the primary process forks worker processes that share the\n * same port via Node's built-in `cluster` module (OS load balancing).\n *\n * - `true` — use all available CPU cores\n * - `{ workers: N }` — use exactly N workers\n *\n * Workers are auto-restarted on crash. SIGTERM/SIGINT on the primary is\n * forwarded to all workers.\n *\n * @example\n * ```ts\n * bootstrap({ modules, cluster: true })\n * bootstrap({ modules, cluster: { workers: 4 } })\n * ```\n */\n cluster?: boolean | { workers?: number }\n\n /**\n * Custom 404 handler for unmatched routes. When omitted, the built-in\n * handler returns `{ message: 'Not Found' }` with status 404.\n *\n * Connect-**shaped**, not Express-**semantic** — see {@link onError} for the\n * per-runtime table. The same two caveats apply: `next()` is inert on\n * Fastify and h3, and Express-only request members (`req.originalUrl`,\n * `req.path`, `req.ip`) are `undefined` there, so read `req.url`.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * onNotFound: (req, res) => {\n * // `req.originalUrl` is Express-only — use `req.url` for portability.\n * res.status(404).json({ error: 'Route not found', path: req.url })\n * },\n * })\n * ```\n *\n * Calling `next()` hands the request to the built-in handler — the usual\n * 404, or the 405 with `Allow` when the path exists under another method —\n * so a custom handler can answer some requests and leave the rest.\n */\n onNotFound?: (req: any, res: RuntimeResponse, next: (err?: unknown) => void) => void\n\n /**\n * Custom global error handler, receiving `(err, req, res, next)`. When\n * omitted, the built-in handler formats ZodError, HttpException, and\n * unexpected errors.\n *\n * The shape is connect-style, but it is NOT \"the standard Express error\n * handler\" on every engine — what you actually receive differs, and the\n * differences bite:\n *\n * | | `req` | `res` | `next` |\n * | --- | --- | --- | --- |\n * | Express | native `Request` | native `Response` | real `next` |\n * | Fastify | `request.raw` | reply driver | **no-op** |\n * | h3 | `event.node.req` | response driver | no-op (or dev fall-through) |\n *\n * Two consequences worth knowing before you write one:\n *\n * - **`next(err)` does nothing on Fastify and h3.** It is a no-op function\n * there, so delegating to the default handler silently drops the error.\n * Send a response yourself rather than passing it on.\n * - **Express-only request members are `undefined` elsewhere.**\n * `req.originalUrl`, `req.path`, and `req.ip` do not exist on the raw node\n * request — use `req.url` and the `x-forwarded-*` headers, or reach for\n * `ctx.ip` in a contributor / guard where a `RequestContext` is available.\n *\n * `res` is typed {@link RuntimeResponse} because that is what every engine\n * genuinely provides: `status`, `json`, `send`, `setHeader`, `render`,\n * `writeHead`, `end`. Express's own `Response` satisfies it.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * onError: (err, req, res) => {\n * logger.error(err, `${req.method} ${req.url}`) // not `originalUrl`\n * res.status(err.status ?? 500).json({ error: err.message })\n * },\n * })\n * ```\n */\n onError?: (err: any, req: any, res: RuntimeResponse, next: (err?: unknown) => void) => void\n\n /**\n * Security defaults applied automatically unless opted out.\n *\n * When not specified, secure defaults are applied:\n * - Helmet security headers are auto-injected\n * - JSON body limit defaults to 1MB\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * security: {\n * helmet: false, // disable auto-helmet\n * },\n * })\n * ```\n */\n security?: {\n /** Auto-inject helmet security headers. Default: true */\n helmet?: boolean\n }\n}\n\n/**\n * The main application class. Wires together Express, the DI container,\n * feature modules, adapters, and the middleware pipeline.\n */\n\n/**\n * A `defineModule()` factory, as opposed to a module class.\n *\n * Both are functions, so `typeof` cannot tell them apart. The factory carries a\n * frozen `definition` and a `scoped` helper, neither of which a class has.\n */\nfunction isModuleFactory(entry: unknown): entry is (() => AppModule) & {\n definition: { name?: string; defaults?: unknown }\n} {\n return (\n typeof entry === 'function' &&\n 'definition' in entry &&\n typeof (entry as { scoped?: unknown }).scoped === 'function'\n )\n}\n\n/**\n * Whether a factory takes configuration.\n *\n * `defaults` is the only runtime signal that a module has config at all — the\n * type parameter is erased. Its presence is what separates \"the bare name is\n * exactly equivalent\" from \"the bare name silently picked defaults for you\".\n */\nfunction isConfigurable(factory: { definition: { defaults?: unknown } }): boolean {\n const defaults = factory.definition?.defaults\n return defaults !== undefined && defaults !== null\n}\n\n/**\n * Resolve a module entry to an `AppModule`, whichever shape it arrives in.\n *\n * Three forms reach here: an instance (`defineModule(...)()` output), a legacy\n * module class, and — the case this exists for — a `defineModule()` factory\n * passed UNINVOKED. That last one used to hit `new factory()` and die with a\n * bare `TypeError: entry is not a constructor`, naming neither the module nor\n * the fix. It is easy to hit because the class form takes the bare name, so\n * the two styles look interchangeable and are not.\n *\n * For a module with NO config, calling the factory with no arguments produces\n * exactly what `Module()` would, so accepting the bare name is equivalent\n * rather than lenient.\n *\n * For a CONFIGURABLE module it is not equivalent in intent: the bare name\n * silently selects the defaults, and an author who meant `Module({ … })` would\n * get a running app wired the wrong way with nothing said. That is the failure\n * mode this whole change exists to remove, so those stay loud — with a message\n * that names the module and both correct spellings, rather than the bare\n * `entry is not a constructor` it used to produce.\n */\nfunction toAppModule(entry: AppModuleEntry): AppModule {\n if (typeof entry !== 'function') return entry as AppModule\n if (isModuleFactory(entry)) {\n if (isConfigurable(entry)) {\n const name = entry.definition?.name || entry.name || '<anonymous>'\n throw new TypeError(\n `bootstrap: module \\`${name}\\` takes configuration, so it must be invoked.\\n` +\n ` Write \\`${name}()\\` for its defaults, or \\`${name}({ … })\\` to configure it.\\n` +\n ` Passing it bare would have silently selected the defaults.`,\n )\n }\n return entry()\n }\n\n const name = (entry as { name?: string }).name || '<anonymous>'\n if (!isConstructible(entry)) {\n throw new TypeError(\n `bootstrap: module entry \\`${name}\\` is a function, but not a module class ` +\n `and not a defineModule() factory (no \\`definition\\`).\\n` +\n ` A defineModule() module is passed as \\`${name}\\` or \\`${name}()\\`; ` +\n `a class must implement AppModule.`,\n )\n }\n\n // Constructed OUTSIDE any catch. Wrapping this was masking real failures:\n // a legacy module whose constructor threw came back reported as \"not a\n // module class\", hiding the actual error and sending the reader to the\n // wrong place entirely.\n const mod = new (entry as AppModuleClass)() as AppModule\n\n // A plain `function Foo() {}` IS constructible and returns `{}`, so it\n // reaches here and would fail later inside the framework at `mod.routes()`\n // — a generic error, far from the entry that caused it. Check the shape\n // where the entry is still in hand.\n if (typeof mod?.routes !== 'function') {\n throw new TypeError(\n `bootstrap: module entry \\`${name}\\` constructed, but the result does not ` +\n `implement AppModule — \\`routes()\\` is missing.\\n` +\n ` A module class needs \\`routes()\\`; a defineModule() module is passed ` +\n `as \\`${name}\\` or \\`${name}()\\`.`,\n )\n }\n return mod\n}\n\n/**\n * Whether `new fn()` is legal, without calling it.\n *\n * Reflect.construct with a deliberately unrelated `newTarget` performs the\n * constructibility check and then throws before running any constructor body,\n * so this stays side-effect free — which matters, because the whole point is\n * to avoid invoking something that may not be a constructor at all.\n */\nfunction isConstructible(fn: unknown): boolean {\n try {\n Reflect.construct(String, [], fn as never)\n return true\n } catch {\n return false\n }\n}\n\nexport class Application {\n private app: Express\n /** The HTTP engine driver. Defaults to {@link expressRuntime}. */\n private readonly runtime: HttpRuntime\n private container: Container\n private httpServer: KickServer | null = null\n /** Set by startWithoutServer() — the serverless path, where cron runs over HTTP. */\n private handlerMode = false\n /** The observer set this app installed — released on shutdown only if still current. */\n private observers: readonly Observer[] = []\n /**\n * Open HTTP/2 sessions. `server.close()` stops new sessions, but on older\n * Node (22 and earlier) existing ones keep accepting new streams — so\n * shutdown closes them itself.\n */\n private readonly http2Sessions = new Set<http2.ServerHttp2Session>()\n /** Forwarding server behind {@link fetch} on Node-based runtimes. */\n private loopback?: Loopback\n private readonly adapters: AppAdapter[]\n /**\n * Flags of every mounted route, for middleware that runs before routing and\n * therefore cannot read `ctx.route` (the connect-style rate limiter). Handed\n * to middleware that declares the slot — see `core/route-policy.ts`.\n */\n private readonly routePolicy = new RoutePolicyTable()\n\n private readonly plugins: KickPlugin[]\n\n /**\n * Snapshot of every module-level Context Contributor registration\n * captured during `setup()`. The registrations themselves are\n * frozen by `defineContextDecorator`, so storing references is\n * safe — they outlive the module instances they came from.\n *\n * Used by {@link getContributors} so DevTools and adopter tooling\n * can render the module-level contributors alongside the adapter /\n * plugin / global ones. Populated once per `setup()` call; cleared\n * at the start of each invocation so a re-setup (test harnesses,\n * dev-server restarts) doesn't accumulate stale entries.\n */\n private _moduleContributors: Array<{\n registration: { key: string; dependsOn: readonly string[] }\n label: string\n }> = []\n\n /** Number of HTTP requests currently being processed */\n private _inFlightRequests = 0\n /** Whether the application is draining (shutting down gracefully) */\n private _draining = false\n /** Whether shutdown has already been initiated (prevents double-shutdown) */\n private _shutdownInitiated = false\n /** Resolvers waiting for in-flight requests to reach zero */\n private _drainResolvers: Array<() => void> = []\n\n constructor(private readonly options: ApplicationOptions) {\n this.runtime = options.runtime ?? expressRuntime()\n // `this.app` is the engine-native app; typed Express here (the default) for\n // the legacy escape hatches. Under another runtime it holds that engine's\n // app — `getRuntimeApp()` / `AdapterContext.app` expose it via ActiveRuntime.\n this.app = this.runtime.createApp({\n trustProxy: options.trustProxy,\n notFoundFallthrough: options.notFoundFallthrough,\n }) as Express\n this.container = Container.getInstance()\n\n // Sort plugins by `dependsOn` declarations BEFORE reading their adapters/etc.\n // Plugins without `dependsOn` keep their declaration order — this is a\n // pure refinement; no behaviour change for apps that don't use the field.\n this.plugins = mountSort(options.plugins ?? [], 'plugin')\n\n // Build adapter list from plugin adapters + user adapters, synthesize\n // a stable name for any anonymous adapter (so duplicate-name detection\n // and `dependsOn` resolution have something to key on), then sort by\n // adapter `dependsOn`. Plugin-shipped adapters keep their plugin's\n // relative order unless their own `dependsOn` says otherwise.\n const allAdapters = [\n ...this.plugins.flatMap((p) => p.adapters?.() ?? []),\n ...(options.adapters ?? []),\n ]\n let anonAdapterCount = 0\n const namedAdapters: Array<AppAdapter & { name: string }> = allAdapters.map((adapter) => {\n if (!adapter.name) {\n // `constructor.name === 'Object'` for plain object-literal adapters —\n // useless as an identity since two anonymous literals would collide.\n // Only borrow the constructor name when it's an actual class.\n const ctorName = adapter.constructor?.name\n const fallback =\n ctorName && ctorName !== 'Object' ? ctorName : `AnonymousAdapter#${anonAdapterCount++}`\n return Object.assign(adapter, { name: fallback })\n }\n return adapter as AppAdapter & { name: string }\n })\n this.adapters = mountSort(namedAdapters, 'adapter')\n // Adapters and plugins with onError / onResponse observe every error and\n // response; the diagnostics_channel channels are fed from the same funnel.\n this.observers = setObservers([...this.adapters, ...this.plugins])\n installChannelPublisher()\n // Wire the request store provider so Container can resolve REQUEST-scoped deps\n Container._requestStoreProvider = () => requestStore.getStore() ?? null\n }\n\n /** Whether the application is currently draining in-flight requests */\n get isDraining(): boolean {\n return this._draining\n }\n\n /** Number of HTTP requests currently being processed */\n get inFlightRequests(): number {\n return this._inFlightRequests\n }\n\n /** Get the DI container instance */\n getContainer(): Container {\n return this.container\n }\n\n /**\n * Express request handler — delegates to the internal Express app.\n *\n * Used by the Vite dev-server plugin to route requests through KickJS:\n * ```ts\n * const mod = await ssrLoadModule('virtual:kickjs/app')\n * mod.app.handle(req, res, next)\n * ```\n *\n * Also works as a standard Node.js request handler for production:\n * ```ts\n * http.createServer(app.handle.bind(app))\n * ```\n */\n /**\n * Run a web `Request` through the app — routing, middleware, validation,\n * contributors, error handling — and return its `Response`, with no\n * listening server required.\n *\n * Runtimes with a native fetch (h3) are called directly.\n * Node-based runtimes (Express, Fastify) are served by forwarding to\n * a server bound to `127.0.0.1` inside this process, started on first use\n * and closed by {@link shutdown}; request bodies are buffered on that path.\n *\n * Used by `createHandler()` and by adapters (`AdapterContext.fetch`) that\n * call the app's own routes, such as MCP and AI tool dispatch.\n */\n fetch(request: Request): Promise<Response> {\n const runtimeApp = this.app as { fetch?: (request: Request) => unknown }\n if (typeof runtimeApp?.fetch === 'function') {\n return Promise.resolve(runtimeApp.fetch(request) as Response | Promise<Response>)\n }\n this.loopback ??= createLoopback((req, res) => this.handle(req, res))\n return this.loopback.fetch(request)\n }\n\n handle(req: http.IncomingMessage, res: http.ServerResponse, next?: (err?: any) => void): void {\n const handler = this.runtime.nodeHandler(this.app)\n if (next) {\n handler(req, res, next)\n } else {\n handler(req, res)\n }\n }\n\n /**\n * Full setup pipeline:\n * 1. Adapter beforeMount hooks (early routes — e.g. docs UI)\n * 2. Adapter middleware (phase: beforeGlobal)\n * 3. Global middleware (user-declared or defaults)\n * 4. Adapter middleware (phase: afterGlobal)\n * 5. Module registration + DI bootstrap (the built-in health module mounts\n * here with everything else — it is NOT an early route)\n * 6. Adapter middleware (phase: beforeRoutes)\n * 7. Module route mounting\n * 8. Adapter middleware (phase: afterRoutes)\n * 9. Adapter beforeStart hooks\n * 10. Error handlers (notFound + global)\n */\n /** Build the adapter context object (shared across all hooks) */\n private adapterCtx(server?: any): AdapterContext {\n const env = process.env.NODE_ENV ?? 'development'\n return {\n http: this.adapterHttp(),\n app: this.app,\n fetch: (request) => this.fetch(request),\n container: this.container,\n server,\n env,\n isProduction: env === 'production',\n }\n }\n\n /**\n * The engine-agnostic HTTP surface handed to adapters as `ctx.http`. Each\n * call routes through the active runtime over the current `this.app`, so an\n * adapter written against this works under any runtime.\n */\n private adapterHttp(): AdapterHttp {\n return {\n route: (method, path, handler) => {\n const entry: RouteEntry = {\n method,\n path,\n middlewares: [],\n contributorRunner: null,\n handler,\n meta: {},\n }\n this.runtime.mountRoutes(this.app, [{ mountPath: '/', routes: [entry] }])\n },\n mount: (prefix, routes) => {\n this.runtime.mountRoutes(this.app, [{ mountPath: prefix, routes }])\n },\n serveStatic: (prefix, dir) => {\n this.runtime.serveStatic(this.app, prefix, dir)\n },\n use: (mw, opts) => {\n this.runtime.useConnect(this.app, mw, opts)\n },\n }\n }\n\n /**\n * Call an adapter hook, awaiting async hooks.\n *\n * A setup hook (`beforeMount`, `beforeStart`) that throws aborts boot: the\n * adapter didn't finish wiring itself — a database adapter refusing to\n * start with pending migrations, say — and serving anyway runs on a\n * half-built app. `afterStart` runs once the server is already listening,\n * so its failure is logged and the server keeps going.\n */\n private async callHook(\n hook: ((ctx: AdapterContext) => void | Promise<void>) | undefined,\n ctx: AdapterContext,\n adapterName = 'adapter',\n phase: 'setup' | 'afterStart' = 'setup',\n ): Promise<void> {\n if (!hook) return\n try {\n await hook(ctx)\n } catch (err) {\n if (phase === 'setup') throw err\n log.error(err, `${adapterName} afterStart hook failed`)\n }\n }\n\n async setup(): Promise<void> {\n log.debug('Bootstrapping application...')\n\n // Reset module-contributor snapshot at the very top of each\n // setup pass — before anything that could throw — so a partial\n // failure doesn't leave stale entries that getContributors()\n // would surface on the next call. See _moduleContributors field\n // declaration for the use case.\n this._moduleContributors = []\n\n // Collect adapter middleware by phase\n const adapterMw = this.collectAdapterMiddleware()\n\n // Expose the Application instance on the Express app for adapter discovery\n ;(this.app as any).__kickApp = this\n\n const ctx = this.adapterCtx()\n\n // ── 1. Adapter beforeMount hooks ──────────────────────────────────\n for (const adapter of this.adapters) {\n await this.callHook(adapter.beforeMount?.bind(adapter), ctx, adapter.name)\n }\n\n // ── 1a. Cron trigger (serverless) ─────────────────────────────────\n if (this.handlerMode) this.mountCronTrigger()\n\n // ── 2. Hardened defaults ──────────────────────────────────────────\n // x-powered-by disable + trust proxy now live in `runtime.createApp()`.\n\n // ── 2a. In-flight request tracking ──────────────────────────────\n this.runtime.useConnect(this.app, this.requestTrackingMiddleware())\n\n // ── 2c. Request scope (AsyncLocalStorage) ────────────────────────\n // Auto-mounted unless the user opted out (`contextStore: 'manual'`)\n // or already included one in their middleware list.\n if (this.shouldAutoMountRequestScope()) {\n this.runtime.useConnect(this.app, requestScopeMiddleware())\n }\n\n // ── 3. Adapter middleware: beforeGlobal ───────────────────────────\n this.mountMiddlewareList(adapterMw.beforeGlobal)\n\n // ── 3b. Plugin registration ──────────────────────────────────────\n for (const plugin of this.plugins) {\n plugin.register?.(this.container)\n }\n\n // ── 3c. Plugin middleware ─────────────────────────────────────────\n for (const plugin of this.plugins) {\n try {\n const mw = plugin.middleware?.() ?? []\n for (const handler of mw) {\n this.runtime.useConnect(this.app, handler)\n }\n } catch (err) {\n log.error(err, `Plugin middleware failed: ${(plugin as any).name ?? 'unknown'}`)\n }\n }\n\n // ── 4. Global middleware ─────────────────────────────────────────\n // Auto-inject helmet unless opted out, or unless the app mounts its own.\n // Injecting alongside a user helmet makes its options half-inert: ours runs\n // first with defaults, and theirs can only overwrite a header, never drop\n // one — so `helmet({ frameguard: false })` would still emit DENY.\n // Only a GLOBAL declaration stands us down. The object form of\n // MiddlewareEntry always carries a `path`, so a helmet scoped to one route\n // does not replace the app-wide pass — standing down for it would strip\n // security headers from every OTHER path.\n // `Symbol.for` registry lookup, so the dynamic import() below stays intact.\n const declaredHelmet = (this.options.middlewares ?? []).some(\n (entry) => typeof entry === 'function' && Symbol.for('kick/http/helmet') in entry,\n )\n const autoHelmet = this.options.security?.helmet !== false && !declaredHelmet\n if (autoHelmet) {\n try {\n const { helmet: helmetFn } = await import('./middleware/helmet')\n this.runtime.useConnect(this.app, helmetFn())\n } catch {\n // helmet middleware not available — skip silently\n }\n }\n\n const userMiddlewares = this.options.middlewares\n if (userMiddlewares) {\n // User-declared pipeline — full control\n for (const entry of userMiddlewares) {\n this.mountMiddlewareEntry(entry)\n }\n } else {\n // Sensible defaults when no middleware declared\n this.runtime.useConnect(this.app, requestId())\n // Skip express.json on engines that parse bodies natively (Fastify, h3) —\n // otherwise the body stream is read twice and the engine's parser hangs.\n if (!this.runtime.capabilities.nativeBodyParsing) {\n const limit = this.options.jsonLimit ?? '1mb'\n // EXPRESS_JSON_TYPES, not the default: `express.json()` matches the\n // exact string `application/json`, so `application/merge-patch+json`\n // arrived unparsed while Fastify answered 415 and h3 parsed it (#590).\n this.runtime.useConnect(this.app, express.json({ limit, type: [...EXPRESS_JSON_TYPES] }))\n // Forms and text were parsed by h3 and rejected by Fastify; on Express\n // they reached the handler as `undefined`, because the engine parsed\n // only what its middleware list happened to cover.\n this.runtime.useConnect(this.app, express.urlencoded({ extended: true, limit }))\n this.runtime.useConnect(this.app, express.text({ limit, type: 'text/*' }))\n // After the parsers: Express leaves an unmatched type as `undefined`,\n // which is indistinguishable from \"no body sent\". This restores the\n // difference so an unsupported type is answered rather than ignored.\n this.runtime.useConnect(this.app, rejectUnsupportedBody())\n }\n }\n\n // ── 5. Adapter middleware: afterGlobal ────────────────────────────\n this.mountMiddlewareList(adapterMw.afterGlobal)\n\n // ── 6. Module registration + DI bootstrap ────────────────────────\n // Module collection — the static `modules: [...]` array AND the\n // imperative `setup(registry)` callback feed into the same\n // ordered list. Plugins go first so plugin modules / setup\n // calls run before user code (existing precedence).\n //\n // Order within the registry:\n // 1. plugin static modules (`plugin.modules?()`)\n // 2. plugin setup() calls (in plugin dependsOn-sorted order)\n // 3. user static modules (`options.modules`)\n // 4. user setup() callback\n const moduleRegistry = new MutableModuleRegistry()\n for (const plugin of this.plugins) {\n for (const m of plugin.modules?.() ?? []) moduleRegistry.mount(m)\n plugin.setup?.(moduleRegistry)\n }\n for (const m of this.options.modules) moduleRegistry.mount(m)\n this.options.setup?.(moduleRegistry)\n\n // The built-in health endpoints, as an ordinary module. Registered last so\n // an app's own `/health` module wins by being mounted first, and skipped\n // entirely with `health: false`.\n //\n // The probe is bound before `register()` runs below, because the module's\n // factory resolves it. It closes over the Application rather than exposing\n // it: `isDraining` and `checks` are the whole surface the endpoints need,\n // so a replacement module can satisfy the same token.\n if (this.options.health !== false) {\n this.container.registerInstance(HEALTH_PROBE, {\n isDraining: () => this._draining,\n checks: async () => {\n const withCheck = this.adapters.filter((a) => a.onHealthCheck)\n const settled = await Promise.allSettled(withCheck.map((a) => a.onHealthCheck!()))\n return settled.map((c, i) =>\n c.status === 'fulfilled'\n ? c.value\n : { name: withCheck[i].name ?? 'unknown', status: 'down' as const },\n )\n },\n })\n moduleRegistry.mount(healthModule(this.options.health === true ? {} : this.options.health))\n }\n const allModuleEntries: AppModuleEntry[] = moduleRegistry.entries\n const modules = allModuleEntries.map((entry) => {\n const mod: AppModule = toAppModule(entry)\n // `register()` is optional — modules whose classes are entirely\n // decorator-managed (@Service, @Controller, @Repository) don't need it.\n mod.register?.(this.container)\n return mod\n })\n this.container.bootstrap()\n\n // Register Logger as an injectable singleton so @Inject(Logger)\n // works in constructors. Without this, Logger is just a plain\n // class with static methods and no DI binding.\n if (!this.container.has(Logger)) {\n this.container.registerInstance(Logger, new Logger())\n }\n\n // ── 7. Adapter middleware: beforeRoutes ───────────────────────────\n this.mountMiddlewareList(adapterMw.beforeRoutes)\n\n // ── 8. Mount module routes with versioning ───────────────────────\n const apiPrefix = this.options.apiPrefix ?? '/api'\n const defaultVersion = this.options.defaultVersion ?? 1\n // Opt-in, default off. `logRouteTable` is the current name;\n // `logRoutesTable` is the deprecated alias.\n const shouldLogRoutes = this.options.logRouteTable ?? this.options.logRoutesTable ?? false\n\n // Collect route metadata during mounting (avoids calling mod.routes() twice)\n const mountedRoutes: Array<{ controller: any; mountPath: string }> = []\n\n // Context Contributors (#107) — collect adapter + plugin + global once;\n // per-module sources are computed inside the loop so module isolation is\n // preserved.\n //\n // Plugin contributors merge at the same `'adapter'` precedence as adapter\n // contributors. Plugins are conceptually \"bundles of adapters + extras\",\n // so a plugin that ships a typed contributor without standing up an\n // accompanying adapter behaves identically to one that does.\n const adapterSources: SourcedRegistration[] = []\n for (const adapter of this.adapters) {\n const adapterContribs = adapter.contributors?.() ?? []\n const adapterLabel = adapter.name ?? adapter.constructor.name ?? 'adapter'\n for (const registration of adapterContribs) {\n adapterSources.push({ source: 'adapter', registration, label: adapterLabel })\n }\n }\n for (const plugin of this.plugins) {\n const pluginContribs = plugin.contributors?.() ?? []\n const pluginLabel = plugin.name ?? plugin.constructor?.name ?? 'plugin'\n for (const registration of pluginContribs) {\n adapterSources.push({ source: 'adapter', registration, label: pluginLabel })\n }\n }\n const globalSources: SourcedRegistration[] = (this.options.contributors ?? []).map(\n (registration): SourcedRegistration => ({\n source: 'global',\n registration,\n label: 'bootstrap',\n }),\n )\n\n // Reset module-contributor snapshot at the start of each setup\n // pass so re-running `setup()` (test harnesses, dev-server\n // restarts) doesn't accumulate stale entries.\n this._moduleContributors = []\n\n // Duplicate-route guard (KICK006): one registry across every module in\n // this setup pass, so cross-module collisions fail at boot instead of\n // silently losing the dispatch race.\n const seenRoutes = new Map<string, string>()\n\n // Every path this app mounts, for the catch-all: a request that matches a\n // path but not its method is a 405, not a 404. Collected here because this\n // is the only place that knows the FULL mounted path — prefix, version and\n // controller route joined.\n const mountedPaths: MountedRoute[] = []\n\n for (const mod of modules) {\n // Prefer the declared `name` field (set by `defineModule({ name: 'X', ... })`)\n // over `constructor.name`. Factory-built modules are plain objects whose\n // constructor is `Object` — falling straight to `constructor.name` would\n // degrade every label in the DevTools Contributors table to \"Object\".\n const declaredName = (mod as { name?: unknown }).name\n const ctorName = mod.constructor?.name\n const moduleLabel =\n typeof declaredName === 'string' && declaredName.length > 0\n ? declaredName\n : ctorName && ctorName !== 'Object'\n ? ctorName\n : 'module'\n const moduleSources: SourcedRegistration[] = (mod.contributors?.() ?? []).map(\n (registration): SourcedRegistration => ({\n source: 'module',\n registration,\n label: moduleLabel,\n }),\n )\n // Retain registrations for `getContributors()` — these would\n // otherwise be unreachable post-bootstrap (module instances\n // are not kept on the Application).\n for (const src of moduleSources) {\n this._moduleContributors.push({\n registration: src.registration as { key: string; dependsOn: readonly string[] },\n label: moduleLabel,\n })\n }\n\n // Thread per-module + adapter + global sources to buildRoutes via the\n // module-scoped slot. Module setup is sequential, so the slot is\n // race-free. The slot MUST stay populated across both `mod.routes()`\n // AND any framework-driven `buildRoutes(controller)` calls below —\n // when a module returns `{ path, controller }` (auto-derive shape),\n // buildRoutes runs here, not inside routes(). Clearing the slot in\n // the routes()-finally would drop module/adapter/global contributors\n // on the floor and surface as `MissingContributorError` for any\n // class/method-level dependsOn referencing a module-level key.\n _setExternalContributorSources([...moduleSources, ...adapterSources, ...globalSources])\n try {\n const result = mod.routes()\n if (!result) continue // Non-HTTP modules (queues, cron) may return null\n\n const routeSets = Array.isArray(result) ? result : [result]\n\n for (const route of routeSets) {\n const version = route.version ?? defaultVersion\n // `prefix: false` mounts at the root — see ModuleRoutes.prefix.\n const mountPath = buildMountPath(\n route.prefix === false ? '' : apiPrefix,\n version,\n route.path,\n )\n // Common-case `{ path, controller }`: build the engine-neutral route\n // table and let the runtime materialize it natively (Express Router /\n // Fastify routes / …). Adopters who hand-build a `router` (composing\n // multiple controllers, third-party routers) pass it explicitly — that\n // is an Express-specific connect handler, mounted via `useConnect`.\n if (route.router) {\n this.runtime.useConnect(this.app, route.router, { path: mountPath })\n } else if (route.controller) {\n const routeTable = buildRouteTable(route.controller, { mountFlags: route.flags })\n const ctrl = route.controller.name ?? 'controller'\n for (const entry of routeTable) {\n // Per-handler owner so intra-controller duplicates name both methods.\n const owner = `${ctrl}.${String(entry.meta.handlerName ?? '?')}`\n const fullPath = joinPaths(mountPath, entry.path)\n assertRouteUnique(seenRoutes, entry.method, fullPath, owner)\n mountedPaths.push({ method: entry.method, path: fullPath })\n this.routePolicy.add(entry.method, fullPath, entry.meta.flags)\n }\n // Each handler runs inside the `kickjs:handler` tracing channel (a\n // no-op without subscribers), and carries its full pattern so\n // metrics and spans are keyed by `/api/v1/users/:id`, not `/:id`.\n // Copies, not edits: `routeTable` entries also feed the route policy\n // table and the duplicate-route check above.\n // oxlint-disable-next-line no-map-spread\n const traced = routeTable.map((entry) => {\n const pattern = joinPaths(mountPath, entry.path)\n return {\n ...entry,\n handler: traceHandler(entry.handler, {\n method: entry.method,\n route: pattern,\n controller: route.controller?.name,\n handler: entry.meta.handlerName,\n }),\n meta: { ...entry.meta, pattern },\n }\n })\n this.runtime.mountRoutes(this.app, [{ mountPath, routes: traced }])\n } else {\n throw moduleRouteMissingControllerError(mountPath)\n }\n\n // Notify adapters (e.g. SwaggerAdapter for OpenAPI spec generation)\n if (route.controller) {\n for (const adapter of this.adapters) {\n try {\n adapter.onRouteMount?.(route.controller, mountPath)\n } catch (err) {\n log.error(err, `adapter.onRouteMount() failed for ${mountPath}`)\n }\n }\n if (shouldLogRoutes) {\n mountedRoutes.push({ controller: route.controller, mountPath })\n }\n }\n }\n } finally {\n _setExternalContributorSources([])\n }\n }\n\n // ── 8b. Log route summary ─────────────────────────────────────────\n if (shouldLogRoutes && mountedRoutes.length > 0) {\n const methodOrder = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']\n const methodRank = (m: string) => {\n const i = methodOrder.indexOf(m)\n return i === -1 ? 99 : i\n }\n\n let totalRoutes = 0\n log.info('Routes:')\n\n for (const { controller, mountPath } of mountedRoutes) {\n const defs: RouteDefinition[] = getClassMeta<RouteDefinition[]>(\n METADATA.ROUTES,\n controller,\n [],\n )\n if (defs.length === 0) continue\n\n const counts: Record<string, number> = {}\n for (const def of defs) {\n const m = def.method.toUpperCase()\n counts[m] = (counts[m] || 0) + 1\n }\n totalRoutes += defs.length\n\n const methods = Object.entries(counts)\n .toSorted(([a], [b]) => methodRank(a) - methodRank(b))\n .map(([m, n]) => `${n} ${m}`)\n .join(', ')\n const name = controller.name || 'Controller'\n log.info(` ${name.padEnd(30)} ${mountPath.padEnd(25)} ${defs.length} routes (${methods})`)\n }\n\n log.info(` Total: ${totalRoutes} routes`)\n }\n\n // ── 9. Adapter middleware: afterRoutes ────────────────────────────\n this.mountMiddlewareList(adapterMw.afterRoutes)\n\n // ── 10. Adapter beforeStart hooks ────────────────────────────────\n // Runs BEFORE error handlers so adapters can still mount Express\n // routes (e.g. `McpAdapter` registers `/_mcp/messages` here). If\n // error handlers were registered first, the notFoundHandler — a\n // catch-all that never calls next() — would pre-empt anything an\n // adapter adds afterwards and adapter-mounted endpoints would 404.\n for (const adapter of this.adapters) {\n await this.callHook(adapter.beforeStart?.bind(adapter), ctx, adapter.name)\n }\n\n // ── 11. Error handlers ───────────────────────────────────────────\n // Last in the chain so any adapter-mounted route (step 10) gets\n // a chance to match before the catch-all 404 fires.\n // `onNotFound` / `onError` still win — the route table only sharpens the\n // DEFAULT, so an app that supplies its own catch-all is unaffected.\n // Routes are mounted: middleware that asked for the policy table can have\n // it now. Global middleware was mounted earlier — its closure reads the\n // table per request, so binding after the fact is in time.\n for (const entry of this.options.middlewares ?? []) {\n offerRoutePolicy(typeof entry === 'function' ? entry : entry.handler, this.routePolicy)\n }\n for (const adapter of this.adapters) {\n for (const mw of adapter.middleware?.() ?? []) offerRoutePolicy(mw.handler, this.routePolicy)\n }\n\n const builtInNotFound = notFoundHandler(mountedPaths)\n const customNotFound = this.options.onNotFound\n this.runtime.setNotFound(\n this.app,\n customNotFound\n ? (req: any, res: RuntimeResponse, next: (err?: unknown) => void) =>\n customNotFound(req, res, (err?: unknown) =>\n err === undefined ? builtInNotFound(req, res, next as () => void) : next(err),\n )\n : builtInNotFound,\n )\n this.runtime.setErrorHandler(this.app, observeErrors(this.options.onError ?? errorHandler()))\n }\n\n /** Register modules and DI without starting the HTTP server (used by kick tinker) */\n async registerOnly(): Promise<void> {\n await this.setup()\n }\n\n /**\n * Make the app ready to serve requests handed to it, with no server of its\n * own — the serverless path behind {@link createHandler}.\n *\n * Runs the same `setup()` as {@link start} and the plugins' `onReady`, but\n * nothing that needs a listening server: there is no port, no `afterStart`\n * (it receives the `http.Server`), and no process signal handlers.\n */\n async startWithoutServer(): Promise<void> {\n this.handlerMode = true\n await this.setup()\n\n const needServer = this.adapters.filter((adapter) => adapter.afterStart)\n if (needServer.length > 0) {\n log.warn(\n `No HTTP server in handler mode, so afterStart does not run for: ${needServer\n .map((adapter) => adapter.name)\n .join(\n ', ',\n )}. Anything they attach to the server (WebSockets, upgrade handlers) is unavailable.`,\n )\n }\n\n for (const plugin of this.plugins) {\n await plugin.onReady?.(this.container)\n }\n }\n\n /**\n * Start the HTTP server.\n *\n * In **dev mode** (Vite plugin active): reuses `globalThis.__kickjs_httpServer`\n * created by Vite. Adapters (WsAdapter, Socket.IO, etc.) receive the real\n * `http.Server` through `afterStart({ server })` — zero adapter changes needed.\n *\n * In **production**: creates its own `http.Server` and binds to the port.\n */\n async start(): Promise<void> {\n const g = globalThis as any\n const serverOptions = this.options.server\n\n // Check the server options before setup() so a bad combination fails\n // before any adapter has done work.\n if (serverOptions && g.__kickjs_httpServer) {\n log.warn('server options (tls / http2) are ignored in dev mode — Vite owns the server')\n } else if (serverOptions) {\n if (serverOptions.http2 && !serverOptions.tls) throw http2RequiresTlsError()\n if (serverOptions.http2 && !this.runtime.capabilities.http2) {\n throw http2UnsupportedRuntimeError(this.runtime.name)\n }\n if (serverOptions.tls) assertUsableTls(serverOptions.tls)\n }\n\n await this.setup()\n\n if (g.__kickjs_httpServer) {\n // ── DEV MODE: Vite owns the http.Server ──────────────────────\n // Don't create a new server or listen — Vite is already listening.\n // Just wire up adapters with the Vite-created server.\n this.httpServer = g.__kickjs_httpServer\n log.debug('Attached to Vite dev server')\n\n // Dev mode suppresses our own SIGINT/SIGTERM handlers (Vite owns\n // the process lifecycle), so a Ctrl+C would otherwise skip graceful\n // shutdown entirely — `adapter.shutdown()`, request draining, and\n // any shutdown logs never run. Expose `shutdown()` on globalThis so\n // the CLI dev server (`kick dev`) can drive it before tearing down\n // Vite. Same process, so the CLI's signal handler can reach this.\n g.__kickjs_app_shutdown = () => this.shutdown()\n\n for (const adapter of this.adapters) {\n const ctx = this.adapterCtx(this.httpServer!)\n await this.callHook(adapter.afterStart?.bind(adapter), ctx, adapter.name, 'afterStart')\n }\n\n for (const plugin of this.plugins) {\n await plugin.onReady?.(this.container)\n }\n\n return\n }\n\n // ── PRODUCTION: Create and own the http.Server ─────────────────\n const port = this.options.port ?? parseInt(process.env.PORT || '3000', 10)\n this.httpServer = this.createNodeServer()\n\n this.httpServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n log.error(\n `Port ${port} is already in use. Kill the existing process or use a different port:\\n` +\n ` PORT=${port + 1} kick dev\\n` +\n ` lsof -i :${port} # find what's using it\\n` +\n ` kill <PID> # stop it`,\n )\n process.exit(1)\n }\n throw err\n })\n\n // Wrap listen in a Promise so afterStart/onReady errors propagate\n await new Promise<void>((resolve, reject) => {\n this.httpServer!.listen(port, async () => {\n try {\n const scheme = serverOptions?.tls ? 'https' : 'http'\n const protocol = serverOptions?.http2 ? ' (HTTP/2)' : ''\n log.info(`Server running on ${scheme}://localhost:${port}${protocol}`)\n\n for (const adapter of this.adapters) {\n const afterCtx = this.adapterCtx(this.httpServer!)\n await this.callHook(\n adapter.afterStart?.bind(adapter),\n afterCtx,\n adapter.name,\n 'afterStart',\n )\n }\n\n // Plugin onReady hooks\n for (const plugin of this.plugins) {\n await plugin.onReady?.(this.container)\n }\n\n resolve()\n } catch (err) {\n reject(err)\n }\n })\n })\n }\n\n /** The production server for {@link ApplicationOptions.server}. */\n private createNodeServer(): KickServer {\n const handler = this.runtime.nodeHandler(this.app)\n const { tls, http2: useHttp2 } = this.options.server ?? {}\n if (useHttp2) {\n // The compat API hands the handler Http2ServerRequest/Response; runtimes\n // that declare `capabilities.http2` accept them.\n const server = http2.createSecureServer({ ...tls, allowHTTP1: true }, handler as never)\n server.on('session', (session) => {\n this.http2Sessions.add(session)\n session.once('close', () => this.http2Sessions.delete(session))\n })\n return server\n }\n if (tls) return https.createServer(tls, handler)\n return http.createServer(handler)\n }\n\n /** HMR rebuild: swap Express handler without restarting the server */\n async rebuild(): Promise<void> {\n // Build the new app fully before swapping — if setup() throws,\n // the old app keeps running so the server stays responsive.\n const prevApp = this.app\n const prevContainer = this.container\n // Snapshot the outgoing app's disposables (session/rate-limit store\n // intervals) BEFORE setup() registers the incoming app's. On success the\n // stale set is disposed — without this, every HMR swap leaked one live\n // interval + Map per store. On failure it's restored, and whatever the\n // failed setup() half-registered is disposed instead.\n const staleDisposables = drainDisposables()\n\n try {\n Container.reset()\n this.container = Container.getInstance()\n this.app = this.runtime.createApp({\n trustProxy: this.options.trustProxy,\n notFoundFallthrough: this.options.notFoundFallthrough,\n }) as Express\n await this.setup()\n } catch (err) {\n log.error(err, 'HMR rebuild failed, keeping previous app')\n // Restore previous state so the server stays responsive\n await runDisposables(drainDisposables())\n for (const d of staleDisposables) registerDisposable(d)\n this.app = prevApp\n this.container = prevContainer\n return\n }\n\n await runDisposables(staleDisposables)\n\n if (this.httpServer) {\n this.httpServer.removeAllListeners('request')\n this.httpServer.on('request', this.runtime.nodeHandler(this.app))\n log.debug('HMR: app rebuilt and swapped')\n }\n }\n\n /**\n * Graceful shutdown with request draining.\n *\n * 1. Stops accepting new connections (server.close())\n * 2. Waits for in-flight requests to complete (up to shutdownTimeout)\n * 3. Calls adapter.shutdown() for all registered adapters\n * 4. Force-closes after timeout\n *\n * Safe to call multiple times — subsequent calls return the same promise.\n */\n async shutdown(options: ShutdownOptions = {}): Promise<void> {\n // `closeServer: false` is the HMR reload path. In dev the HTTP server is\n // SHARED across rebuilds via `globalThis.__kickjs_httpServer`, so closing\n // it would kill the dev server on the first save — which is why the reload\n // path used to skip teardown entirely, leaking an adapter set per reload.\n // Everything below Step 2 is what a reload actually needs.\n const closeServer = options.closeServer ?? true\n // Prevent double-shutdown — return immediately if already initiated\n if (this._shutdownInitiated) {\n log.debug('Shutdown already in progress, skipping duplicate call')\n return\n }\n this._shutdownInitiated = true\n this._draining = true\n\n log.debug('Shutting down — draining in-flight requests...')\n\n const timeoutMs = this.options.shutdownTimeout ?? 30_000\n let timer: ReturnType<typeof setTimeout> | undefined\n\n // Start a force-exit timer if timeout is configured\n const forceExitPromise =\n timeoutMs > 0\n ? new Promise<'timeout'>((resolve) => {\n timer = setTimeout(() => resolve('timeout'), timeoutMs)\n timer.unref()\n })\n : new Promise<never>(() => {}) // never resolves — no forced exit\n\n try {\n // Step 1: Stop accepting new connections.\n // server.close() prevents new connections. Its callback fires only when\n // ALL existing connections are fully closed, so we do NOT await it here —\n // we track request draining separately via the tracking middleware.\n if (closeServer && this.httpServer) {\n this.httpServer.close(() => {})\n // GOAWAY on every open HTTP/2 session: streams in flight finish,\n // new ones are refused.\n for (const session of this.http2Sessions) session.close()\n }\n\n // Step 2: Wait for in-flight requests to drain (or timeout).\n // Skipped on a reload: the server keeps listening and the fresh app\n // takes over, so there is nothing to drain toward — waiting would just\n // stall the rebuild for up to `shutdownTimeout`.\n if (closeServer && this._inFlightRequests > 0) {\n log.debug(`Waiting for ${this._inFlightRequests} in-flight request(s) to complete...`)\n const drainPromise = new Promise<'drained'>((resolve) => {\n this._drainResolvers.push(() => resolve('drained'))\n })\n\n const result = await Promise.race([drainPromise, forceExitPromise])\n if (result === 'timeout') {\n log.warn(\n `Shutdown timeout (${timeoutMs}ms) reached with ${this._inFlightRequests} request(s) still in-flight, forcing shutdown`,\n )\n for (const session of this.http2Sessions) session.destroy()\n } else {\n log.debug('All in-flight requests completed')\n }\n }\n\n // Step 2b: Wait for work handed to `waitUntil()` — it outlives its\n // response, so draining requests alone doesn't cover it. Same budget\n // as the drain; skipped on a reload, where the process keeps running.\n if (closeServer && pendingBackgroundWork() > 0) {\n log.debug(`Waiting for ${pendingBackgroundWork()} waitUntil() task(s) to settle...`)\n const settled = await Promise.race([\n settleBackgroundWork().then(() => 'settled' as const),\n forceExitPromise,\n ])\n if (settled === 'timeout') {\n log.warn(\n `Shutdown timeout (${timeoutMs}ms) reached with ${pendingBackgroundWork()} waitUntil() task(s) still running`,\n )\n }\n }\n\n // Step 3: Run all plugin + adapter shutdowns concurrently.\n //\n // Each hook is time-boxed INDIVIDUALLY. An unbounded `allSettled` here\n // means one hook that never settles wedges the whole shutdown, and on\n // the reload path that leaves the dev server with no app and no error —\n // it just stops rebuilding. Seen with a socket.io adapter calling\n // `io.close(cb)`: that callback fires only once every client\n // disconnects, so a single open browser tab hung every save forever.\n //\n // A reload gets a much shorter budget than a real shutdown — nobody\n // wants to wait `shutdownTimeout` (30s by default) per keystroke-save.\n // `shutdownTimeout: 0` means \"no forced exit\" for the drain above, so it\n // disables the per-hook budget too. Anything else would invert the\n // setting: `Math.min(0, 5_000)` is a ZERO-ms budget, which fires before\n // any hook can settle and silently skips every cleanup there is.\n const hookTimeoutMs = closeServer ? timeoutMs : Math.min(timeoutMs, 5_000)\n const timeBoxed = (label: string, run: () => unknown): Promise<unknown> => {\n // Invoke inside the promise: a hook that throws SYNCHRONOUSLY would\n // otherwise escape at the argument site, before `allSettled` wraps it,\n // taking down the whole teardown instead of just its own entry.\n const hook = (async () => run())()\n if (hookTimeoutMs <= 0) return hook\n return new Promise<unknown>((resolve, reject) => {\n const t = setTimeout(() => {\n log.warn(\n `${label} did not finish shutting down within ${hookTimeoutMs}ms — continuing without it. ` +\n `Its resources may still be held.`,\n )\n resolve(undefined)\n }, hookTimeoutMs)\n t.unref()\n // Clear on BOTH paths. `Promise.race` alone leaves the timer armed,\n // so a hook that finished promptly still logged a timeout warning\n // once the budget elapsed — on the reload path that is a false\n // accusation after every save.\n hook.then(\n (value) => {\n clearTimeout(t)\n resolve(value)\n },\n (err) => {\n clearTimeout(t)\n reject(err)\n },\n )\n })\n }\n\n const wasListening = this.httpServer?.listening === true\n const results = await Promise.allSettled([\n ...this.plugins.map((plugin) =>\n timeBoxed(`Plugin '${plugin.name}'`, () => plugin.shutdown?.()),\n ),\n ...this.adapters.map((adapter) =>\n timeBoxed(`Adapter '${adapter.name}'`, () => adapter.shutdown?.()),\n ),\n ])\n\n // A reload must leave the shared dev server listening. If a hook closed\n // it, every later request is ECONNREFUSED and the port never comes back\n // — with nothing in the log to say why. socket.io's `close()` does\n // exactly this: it closes the HTTP server it was constructed with.\n if (!closeServer && wasListening && this.httpServer?.listening === false) {\n log.error(\n 'An adapter or plugin closed the shared HTTP server during an HMR reload. ' +\n 'The dev server is now unreachable and will not rebind until you restart it. ' +\n \"Close only what you own: socket.io's `io.close()` closes the HTTP server passed \" +\n 'to its constructor — on a reload, disconnect the sockets instead.',\n )\n }\n for (const result of results) {\n if (result.status === 'rejected') {\n log.error({ err: result.reason }, 'Adapter shutdown failed')\n }\n }\n\n // Step 4: Release framework-owned resources started outside the\n // adapter lifecycle — in-memory session/rate-limit store intervals —\n // and flush any pending container change batch so its debounce timer\n // doesn't outlive the app.\n await disposeAll()\n this.container.flushChanges()\n await this.loopback?.close()\n this.loopback = undefined\n // The adapters are shut down: stop routing reports to them. A reload\n // keeps the same adapters, so it keeps them observing.\n if (closeServer) releaseObservers(this.observers)\n } finally {\n if (timer) clearTimeout(timer)\n }\n }\n\n /**\n * The engine-native app instance, typed from the active runtime\n * ({@link ActiveRuntime} — Express by default). Prefer this over\n * {@link getExpressApp}; under a non-Express runtime the type follows the\n * augmented registry.\n */\n getRuntimeApp(): ActiveRuntime['app'] {\n return this.app\n }\n\n /**\n * The active HTTP runtime's identity + capabilities — `name` is `'express'`\n * (default), `'fastify'`, or `'h3'`; `capabilities` reports what the engine\n * supports (render / uploads / connectMiddleware / nativeBodyParsing).\n * Surfaced by DevTools and the VS Code extension so tooling can show which\n * engine the app is running on. Cheap, side-effect free.\n */\n getActiveRuntime(): { name: string; capabilities: RuntimeCapabilities } {\n return { name: this.runtime.name, capabilities: this.runtime.capabilities }\n }\n\n /** @deprecated Use {@link getRuntimeApp}. Returns the engine-native app. */\n getExpressApp(): Express {\n return this.app\n }\n\n /** Get registered adapters — used by DevToolsAdapter for peer discovery */\n getAdapters(): AppAdapter[] {\n return this.adapters\n }\n\n /**\n * Get registered plugins — used by DevToolsAdapter for topology\n * introspection (architecture.md §23). Read-only; returns the\n * already-sorted plugin list (post-`dependsOn` topo-sort).\n */\n getPlugins(): readonly KickPlugin[] {\n return this.plugins\n }\n\n /**\n * Snapshot of every Context Contributor reachable from this app —\n * walks **four** registration sites: adapters, plugins, modules,\n * and the bootstrap `contributors` option. The devtools Topology\n * tab consumes this for the Contributors panel; tests + adopters\n * can call it for diagnostics.\n *\n * Per-route (method/class-decorator) registrations are NOT included\n * here — they live on the route registry rather than on any of\n * the four collection sites this method walks. A future RPC will\n * surface them separately.\n *\n * Result shape stays minimal — `{ key, source, label, dependsOn }`\n * — so we don't leak `resolve` closures or other internal fields.\n * `source` matches the five-level precedence union from\n * `ContributorSource` (minus `'method'` / `'class'` which require\n * the per-route walk noted above).\n */\n getContributors(): ReadonlyArray<{\n key: string\n source: 'module' | 'adapter' | 'plugin' | 'global'\n label: string\n dependsOn: readonly string[]\n }> {\n const out: Array<{\n key: string\n source: 'module' | 'adapter' | 'plugin' | 'global'\n label: string\n dependsOn: readonly string[]\n }> = []\n const ingest = (\n list: ReadonlyArray<unknown> | null | undefined,\n source: 'module' | 'adapter' | 'plugin' | 'global',\n label: string,\n ): void => {\n if (!list) return\n for (const reg of list) {\n const r = reg as { key?: unknown; dependsOn?: unknown }\n if (r.key === undefined || r.key === null) continue\n // `key` is typically a string but createToken<T>() yields a\n // branded token object — normalise via tokenName so both\n // surfaces flow through.\n const key = typeof r.key === 'string' ? r.key : tokenName(r.key)\n out.push({\n key,\n source,\n label,\n dependsOn: Array.isArray(r.dependsOn) ? (r.dependsOn as string[]) : [],\n })\n }\n }\n for (const adapter of this.adapters) {\n ingest(adapter.contributors?.(), 'adapter', adapter.name ?? 'adapter')\n }\n for (const plugin of this.plugins) {\n ingest(plugin.contributors?.(), 'plugin', plugin.name ?? 'plugin')\n }\n // Module-level registrations — captured during `setup()` because\n // module instances are not retained on the Application. Walk the\n // snapshot rather than calling `mod.contributors()` here.\n for (const { registration, label } of this._moduleContributors) {\n ingest([registration], 'module', label)\n }\n ingest(this.options.contributors, 'global', 'bootstrap')\n return out\n }\n\n getHttpServer(): KickServer | null {\n return this.httpServer\n }\n\n // ── Internal helpers ────────────────────────────────────────────────\n\n /**\n * Decide whether {@link Application} should auto-mount the\n * {@link requestScopeMiddleware} ALS wrapper.\n *\n * Returns `false` when:\n * - `contextStore: 'manual'` was set (caller manages ALS frames), or\n * - the user-supplied `middleware` list already includes a\n * `requestScopeMiddleware()` (detected via the symbol marker stamped\n * in `middleware/request-scope.ts`).\n *\n * Otherwise `true` — preserves the historical default of \"always wrap\n * requests in an ALS frame\" so existing apps see no behavior change.\n */\n private shouldAutoMountRequestScope(): boolean {\n if (this.options.contextStore === 'manual') return false\n\n const userEntries = this.options.middlewares ?? []\n for (const entry of userEntries) {\n const handler = typeof entry === 'function' ? entry : entry.handler\n if (isRequestScopeMiddleware(handler)) return false\n }\n return true\n }\n\n private collectAdapterMiddleware() {\n const result = {\n beforeGlobal: [] as AdapterMiddleware[],\n afterGlobal: [] as AdapterMiddleware[],\n beforeRoutes: [] as AdapterMiddleware[],\n afterRoutes: [] as AdapterMiddleware[],\n }\n\n for (const adapter of this.adapters) {\n const entries = adapter.middleware?.() ?? []\n for (const entry of entries) {\n const phase = entry.phase ?? 'afterGlobal'\n result[phase].push(entry)\n }\n }\n\n return result\n }\n\n private mountMiddlewareList(entries: AdapterMiddleware[]): void {\n for (const entry of entries) {\n if (entry.path === undefined) {\n this.runtime.useConnect(this.app, entry.handler)\n continue\n }\n this.runtime.useConnect(this.app, entry.handler, { path: entry.path })\n }\n }\n\n private mountMiddlewareEntry(entry: MiddlewareEntry): void {\n if (typeof entry === 'function') {\n this.runtime.useConnect(this.app, entry)\n } else {\n this.runtime.useConnect(this.app, entry.handler, { path: entry.path })\n }\n }\n\n /**\n * `GET /_kick/cron/:scheduleId` — how a serverless platform\n * (Vercel crons) runs `@Cron` jobs: `kick build:vercel` emits one cron entry\n * per schedule pointing here. Mounted only in handler mode and only when\n * `CRON_SECRET` is set; the request must carry `Authorization: Bearer\n * $CRON_SECRET` (Vercel sends it). Runs every job on that schedule.\n */\n private mountCronTrigger(): void {\n const secret = process.env.CRON_SECRET\n if (!secret) return\n // Fixed and outside `apiPrefix` (like /health): `kick build:vercel` writes\n // the exact path, and can't see the app's `apiPrefix`.\n const path = '/_kick/cron/:scheduleId'\n this.adapterHttp().route('GET', path, async (ctx) => {\n if (!matchesBearer(ctx.headers.authorization, secret)) {\n throw new HttpException(401, 'Missing or invalid CRON_SECRET bearer token')\n }\n const result = await runCronJobs(\n this.container,\n { scheduleId: ctx.params.scheduleId },\n 'http',\n )\n ctx.json(result, result.failed > 0 ? 500 : 200)\n })\n }\n\n /** Middleware that tracks in-flight requests for graceful draining */\n private requestTrackingMiddleware(): RequestHandler {\n return (req, res, next) => {\n this._inFlightRequests++\n // Timed only while something observes responses — checked per request,\n // since a diagnostics channel can gain a subscriber at any time.\n const started = hasResponseObservers() ? performance.now() : undefined\n // `finish` means the response went out; a bare `close` means the client\n // gave up first. Only the first is a response to report — on an abort\n // `res.statusCode` still holds its default (200) and would read as a\n // success. Both still leave the in-flight count.\n const onFinish = () => settle(true)\n const onClose = () => settle(false)\n const settle = (finished: boolean) => {\n res.removeListener('finish', onFinish)\n res.removeListener('close', onClose)\n if (finished && started !== undefined) {\n const matched = (req as unknown as Record<symbol, MatchedRoute | undefined>)[ROUTE_SLOT]\n reportResponse({\n method: req.method,\n path: requestPath(req),\n route: matched?.pattern,\n status: res.statusCode,\n durationMs: performance.now() - started,\n requestId: requestIdOf(req),\n })\n }\n this._inFlightRequests--\n // If draining and no more in-flight requests, resolve all waiters\n if (this._draining && this._inFlightRequests === 0) {\n for (const resolve of this._drainResolvers) {\n resolve()\n }\n this._drainResolvers = []\n }\n }\n res.on('finish', onFinish)\n res.on('close', onClose)\n next()\n }\n }\n\n /** Mount /health/live and /health/ready endpoints at the root (no API prefix) */\n}\n\n/** The request path without the query string, on every engine. */\nfunction requestPath(req: { originalUrl?: string; url?: string }): string {\n return (req.originalUrl ?? req.url ?? '').split('?')[0]\n}\n\nfunction requestIdOf(req: {\n requestId?: string\n headers?: Record<string, unknown>\n}): string | undefined {\n const header = req.headers?.['x-request-id']\n return req.requestId ?? (typeof header === 'string' ? header : undefined)\n}\n\n/** The status an error will be answered with, as the default handler decides it. */\nfunction errorStatus(err: any): number {\n if (typeof err?.problem?.status === 'number') return err.problem.status\n if (typeof err?.status === 'number') return err.status\n if (typeof err?.statusCode === 'number') return err.statusCode\n if (err?.name === 'ZodError') return 422\n return 500\n}\n\n/**\n * Report every request error to the observers, then hand it to the real error\n * handler (the default one or `bootstrap({ onError })`) unchanged.\n */\nfunction observeErrors<H extends (err: any, req: any, res: any, next: any) => unknown>(\n handler: H,\n): H {\n return ((err: any, req: any, res: any, next: any) => {\n const matched = req?.[ROUTE_SLOT] as MatchedRoute | undefined\n reportError(err, {\n source: 'request',\n requestId: requestIdOf(req ?? {}),\n method: req?.method,\n path: requestPath(req ?? {}),\n route: matched?.pattern,\n status: errorStatus(err),\n })\n return handler(err, req, res, next)\n }) as H\n}\n\n/** Constant-time check of an `Authorization: Bearer <secret>` header. */\nfunction matchesBearer(header: unknown, secret: string): boolean {\n if (typeof header !== 'string') return false\n const given = Buffer.from(header)\n const expected = Buffer.from(`Bearer ${secret}`)\n return given.length === expected.length && timingSafeEqual(given, expected)\n}\n"],"mappings":"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"}