@forinda/kickjs 6.4.0 → 6.5.1

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 (218) hide show
  1. package/dist/{adapter-BjxEv8oW.d.mts → adapter-CbMq-NBf.d.mts} +95 -341
  2. package/dist/adapter-CbMq-NBf.d.mts.map +1 -0
  3. package/dist/{app-module-BwYtACD6.d.mts → app-module-DtoySG6l.d.mts} +153 -31
  4. package/dist/app-module-DtoySG6l.d.mts.map +1 -0
  5. package/dist/application-Cl0JlmNY.mjs +12 -0
  6. package/dist/application-Cl0JlmNY.mjs.map +1 -0
  7. package/dist/{application-Bo3jSfmL.d.mts → application-zlzYGgKD.d.mts} +6 -6
  8. package/dist/application-zlzYGgKD.d.mts.map +1 -0
  9. package/dist/{bootstrap-DW2yzKh6.mjs → bootstrap-DzyWLYpU.mjs} +3 -3
  10. package/dist/{bootstrap-DW2yzKh6.mjs.map → bootstrap-DzyWLYpU.mjs.map} +1 -1
  11. package/dist/{bootstrap-DUUxZXvF.d.mts → bootstrap-Nzgi7K2d.d.mts} +2 -3
  12. package/dist/bootstrap-Nzgi7K2d.d.mts.map +1 -0
  13. package/dist/{cache-CwZzIH0D.d.mts → cache-C0qL0NSD.d.mts} +2 -1
  14. package/dist/cache-C0qL0NSD.d.mts.map +1 -0
  15. package/dist/config/index.d.mts +1 -1
  16. package/dist/config/index.mjs +2 -2
  17. package/dist/{config-BTDTXaeZ.mjs → config-CwFPFwsN.mjs} +3 -3
  18. package/dist/{config-BTDTXaeZ.mjs.map → config-CwFPFwsN.mjs.map} +1 -1
  19. package/dist/{container-iED1swkd.d.mts → container-BW_MlgLj.d.mts} +3 -2
  20. package/dist/container-BW_MlgLj.d.mts.map +1 -0
  21. package/dist/{container-Da9BLMHy.mjs → container-C7kmdVwh.mjs} +3 -3
  22. package/dist/{container-Da9BLMHy.mjs.map → container-C7kmdVwh.mjs.map} +1 -1
  23. package/dist/context-DQaUwwQM.mjs +12 -0
  24. package/dist/context-DQaUwwQM.mjs.map +1 -0
  25. package/dist/context-errors-97x9siRx.mjs +13 -0
  26. package/dist/context-errors-97x9siRx.mjs.map +1 -0
  27. package/dist/core/adapter.d.mts +1 -1
  28. package/dist/core/adapter.mjs +1 -1
  29. package/dist/core/app-module.d.mts +1 -1
  30. package/dist/core/app-module.mjs +1 -1
  31. package/dist/core/cache.d.mts +1 -1
  32. package/dist/core/cache.mjs +1 -1
  33. package/dist/core/container.d.mts +1 -1
  34. package/dist/core/container.mjs +2 -2
  35. package/dist/core/cron.mjs +2 -2
  36. package/dist/core/decorators.d.mts +1 -1
  37. package/dist/core/decorators.mjs +2 -2
  38. package/dist/core/errors.d.mts +1 -1
  39. package/dist/core/errors.mjs +1 -1
  40. package/dist/core/interfaces.d.mts +1 -1
  41. package/dist/core/interfaces.mjs +1 -1
  42. package/dist/core/logger.d.mts +2 -2
  43. package/dist/core/logger.mjs +2 -2
  44. package/dist/core/logger.mjs.map +1 -1
  45. package/dist/core/path.mjs +1 -1
  46. package/dist/core/plugin.d.mts +1 -1
  47. package/dist/core/plugin.mjs +1 -1
  48. package/dist/core/reactivity.d.mts +1 -1
  49. package/dist/core/reactivity.mjs +1 -1
  50. package/dist/core-CLmwVWlw.mjs +12 -0
  51. package/dist/core-CLmwVWlw.mjs.map +1 -0
  52. package/dist/{cors-C4aSCD3J.d.mts → cors-BuDU9CpA.d.mts} +1 -2
  53. package/dist/cors-BuDU9CpA.d.mts.map +1 -0
  54. package/dist/cron-DOOtTY4q.d.mts.map +1 -1
  55. package/dist/{csrf-Dlo-P8Il.mjs → csrf-BsrsObK-.mjs} +2 -2
  56. package/dist/{csrf-Dlo-P8Il.mjs.map → csrf-BsrsObK-.mjs.map} +1 -1
  57. package/dist/{csrf-7nUXGTuz.d.mts → csrf-vR23CJas.d.mts} +1 -2
  58. package/dist/csrf-vR23CJas.d.mts.map +1 -0
  59. package/dist/{decorators-wEJBqXHg.d.mts → decorators-Bks7cgBJ.d.mts} +12 -7
  60. package/dist/decorators-Bks7cgBJ.d.mts.map +1 -0
  61. package/dist/{disposables-rPSyh69r.mjs → disposables-D46xX96x.mjs} +2 -2
  62. package/dist/{disposables-rPSyh69r.mjs.map → disposables-D46xX96x.mjs.map} +1 -1
  63. package/dist/env-Biq_60jq.mjs +13 -0
  64. package/dist/{env-Ce-aYRxQ.mjs.map → env-Biq_60jq.mjs.map} +1 -1
  65. package/dist/{error-handler-3ih4E-QZ.d.mts → error-handler-Cr6Yt9as.d.mts} +5 -2
  66. package/dist/error-handler-Cr6Yt9as.d.mts.map +1 -0
  67. package/dist/{errors-n0H96eEz.d.mts → errors-By6UueCm.d.mts} +18 -9
  68. package/dist/errors-By6UueCm.d.mts.map +1 -0
  69. package/dist/handler-Ci1NMv-9.mjs +13 -0
  70. package/dist/handler-Ci1NMv-9.mjs.map +1 -0
  71. package/dist/{helmet-CKzZzeNl.d.mts → helmet-CFQUTVig.d.mts} +1 -2
  72. package/dist/helmet-CFQUTVig.d.mts.map +1 -0
  73. package/dist/http/application.d.mts +1 -1
  74. package/dist/http/application.mjs +2 -2
  75. package/dist/http/bootstrap.d.mts +1 -1
  76. package/dist/http/bootstrap.mjs +2 -2
  77. package/dist/http/context.d.mts +2 -2
  78. package/dist/http/context.mjs +2 -2
  79. package/dist/http/middleware/cors.d.mts +1 -1
  80. package/dist/http/middleware/cors.mjs +1 -1
  81. package/dist/http/middleware/csrf.d.mts +1 -1
  82. package/dist/http/middleware/csrf.mjs +2 -2
  83. package/dist/http/middleware/error-handler.d.mts +1 -1
  84. package/dist/http/middleware/error-handler.mjs +3 -2
  85. package/dist/http/middleware/error-handler.mjs.map +1 -1
  86. package/dist/http/middleware/helmet.d.mts +1 -1
  87. package/dist/http/middleware/helmet.mjs +1 -1
  88. package/dist/http/middleware/rate-limit.d.mts +1 -1
  89. package/dist/http/middleware/rate-limit.mjs +2 -2
  90. package/dist/http/middleware/request-id.d.mts +1 -1
  91. package/dist/http/middleware/request-id.mjs +1 -1
  92. package/dist/http/middleware/request-logger.d.mts +1 -1
  93. package/dist/http/middleware/request-logger.mjs +2 -2
  94. package/dist/http/middleware/session.d.mts +1 -1
  95. package/dist/http/middleware/session.mjs +2 -2
  96. package/dist/http/middleware/spa.d.mts +1 -1
  97. package/dist/http/middleware/spa.mjs +2 -2
  98. package/dist/http/middleware/spa.mjs.map +1 -1
  99. package/dist/http/middleware/upload.d.mts +1 -1
  100. package/dist/http/middleware/upload.mjs +2 -2
  101. package/dist/http/middleware/upload.mjs.map +1 -1
  102. package/dist/http/middleware/validate.d.mts +1 -1
  103. package/dist/http/middleware/validate.mjs +1 -1
  104. package/dist/http/middleware/views.d.mts +1 -1
  105. package/dist/http/middleware/views.mjs +2 -3
  106. package/dist/http/query/index.d.mts +1 -1
  107. package/dist/http/query/index.mjs +2 -2
  108. package/dist/http/router-builder.d.mts +1 -1
  109. package/dist/http/router-builder.mjs +2 -3
  110. package/dist/http/runtimes/fastify.d.mts +1 -2
  111. package/dist/http/runtimes/fastify.d.mts.map +1 -1
  112. package/dist/http/runtimes/fastify.mjs +3 -3
  113. package/dist/http/runtimes/fastify.mjs.map +1 -1
  114. package/dist/http/runtimes/h3-web.d.mts +1 -2
  115. package/dist/http/runtimes/h3-web.d.mts.map +1 -1
  116. package/dist/http/runtimes/h3-web.mjs +2 -2
  117. package/dist/http/runtimes/h3-web.mjs.map +1 -1
  118. package/dist/http/runtimes/h3.d.mts +1 -2
  119. package/dist/http/runtimes/h3.d.mts.map +1 -1
  120. package/dist/http/runtimes/h3.mjs +2 -2
  121. package/dist/{index-BtWJ9SLP.d.mts → index-BfT5KZ-M.d.mts} +2 -2
  122. package/dist/index-BfT5KZ-M.d.mts.map +1 -0
  123. package/dist/{index-D98NLfS5.d.mts → index-M6TF75MS.d.mts} +2 -3
  124. package/dist/index-M6TF75MS.d.mts.map +1 -0
  125. package/dist/index.d.mts +28 -28
  126. package/dist/index.mjs +2 -2
  127. package/dist/index.mjs.map +1 -1
  128. package/dist/{interfaces-CayXPOAf.d.mts → interfaces-BTTBdkvG.d.mts} +3 -2
  129. package/dist/interfaces-BTTBdkvG.d.mts.map +1 -0
  130. package/dist/{kv-stores-CAYc5VKm.mjs → kv-stores-BLJdf3Fn.mjs} +2 -2
  131. package/dist/{kv-stores-CAYc5VKm.mjs.map → kv-stores-BLJdf3Fn.mjs.map} +1 -1
  132. package/dist/{kv-stores-gQrfH0KQ.d.mts → kv-stores-CHowq8VP.d.mts} +5 -6
  133. package/dist/kv-stores-CHowq8VP.d.mts.map +1 -0
  134. package/dist/{logger-COlEkn_C.d.mts → logger-DA43DvNY.d.mts} +18 -3
  135. package/dist/logger-DA43DvNY.d.mts.map +1 -0
  136. package/dist/{metadata-BhKXlN2F.mjs → metadata-DUBbuHqY.mjs} +2 -2
  137. package/dist/{metadata-BhKXlN2F.mjs.map → metadata-DUBbuHqY.mjs.map} +1 -1
  138. package/dist/{module-registry-CvP9mee-.mjs → module-registry-aIt66if2.mjs} +2 -2
  139. package/dist/{module-registry-CvP9mee-.mjs.map → module-registry-aIt66if2.mjs.map} +1 -1
  140. package/dist/path-BVA9MfN1.d.mts.map +1 -1
  141. package/dist/query-CXN52dzy.mjs +12 -0
  142. package/dist/{query-Cs_Llg77.mjs.map → query-CXN52dzy.mjs.map} +1 -1
  143. package/dist/{rate-limit-oLL5EPUf.d.mts → rate-limit-Dme747JJ.d.mts} +1 -2
  144. package/dist/rate-limit-Dme747JJ.d.mts.map +1 -0
  145. package/dist/{reactivity-XL0fFDvG.d.mts → reactivity-BkMNA0kp.d.mts} +2 -2
  146. package/dist/reactivity-BkMNA0kp.d.mts.map +1 -0
  147. package/dist/{reply-edT8RGBB.mjs → reply-C1ljl6xS.mjs} +3 -3
  148. package/dist/{reply-edT8RGBB.mjs.map → reply-C1ljl6xS.mjs.map} +1 -1
  149. package/dist/{request-id-IDobbxU-.d.mts → request-id-CVzJ1uHD.d.mts} +1 -2
  150. package/dist/request-id-CVzJ1uHD.d.mts.map +1 -0
  151. package/dist/{request-logger-DZVW6VNI.d.mts → request-logger-BU4ulKLp.d.mts} +1 -2
  152. package/dist/request-logger-BU4ulKLp.d.mts.map +1 -0
  153. package/dist/router-builder-BbgVXuy3.mjs +12 -0
  154. package/dist/router-builder-BbgVXuy3.mjs.map +1 -0
  155. package/dist/{router-builder-CiS2ASTJ.d.mts → router-builder-CMGhEBU2.d.mts} +2 -3
  156. package/dist/router-builder-CMGhEBU2.d.mts.map +1 -0
  157. package/dist/{session-DSRA-PUX.d.mts → session-z9iewOXA.d.mts} +1 -2
  158. package/dist/session-z9iewOXA.d.mts.map +1 -0
  159. package/dist/{spa-Dp9onKJt.d.mts → spa-DsRUrm69.d.mts} +4 -9
  160. package/dist/spa-DsRUrm69.d.mts.map +1 -0
  161. package/dist/{upload-CIZc803m.d.mts → upload-XVTyQcNN.d.mts} +3 -3
  162. package/dist/upload-XVTyQcNN.d.mts.map +1 -0
  163. package/dist/{upload-config-C3MSF7RF.mjs → upload-config-Dd_QvlpC.mjs} +2 -2
  164. package/dist/{upload-config-C3MSF7RF.mjs.map → upload-config-Dd_QvlpC.mjs.map} +1 -1
  165. package/dist/{validate-DoZDOhCi.d.mts → validate-DMYpUnrf.d.mts} +1 -2
  166. package/dist/validate-DMYpUnrf.d.mts.map +1 -0
  167. package/dist/views-Dj6XFaca.mjs +12 -0
  168. package/dist/views-Dj6XFaca.mjs.map +1 -0
  169. package/dist/views-DsfBT1fR.d.mts +659 -0
  170. package/dist/views-DsfBT1fR.d.mts.map +1 -0
  171. package/dist/web.d.mts +4 -5
  172. package/dist/web.d.mts.map +1 -1
  173. package/dist/web.mjs +2 -2
  174. package/dist/web.mjs.map +1 -1
  175. package/package.json +10 -10
  176. package/dist/adapter-BjxEv8oW.d.mts.map +0 -1
  177. package/dist/app-module-BwYtACD6.d.mts.map +0 -1
  178. package/dist/application-Bo3jSfmL.d.mts.map +0 -1
  179. package/dist/application-TiHVcGRL.mjs +0 -12
  180. package/dist/application-TiHVcGRL.mjs.map +0 -1
  181. package/dist/bootstrap-DUUxZXvF.d.mts.map +0 -1
  182. package/dist/cache-CwZzIH0D.d.mts.map +0 -1
  183. package/dist/container-iED1swkd.d.mts.map +0 -1
  184. package/dist/context-Cs0939u1.mjs +0 -12
  185. package/dist/context-Cs0939u1.mjs.map +0 -1
  186. package/dist/contributor-runner-B3Z3Q7J5.mjs +0 -13
  187. package/dist/contributor-runner-B3Z3Q7J5.mjs.map +0 -1
  188. package/dist/core-CQ6u5kOq.mjs +0 -12
  189. package/dist/core-CQ6u5kOq.mjs.map +0 -1
  190. package/dist/cors-C4aSCD3J.d.mts.map +0 -1
  191. package/dist/csrf-7nUXGTuz.d.mts.map +0 -1
  192. package/dist/decorators-wEJBqXHg.d.mts.map +0 -1
  193. package/dist/env-Ce-aYRxQ.mjs +0 -13
  194. package/dist/error-handler-3ih4E-QZ.d.mts.map +0 -1
  195. package/dist/errors-n0H96eEz.d.mts.map +0 -1
  196. package/dist/handler-D8Zyie-B.mjs +0 -12
  197. package/dist/handler-D8Zyie-B.mjs.map +0 -1
  198. package/dist/helmet-CKzZzeNl.d.mts.map +0 -1
  199. package/dist/http/middleware/views.mjs.map +0 -1
  200. package/dist/http/router-builder.mjs.map +0 -1
  201. package/dist/index-BtWJ9SLP.d.mts.map +0 -1
  202. package/dist/index-D98NLfS5.d.mts.map +0 -1
  203. package/dist/interfaces-CayXPOAf.d.mts.map +0 -1
  204. package/dist/kv-stores-gQrfH0KQ.d.mts.map +0 -1
  205. package/dist/logger-COlEkn_C.d.mts.map +0 -1
  206. package/dist/query-Cs_Llg77.mjs +0 -12
  207. package/dist/rate-limit-oLL5EPUf.d.mts.map +0 -1
  208. package/dist/reactivity-XL0fFDvG.d.mts.map +0 -1
  209. package/dist/request-id-IDobbxU-.d.mts.map +0 -1
  210. package/dist/request-logger-DZVW6VNI.d.mts.map +0 -1
  211. package/dist/rolldown-runtime-Bzotz84Y.mjs +0 -11
  212. package/dist/router-builder-CiS2ASTJ.d.mts.map +0 -1
  213. package/dist/session-DSRA-PUX.d.mts.map +0 -1
  214. package/dist/spa-Dp9onKJt.d.mts.map +0 -1
  215. package/dist/upload-CIZc803m.d.mts.map +0 -1
  216. package/dist/validate-DoZDOhCi.d.mts.map +0 -1
  217. package/dist/views-DjgUXRdu.d.mts +0 -275
  218. package/dist/views-DjgUXRdu.d.mts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.4.0
2
+ * @forinda/kickjs v6.5.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{t as __require}from"../../rolldown-runtime-Bzotz84Y.mjs";import{Logger}from"../../core/logger.mjs";import"../../core-CQ6u5kOq.mjs";import{existsSync,readFileSync}from"node:fs";import{join,resolve}from"node:path";const log=Logger.for(`SpaAdapter`);var SpaAdapter=class{options;name=`SpaAdapter`;clientDir;apiPrefixes;excludePaths;cacheControl;indexCacheControl;indexHtml=null;constructor(options={}){this.options=options,this.clientDir=resolve(options.clientDir??`dist/client`),this.excludePaths=options.exclude??[],this.cacheControl=options.cacheControl??`public, max-age=31536000, immutable`,this.indexCacheControl=options.indexCacheControl??`no-cache`;let prefix=options.apiPrefix??`/api`;this.apiPrefixes=Array.isArray(prefix)?prefix:[prefix]}beforeMount({app}){if(!existsSync(this.clientDir)){log.warn(`SPA client directory not found: ${this.clientDir}`),log.warn(`Build your frontend first, or set clientDir to the correct path.`);return}let indexPath=join(this.clientDir,`index.html`);if(existsSync(indexPath))this.indexHtml=readFileSync(indexPath,`utf-8`);else{log.warn(`index.html not found in ${this.clientDir}`);return}try{let express=__require(`express`),staticOpts={};this.cacheControl&&(staticOpts.setHeaders=(res,filePath)=>{filePath.endsWith(`.html`)?res.setHeader(`Cache-Control`,this.indexCacheControl):res.setHeader(`Cache-Control`,this.cacheControl)}),app.use(express.static(this.clientDir,staticOpts))}catch{log.error(`express.static not available — SPA static file serving disabled`);return}log.debug(`Serving SPA from ${this.clientDir}`)}beforeStart({app}){this.indexHtml&&app.use((req,res,next)=>{for(let prefix of this.apiPrefixes)if(req.path.startsWith(prefix))return next();for(let path of this.excludePaths)if(req.path.startsWith(path))return next();if(req.path.includes(`.`)||req.method!==`GET`)return next();res.setHeader(`Content-Type`,`text/html`),res.setHeader(`Cache-Control`,this.indexCacheControl),res.send(this.indexHtml)})}};export{SpaAdapter};
11
+ import{c as __require}from"../../env-Biq_60jq.mjs";import{Logger}from"../../core/logger.mjs";import"../../core-CLmwVWlw.mjs";import{existsSync,readFileSync}from"node:fs";import{join,resolve}from"node:path";const log=Logger.for(`SpaAdapter`);var SpaAdapter=class{options;name=`SpaAdapter`;clientDir;apiPrefixes;excludePaths;cacheControl;indexCacheControl;indexHtml=null;constructor(options={}){this.options=options,this.clientDir=resolve(options.clientDir??`dist/client`),this.excludePaths=options.exclude??[],this.cacheControl=options.cacheControl??`public, max-age=31536000, immutable`,this.indexCacheControl=options.indexCacheControl??`no-cache`;let prefix=options.apiPrefix??`/api`;this.apiPrefixes=Array.isArray(prefix)?prefix:[prefix]}beforeMount({app}){if(!existsSync(this.clientDir)){log.warn(`SPA client directory not found: ${this.clientDir}`),log.warn(`Build your frontend first, or set clientDir to the correct path.`);return}let indexPath=join(this.clientDir,`index.html`);if(existsSync(indexPath))this.indexHtml=readFileSync(indexPath,`utf-8`);else{log.warn(`index.html not found in ${this.clientDir}`);return}try{let express=__require(`express`),staticOpts={};this.cacheControl&&(staticOpts.setHeaders=(res,filePath)=>{filePath.endsWith(`.html`)?res.setHeader(`Cache-Control`,this.indexCacheControl):res.setHeader(`Cache-Control`,this.cacheControl)}),app.use(express.static(this.clientDir,staticOpts))}catch{log.error(`express.static not available — SPA static file serving disabled`);return}log.debug(`Serving SPA from ${this.clientDir}`)}beforeStart({app}){this.indexHtml&&app.use((req,res,next)=>{for(let prefix of this.apiPrefixes)if(req.path.startsWith(prefix))return next();for(let path of this.excludePaths)if(req.path.startsWith(path))return next();if(req.path.includes(`.`)||req.method!==`GET`)return next();res.setHeader(`Content-Type`,`text/html`),res.setHeader(`Cache-Control`,this.indexCacheControl),res.send(this.indexHtml)})}};export{SpaAdapter};
12
12
  //# sourceMappingURL=spa.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"spa.mjs","names":[],"sources":["../../../src/http/middleware/spa.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { resolve, join } from 'node:path'\nimport { Logger, type AppAdapter, type AdapterContext } from '../../core'\n\nconst log = Logger.for('SpaAdapter')\n\nexport interface SpaAdapterOptions {\n /**\n * Directory containing the built SPA files (index.html, assets, etc.)\n * Default: 'dist/client'\n *\n * @example\n * ```ts\n * // Vue: 'dist'\n * // React (Vite): 'dist'\n * // React (CRA): 'build'\n * // Svelte: 'build'\n * // Angular: 'dist/my-app/browser'\n * ```\n */\n clientDir?: string\n\n /**\n * URL prefix for API routes. SPA fallback only applies to\n * non-API routes (routes NOT starting with this prefix).\n * Default: '/api'\n *\n * Set to an array for multiple prefixes:\n * ```ts\n * apiPrefix: ['/api', '/graphql', '/_debug']\n * ```\n */\n apiPrefix?: string | string[]\n\n /**\n * Additional paths to exclude from SPA fallback.\n * These paths will NOT serve index.html.\n *\n * @example\n * ```ts\n * exclude: ['/health', '/metrics', '/ws']\n * ```\n */\n exclude?: string[]\n\n /**\n * Cache-Control header for static assets (default: 'public, max-age=31536000, immutable')\n * Set to false to disable caching headers.\n */\n cacheControl?: string | false\n\n /**\n * Cache-Control for index.html (default: 'no-cache')\n * index.html should not be cached to ensure clients get fresh builds.\n */\n indexCacheControl?: string\n}\n\n/**\n * SPA adapter — serve a Vue, React, Svelte, or Angular build alongside\n * your KickJS API. API routes are handled by controllers; everything else\n * falls back to index.html for client-side routing.\n *\n * @example\n * ```ts\n * import { SpaAdapter } from '@forinda/kickjs/spa'\n *\n * bootstrap({\n * modules,\n * adapters: [\n * new SpaAdapter({\n * clientDir: 'dist/client', // or 'build', 'dist', etc.\n * apiPrefix: '/api',\n * }),\n * ],\n * })\n * ```\n *\n * File structure:\n * ```\n * dist/\n * client/ ← SPA build output\n * index.html\n * assets/\n * app.js\n * style.css\n * server/ ← KickJS server\n * ```\n */\nexport class SpaAdapter implements AppAdapter {\n name = 'SpaAdapter'\n private clientDir: string\n private apiPrefixes: string[]\n private excludePaths: string[]\n private cacheControl: string | false\n private indexCacheControl: string\n private indexHtml: string | null = null\n\n constructor(private options: SpaAdapterOptions = {}) {\n this.clientDir = resolve(options.clientDir ?? 'dist/client')\n this.excludePaths = options.exclude ?? []\n this.cacheControl = options.cacheControl ?? 'public, max-age=31536000, immutable'\n this.indexCacheControl = options.indexCacheControl ?? 'no-cache'\n\n const prefix = options.apiPrefix ?? '/api'\n this.apiPrefixes = Array.isArray(prefix) ? prefix : [prefix]\n }\n\n beforeMount({ app }: AdapterContext): void {\n if (!existsSync(this.clientDir)) {\n log.warn(`SPA client directory not found: ${this.clientDir}`)\n log.warn('Build your frontend first, or set clientDir to the correct path.')\n return\n }\n\n // Read index.html into memory\n const indexPath = join(this.clientDir, 'index.html')\n if (existsSync(indexPath)) {\n this.indexHtml = readFileSync(indexPath, 'utf-8')\n } else {\n log.warn(`index.html not found in ${this.clientDir}`)\n return\n }\n\n // Serve static files with cache headers\n try {\n // Dynamic import to avoid hard dependency on express.static\n const express = require('express')\n const staticOpts: any = {}\n\n if (this.cacheControl) {\n staticOpts.setHeaders = (res: any, filePath: string) => {\n if (filePath.endsWith('.html')) {\n res.setHeader('Cache-Control', this.indexCacheControl)\n } else {\n res.setHeader('Cache-Control', this.cacheControl as string)\n }\n }\n }\n\n app.use(express.static(this.clientDir, staticOpts))\n } catch {\n log.error('express.static not available — SPA static file serving disabled')\n return\n }\n\n log.debug(`Serving SPA from ${this.clientDir}`)\n }\n\n beforeStart({ app }: AdapterContext): void {\n if (!this.indexHtml) return\n\n // SPA fallback: serve index.html for all non-API, non-file routes\n app.use((req: any, res: any, next: any) => {\n // Skip API routes\n for (const prefix of this.apiPrefixes) {\n if (req.path.startsWith(prefix)) return next()\n }\n\n // Skip excluded paths\n for (const path of this.excludePaths) {\n if (req.path.startsWith(path)) return next()\n }\n\n // Skip requests for files (have an extension)\n if (req.path.includes('.')) return next()\n\n // Skip non-GET requests\n if (req.method !== 'GET') return next()\n\n // Serve index.html\n res.setHeader('Content-Type', 'text/html')\n res.setHeader('Cache-Control', this.indexCacheControl)\n res.send(this.indexHtml)\n })\n }\n}\n"],"mappings":";;;;;;;;;;2NAIA,MAAM,IAAM,OAAO,IAAI,YAAY,EAqFnC,IAAa,WAAb,KAA8C,CASxB,QARpB,KAAO,aACP,UACA,YACA,aACA,aACA,kBACA,UAAmC,KAEnC,YAAY,QAAqC,CAAC,EAAG,CAAjC,KAAA,QAAA,QAClB,KAAK,UAAY,QAAQ,QAAQ,WAAa,aAAa,EAC3D,KAAK,aAAe,QAAQ,SAAW,CAAC,EACxC,KAAK,aAAe,QAAQ,cAAgB,sCAC5C,KAAK,kBAAoB,QAAQ,mBAAqB,WAEtD,IAAM,OAAS,QAAQ,WAAa,OACpC,KAAK,YAAc,MAAM,QAAQ,MAAM,EAAI,OAAS,CAAC,MAAM,CAC7D,CAEA,YAAY,CAAE,KAA6B,CACzC,GAAI,CAAC,WAAW,KAAK,SAAS,EAAG,CAC/B,IAAI,KAAK,mCAAmC,KAAK,WAAW,EAC5D,IAAI,KAAK,kEAAkE,EAC3E,MACF,CAGA,IAAM,UAAY,KAAK,KAAK,UAAW,YAAY,EACnD,GAAI,WAAW,SAAS,EACtB,KAAK,UAAY,aAAa,UAAW,OAAO,MAC3C,CACL,IAAI,KAAK,2BAA2B,KAAK,WAAW,EACpD,MACF,CAGA,GAAI,CAEF,IAAM,QAAA,UAAkB,SAAS,EAC3B,WAAkB,CAAC,EAErB,KAAK,eACP,WAAW,YAAc,IAAU,WAAqB,CAClD,SAAS,SAAS,OAAO,EAC3B,IAAI,UAAU,gBAAiB,KAAK,iBAAiB,EAErD,IAAI,UAAU,gBAAiB,KAAK,YAAsB,CAE9D,GAGF,IAAI,IAAI,QAAQ,OAAO,KAAK,UAAW,UAAU,CAAC,CACpD,MAAQ,CACN,IAAI,MAAM,iEAAiE,EAC3E,MACF,CAEA,IAAI,MAAM,oBAAoB,KAAK,WAAW,CAChD,CAEA,YAAY,CAAE,KAA6B,CACpC,KAAK,WAGV,IAAI,KAAK,IAAU,IAAU,OAAc,CAEzC,IAAK,IAAM,UAAU,KAAK,YACxB,GAAI,IAAI,KAAK,WAAW,MAAM,EAAG,OAAO,KAAK,EAI/C,IAAK,IAAM,QAAQ,KAAK,aACtB,GAAI,IAAI,KAAK,WAAW,IAAI,EAAG,OAAO,KAAK,EAO7C,GAHI,IAAI,KAAK,SAAS,GAAG,GAGrB,IAAI,SAAW,MAAO,OAAO,KAAK,EAGtC,IAAI,UAAU,eAAgB,WAAW,EACzC,IAAI,UAAU,gBAAiB,KAAK,iBAAiB,EACrD,IAAI,KAAK,KAAK,SAAS,CACzB,CAAC,CACH,CACF"}
1
+ {"version":3,"file":"spa.mjs","names":[],"sources":["../../../src/http/middleware/spa.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { resolve, join } from 'node:path'\nimport { Logger, type AppAdapter, type AdapterContext } from '../../core'\n\nconst log = Logger.for('SpaAdapter')\n\nexport interface SpaAdapterOptions {\n /**\n * Directory containing the built SPA files (index.html, assets, etc.)\n * Default: 'dist/client'\n *\n * @example\n * ```ts\n * // Vue: 'dist'\n * // React (Vite): 'dist'\n * // React (CRA): 'build'\n * // Svelte: 'build'\n * // Angular: 'dist/my-app/browser'\n * ```\n */\n clientDir?: string\n\n /**\n * URL prefix for API routes. SPA fallback only applies to\n * non-API routes (routes NOT starting with this prefix).\n * Default: '/api'\n *\n * Set to an array for multiple prefixes:\n * ```ts\n * apiPrefix: ['/api', '/graphql', '/_debug']\n * ```\n */\n apiPrefix?: string | string[]\n\n /**\n * Additional paths to exclude from SPA fallback.\n * These paths will NOT serve index.html.\n *\n * @example\n * ```ts\n * exclude: ['/health', '/metrics', '/ws']\n * ```\n */\n exclude?: string[]\n\n /**\n * Cache-Control header for static assets (default: 'public, max-age=31536000, immutable')\n * Set to false to disable caching headers.\n */\n cacheControl?: string | false\n\n /**\n * Cache-Control for index.html (default: 'no-cache')\n * index.html should not be cached to ensure clients get fresh builds.\n */\n indexCacheControl?: string\n}\n\n/**\n * SPA adapter — serve a Vue, React, Svelte, or Angular build alongside\n * your KickJS API. API routes are handled by controllers; everything else\n * falls back to index.html for client-side routing.\n *\n * @example\n * ```ts\n * import { SpaAdapter } from '@forinda/kickjs/spa'\n *\n * bootstrap({\n * modules,\n * adapters: [\n * new SpaAdapter({\n * clientDir: 'dist/client', // or 'build', 'dist', etc.\n * apiPrefix: '/api',\n * }),\n * ],\n * })\n * ```\n *\n * File structure:\n * ```\n * dist/\n * client/ ← SPA build output\n * index.html\n * assets/\n * app.js\n * style.css\n * server/ ← KickJS server\n * ```\n */\nexport class SpaAdapter implements AppAdapter {\n name = 'SpaAdapter'\n private clientDir: string\n private apiPrefixes: string[]\n private excludePaths: string[]\n private cacheControl: string | false\n private indexCacheControl: string\n private indexHtml: string | null = null\n\n constructor(private options: SpaAdapterOptions = {}) {\n this.clientDir = resolve(options.clientDir ?? 'dist/client')\n this.excludePaths = options.exclude ?? []\n this.cacheControl = options.cacheControl ?? 'public, max-age=31536000, immutable'\n this.indexCacheControl = options.indexCacheControl ?? 'no-cache'\n\n const prefix = options.apiPrefix ?? '/api'\n this.apiPrefixes = Array.isArray(prefix) ? prefix : [prefix]\n }\n\n beforeMount({ app }: AdapterContext): void {\n if (!existsSync(this.clientDir)) {\n log.warn(`SPA client directory not found: ${this.clientDir}`)\n log.warn('Build your frontend first, or set clientDir to the correct path.')\n return\n }\n\n // Read index.html into memory\n const indexPath = join(this.clientDir, 'index.html')\n if (existsSync(indexPath)) {\n this.indexHtml = readFileSync(indexPath, 'utf-8')\n } else {\n log.warn(`index.html not found in ${this.clientDir}`)\n return\n }\n\n // Serve static files with cache headers\n try {\n // Dynamic import to avoid hard dependency on express.static\n const express = require('express')\n const staticOpts: any = {}\n\n if (this.cacheControl) {\n staticOpts.setHeaders = (res: any, filePath: string) => {\n if (filePath.endsWith('.html')) {\n res.setHeader('Cache-Control', this.indexCacheControl)\n } else {\n res.setHeader('Cache-Control', this.cacheControl as string)\n }\n }\n }\n\n app.use(express.static(this.clientDir, staticOpts))\n } catch {\n log.error('express.static not available — SPA static file serving disabled')\n return\n }\n\n log.debug(`Serving SPA from ${this.clientDir}`)\n }\n\n beforeStart({ app }: AdapterContext): void {\n if (!this.indexHtml) return\n\n // SPA fallback: serve index.html for all non-API, non-file routes\n app.use((req: any, res: any, next: any) => {\n // Skip API routes\n for (const prefix of this.apiPrefixes) {\n if (req.path.startsWith(prefix)) return next()\n }\n\n // Skip excluded paths\n for (const path of this.excludePaths) {\n if (req.path.startsWith(path)) return next()\n }\n\n // Skip requests for files (have an extension)\n if (req.path.includes('.')) return next()\n\n // Skip non-GET requests\n if (req.method !== 'GET') return next()\n\n // Serve index.html\n res.setHeader('Content-Type', 'text/html')\n res.setHeader('Cache-Control', this.indexCacheControl)\n res.send(this.indexHtml)\n })\n }\n}\n"],"mappings":";;;;;;;;;;8MAIA,MAAM,IAAM,OAAO,IAAI,YAAY,EAqFnC,IAAa,WAAb,KAA8C,CASxB,QARpB,KAAO,aACP,UACA,YACA,aACA,aACA,kBACA,UAAmC,KAEnC,YAAY,QAAqC,CAAC,EAAG,CAAjC,KAAA,QAAA,QAClB,KAAK,UAAY,QAAQ,QAAQ,WAAa,aAAa,EAC3D,KAAK,aAAe,QAAQ,SAAW,CAAC,EACxC,KAAK,aAAe,QAAQ,cAAgB,sCAC5C,KAAK,kBAAoB,QAAQ,mBAAqB,WAEtD,IAAM,OAAS,QAAQ,WAAa,OACpC,KAAK,YAAc,MAAM,QAAQ,MAAM,EAAI,OAAS,CAAC,MAAM,CAC7D,CAEA,YAAY,CAAE,KAA6B,CACzC,GAAI,CAAC,WAAW,KAAK,SAAS,EAAG,CAC/B,IAAI,KAAK,mCAAmC,KAAK,WAAW,EAC5D,IAAI,KAAK,kEAAkE,EAC3E,MACF,CAGA,IAAM,UAAY,KAAK,KAAK,UAAW,YAAY,EACnD,GAAI,WAAW,SAAS,EACtB,KAAK,UAAY,aAAa,UAAW,OAAO,MAC3C,CACL,IAAI,KAAK,2BAA2B,KAAK,WAAW,EACpD,MACF,CAGA,GAAI,CAEF,IAAM,QAAA,UAAkB,SAAS,EAC3B,WAAkB,CAAC,EAErB,KAAK,eACP,WAAW,YAAc,IAAU,WAAqB,CAClD,SAAS,SAAS,OAAO,EAC3B,IAAI,UAAU,gBAAiB,KAAK,iBAAiB,EAErD,IAAI,UAAU,gBAAiB,KAAK,YAAsB,CAE9D,GAGF,IAAI,IAAI,QAAQ,OAAO,KAAK,UAAW,UAAU,CAAC,CACpD,MAAQ,CACN,IAAI,MAAM,iEAAiE,EAC3E,MACF,CAEA,IAAI,MAAM,oBAAoB,KAAK,WAAW,CAChD,CAEA,YAAY,CAAE,KAA6B,CACpC,KAAK,WAGV,IAAI,KAAK,IAAU,IAAU,OAAc,CAEzC,IAAK,IAAM,UAAU,KAAK,YACxB,GAAI,IAAI,KAAK,WAAW,MAAM,EAAG,OAAO,KAAK,EAI/C,IAAK,IAAM,QAAQ,KAAK,aACtB,GAAI,IAAI,KAAK,WAAW,IAAI,EAAG,OAAO,KAAK,EAO7C,GAHI,IAAI,KAAK,SAAS,GAAG,GAGrB,IAAI,SAAW,MAAO,OAAO,KAAK,EAGtC,IAAI,UAAU,eAAgB,WAAW,EACzC,IAAI,UAAU,gBAAiB,KAAK,iBAAiB,EACrD,IAAI,KAAK,KAAK,SAAS,CACzB,CAAC,CACH,CACF"}
@@ -1,3 +1,3 @@
1
1
 
2
- import { a as RawUploadPart, c as buildFileTypeFilter, i as upload, l as resolveMimeTypes, n as buildUploadMiddleware, o as UploadedFileLike, r as cleanupFiles, s as applyUploadConfig, t as UploadOptions } from "../../upload-CIZc803m.mjs";
2
+ import { a as RawUploadPart, c as buildFileTypeFilter, i as upload, l as resolveMimeTypes, n as buildUploadMiddleware, o as UploadedFileLike, r as cleanupFiles, s as applyUploadConfig, t as UploadOptions } from "../../upload-XVTyQcNN.mjs";
3
3
  export { type RawUploadPart, UploadOptions, type UploadedFileLike, applyUploadConfig, buildFileTypeFilter, buildUploadMiddleware, cleanupFiles, resolveMimeTypes, upload };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.4.0
2
+ * @forinda/kickjs v6.5.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,6 +8,6 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{n as buildFileTypeFilter,r as resolveMimeTypes,t as applyUploadConfig}from"../../upload-config-C3MSF7RF.mjs";import{createRequire}from"node:module";import{unlink}from"node:fs/promises";const requireFromHere=createRequire(import.meta.url);let _multer;function loadMulter(){if(_multer)return _multer;try{return _multer=requireFromHere(`multer`),_multer}catch{throw Error(`@forinda/kickjs: file uploads require the 'multer' package, which is not installed.
11
+ import{n as buildFileTypeFilter,r as resolveMimeTypes,t as applyUploadConfig}from"../../upload-config-Dd_QvlpC.mjs";import{createRequire}from"node:module";import{unlink}from"node:fs/promises";const requireFromHere=createRequire(import.meta.url);let _multer;function loadMulter(){if(_multer)return _multer;try{return _multer=requireFromHere(`multer`),_multer}catch{throw Error(`@forinda/kickjs: file uploads require the 'multer' package, which is not installed.
12
12
  Install it as a peer dependency: pnpm add multer (or npm i multer / yarn add multer).`)}}function createMulter(options){let limits={fileSize:options.maxSize??5*1024*1024},fileFilter;if(options.allowedTypes){let typeAllowed=buildFileTypeFilter(options.allowedTypes,options.customMimeMap);fileFilter=(_req,file,cb)=>{typeAllowed(file.mimetype,file.originalname)?cb(null,!0):cb(Error(`File type ${file.mimetype} is not allowed`))}}let multerOptions={limits,...fileFilter?{fileFilter}:{},...options.storage?{storage:options.storage}:{},...options.dest?{dest:options.dest}:{}};return loadMulter()(multerOptions)}function single(fieldName,options={}){return createMulter(options).single(fieldName)}function array(fieldName,maxCount=10,options={}){return createMulter(options).array(fieldName,maxCount)}function none(options={}){return createMulter(options).none()}function cleanupFiles(){return(req,res,next)=>{res.on(`finish`,async()=>{let files=[];if(req.file?.path&&files.push(req.file),Array.isArray(req.files))for(let f of req.files)f?.path&&files.push(f);for(let file of files)try{await unlink(file.path)}catch{}}),next()}}function buildUploadMiddleware(config){let options={};config.maxSize&&(options.maxSize=config.maxSize),config.allowedTypes&&(options.allowedTypes=config.allowedTypes),config.customMimeMap&&(options.customMimeMap=config.customMimeMap);let fieldName=config.fieldName??`file`;switch(config.mode){case`single`:return single(fieldName,options);case`array`:return array(fieldName,config.maxCount??10,options);case`none`:return none(options)}}const upload={single,array,none};export{applyUploadConfig,buildFileTypeFilter,buildUploadMiddleware,cleanupFiles,resolveMimeTypes,upload};
13
13
  //# sourceMappingURL=upload.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"upload.mjs","names":[],"sources":["../../../src/http/middleware/upload.ts"],"sourcesContent":["import { unlink } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport type { Request, Response, NextFunction, RequestHandler } from 'express'\nimport type { Options as MulterOptions } from 'multer'\nimport type { BaseUploadOptions, FileUploadConfig } from '../../core'\n\n// `multer` is an optional peer dependency. We load it lazily via\n// `createRequire` so importing this module never touches `multer` —\n// only constructing an upload middleware does. Adopters who never\n// call `upload.single/array/none()` or use `@FileUpload` don't need\n// `multer` installed at all.\n//\n// multer uses CJS `export = multer`, so the module is itself the\n// callable factory — no `.default` indirection on either the type\n// or the runtime side.\nconst requireFromHere = createRequire(import.meta.url)\ntype MulterFactory = typeof import('multer')\nlet _multer: MulterFactory | undefined\n\nfunction loadMulter(): MulterFactory {\n if (_multer) return _multer\n try {\n _multer = requireFromHere('multer') as MulterFactory\n return _multer\n } catch {\n throw new Error(\n \"@forinda/kickjs: file uploads require the 'multer' package, which is not installed.\\n\" +\n 'Install it as a peer dependency: pnpm add multer (or npm i multer / yarn add multer).',\n )\n }\n}\n\n// Pure upload core (MIME map, type filter, applyUploadConfig) moved to\n// `./upload-config` so the edge-safe web entry can use it without this\n// module's node-only surface. Re-exported here for compatibility.\nexport {\n buildFileTypeFilter,\n resolveMimeTypes,\n applyUploadConfig,\n type UploadedFileLike,\n type RawUploadPart,\n} from './upload-config'\nimport { buildFileTypeFilter } from './upload-config'\n\n/**\n * Upload options for the middleware.\n * Extends BaseUploadOptions from core (shared with @FileUpload decorator)\n * and adds Multer-specific storage options.\n */\nexport interface UploadOptions extends BaseUploadOptions {\n /** Multer storage config (default: memory storage) */\n storage?: MulterOptions['storage']\n /** Multer dest for disk storage shorthand */\n dest?: string\n}\n\nfunction createMulter(options: UploadOptions) {\n const limits: MulterOptions['limits'] = {\n fileSize: options.maxSize ?? 5 * 1024 * 1024,\n }\n\n let fileFilter: MulterOptions['fileFilter'] | undefined\n if (options.allowedTypes) {\n const typeAllowed = buildFileTypeFilter(options.allowedTypes, options.customMimeMap)\n fileFilter = (_req, file, cb) => {\n if (typeAllowed(file.mimetype, file.originalname)) {\n cb(null, true)\n } else {\n cb(new Error(`File type ${file.mimetype} is not allowed`))\n }\n }\n }\n\n const multerOptions: MulterOptions = {\n limits,\n ...(fileFilter ? { fileFilter } : {}),\n ...(options.storage ? { storage: options.storage } : {}),\n ...(options.dest ? { dest: options.dest } : {}),\n }\n\n return loadMulter()(multerOptions)\n}\n\n/**\n * Single file upload middleware. Attaches the file to `req.file`.\n *\n * @example\n * ```ts\n * @Post('/avatar')\n * @Middleware(upload.single('avatar', { maxSize: 2 * 1024 * 1024, allowedTypes: ['jpg', 'png'] }))\n * async uploadAvatar(ctx: RequestContext) {\n * ctx.json({ filename: ctx.file.originalname })\n * }\n * ```\n */\nfunction single(fieldName: string, options: UploadOptions = {}): RequestHandler {\n const m = createMulter(options)\n return m.single(fieldName) as RequestHandler\n}\n\n/**\n * Multiple file upload middleware. Attaches files to `req.files`.\n */\nfunction array(fieldName: string, maxCount = 10, options: UploadOptions = {}): RequestHandler {\n const m = createMulter(options)\n return m.array(fieldName, maxCount) as RequestHandler\n}\n\n/**\n * No file upload — just parse multipart form data without file fields.\n */\nfunction none(options: UploadOptions = {}): RequestHandler {\n const m = createMulter(options)\n return m.none() as RequestHandler\n}\n\n/**\n * Removes temporary files from disk after the response is sent.\n * Attach this as Express middleware after the upload middleware.\n * Works with both `req.file` (single) and `req.files` (array).\n *\n * @example\n * ```ts\n * @Post('/process')\n * @Middleware(upload.single('document', { dest: '/tmp/uploads' }), cleanupFiles())\n * async processDocument(ctx: RequestContext) {\n * ctx.json({ ok: true })\n * }\n * ```\n */\nexport function cleanupFiles() {\n return (req: Request, res: Response, next: NextFunction) => {\n res.on('finish', async () => {\n const files: any[] = []\n\n if ((req as any).file?.path) {\n files.push((req as any).file)\n }\n if (Array.isArray((req as any).files)) {\n for (const f of (req as any).files) {\n if (f?.path) files.push(f)\n }\n }\n\n for (const file of files) {\n try {\n await unlink(file.path)\n } catch {\n // File may already be moved/deleted by the handler — ignore\n }\n }\n })\n\n next()\n }\n}\n\n/**\n * Build upload middleware from a @FileUpload decorator config.\n * Used internally by the router builder when it detects FILE_UPLOAD metadata.\n * Accepts the same FileUploadConfig interface used by the @FileUpload decorator.\n */\nexport function buildUploadMiddleware(config: FileUploadConfig): RequestHandler {\n const options: UploadOptions = {}\n if (config.maxSize) options.maxSize = config.maxSize\n if (config.allowedTypes) options.allowedTypes = config.allowedTypes\n if (config.customMimeMap) options.customMimeMap = config.customMimeMap\n\n const fieldName = config.fieldName ?? 'file'\n\n switch (config.mode) {\n case 'single':\n return single(fieldName, options)\n case 'array':\n return array(fieldName, config.maxCount ?? 10, options)\n case 'none':\n return none(options)\n }\n}\n\n/** Upload middleware factory with `.single()`, `.array()`, `.none()` methods */\nexport const upload = { single, array, none }\n"],"mappings":";;;;;;;;;;gMAeA,MAAM,gBAAkB,cAAc,OAAO,KAAK,GAAG,EAErD,IAAI,QAEJ,SAAS,YAA4B,CACnC,GAAI,QAAS,OAAO,QACpB,GAAI,CAEF,MADA,SAAU,gBAAgB,QAAQ,EAC3B,OACT,MAAQ,CACN,MAAU,MACR;sFAEF,CACF,CACF,CA0BA,SAAS,aAAa,QAAwB,CAC5C,IAAM,OAAkC,CACtC,SAAU,QAAQ,SAAW,EAAI,KAAO,IAC1C,EAEI,WACJ,GAAI,QAAQ,aAAc,CACxB,IAAM,YAAc,oBAAoB,QAAQ,aAAc,QAAQ,aAAa,EACnF,YAAc,KAAM,KAAM,KAAO,CAC3B,YAAY,KAAK,SAAU,KAAK,YAAY,EAC9C,GAAG,KAAM,EAAI,EAEb,GAAO,MAAM,aAAa,KAAK,SAAS,gBAAgB,CAAC,CAE7D,CACF,CAEA,IAAM,cAA+B,CACnC,OACA,GAAI,WAAa,CAAE,UAAW,EAAI,CAAC,EACnC,GAAI,QAAQ,QAAU,CAAE,QAAS,QAAQ,OAAQ,EAAI,CAAC,EACtD,GAAI,QAAQ,KAAO,CAAE,KAAM,QAAQ,IAAK,EAAI,CAAC,CAC/C,EAEA,OAAO,WAAW,CAAC,CAAC,aAAa,CACnC,CAcA,SAAS,OAAO,UAAmB,QAAyB,CAAC,EAAmB,CAE9E,OADU,aAAa,OAChB,CAAC,CAAC,OAAO,SAAS,CAC3B,CAKA,SAAS,MAAM,UAAmB,SAAW,GAAI,QAAyB,CAAC,EAAmB,CAE5F,OADU,aAAa,OAChB,CAAC,CAAC,MAAM,UAAW,QAAQ,CACpC,CAKA,SAAS,KAAK,QAAyB,CAAC,EAAmB,CAEzD,OADU,aAAa,OAChB,CAAC,CAAC,KAAK,CAChB,CAgBA,SAAgB,cAAe,CAC7B,OAAQ,IAAc,IAAe,OAAuB,CAC1D,IAAI,GAAG,SAAU,SAAY,CAC3B,IAAM,MAAe,CAAC,EAKtB,GAHK,IAAY,MAAM,MACrB,MAAM,KAAM,IAAY,IAAI,EAE1B,MAAM,QAAS,IAAY,KAAK,MAC7B,IAAM,KAAM,IAAY,MACvB,GAAG,MAAM,MAAM,KAAK,CAAC,EAI7B,IAAK,IAAM,QAAQ,MACjB,GAAI,CACF,MAAM,OAAO,KAAK,IAAI,CACxB,MAAQ,CAER,CAEJ,CAAC,EAED,KAAK,CACP,CACF,CAOA,SAAgB,sBAAsB,OAA0C,CAC9E,IAAM,QAAyB,CAAC,EAC5B,OAAO,UAAS,QAAQ,QAAU,OAAO,SACzC,OAAO,eAAc,QAAQ,aAAe,OAAO,cACnD,OAAO,gBAAe,QAAQ,cAAgB,OAAO,eAEzD,IAAM,UAAY,OAAO,WAAa,OAEtC,OAAQ,OAAO,KAAf,CACE,IAAK,SACH,OAAO,OAAO,UAAW,OAAO,EAClC,IAAK,QACH,OAAO,MAAM,UAAW,OAAO,UAAY,GAAI,OAAO,EACxD,IAAK,OACH,OAAO,KAAK,OAAO,CACvB,CACF,CAGA,MAAa,OAAS,CAAE,OAAQ,MAAO,IAAK"}
1
+ {"version":3,"file":"upload.mjs","names":[],"sources":["../../../src/http/middleware/upload.ts"],"sourcesContent":["import { unlink } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport type { Request, Response, NextFunction, RequestHandler } from 'express'\nimport type { Options as MulterOptions } from 'multer'\nimport type { BaseUploadOptions, FileUploadConfig } from '../../core'\n\n// `multer` is an optional peer dependency. We load it lazily via\n// `createRequire` so importing this module never touches `multer` —\n// only constructing an upload middleware does. Adopters who never\n// call `upload.single/array/none()` or use `@FileUpload` don't need\n// `multer` installed at all.\n//\n// multer uses CJS `export = multer`, so the module is itself the\n// callable factory — no `.default` indirection on either the type\n// or the runtime side.\nconst requireFromHere = createRequire(import.meta.url)\ntype MulterFactory = typeof import('multer')\nlet _multer: MulterFactory | undefined\n\nfunction loadMulter(): MulterFactory {\n if (_multer) return _multer\n try {\n _multer = requireFromHere('multer') as MulterFactory\n return _multer\n } catch {\n throw new Error(\n \"@forinda/kickjs: file uploads require the 'multer' package, which is not installed.\\n\" +\n 'Install it as a peer dependency: pnpm add multer (or npm i multer / yarn add multer).',\n )\n }\n}\n\n// Pure upload core (MIME map, type filter, applyUploadConfig) moved to\n// `./upload-config` so the edge-safe web entry can use it without this\n// module's node-only surface. Re-exported here for compatibility.\nexport {\n buildFileTypeFilter,\n resolveMimeTypes,\n applyUploadConfig,\n type UploadedFileLike,\n type RawUploadPart,\n} from './upload-config'\nimport { buildFileTypeFilter } from './upload-config'\n\n/**\n * Upload options for the middleware.\n * Extends BaseUploadOptions from core (shared with @FileUpload decorator)\n * and adds Multer-specific storage options.\n */\nexport interface UploadOptions extends BaseUploadOptions {\n /** Multer storage config (default: memory storage) */\n storage?: MulterOptions['storage']\n /** Multer dest for disk storage shorthand */\n dest?: string\n}\n\nfunction createMulter(options: UploadOptions) {\n const limits: MulterOptions['limits'] = {\n fileSize: options.maxSize ?? 5 * 1024 * 1024,\n }\n\n let fileFilter: MulterOptions['fileFilter'] | undefined\n if (options.allowedTypes) {\n const typeAllowed = buildFileTypeFilter(options.allowedTypes, options.customMimeMap)\n fileFilter = (_req, file, cb) => {\n if (typeAllowed(file.mimetype, file.originalname)) {\n cb(null, true)\n } else {\n cb(new Error(`File type ${file.mimetype} is not allowed`))\n }\n }\n }\n\n const multerOptions: MulterOptions = {\n limits,\n ...(fileFilter ? { fileFilter } : {}),\n ...(options.storage ? { storage: options.storage } : {}),\n ...(options.dest ? { dest: options.dest } : {}),\n }\n\n return loadMulter()(multerOptions)\n}\n\n/**\n * Single file upload middleware. Attaches the file to `req.file`.\n *\n * @example\n * ```ts\n * @Post('/avatar')\n * @Middleware(upload.single('avatar', { maxSize: 2 * 1024 * 1024, allowedTypes: ['jpg', 'png'] }))\n * async uploadAvatar(ctx: RequestContext) {\n * ctx.json({ filename: ctx.file.originalname })\n * }\n * ```\n */\nfunction single(fieldName: string, options: UploadOptions = {}): RequestHandler {\n const m = createMulter(options)\n return m.single(fieldName) as RequestHandler\n}\n\n/**\n * Multiple file upload middleware. Attaches files to `req.files`.\n */\nfunction array(fieldName: string, maxCount = 10, options: UploadOptions = {}): RequestHandler {\n const m = createMulter(options)\n return m.array(fieldName, maxCount) as RequestHandler\n}\n\n/**\n * No file upload — just parse multipart form data without file fields.\n */\nfunction none(options: UploadOptions = {}): RequestHandler {\n const m = createMulter(options)\n return m.none() as RequestHandler\n}\n\n/**\n * Removes temporary files from disk after the response is sent.\n * Attach this as Express middleware after the upload middleware.\n * Works with both `req.file` (single) and `req.files` (array).\n *\n * @example\n * ```ts\n * @Post('/process')\n * @Middleware(upload.single('document', { dest: '/tmp/uploads' }), cleanupFiles())\n * async processDocument(ctx: RequestContext) {\n * ctx.json({ ok: true })\n * }\n * ```\n */\nexport function cleanupFiles() {\n return (req: Request, res: Response, next: NextFunction) => {\n res.on('finish', async () => {\n const files: any[] = []\n\n if ((req as any).file?.path) {\n files.push((req as any).file)\n }\n if (Array.isArray((req as any).files)) {\n for (const f of (req as any).files) {\n if (f?.path) files.push(f)\n }\n }\n\n for (const file of files) {\n try {\n await unlink(file.path)\n } catch {\n // File may already be moved/deleted by the handler — ignore\n }\n }\n })\n\n next()\n }\n}\n\n/**\n * Build upload middleware from a @FileUpload decorator config.\n * Used internally by the router builder when it detects FILE_UPLOAD metadata.\n * Accepts the same FileUploadConfig interface used by the @FileUpload decorator.\n */\nexport function buildUploadMiddleware(config: FileUploadConfig): RequestHandler {\n const options: UploadOptions = {}\n if (config.maxSize) options.maxSize = config.maxSize\n if (config.allowedTypes) options.allowedTypes = config.allowedTypes\n if (config.customMimeMap) options.customMimeMap = config.customMimeMap\n\n const fieldName = config.fieldName ?? 'file'\n\n switch (config.mode) {\n case 'single':\n return single(fieldName, options)\n case 'array':\n return array(fieldName, config.maxCount ?? 10, options)\n case 'none':\n return none(options)\n }\n}\n\n/** Upload middleware factory with `.single()`, `.array()`, `.none()` methods */\nexport const upload = { single, array, none }\n"],"mappings":";;;;;;;;;;gMAeA,MAAM,gBAAkB,cAAc,OAAO,KAAK,GAAG,EAErD,IAAI,QAEJ,SAAS,YAA4B,CACnC,GAAI,QAAS,OAAO,QACpB,GAAI,CAEF,MADA,SAAU,gBAAgB,QAAQ,EAC3B,OACT,MAAQ,CACN,MAAU,MACR;sFAEF,CACF,CACF,CA0BA,SAAS,aAAa,QAAwB,CAC5C,IAAM,OAAkC,CACtC,SAAU,QAAQ,SAAW,EAAI,KAAO,IAC1C,EAEI,WACJ,GAAI,QAAQ,aAAc,CACxB,IAAM,YAAc,oBAAoB,QAAQ,aAAc,QAAQ,aAAa,EACnF,YAAc,KAAM,KAAM,KAAO,CAC3B,YAAY,KAAK,SAAU,KAAK,YAAY,EAC9C,GAAG,KAAM,EAAI,EAEb,GAAO,MAAM,aAAa,KAAK,SAAS,gBAAgB,CAAC,CAE7D,CACF,CAEA,IAAM,cAA+B,CACnC,OACA,GAAI,WAAa,CAAE,UAAW,EAAI,CAAC,EACnC,GAAI,QAAQ,QAAU,CAAE,QAAS,QAAQ,OAAQ,EAAI,CAAC,EACtD,GAAI,QAAQ,KAAO,CAAE,KAAM,QAAQ,IAAK,EAAI,CAAC,CAC/C,EAEA,OAAO,WAAW,CAAC,CAAC,aAAa,CACnC,CAcA,SAAS,OAAO,UAAmB,QAAyB,CAAC,EAAmB,CAE9E,OADU,aAAa,OAChB,CAAC,CAAC,OAAO,SAAS,CAC3B,CAKA,SAAS,MAAM,UAAmB,SAAW,GAAI,QAAyB,CAAC,EAAmB,CAE5F,OADU,aAAa,OAChB,CAAC,CAAC,MAAM,UAAW,QAAQ,CACpC,CAKA,SAAS,KAAK,QAAyB,CAAC,EAAmB,CAEzD,OADU,aAAa,OAChB,CAAC,CAAC,KAAK,CAChB,CAgBA,SAAgB,cAAe,CAC7B,OAAQ,IAAc,IAAe,OAAuB,CAC1D,IAAI,GAAG,SAAU,SAAY,CAC3B,IAAM,MAAe,CAAC,EAKtB,GAHK,IAAY,MAAM,MACrB,MAAM,KAAM,IAAY,IAAI,EAE1B,MAAM,QAAS,IAAY,KAAK,EAC7B,IAAA,IAAM,KAAM,IAAY,MACvB,GAAG,MAAM,MAAM,KAAK,CAAC,EAI7B,IAAK,IAAM,QAAQ,MACjB,GAAI,CACF,MAAM,OAAO,KAAK,IAAI,CACxB,MAAQ,CAER,CAEJ,CAAC,EAED,KAAK,CACP,CACF,CAOA,SAAgB,sBAAsB,OAA0C,CAC9E,IAAM,QAAyB,CAAC,EAC5B,OAAO,UAAS,QAAQ,QAAU,OAAO,SACzC,OAAO,eAAc,QAAQ,aAAe,OAAO,cACnD,OAAO,gBAAe,QAAQ,cAAgB,OAAO,eAEzD,IAAM,UAAY,OAAO,WAAa,OAEtC,OAAQ,OAAO,KAAf,CACE,IAAK,SACH,OAAO,OAAO,UAAW,OAAO,EAClC,IAAK,QACH,OAAO,MAAM,UAAW,OAAO,UAAY,GAAI,OAAO,EACxD,IAAK,OACH,OAAO,KAAK,OAAO,CACvB,CACF,CAGA,MAAa,OAAS,CAAE,OAAQ,MAAO,IAAK"}
@@ -1,3 +1,3 @@
1
1
 
2
- import { n as validate, t as ValidationSchema } from "../../validate-DoZDOhCi.mjs";
2
+ import { n as validate, t as ValidationSchema } from "../../validate-DMYpUnrf.mjs";
3
3
  export { ValidationSchema, validate };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.4.0
2
+ * @forinda/kickjs v6.5.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,3 +1,3 @@
1
1
 
2
- import { n as ViewAdapterOptions, t as ViewAdapter } from "../../views-DjgUXRdu.mjs";
2
+ import { n as ViewAdapterOptions, t as ViewAdapter } from "../../views-DsfBT1fR.mjs";
3
3
  export { ViewAdapter, ViewAdapterOptions };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.4.0
2
+ * @forinda/kickjs v6.5.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,4 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{Logger}from"../../core/logger.mjs";import{d as defineAdapter}from"../../core-CQ6u5kOq.mjs";import{resolve}from"node:path";const log=Logger.for(`ViewEngine`),ViewAdapter=defineAdapter({name:`ViewAdapter`,build:options=>({beforeMount({app}){let{engine,ext,viewsDir=`src/views`}=options;engine.__express?app.engine(ext,engine.__express):typeof engine.renderFile==`function`?app.engine(ext,(path,opts,callback)=>{engine.renderFile(path,opts,callback)}):(typeof engine==`function`||log.warn(`Engine for .${ext} does not have __express or renderFile. Trying as-is.`),app.engine(ext,engine)),app.set(`view engine`,ext),app.set(`views`,resolve(viewsDir)),log.debug(`View engine: ${ext} (${resolve(viewsDir)})`)}})});export{ViewAdapter};
12
- //# sourceMappingURL=views.mjs.map
11
+ import{t as ViewAdapter}from"../../views-Dj6XFaca.mjs";export{ViewAdapter};
@@ -1,3 +1,3 @@
1
1
 
2
- import { C as SortItem, S as QueryFieldConfig, T as TypedParsedQuery, _ as FilterOperator, a as getQueryParsingDefaults, b as ParsedQuery, c as parseQuery, d as resetQueryParsingDefaults, f as setQueryParsingDefaults, g as FilterItem, h as FieldsOf, i as buildQueryParams, l as parseSearchQuery, m as FILTER_OPERATORS, n as QueryRejectReason, o as parseFilters, p as ColumnQueryFieldConfig, r as QueryRejection, s as parsePagination, t as ParseQueryOptions, u as parseSort, v as PaginatedResponse, w as StringQueryFieldConfig, x as QueryBuilderAdapter, y as PaginationParams } from "../../index-BtWJ9SLP.mjs";
2
+ import { C as SortItem, S as QueryFieldConfig, T as TypedParsedQuery, _ as FilterOperator, a as getQueryParsingDefaults, b as ParsedQuery, c as parseQuery, d as resetQueryParsingDefaults, f as setQueryParsingDefaults, g as FilterItem, h as FieldsOf, i as buildQueryParams, l as parseSearchQuery, m as FILTER_OPERATORS, n as QueryRejectReason, o as parseFilters, p as ColumnQueryFieldConfig, r as QueryRejection, s as parsePagination, t as ParseQueryOptions, u as parseSort, v as PaginatedResponse, w as StringQueryFieldConfig, x as QueryBuilderAdapter, y as PaginationParams } from "../../index-BfT5KZ-M.mjs";
3
3
  export { type ColumnQueryFieldConfig, FILTER_OPERATORS, type FieldsOf, type FilterItem, type FilterOperator, type PaginatedResponse, type PaginationParams, type ParseQueryOptions, type ParsedQuery, type QueryBuilderAdapter, type QueryFieldConfig, type QueryRejectReason, type QueryRejection, type SortItem, type StringQueryFieldConfig, type TypedParsedQuery, buildQueryParams, getQueryParsingDefaults, parseFilters, parsePagination, parseQuery, parseSearchQuery, parseSort, resetQueryParsingDefaults, setQueryParsingDefaults };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.4.0
2
+ * @forinda/kickjs v6.5.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,4 +8,4 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{a as parseQuery,c as resetQueryParsingDefaults,i as parsePagination,l as setQueryParsingDefaults,n as getQueryParsingDefaults,o as parseSearchQuery,r as parseFilters,s as parseSort,t as buildQueryParams,u as FILTER_OPERATORS}from"../../query-Cs_Llg77.mjs";export{FILTER_OPERATORS,buildQueryParams,getQueryParsingDefaults,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort,resetQueryParsingDefaults,setQueryParsingDefaults};
11
+ import{a as parseQuery,c as resetQueryParsingDefaults,i as parsePagination,l as setQueryParsingDefaults,n as getQueryParsingDefaults,o as parseSearchQuery,r as parseFilters,s as parseSort,t as buildQueryParams,u as FILTER_OPERATORS}from"../../query-CXN52dzy.mjs";export{FILTER_OPERATORS,buildQueryParams,getQueryParsingDefaults,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort,resetQueryParsingDefaults,setQueryParsingDefaults};
@@ -1,3 +1,3 @@
1
1
 
2
- import { i as buildRouteTable, n as _setExternalContributorSources, r as assertRouteUnique, t as BuildRoutesOptions } from "../router-builder-CiS2ASTJ.mjs";
2
+ import { i as buildRouteTable, n as _setExternalContributorSources, r as assertRouteUnique, t as BuildRoutesOptions } from "../router-builder-CMGhEBU2.mjs";
3
3
  export { BuildRoutesOptions, _setExternalContributorSources, assertRouteUnique, buildRouteTable };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.4.0
2
+ * @forinda/kickjs v6.5.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,4 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{METADATA}from"../core/interfaces.mjs";import{o as duplicateRouteError,t as Container}from"../container-Da9BLMHy.mjs";import{a as getMethodMeta,o as getMethodMetaOrUndefined,t as getClassMeta}from"../metadata-BhKXlN2F.mjs";import{n as buildPipeline,t as runContributors}from"../contributor-runner-B3Z3Q7J5.mjs";let _externalContributorSources=[];function _setExternalContributorSources(sources){_externalContributorSources=sources}function assertRouteUnique(registry,method,fullPath,owner){let key=`${method.toUpperCase()} ${fullPath.replace(/:[^/]+/g,`:*`)}`,prior=registry.get(key);if(prior!==void 0)throw duplicateRouteError(method.toUpperCase(),fullPath,owner,prior);registry.set(key,owner)}function buildRouteTable(controllerClass,options={}){let container=Container.getInstance(),externalSources=options.externalSources??_externalContributorSources,routes=getClassMeta(METADATA.ROUTES,controllerClass,[]),classMiddlewares=getClassMeta(METADATA.CLASS_MIDDLEWARES,controllerClass,[]),classContributors=getClassMeta(METADATA.CLASS_CONTRIBUTORS,controllerClass,[]),entries=[];for(let route of routes){let method=route.method.toUpperCase(),fullPath=route.path||`/`,methodMiddlewares=getMethodMeta(METADATA.METHOD_MIDDLEWARES,controllerClass,route.handlerName,[]),fileUploadConfig=getMethodMetaOrUndefined(METADATA.FILE_UPLOAD,controllerClass,route.handlerName),methodContributors=getMethodMeta(METADATA.METHOD_CONTRIBUTORS,controllerClass,route.handlerName,[]),contributorRunner=null;if(classContributors.length>0||methodContributors.length>0||externalSources.length>0){let pipeline=buildPipeline([...methodContributors.map((registration,i)=>({source:`method`,registration,label:`${controllerClass.name}.${String(route.handlerName)}#${i}(${registration.key})`})),...classContributors.map((registration,i)=>({source:`class`,registration,label:`${controllerClass.name}.@class#${i}(${registration.key})`})),...externalSources],{route:`${method} ${fullPath}`});contributorRunner=ctx=>runContributors({pipeline,ctx,container})}let handler=ctx=>container.resolve(controllerClass)[route.handlerName](ctx);entries.push({method,path:fullPath,middlewares:[...classMiddlewares,...methodMiddlewares],contributorRunner,handler,meta:{controller:controllerClass,handlerName:route.handlerName,validation:route.validation,upload:fileUploadConfig}})}return entries}export{_setExternalContributorSources,assertRouteUnique,buildRouteTable};
12
- //# sourceMappingURL=router-builder.mjs.map
11
+ import{n as assertRouteUnique,r as buildRouteTable,t as _setExternalContributorSources}from"../router-builder-BbgVXuy3.mjs";export{_setExternalContributorSources,assertRouteUnique,buildRouteTable};
@@ -1,7 +1,6 @@
1
1
 
2
- import { f as HttpRuntime } from "../../adapter-BjxEv8oW.mjs";
2
+ import { f as HttpRuntime } from "../../adapter-CbMq-NBf.mjs";
3
3
  import { IncomingMessage, ServerResponse } from "node:http";
4
-
5
4
  //#region src/http/runtimes/fastify.d.ts
6
5
  interface FastifyReplyLike {
7
6
  code(statusCode: number): FastifyReplyLike;
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.d.mts","names":[],"sources":["../../../src/http/runtimes/fastify.ts"],"mappings":";;;;;UA6CU,gBAAA;EACR,IAAA,CAAK,UAAA,WAAqB,gBAAA;EAC1B,IAAA,CAAK,OAAA,aAAoB,gBAAA;EACzB,MAAA,CAAO,IAAA,UAAc,KAAA,YAAiB,gBAAA;EACtC,IAAA,CAAK,WAAA,WAAsB,gBAAA;EAC3B,MAAA;EAAA,SACS,IAAA;EAAA,SACA,GAAA,EAAK,cAAA;AAAA;AAAA,UAEN,kBAAA;EACR,GAAA,EAAK,eAAA;EACL,IAAA;EACA,MAAA;EACA,KAAA;EAZ0B;EAc1B,KAAA,SAAc,qBAAA,CAAsB,aAAA;EACpC,WAAA;AAAA;AAAA,KAEG,cAAA,IAAkB,OAAA,EAAS,kBAAA,EAAoB,KAAA,EAAO,gBAAA,YAA4B,OAAA;AAAA,UAC7E,cAAA;EACR,QAAA,CAAS,MAAA,WAAiB,IAAA,aAAiB,OAAA,SAAgB,cAAA;EAC3D,GAAA,CAAI,IAAA,WAAe,EAAA;EACnB,KAAA,CAAM,IAAA;IAAQ,MAAA;IAAgB,GAAA;IAAa,OAAA,EAAS,cAAA;EAAA;EACpD,kBAAA,CAAmB,OAAA,EAAS,cAAA;EAC5B,eAAA,CACE,OAAA,GAAU,GAAA,WAAc,GAAA,EAAK,kBAAA,EAAoB,KAAA,EAAO,gBAAA;EAE1D,KAAA,IAAS,OAAA;EACT,OAAA,CAAQ,GAAA,EAAK,eAAA,EAAiB,GAAA,EAAK,cAAA;AAAA;AAAA,UAY3B,aAAA;EACR,IAAA;EACA,SAAA;EACA,QAAA;EACA,QAAA;EACA,KAAA;EACA,QAAA,SAAiB,OAAO,CAAC,MAAA;AAAA;;;;;;iBAsLX,cAAA,IAAkB,WAAW,CAAC,cAAA;;;;AApNjC;AAAA;;;UA8VI,mBAAA;EACf,OAAA,oBAA2B,cAAA;EAC3B,QAAA,oBAA4B,YAAA;EAC5B,GAAA,oBAAuB,eAAA;AAAA"}
1
+ {"version":3,"file":"fastify.d.mts","names":[],"sources":["../../../src/http/runtimes/fastify.ts"],"mappings":";;;;UA6CU;EACR,KAAK,qBAAqB;EAC1B,KAAK,oBAAoB;EACzB,OAAO,cAAc,iBAAiB;EACtC,KAAK,sBAAsB;EAC3B;WACS;WACA,KAAK;;UAEN;EACR,KAAK;EACL;EACA;EACA;;EAEA,cAAc,sBAAsB;EACpC;;KAEG,kBAAkB,SAAS,oBAAoB,OAAO,4BAA4B;UAC7E;EACR,SAAS,iBAAiB,iBAAiB,gBAAgB;EAC3D,IAAI,eAAe;EACnB,MAAM;IAAQ;IAAgB;IAAa,SAAS;;EACpD,mBAAmB,SAAS;EAC5B,gBACE,UAAU,cAAc,KAAK,oBAAoB,OAAO;EAE1D,SAAS;EACT,QAAQ,KAAK,iBAAiB,KAAK;;UAY3B;EACR;EACA;EACA;EACA;EACA;EACA,iBAAiB,QAAQ;;;;;;;iBAsLX,kBAAkB,YAAY;;;;;;;;UA0I7B;EACf,2BAA2B;EAC3B,4BAA4B;EAC5B,uBAAuB"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.4.0
2
+ * @forinda/kickjs v6.5.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,6 +8,6 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{i as requestStore,t as RequestContext}from"../../context-Cs0939u1.mjs";import{a as disposeRequestStore,i as createRequestStore,t as applyHandlerResult}from"../../reply-edT8RGBB.mjs";import{validate}from"../middleware/validate.mjs";import{t as applyUploadConfig}from"../../upload-config-C3MSF7RF.mjs";import"../middleware/upload.mjs";import{createRequire}from"node:module";const peerRequire=createRequire(import.meta.url),loadPeer=name=>{let mod=peerRequire(name);return mod&&mod.__esModule&&mod.default?mod.default:mod.default??mod},QUEUE=Symbol(`kickjs.fastify.connectQueue`),READY=Symbol(`kickjs.fastify.ready`),NEXT=Symbol(`kickjs.fastify.next`),NEEDS_MULTIPART=Symbol(`kickjs.fastify.needsMultipart`),NOOP_NEXT=()=>{};var FastifyReplyDriver=class{reply;constructor(reply){this.reply=reply}status(code){return this.reply.code(code),this}json(data){return this.reply.send(data),this}send(data){return this.reply.send(data),this}type(contentType){return this.reply.type(contentType),this}setHeader(name,value){return this.reply.header(name,value),this}render(){throw Error(`ctx.render() is not supported on the Fastify runtime (no view engine).`)}writeHead(statusCode,headers){return this.reply.hijack(),this.reply.raw.writeHead(statusCode,headers),this}flushHeaders(){return this.reply.raw.flushHeaders?.(),this}write(chunk){return this.reply.raw.write(chunk)}end(data){return this.reply.raw.end(data),this}once(event,listener){return this.reply.raw.once(event,listener),this}get headersSent(){return this.reply.sent||this.reply.raw.headersSent}};function replyDriver(reply){return new FastifyReplyDriver(reply)}function routeHandler(entry){let validator=entry.meta.validation?validate(entry.meta.validation):void 0;return async(request,reply)=>{let raw=request.raw;if(raw.body=request.body,raw.params=request.params,raw.query=request.query,entry.meta.upload&&entry.meta.upload.mode!==`none`){if(typeof request.parts!=`function`)throw Error(`@forinda/kickjs: @FileUpload on the Fastify runtime requires '@fastify/multipart'.
12
- Install it as a peer dependency: pnpm add @fastify/multipart.`);let rawParts=[],fields={};for await(let part of request.parts())part.type===`file`&&part.toBuffer?rawParts.push({fieldname:part.fieldname,filename:part.filename??``,mimetype:part.mimetype??`application/octet-stream`,buffer:await part.toBuffer()}):fields[part.fieldname]=part.value;let{file,files}=applyUploadConfig(rawParts,entry.meta.upload);raw.file=file,raw.files=files,raw.body={...raw.body,...fields}}let headerId=raw.headers[`x-request-id`],store=createRequestStore((Array.isArray(headerId)?headerId[0]:headerId)||raw.requestId);raw.requestId=store.requestId,reply.raw.once(`close`,()=>disposeRequestStore(store)),await requestStore.run(store,async()=>{let ctx=new RequestContext(raw,reply,NOOP_NEXT,replyDriver(reply));validator&&await new Promise((resolve,reject)=>{validator(raw,void 0,err=>err?reject(err):resolve())});for(let mw of entry.middlewares){let advanced=!1;if(await new Promise((resolve,reject)=>{Promise.resolve(mw(ctx,err=>{advanced=!0,err?reject(err):resolve()})).catch(reject)}),!advanced||reply.sent)return}if(entry.contributorRunner&&await entry.contributorRunner(ctx),reply.sent)return;let result=await entry.handler(ctx);reply.sent||applyHandlerResult(ctx,result)})}}function fastifyRuntime(){return{name:`fastify`,createApp(options={}){let app=loadPeer(`fastify`)({logger:!1,routerOptions:{ignoreTrailingSlash:!0},trustProxy:typeof options.trustProxy==`function`?!1:options.trustProxy});return app[QUEUE]=[],app},nodeHandler(app){let state=app;state[READY]||(state[READY]=(async()=>{if(state[NEEDS_MULTIPART]){let multipart=loadPeer(`@fastify/multipart`);await app.register(multipart,{limits:{fileSize:2**53-1}})}let middie=loadPeer(`@fastify/middie`);await app.register(middie);for(let{mw,opts}of state[QUEUE]??[])opts?.path===void 0?app.use(mw):app.use(opts.path,mw);await app.ready()})());let readyP=state[READY];return(req,res,next)=>{next&&(req[NEXT]=next),readyP.then(()=>app.routing(req,res)).catch(err=>{next&&next(err)})}},mountRoutes(app,table){for(let{mountPath,routes}of table)for(let entry of routes){entry.meta.upload&&(app[NEEDS_MULTIPART]=!0);let url=joinPath(mountPath,entry.path);app.route({method:entry.method,url,handler:routeHandler(entry)})}},useConnect(app,mw,opts){app[QUEUE]?.push({mw,opts})},serveStatic(app,prefix,dir){let serveStatic=loadPeer(`serve-static`);this.useConnect(app,serveStatic(dir),{path:prefix})},setNotFound(app,mw){app.setNotFoundHandler((request,reply)=>{let next=request.raw[NEXT];if(next){reply.hijack(),next();return}mw(request.raw,replyDriver(reply),NOOP_NEXT)})},setErrorHandler(app,mw){app.setErrorHandler((err,request,reply)=>{mw(err,request.raw,replyDriver(reply),NOOP_NEXT)})},capabilities:{render:!1,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!0}}}function joinPath(mountPath,path){let a=mountPath.endsWith(`/`)?mountPath.slice(0,-1):mountPath;return path===`/`||path===``?a===``?`/`:a:`${a}${path.startsWith(`/`)?path:`/${path}`}`}export{fastifyRuntime};
11
+ import{i as requestStore,t as RequestContext}from"../../context-DQaUwwQM.mjs";import{a as disposeRequestStore,i as createRequestStore,t as applyHandlerResult}from"../../reply-C1ljl6xS.mjs";import{validate}from"../middleware/validate.mjs";import{t as applyUploadConfig}from"../../upload-config-Dd_QvlpC.mjs";import"../middleware/upload.mjs";import{createRequire}from"node:module";const peerRequire=createRequire(import.meta.url),loadPeer=name=>{let mod=peerRequire(name);return mod&&mod.__esModule&&mod.default?mod.default:mod.default??mod},QUEUE=Symbol(`kickjs.fastify.connectQueue`),READY=Symbol(`kickjs.fastify.ready`),NEXT=Symbol(`kickjs.fastify.next`),NEEDS_MULTIPART=Symbol(`kickjs.fastify.needsMultipart`),NOOP_NEXT=()=>{};var FastifyReplyDriver=class{reply;constructor(reply){this.reply=reply}status(code){return this.reply.code(code),this}json(data){return this.reply.send(data),this}send(data){return this.reply.send(data),this}type(contentType){return this.reply.type(contentType),this}setHeader(name,value){return this.reply.header(name,value),this}render(){throw Error(`ctx.render() is not supported on the Fastify runtime (no view engine).`)}writeHead(statusCode,headers){return this.reply.hijack(),this.reply.raw.writeHead(statusCode,headers),this}flushHeaders(){return this.reply.raw.flushHeaders?.(),this}write(chunk){return this.reply.raw.write(chunk)}end(data){return this.reply.raw.end(data),this}once(event,listener){return this.reply.raw.once(event,listener),this}get headersSent(){return this.reply.sent||this.reply.raw.headersSent}};function replyDriver(reply){return new FastifyReplyDriver(reply)}function routeHandler(entry){let validator=entry.meta.validation?validate(entry.meta.validation):void 0;return async(request,reply)=>{let raw=request.raw;if(raw.body=request.body,raw.params=request.params,raw.query=request.query,entry.meta.upload&&entry.meta.upload.mode!==`none`){if(typeof request.parts!=`function`)throw Error(`@forinda/kickjs: @FileUpload on the Fastify runtime requires '@fastify/multipart'.
12
+ Install it as a peer dependency: pnpm add @fastify/multipart.`);let rawParts=[],fields={};for await(let part of request.parts())part.type===`file`&&part.toBuffer?rawParts.push({fieldname:part.fieldname,filename:part.filename??``,mimetype:part.mimetype??`application/octet-stream`,buffer:await part.toBuffer()}):fields[part.fieldname]=part.value;let{file,files}=applyUploadConfig(rawParts,entry.meta.upload);raw.file=file,raw.files=files,raw.body={...raw.body,...fields}}let headerId=raw.headers[`x-request-id`],store=createRequestStore((Array.isArray(headerId)?headerId[0]:headerId)||raw.requestId);raw.requestId=store.requestId,reply.raw.once(`close`,()=>disposeRequestStore(store)),await requestStore.run(store,async()=>{let ctx=new RequestContext(raw,reply,NOOP_NEXT,replyDriver(reply));validator&&await new Promise((resolve,reject)=>{validator(raw,void 0,err=>err?reject(err):resolve())});for(let mw of entry.middlewares){let advanced=!1;if(await new Promise((resolve,reject)=>{Promise.resolve(mw(ctx,err=>{advanced=!0,err?reject(err):resolve()})).catch(reject)}),!advanced||reply.sent)return}if(entry.contributorRunner&&await entry.contributorRunner(ctx),reply.sent)return;let result=await entry.handler(ctx);reply.sent||applyHandlerResult(ctx,result)})}}function fastifyRuntime(){return{name:`fastify`,createApp(options={}){let app=loadPeer(`fastify`)({logger:!1,routerOptions:{ignoreTrailingSlash:!0},trustProxy:typeof options.trustProxy!=`function`&&options.trustProxy});return app[QUEUE]=[],app},nodeHandler(app){let state=app;state[READY]||(state[READY]=(async()=>{if(state[NEEDS_MULTIPART]){let multipart=loadPeer(`@fastify/multipart`);await app.register(multipart,{limits:{fileSize:2**53-1}})}let middie=loadPeer(`@fastify/middie`);await app.register(middie);for(let{mw,opts}of state[QUEUE]??[])opts?.path===void 0?app.use(mw):app.use(opts.path,mw);await app.ready()})());let readyP=state[READY];return(req,res,next)=>{next&&(req[NEXT]=next),readyP.then(()=>app.routing(req,res)).catch(err=>{next&&next(err)})}},mountRoutes(app,table){for(let{mountPath,routes}of table)for(let entry of routes){entry.meta.upload&&(app[NEEDS_MULTIPART]=!0);let url=joinPath(mountPath,entry.path);app.route({method:entry.method,url,handler:routeHandler(entry)})}},useConnect(app,mw,opts){app[QUEUE]?.push({mw,opts})},serveStatic(app,prefix,dir){let serveStatic=loadPeer(`serve-static`);this.useConnect(app,serveStatic(dir),{path:prefix})},setNotFound(app,mw){app.setNotFoundHandler((request,reply)=>{let next=request.raw[NEXT];if(next){reply.hijack(),next();return}mw(request.raw,replyDriver(reply),NOOP_NEXT)})},setErrorHandler(app,mw){app.setErrorHandler((err,request,reply)=>{mw(err,request.raw,replyDriver(reply),NOOP_NEXT)})},capabilities:{render:!1,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!0}}}function joinPath(mountPath,path){let a=mountPath.endsWith(`/`)?mountPath.slice(0,-1):mountPath;return path===`/`||path===``?a===``?`/`:a:`${a}${path.startsWith(`/`)?path:`/${path}`}`}export{fastifyRuntime};
13
13
  //# sourceMappingURL=fastify.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.mjs","names":[],"sources":["../../../src/http/runtimes/fastify.ts"],"sourcesContent":["// The Fastify runtime — `@forinda/kickjs/fastify`.\n//\n// Implements the same HttpRuntime contract as expressRuntime, over Fastify 5.\n// KickJS still owns decorators → RouteTable, the contributor pipeline, and the\n// RequestContext surface; this runtime materializes routes as NATIVE Fastify\n// routes and wraps `reply` in a RuntimeResponse so `ctx.json` / `ctx.html` /\n// `ctx.sse` work unchanged.\n//\n// `fastify`, `@fastify/middie` are optional peers — install them alongside\n// `@forinda/kickjs` to use this subpath.\n//\n// Lifecycle note: Fastify registration (`register` / `use` / `route`) is\n// deferred until `ready()`. Connect middleware needs `@fastify/middie`\n// registered first, so `useConnect` only QUEUES middleware; `nodeHandler`\n// registers middie, then the queued middleware, then awaits `ready()`.\n\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createRequire } from 'node:module'\n\nimport { RequestContext } from '../context'\nimport { applyHandlerResult } from '../reply'\nimport { requestStore } from '../request-store'\nimport { createRequestStore, disposeRequestStore } from '../middleware/request-scope'\nimport { validate } from '../middleware/validate'\nimport { applyUploadConfig, type RawUploadPart } from '../middleware/upload'\nimport type {\n ConnectMiddleware,\n HttpRuntime,\n RouteEntry,\n RouteTable,\n RuntimeAppOptions,\n RuntimeResponse,\n UseConnectOptions,\n} from '../runtime'\n\n// ESM-safe require for the optional peers (fastify / @fastify/middie /\n// serve-static), resolved from wherever this module is installed.\nconst peerRequire = createRequire(import.meta.url)\nconst loadPeer = (name: string): any => {\n const mod = peerRequire(name)\n return mod && mod.__esModule && mod.default ? mod.default : (mod.default ?? mod)\n}\n\n// Minimal structural surface we use from Fastify — avoids a hard type dep so\n// the root package never needs `fastify` installed to typecheck.\ninterface FastifyReplyLike {\n code(statusCode: number): FastifyReplyLike\n send(payload?: unknown): FastifyReplyLike\n header(name: string, value: unknown): FastifyReplyLike\n type(contentType: string): FastifyReplyLike\n hijack(): void\n readonly sent: boolean\n readonly raw: ServerResponse\n}\ninterface FastifyRequestLike {\n raw: IncomingMessage\n body?: unknown\n params?: unknown\n query?: unknown\n /** Present once @fastify/multipart is registered — async iterator of parts. */\n parts?: () => AsyncIterableIterator<MultipartPart>\n isMultipart?: () => boolean\n}\ntype FastifyHandler = (request: FastifyRequestLike, reply: FastifyReplyLike) => void | Promise<void>\ninterface FastifyAppLike {\n register(plugin: unknown, opts?: unknown): Promise<void> | FastifyAppLike\n use(path: unknown, mw?: unknown): unknown\n route(opts: { method: string; url: string; handler: FastifyHandler }): unknown\n setNotFoundHandler(handler: FastifyHandler): unknown\n setErrorHandler(\n handler: (err: unknown, req: FastifyRequestLike, reply: FastifyReplyLike) => void,\n ): unknown\n ready(): Promise<void>\n routing(req: IncomingMessage, res: ServerResponse): void\n}\n\nconst QUEUE = Symbol('kickjs.fastify.connectQueue')\nconst READY = Symbol('kickjs.fastify.ready')\nconst NEXT = Symbol('kickjs.fastify.next')\n// Set during mountRoutes when any route carries @FileUpload metadata, so\n// nodeHandler knows to register @fastify/multipart before ready().\nconst NEEDS_MULTIPART = Symbol('kickjs.fastify.needsMultipart')\nconst NOOP_NEXT = (): void => {}\n\n// A @fastify/multipart part as we consume it from `request.parts()`.\ninterface MultipartPart {\n type: 'file' | 'field'\n fieldname: string\n filename?: string\n mimetype?: string\n value?: unknown\n toBuffer?: () => Promise<Buffer>\n}\n\n/**\n * Wrap a Fastify reply so the RequestContext response helpers drive it.\n * A class so the ~12 driver methods live on a shared prototype — the previous\n * object-literal version re-allocated every method closure on each request.\n */\nclass FastifyReplyDriver implements RuntimeResponse {\n constructor(private readonly reply: FastifyReplyLike) {}\n status(code: number): this {\n this.reply.code(code)\n return this\n }\n json(data: unknown): this {\n this.reply.send(data)\n return this\n }\n send(data: unknown): this {\n this.reply.send(data)\n return this\n }\n type(contentType: string): this {\n this.reply.type(contentType)\n return this\n }\n setHeader(name: string, value: unknown): this {\n this.reply.header(name, value)\n return this\n }\n render(): never {\n throw new Error('ctx.render() is not supported on the Fastify runtime (no view engine).')\n }\n writeHead(statusCode: number, headers?: Record<string, string | number | string[]>): this {\n // Streaming / SSE: take over the raw socket so Fastify doesn't try to\n // serialize a reply we're writing by hand.\n this.reply.hijack()\n this.reply.raw.writeHead(statusCode, headers)\n return this\n }\n flushHeaders(): this {\n ;(this.reply.raw as ServerResponse & { flushHeaders?: () => void }).flushHeaders?.()\n return this\n }\n write(chunk: unknown): boolean {\n return this.reply.raw.write(chunk as never)\n }\n end(data?: unknown): this {\n this.reply.raw.end(data as never)\n return this\n }\n once(event: string, listener: (...args: unknown[]) => void): this {\n this.reply.raw.once(event, listener)\n return this\n }\n get headersSent(): boolean {\n return this.reply.sent || this.reply.raw.headersSent\n }\n}\n\nfunction replyDriver(reply: FastifyReplyLike): RuntimeResponse {\n return new FastifyReplyDriver(reply) as unknown as RuntimeResponse\n}\n\n/** Build the Fastify per-route handler that runs the kickjs pipeline. */\nfunction routeHandler(entry: RouteEntry): FastifyHandler {\n // Validation middleware is built ONCE per route (parity with the Express\n // materializer) — `validate()` constructs a fresh closure, so calling it\n // per request was pure allocation waste.\n const validator = entry.meta.validation ? validate(entry.meta.validation) : undefined\n return async (request, reply) => {\n // Fastify runs the route handler OUTSIDE the connect-middleware chain, so\n // (unlike Express) the requestScopeMiddleware ALS frame isn't active here.\n // Establish it around the pipeline so REQUEST-scoped DI, contributors, and\n // ctx.set/get work. Reuse the inbound x-request-id, or the id a connect\n // middleware (requestId / requestScope, run earlier via middie) already\n // stamped on the raw request, before generating a fresh one.\n // Use the raw node request as `ctx.req`: it natively provides the stream\n // events (`on('close')` / `once`) that SSE and `ctx.signal` need — Fastify's\n // request object doesn't. Copy Fastify's parsed body/params/query onto it so\n // `ctx.body` / `ctx.params` / `ctx.query` keep working.\n const raw = request.raw as IncomingMessage & {\n requestId?: string\n body?: unknown\n params?: unknown\n query?: unknown\n file?: unknown\n files?: unknown\n }\n raw.body = request.body\n raw.params = request.params\n raw.query = request.query\n\n // @FileUpload: buffer multipart parts into the multer-shaped file(s) that\n // `ctx.file` / `ctx.files` expose. Fastify doesn't parse multipart natively\n // — @fastify/multipart (registered in nodeHandler when any route needs it)\n // provides `request.parts()`. Non-file fields are merged onto `raw.body` so\n // validation / `ctx.body` see them, matching multer's req.body behavior.\n if (entry.meta.upload && entry.meta.upload.mode !== 'none') {\n if (typeof request.parts !== 'function') {\n throw new Error(\n \"@forinda/kickjs: @FileUpload on the Fastify runtime requires '@fastify/multipart'.\\n\" +\n 'Install it as a peer dependency: pnpm add @fastify/multipart.',\n )\n }\n const rawParts: RawUploadPart[] = []\n const fields: Record<string, unknown> = {}\n for await (const part of request.parts()) {\n if (part.type === 'file' && part.toBuffer) {\n rawParts.push({\n fieldname: part.fieldname,\n filename: part.filename ?? '',\n mimetype: part.mimetype ?? 'application/octet-stream',\n buffer: await part.toBuffer(),\n })\n } else {\n fields[part.fieldname] = part.value\n }\n }\n const { file, files } = applyUploadConfig(rawParts, entry.meta.upload)\n raw.file = file\n raw.files = files\n raw.body = { ...(raw.body as Record<string, unknown> | undefined), ...fields }\n }\n\n const headerId = raw.headers['x-request-id']\n const requestId = (Array.isArray(headerId) ? headerId[0] : headerId) || raw.requestId\n const store = createRequestStore(requestId)\n // Propagate the resolved id back onto the raw request so anything reading\n // `req.requestId` inside the frame (e.g. request-logger) matches the store\n // and the X-Request-Id response header — parity with Express.\n raw.requestId = store.requestId\n\n // @PreDestroy teardown for REQUEST-scoped instances once the response\n // closes (finished or aborted) — parity with requestScopeMiddleware.\n reply.raw.once('close', () => disposeRequestStore(store))\n\n await requestStore.run(store, async () => {\n const ctx = new RequestContext(raw as never, reply as never, NOOP_NEXT, replyDriver(reply))\n\n // Validation (from @Get(path, schema) / route.validation). `validator` is a\n // connect-style middleware built once at route registration; it mutates\n // req.body/query/params to the parsed value and calls next(err) on failure\n // — it never touches `res`, so it runs cleanly here. A rejection\n // propagates to the error handler (422).\n if (validator) {\n await new Promise<void>((resolve, reject) => {\n validator(raw as never, undefined as never, (err?: unknown) =>\n err ? reject(err) : resolve(),\n )\n })\n }\n\n // Class + method middleware — `(ctx, next)`; await each, stop if it ended\n // the response or called next(err).\n for (const mw of entry.middlewares) {\n let advanced = false\n await new Promise<void>((resolve, reject) => {\n const next = (err?: unknown): void => {\n advanced = true\n if (err) reject(err)\n else resolve()\n }\n Promise.resolve(mw(ctx, next)).catch(reject)\n })\n if (!advanced || reply.sent) return\n }\n\n if (entry.contributorRunner) await entry.contributorRunner(ctx)\n if (reply.sent) return\n const result = await entry.handler(ctx)\n // Return-value handlers (reply.ts): auto-send when nothing was written.\n if (!reply.sent) applyHandlerResult(ctx, result)\n })\n }\n}\n\n/**\n * The Fastify HTTP runtime. Pass to `bootstrap({ runtime: fastifyRuntime() })`.\n * Per spec §10: Fastify's built-in pino logger is disabled (`logger: false`) so\n * the kickjs `requestLogger` middleware stays the single log format.\n */\nexport function fastifyRuntime(): HttpRuntime<FastifyAppLike> {\n return {\n name: 'fastify',\n\n createApp(options: RuntimeAppOptions = {}): FastifyAppLike {\n const Fastify = loadPeer('fastify')\n const app = Fastify({\n logger: false,\n // Match Express / h3 routing leniency: `/api/v1/hello` and\n // `/api/v1/hello/` resolve to the same route. Without this, Fastify's\n // strict router 404s a trailing-slash request — e.g. a controller's\n // root `@Get('/')` (mounted at the prefix) misses `${prefix}/`. Set under\n // `routerOptions` (Fastify 5+; top-level `ignoreTrailingSlash` is\n // deprecated, removed in Fastify 6).\n routerOptions: { ignoreTrailingSlash: true },\n // trustProxy passed through for now; core X-Forwarded-For\n // normalization (spec §10 Q1) is a follow-up.\n trustProxy: typeof options.trustProxy === 'function' ? false : options.trustProxy,\n }) as FastifyAppLike & {\n [QUEUE]?: Array<{ mw: ConnectMiddleware; opts?: UseConnectOptions }>\n }\n app[QUEUE] = []\n return app\n },\n\n nodeHandler(app) {\n // Register middie + queued connect middleware + finalize `ready()` exactly\n // ONCE per app, even though Application may call nodeHandler per request.\n const state = app as {\n [QUEUE]?: Array<{ mw: ConnectMiddleware; opts?: UseConnectOptions }>\n [READY]?: Promise<void>\n [NEEDS_MULTIPART]?: boolean\n }\n if (!state[READY]) {\n state[READY] = (async () => {\n // Register @fastify/multipart first when any route uses @FileUpload so\n // `request.parts()` is available. Our applyUploadConfig enforces the\n // real per-file maxSize, so give the plugin a generous fileSize ceiling\n // (its 1MB default would silently truncate larger uploads first).\n if (state[NEEDS_MULTIPART]) {\n const multipart = loadPeer('@fastify/multipart')\n await app.register(multipart, { limits: { fileSize: Number.MAX_SAFE_INTEGER } })\n }\n const middie = loadPeer('@fastify/middie')\n await app.register(middie)\n for (const { mw, opts } of state[QUEUE] ?? []) {\n if (opts?.path !== undefined)\n (app.use as (p: unknown, m: unknown) => void)(opts.path, mw)\n else (app.use as (m: unknown) => void)(mw)\n }\n await app.ready()\n })()\n }\n const readyP = state[READY]\n\n return (req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void) => {\n if (next) (req as IncomingMessage & { [NEXT]?: (err?: unknown) => void })[NEXT] = next\n readyP\n .then(() => app.routing(req, res))\n .catch((err) => {\n if (next) next(err)\n })\n }\n },\n\n mountRoutes(app, table: RouteTable) {\n for (const { mountPath, routes } of table) {\n for (const entry of routes) {\n if (entry.meta.upload) (app as { [NEEDS_MULTIPART]?: boolean })[NEEDS_MULTIPART] = true\n const url = joinPath(mountPath, entry.path)\n app.route({ method: entry.method, url, handler: routeHandler(entry) })\n }\n }\n },\n\n useConnect(app, mw: ConnectMiddleware, opts?: UseConnectOptions) {\n const queue = (\n app as { [QUEUE]?: Array<{ mw: ConnectMiddleware; opts?: UseConnectOptions }> }\n )[QUEUE]\n queue?.push({ mw, opts })\n },\n\n serveStatic(app, prefix, dir) {\n // Serve via `serve-static` (the standalone connect middleware behind\n // express.static) so the Fastify runtime carries no `express` dependency —\n // queued like any other connect middleware, bridged through middie.\n const serveStatic = loadPeer('serve-static')\n this.useConnect(app, serveStatic(dir) as ConnectMiddleware, { path: prefix })\n },\n\n setNotFound(app, mw: ConnectMiddleware) {\n app.setNotFoundHandler((request, reply) => {\n const next = (request.raw as IncomingMessage & { [NEXT]?: (err?: unknown) => void })[NEXT]\n if (next) {\n // Vite dev fall-through: hand the request back to the outer chain.\n reply.hijack()\n next()\n return\n }\n // Pass the reply DRIVER (not raw res) so the connect-style\n // notFoundHandler's `res.status().json()` lands on the Fastify reply.\n ;(mw as (req: unknown, res: unknown, next: () => void) => void)(\n request.raw,\n replyDriver(reply),\n NOOP_NEXT,\n )\n })\n },\n\n setErrorHandler(app, mw: ConnectMiddleware) {\n app.setErrorHandler((err, request, reply) => {\n // Reply driver (not raw res) so the connect-style errorHandler's\n // `res.status().json()` / `setHeader` land on the Fastify reply.\n ;(mw as (e: unknown, req: unknown, res: unknown, next: () => void) => void)(\n err,\n request.raw,\n replyDriver(reply),\n NOOP_NEXT,\n )\n })\n },\n\n capabilities: {\n render: false,\n uploads: true,\n connectMiddleware: true,\n nativeBodyParsing: true,\n },\n }\n}\n\n/**\n * The Fastify runtime's engine-native types — what the runtime-typed escape\n * hatches resolve to once the `kick/runtime` typegen emits a `KickRuntimeRegister`\n * augmentation pointing here (spec §4.3b). `AdapterContext.app` / `getRuntimeApp()`\n * become `FastifyInstance`; `ctx.req` / `ctx.res` the Fastify request / reply.\n * Mirrors {@link import('../runtime').ExpressRuntimeTypes} for the default engine.\n */\nexport interface FastifyRuntimeTypes {\n request: import('fastify').FastifyRequest\n response: import('fastify').FastifyReply\n app: import('fastify').FastifyInstance\n}\n\n/** Join a mount prefix and a route path into one URL, collapsing slashes. */\nfunction joinPath(mountPath: string, path: string): string {\n const a = mountPath.endsWith('/') ? mountPath.slice(0, -1) : mountPath\n // A root route path ('/' or '') maps to the mount prefix itself — otherwise a\n // controller `@Post('/')` would register at `${prefix}/`, and a request to\n // `${prefix}` (no trailing slash) wouldn't match under Fastify's strict router.\n if (path === '/' || path === '') return a === '' ? '/' : a\n const b = path.startsWith('/') ? path : `/${path}`\n return `${a}${b}`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"fastify.mjs","names":[],"sources":["../../../src/http/runtimes/fastify.ts"],"sourcesContent":["// The Fastify runtime — `@forinda/kickjs/fastify`.\n//\n// Implements the same HttpRuntime contract as expressRuntime, over Fastify 5.\n// KickJS still owns decorators → RouteTable, the contributor pipeline, and the\n// RequestContext surface; this runtime materializes routes as NATIVE Fastify\n// routes and wraps `reply` in a RuntimeResponse so `ctx.json` / `ctx.html` /\n// `ctx.sse` work unchanged.\n//\n// `fastify`, `@fastify/middie` are optional peers — install them alongside\n// `@forinda/kickjs` to use this subpath.\n//\n// Lifecycle note: Fastify registration (`register` / `use` / `route`) is\n// deferred until `ready()`. Connect middleware needs `@fastify/middie`\n// registered first, so `useConnect` only QUEUES middleware; `nodeHandler`\n// registers middie, then the queued middleware, then awaits `ready()`.\n\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createRequire } from 'node:module'\n\nimport { RequestContext } from '../context'\nimport { applyHandlerResult } from '../reply'\nimport { requestStore } from '../request-store'\nimport { createRequestStore, disposeRequestStore } from '../middleware/request-scope'\nimport { validate } from '../middleware/validate'\nimport { applyUploadConfig, type RawUploadPart } from '../middleware/upload'\nimport type {\n ConnectMiddleware,\n HttpRuntime,\n RouteEntry,\n RouteTable,\n RuntimeAppOptions,\n RuntimeResponse,\n UseConnectOptions,\n} from '../runtime'\n\n// ESM-safe require for the optional peers (fastify / @fastify/middie /\n// serve-static), resolved from wherever this module is installed.\nconst peerRequire = createRequire(import.meta.url)\nconst loadPeer = (name: string): any => {\n const mod = peerRequire(name)\n return mod && mod.__esModule && mod.default ? mod.default : (mod.default ?? mod)\n}\n\n// Minimal structural surface we use from Fastify — avoids a hard type dep so\n// the root package never needs `fastify` installed to typecheck.\ninterface FastifyReplyLike {\n code(statusCode: number): FastifyReplyLike\n send(payload?: unknown): FastifyReplyLike\n header(name: string, value: unknown): FastifyReplyLike\n type(contentType: string): FastifyReplyLike\n hijack(): void\n readonly sent: boolean\n readonly raw: ServerResponse\n}\ninterface FastifyRequestLike {\n raw: IncomingMessage\n body?: unknown\n params?: unknown\n query?: unknown\n /** Present once @fastify/multipart is registered — async iterator of parts. */\n parts?: () => AsyncIterableIterator<MultipartPart>\n isMultipart?: () => boolean\n}\ntype FastifyHandler = (request: FastifyRequestLike, reply: FastifyReplyLike) => void | Promise<void>\ninterface FastifyAppLike {\n register(plugin: unknown, opts?: unknown): Promise<void> | FastifyAppLike\n use(path: unknown, mw?: unknown): unknown\n route(opts: { method: string; url: string; handler: FastifyHandler }): unknown\n setNotFoundHandler(handler: FastifyHandler): unknown\n setErrorHandler(\n handler: (err: unknown, req: FastifyRequestLike, reply: FastifyReplyLike) => void,\n ): unknown\n ready(): Promise<void>\n routing(req: IncomingMessage, res: ServerResponse): void\n}\n\nconst QUEUE = Symbol('kickjs.fastify.connectQueue')\nconst READY = Symbol('kickjs.fastify.ready')\nconst NEXT = Symbol('kickjs.fastify.next')\n// Set during mountRoutes when any route carries @FileUpload metadata, so\n// nodeHandler knows to register @fastify/multipart before ready().\nconst NEEDS_MULTIPART = Symbol('kickjs.fastify.needsMultipart')\nconst NOOP_NEXT = (): void => {}\n\n// A @fastify/multipart part as we consume it from `request.parts()`.\ninterface MultipartPart {\n type: 'file' | 'field'\n fieldname: string\n filename?: string\n mimetype?: string\n value?: unknown\n toBuffer?: () => Promise<Buffer>\n}\n\n/**\n * Wrap a Fastify reply so the RequestContext response helpers drive it.\n * A class so the ~12 driver methods live on a shared prototype — the previous\n * object-literal version re-allocated every method closure on each request.\n */\nclass FastifyReplyDriver implements RuntimeResponse {\n constructor(private readonly reply: FastifyReplyLike) {}\n status(code: number): this {\n this.reply.code(code)\n return this\n }\n json(data: unknown): this {\n this.reply.send(data)\n return this\n }\n send(data: unknown): this {\n this.reply.send(data)\n return this\n }\n type(contentType: string): this {\n this.reply.type(contentType)\n return this\n }\n setHeader(name: string, value: unknown): this {\n this.reply.header(name, value)\n return this\n }\n render(): never {\n throw new Error('ctx.render() is not supported on the Fastify runtime (no view engine).')\n }\n writeHead(statusCode: number, headers?: Record<string, string | number | string[]>): this {\n // Streaming / SSE: take over the raw socket so Fastify doesn't try to\n // serialize a reply we're writing by hand.\n this.reply.hijack()\n this.reply.raw.writeHead(statusCode, headers)\n return this\n }\n flushHeaders(): this {\n ;(this.reply.raw as ServerResponse & { flushHeaders?: () => void }).flushHeaders?.()\n return this\n }\n write(chunk: unknown): boolean {\n return this.reply.raw.write(chunk as never)\n }\n end(data?: unknown): this {\n this.reply.raw.end(data as never)\n return this\n }\n once(event: string, listener: (...args: unknown[]) => void): this {\n this.reply.raw.once(event, listener)\n return this\n }\n get headersSent(): boolean {\n return this.reply.sent || this.reply.raw.headersSent\n }\n}\n\nfunction replyDriver(reply: FastifyReplyLike): RuntimeResponse {\n return new FastifyReplyDriver(reply) as unknown as RuntimeResponse\n}\n\n/** Build the Fastify per-route handler that runs the kickjs pipeline. */\nfunction routeHandler(entry: RouteEntry): FastifyHandler {\n // Validation middleware is built ONCE per route (parity with the Express\n // materializer) — `validate()` constructs a fresh closure, so calling it\n // per request was pure allocation waste.\n const validator = entry.meta.validation ? validate(entry.meta.validation) : undefined\n return async (request, reply) => {\n // Fastify runs the route handler OUTSIDE the connect-middleware chain, so\n // (unlike Express) the requestScopeMiddleware ALS frame isn't active here.\n // Establish it around the pipeline so REQUEST-scoped DI, contributors, and\n // ctx.set/get work. Reuse the inbound x-request-id, or the id a connect\n // middleware (requestId / requestScope, run earlier via middie) already\n // stamped on the raw request, before generating a fresh one.\n // Use the raw node request as `ctx.req`: it natively provides the stream\n // events (`on('close')` / `once`) that SSE and `ctx.signal` need — Fastify's\n // request object doesn't. Copy Fastify's parsed body/params/query onto it so\n // `ctx.body` / `ctx.params` / `ctx.query` keep working.\n const raw = request.raw as IncomingMessage & {\n requestId?: string\n body?: unknown\n params?: unknown\n query?: unknown\n file?: unknown\n files?: unknown\n }\n raw.body = request.body\n raw.params = request.params\n raw.query = request.query\n\n // @FileUpload: buffer multipart parts into the multer-shaped file(s) that\n // `ctx.file` / `ctx.files` expose. Fastify doesn't parse multipart natively\n // — @fastify/multipart (registered in nodeHandler when any route needs it)\n // provides `request.parts()`. Non-file fields are merged onto `raw.body` so\n // validation / `ctx.body` see them, matching multer's req.body behavior.\n if (entry.meta.upload && entry.meta.upload.mode !== 'none') {\n if (typeof request.parts !== 'function') {\n throw new Error(\n \"@forinda/kickjs: @FileUpload on the Fastify runtime requires '@fastify/multipart'.\\n\" +\n 'Install it as a peer dependency: pnpm add @fastify/multipart.',\n )\n }\n const rawParts: RawUploadPart[] = []\n const fields: Record<string, unknown> = {}\n for await (const part of request.parts()) {\n if (part.type === 'file' && part.toBuffer) {\n rawParts.push({\n fieldname: part.fieldname,\n filename: part.filename ?? '',\n mimetype: part.mimetype ?? 'application/octet-stream',\n buffer: await part.toBuffer(),\n })\n } else {\n fields[part.fieldname] = part.value\n }\n }\n const { file, files } = applyUploadConfig(rawParts, entry.meta.upload)\n raw.file = file\n raw.files = files\n raw.body = { ...(raw.body as Record<string, unknown> | undefined), ...fields }\n }\n\n const headerId = raw.headers['x-request-id']\n const requestId = (Array.isArray(headerId) ? headerId[0] : headerId) || raw.requestId\n const store = createRequestStore(requestId)\n // Propagate the resolved id back onto the raw request so anything reading\n // `req.requestId` inside the frame (e.g. request-logger) matches the store\n // and the X-Request-Id response header — parity with Express.\n raw.requestId = store.requestId\n\n // @PreDestroy teardown for REQUEST-scoped instances once the response\n // closes (finished or aborted) — parity with requestScopeMiddleware.\n reply.raw.once('close', () => disposeRequestStore(store))\n\n await requestStore.run(store, async () => {\n const ctx = new RequestContext(raw as never, reply as never, NOOP_NEXT, replyDriver(reply))\n\n // Validation (from @Get(path, schema) / route.validation). `validator` is a\n // connect-style middleware built once at route registration; it mutates\n // req.body/query/params to the parsed value and calls next(err) on failure\n // — it never touches `res`, so it runs cleanly here. A rejection\n // propagates to the error handler (422).\n if (validator) {\n await new Promise<void>((resolve, reject) => {\n validator(raw as never, undefined as never, (err?: unknown) =>\n err ? reject(err) : resolve(),\n )\n })\n }\n\n // Class + method middleware — `(ctx, next)`; await each, stop if it ended\n // the response or called next(err).\n for (const mw of entry.middlewares) {\n let advanced = false\n await new Promise<void>((resolve, reject) => {\n const next = (err?: unknown): void => {\n advanced = true\n if (err) reject(err)\n else resolve()\n }\n Promise.resolve(mw(ctx, next)).catch(reject)\n })\n if (!advanced || reply.sent) return\n }\n\n if (entry.contributorRunner) await entry.contributorRunner(ctx)\n if (reply.sent) return\n const result = await entry.handler(ctx)\n // Return-value handlers (reply.ts): auto-send when nothing was written.\n if (!reply.sent) applyHandlerResult(ctx, result)\n })\n }\n}\n\n/**\n * The Fastify HTTP runtime. Pass to `bootstrap({ runtime: fastifyRuntime() })`.\n * Per spec §10: Fastify's built-in pino logger is disabled (`logger: false`) so\n * the kickjs `requestLogger` middleware stays the single log format.\n */\nexport function fastifyRuntime(): HttpRuntime<FastifyAppLike> {\n return {\n name: 'fastify',\n\n createApp(options: RuntimeAppOptions = {}): FastifyAppLike {\n const Fastify = loadPeer('fastify')\n const app = Fastify({\n logger: false,\n // Match Express / h3 routing leniency: `/api/v1/hello` and\n // `/api/v1/hello/` resolve to the same route. Without this, Fastify's\n // strict router 404s a trailing-slash request — e.g. a controller's\n // root `@Get('/')` (mounted at the prefix) misses `${prefix}/`. Set under\n // `routerOptions` (Fastify 5+; top-level `ignoreTrailingSlash` is\n // deprecated, removed in Fastify 6).\n routerOptions: { ignoreTrailingSlash: true },\n // trustProxy passed through for now; core X-Forwarded-For\n // normalization (spec §10 Q1) is a follow-up.\n trustProxy: typeof options.trustProxy === 'function' ? false : options.trustProxy,\n }) as FastifyAppLike & {\n [QUEUE]?: Array<{ mw: ConnectMiddleware; opts?: UseConnectOptions }>\n }\n app[QUEUE] = []\n return app\n },\n\n nodeHandler(app) {\n // Register middie + queued connect middleware + finalize `ready()` exactly\n // ONCE per app, even though Application may call nodeHandler per request.\n const state = app as {\n [QUEUE]?: Array<{ mw: ConnectMiddleware; opts?: UseConnectOptions }>\n [READY]?: Promise<void>\n [NEEDS_MULTIPART]?: boolean\n }\n if (!state[READY]) {\n state[READY] = (async () => {\n // Register @fastify/multipart first when any route uses @FileUpload so\n // `request.parts()` is available. Our applyUploadConfig enforces the\n // real per-file maxSize, so give the plugin a generous fileSize ceiling\n // (its 1MB default would silently truncate larger uploads first).\n if (state[NEEDS_MULTIPART]) {\n const multipart = loadPeer('@fastify/multipart')\n await app.register(multipart, { limits: { fileSize: Number.MAX_SAFE_INTEGER } })\n }\n const middie = loadPeer('@fastify/middie')\n await app.register(middie)\n for (const { mw, opts } of state[QUEUE] ?? []) {\n if (opts?.path !== undefined)\n (app.use as (p: unknown, m: unknown) => void)(opts.path, mw)\n else (app.use as (m: unknown) => void)(mw)\n }\n await app.ready()\n })()\n }\n const readyP = state[READY]\n\n return (req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void) => {\n if (next) (req as IncomingMessage & { [NEXT]?: (err?: unknown) => void })[NEXT] = next\n readyP\n .then(() => app.routing(req, res))\n .catch((err) => {\n if (next) next(err)\n })\n }\n },\n\n mountRoutes(app, table: RouteTable) {\n for (const { mountPath, routes } of table) {\n for (const entry of routes) {\n if (entry.meta.upload) (app as { [NEEDS_MULTIPART]?: boolean })[NEEDS_MULTIPART] = true\n const url = joinPath(mountPath, entry.path)\n app.route({ method: entry.method, url, handler: routeHandler(entry) })\n }\n }\n },\n\n useConnect(app, mw: ConnectMiddleware, opts?: UseConnectOptions) {\n const queue = (\n app as { [QUEUE]?: Array<{ mw: ConnectMiddleware; opts?: UseConnectOptions }> }\n )[QUEUE]\n queue?.push({ mw, opts })\n },\n\n serveStatic(app, prefix, dir) {\n // Serve via `serve-static` (the standalone connect middleware behind\n // express.static) so the Fastify runtime carries no `express` dependency —\n // queued like any other connect middleware, bridged through middie.\n const serveStatic = loadPeer('serve-static')\n this.useConnect(app, serveStatic(dir) as ConnectMiddleware, { path: prefix })\n },\n\n setNotFound(app, mw: ConnectMiddleware) {\n app.setNotFoundHandler((request, reply) => {\n const next = (request.raw as IncomingMessage & { [NEXT]?: (err?: unknown) => void })[NEXT]\n if (next) {\n // Vite dev fall-through: hand the request back to the outer chain.\n reply.hijack()\n next()\n return\n }\n // Pass the reply DRIVER (not raw res) so the connect-style\n // notFoundHandler's `res.status().json()` lands on the Fastify reply.\n ;(mw as (req: unknown, res: unknown, next: () => void) => void)(\n request.raw,\n replyDriver(reply),\n NOOP_NEXT,\n )\n })\n },\n\n setErrorHandler(app, mw: ConnectMiddleware) {\n app.setErrorHandler((err, request, reply) => {\n // Reply driver (not raw res) so the connect-style errorHandler's\n // `res.status().json()` / `setHeader` land on the Fastify reply.\n ;(mw as (e: unknown, req: unknown, res: unknown, next: () => void) => void)(\n err,\n request.raw,\n replyDriver(reply),\n NOOP_NEXT,\n )\n })\n },\n\n capabilities: {\n render: false,\n uploads: true,\n connectMiddleware: true,\n nativeBodyParsing: true,\n },\n }\n}\n\n/**\n * The Fastify runtime's engine-native types — what the runtime-typed escape\n * hatches resolve to once the `kick/runtime` typegen emits a `KickRuntimeRegister`\n * augmentation pointing here (spec §4.3b). `AdapterContext.app` / `getRuntimeApp()`\n * become `FastifyInstance`; `ctx.req` / `ctx.res` the Fastify request / reply.\n * Mirrors {@link import('../runtime').ExpressRuntimeTypes} for the default engine.\n */\nexport interface FastifyRuntimeTypes {\n request: import('fastify').FastifyRequest\n response: import('fastify').FastifyReply\n app: import('fastify').FastifyInstance\n}\n\n/** Join a mount prefix and a route path into one URL, collapsing slashes. */\nfunction joinPath(mountPath: string, path: string): string {\n const a = mountPath.endsWith('/') ? mountPath.slice(0, -1) : mountPath\n // A root route path ('/' or '') maps to the mount prefix itself — otherwise a\n // controller `@Post('/')` would register at `${prefix}/`, and a request to\n // `${prefix}` (no trailing slash) wouldn't match under Fastify's strict router.\n if (path === '/' || path === '') return a === '' ? '/' : a\n const b = path.startsWith('/') ? path : `/${path}`\n return `${a}${b}`\n}\n"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
 
2
- import { f as HttpRuntime } from "../../adapter-BjxEv8oW.mjs";
2
+ import { f as HttpRuntime } from "../../adapter-CbMq-NBf.mjs";
3
3
  import { IncomingMessage, ServerResponse } from "node:http";
4
-
5
4
  //#region src/http/runtimes/h3-web.d.ts
6
5
  interface H3AppLike {
7
6
  on(method: string, path: string, handler: (event: H3EventLike) => unknown): unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"h3-web.d.mts","names":[],"sources":["../../../src/http/runtimes/h3-web.ts"],"mappings":";;;;;UAuCU,SAAA;EACR,EAAA,CAAG,MAAA,UAAgB,IAAA,UAAc,OAAA,GAAU,KAAA,EAAO,WAAA;EAClD,GAAA,CAAI,IAAA,UAAc,OAAA,GAAU,KAAA,EAAO,WAAA;EACnC,GAAA,IAAO,IAAA;EACP,KAAA,CAAM,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,QAAA;AAAA;AAAA,UAEzB,WAAA;EACR,GAAA,EAAK,OAAA;IAAY,OAAA;MAAY,IAAA;QAAS,GAAA,EAAK,eAAA;QAAiB,GAAA,EAAK,cAAA;MAAA;IAAA;EAAA;EACjE,GAAA,EAAK,GAAA;EACL,OAAA;IAAW,MAAA,GAAS,MAAA;EAAA;AAAA;AAAA,UASL,mBAAA;EAhBa;;;;;;;EAwB5B,EAAE;AAAA;;AAtBuC;AAAA;;iBA+E3B,YAAA,CAAa,OAAA,GAAS,mBAAA,GAA2B,WAAA,CAAY,SAAA;;;;;UAiH5D,iBAAA;EACf,OAAA,EAAS,OAAA;EACT,QAAA,EAAU,QAAQ;EAClB,GAAA;AAAA"}
1
+ {"version":3,"file":"h3-web.d.mts","names":[],"sources":["../../../src/http/runtimes/h3-web.ts"],"mappings":";;;;UA0CU;EACR,GAAG,gBAAgB,cAAc,UAAU,OAAO;EAClD,IAAI,cAAc,UAAU,OAAO;EACnC,OAAO;EACP,MAAM,SAAS,UAAU,QAAQ;;UAEzB;EACR,KAAK;IAAY;MAAY;QAAS,KAAK;QAAiB,KAAK;;;;EACjE,KAAK;EACL;IAAW,SAAS;;;UASL;;;;;;;;EAQf;;;;;;iBAsEc,aAAa,UAAS,sBAA2B,YAAY;;;;;UAiH5D;EACf,SAAS;EACT,UAAU;EACV"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.4.0
2
+ * @forinda/kickjs v6.5.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{n as WebRequestShim,r as WebResponseDriver,t as compileWebRoute}from"../../handler-D8Zyie-B.mjs";import{createRequire}from"node:module";const peerRequire=createRequire(import.meta.url),NOTFOUND_MW=Symbol(`kickjs.h3web.notFoundMw`),ERROR_MW=Symbol(`kickjs.h3web.errorMw`),ASSEMBLED=Symbol(`kickjs.h3web.assembled`),NOOP_NEXT=()=>{};function loadH3v2(injected){let m=injected??peerRequire(`h3`),resolved=m&&m.__esModule&&m.default?{...m.default,...m}:m;if(typeof resolved.H3!=`function`)throw Error("@forinda/kickjs: h3WebRuntime() requires h3 v2 (the web-standards line, npm dist-tag 'latest'), but the installed h3 has no H3 class — that is the v1 line. Either `pnpm add h3@latest` for this runtime, or keep h3 v1 and use h3Runtime() from '@forinda/kickjs/h3'.");return resolved}async function runConnectTerminal(mw,event,err){let req=new WebRequestShim(event.req,event.url),driver=new WebResponseDriver(event.req.signal);return mw&&(err===void 0?mw(req,driver,NOOP_NEXT):mw(err,req,driver,NOOP_NEXT)),driver.settled||(err===void 0?driver.status(404).json({error:`Not Found`}):driver.status(500).json({error:`Internal Server Error`})),driver.ready}function h3WebRuntime(options={}){let{H3,toNodeHandler,fromNodeHandler}=loadH3v2(options.h3);return{name:`h3-web`,createApp(_options={}){let holder={},app=new H3({onError:(error,event)=>{let mw=holder.app?.[ERROR_MW];return runConnectTerminal(mw,event,error)}});return holder.app=app,app},nodeHandler(app){let state=app;state[ASSEMBLED]||(state[ASSEMBLED]=!0,app.all(`/**`,event=>{let mw=state[NOTFOUND_MW];return runConnectTerminal(mw,event)}));let listener=toNodeHandler(app);return(req,res,next)=>{try{let result=listener(req,res);result&&typeof result.then==`function`&&result.catch(err=>next?.(err))}catch(err){next?.(err)}}},mountRoutes(app,table){for(let{mountPath,routes}of table)for(let entry of routes){let url=joinPath(mountPath,entry.path),run=compileWebRoute(entry,{onError:async(err,req,driver)=>{let mw=app[ERROR_MW];mw&&mw(err,req,driver,NOOP_NEXT)}});app.on(entry.method,url,event=>run({request:event.req,url:event.url,params:event.context.params??{}}))}},useConnect(app,mw,opts){let handler=fromNodeHandler(mw);opts?.path===void 0?app.use(handler):app.use(joinPath(String(opts.path),`/**`),handler)},serveStatic(app,prefix,dir){let serveStatic=peerRequire(`serve-static`);this.useConnect(app,serveStatic(dir),{path:prefix})},setNotFound(app,mw){app[NOTFOUND_MW]=mw},setErrorHandler(app,mw){app[ERROR_MW]=mw},capabilities:{render:!1,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!0}}}function joinPath(mountPath,path){let a=mountPath.endsWith(`/`)?mountPath.slice(0,-1):mountPath;return path===`/`||path===``?a===``?`/`:a:`${a}${path.startsWith(`/`)?path:`/${path}`}`}export{h3WebRuntime};
11
+ import{createLogger,describeError}from"../../core/logger.mjs";import{n as WebRequestShim,r as WebResponseDriver,t as compileWebRoute}from"../../handler-Ci1NMv-9.mjs";import{createRequire}from"node:module";const log=createLogger(`H3WebRuntime`),peerRequire=createRequire(import.meta.url),NOTFOUND_MW=Symbol(`kickjs.h3web.notFoundMw`),ERROR_MW=Symbol(`kickjs.h3web.errorMw`),ASSEMBLED=Symbol(`kickjs.h3web.assembled`),NOOP_NEXT=()=>{};function loadH3v2(injected){let m=injected??peerRequire(`h3`),resolved=m&&m.__esModule&&m.default?{...m.default,...m}:m;if(typeof resolved.H3!=`function`)throw Error("@forinda/kickjs: h3WebRuntime() requires h3 v2 (the web-standards line, npm dist-tag 'latest'), but the installed h3 has no H3 class — that is the v1 line. Either `pnpm add h3@latest` for this runtime, or keep h3 v1 and use h3Runtime() from '@forinda/kickjs/h3'.");return resolved}async function runConnectTerminal(mw,event,err){let req=new WebRequestShim(event.req,event.url),driver=new WebResponseDriver(event.req.signal);return mw&&(err===void 0?mw(req,driver,NOOP_NEXT):mw(err,req,driver,NOOP_NEXT)),driver.settled||(err===void 0?driver.status(404).json({error:`Not Found`}):(log.error(err,`h3-web: error handler did not settle the response — ${describeError(err)}`),driver.status(500).json({error:`Internal Server Error`,...typeof process<`u`&&process.env?.NODE_ENV===`production`?{}:{message:describeError(err)}}))),driver.ready}function h3WebRuntime(options={}){let{H3,toNodeHandler,fromNodeHandler}=loadH3v2(options.h3);return{name:`h3-web`,createApp(_options={}){let holder={},app=new H3({onError:(error,event)=>{let mw=holder.app?.[ERROR_MW];return runConnectTerminal(mw,event,error)}});return holder.app=app,app},nodeHandler(app){let state=app;state[ASSEMBLED]||(state[ASSEMBLED]=!0,app.all(`/**`,event=>{let mw=state[NOTFOUND_MW];return runConnectTerminal(mw,event)}));let listener=toNodeHandler(app);return(req,res,next)=>{try{let result=listener(req,res);result&&typeof result.then==`function`&&result.catch(err=>next?.(err))}catch(err){next?.(err)}}},mountRoutes(app,table){for(let{mountPath,routes}of table)for(let entry of routes){let url=joinPath(mountPath,entry.path),run=compileWebRoute(entry,{onError:async(err,req,driver)=>{let mw=app[ERROR_MW];mw&&mw(err,req,driver,NOOP_NEXT)}});app.on(entry.method,url,event=>run({request:event.req,url:event.url,params:event.context.params??{}}))}},useConnect(app,mw,opts){let handler=fromNodeHandler(mw);opts?.path===void 0?app.use(handler):app.use(joinPath(String(opts.path),`/**`),handler)},serveStatic(app,prefix,dir){let serveStatic=peerRequire(`serve-static`);this.useConnect(app,serveStatic(dir),{path:prefix})},setNotFound(app,mw){app[NOTFOUND_MW]=mw},setErrorHandler(app,mw){app[ERROR_MW]=mw},capabilities:{render:!1,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!0}}}function joinPath(mountPath,path){let a=mountPath.endsWith(`/`)?mountPath.slice(0,-1):mountPath;return path===`/`||path===``?a===``?`/`:a:`${a}${path.startsWith(`/`)?path:`/${path}`}`}export{h3WebRuntime};
12
12
  //# sourceMappingURL=h3-web.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"h3-web.mjs","names":[],"sources":["../../../src/http/runtimes/h3-web.ts"],"sourcesContent":["// The h3 v2 (web-standards) runtime — `@forinda/kickjs/h3-web`.\n//\n// ADDITIVE runtime: the h3 v1 runtime (`./h3.ts`, `@forinda/kickjs/h3`)\n// stays untouched so existing adopters keep working (locked decision,\n// web-standards-edge-design.md §3.1). This runtime targets h3 >= 2 — the\n// web-standard rebase where `event.req` is a WHATWG Request, handlers\n// return values/Responses, and `app.fetch(request)` is the universal entry.\n//\n// The request pipeline runs through the shared web driver pair\n// (`../web/driver`, `../web/handler`) — the same code path the\n// `@forinda/kickjs/web` edge entry uses, so node bootstrap and edge deploys\n// exercise identical request semantics.\n//\n// Known limitation vs the v1 runtime: no Vite dev-server fall-through —\n// unmatched requests are answered by the kick notFound handler (h3 v2 owns\n// the full request; there is no node `next()` bail-out mid-engine). Use the\n// v1 h3 runtime or Express for Vite-integrated dev setups.\n\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createRequire } from 'node:module'\n\nimport type {\n ConnectMiddleware,\n HttpRuntime,\n RouteTable,\n RuntimeAppOptions,\n UseConnectOptions,\n} from '../runtime'\nimport { compileWebRoute, type WebRouteHooks } from '../web/handler'\nimport { WebRequestShim, WebResponseDriver } from '../web/driver'\n\nconst peerRequire = createRequire(import.meta.url)\n\nconst NOTFOUND_MW = Symbol('kickjs.h3web.notFoundMw')\nconst ERROR_MW = Symbol('kickjs.h3web.errorMw')\nconst ASSEMBLED = Symbol('kickjs.h3web.assembled')\nconst NOOP_NEXT = (): void => {}\n\n// Minimal structural surface of h3 v2 we depend on.\ninterface H3AppLike {\n on(method: string, path: string, handler: (event: H3EventLike) => unknown): unknown\n all(path: string, handler: (event: H3EventLike) => unknown): unknown\n use(...args: unknown[]): unknown\n fetch(request: Request): Promise<Response>\n}\ninterface H3EventLike {\n req: Request & { runtime?: { node?: { req: IncomingMessage; res: ServerResponse } } }\n url: URL\n context: { params?: Record<string, string> }\n}\n\ninterface H3v2Module {\n H3: new (config?: unknown) => H3AppLike\n toNodeHandler: (app: H3AppLike) => (req: IncomingMessage, res: ServerResponse) => void\n fromNodeHandler: (mw: unknown) => unknown\n}\n\nexport interface H3WebRuntimeOptions {\n /**\n * Pre-imported h3 v2 module. Two audiences:\n * - Edge/worker bundlers with no `createRequire` at runtime:\n * `h3WebRuntime({ h3: await import('h3') })`\n * - Tests running both h3 majors side by side via an npm alias.\n * Omitted → the peer is loaded via `createRequire` (node bootstrap path).\n */\n h3?: unknown\n}\n\n/** Resolve h3 (injected or peer-required) and fail fast unless it is v2. */\nfunction loadH3v2(injected?: unknown): H3v2Module {\n const mod = injected ?? peerRequire('h3')\n const m = mod as { __esModule?: boolean; default?: Record<string, unknown> } & Record<\n string,\n unknown\n >\n const resolved = (m && m.__esModule && m.default\n ? { ...m.default, ...m }\n : m) as unknown as H3v2Module\n if (typeof resolved.H3 !== 'function') {\n throw new Error(\n \"@forinda/kickjs: h3WebRuntime() requires h3 v2 (the web-standards line, npm dist-tag 'latest'), \" +\n 'but the installed h3 has no H3 class — that is the v1 line. ' +\n \"Either `pnpm add h3@latest` for this runtime, or keep h3 v1 and use h3Runtime() from '@forinda/kickjs/h3'.\",\n )\n }\n return resolved\n}\n\n/**\n * Bridge a connect-style terminal middleware (kick's notFound / error\n * handlers) onto the web driver pair, returning the produced Response.\n */\nasync function runConnectTerminal(\n mw: ConnectMiddleware | undefined,\n event: H3EventLike,\n err?: unknown,\n): Promise<Response> {\n const req = new WebRequestShim(event.req, event.url)\n const driver = new WebResponseDriver(event.req.signal)\n if (mw) {\n if (err !== undefined) {\n ;(mw as (e: unknown, rq: unknown, rs: unknown, n: () => void) => void)(\n err,\n req,\n driver,\n NOOP_NEXT,\n )\n } else {\n ;(mw as (rq: unknown, rs: unknown, n: () => void) => void)(req, driver, NOOP_NEXT)\n }\n }\n if (!driver.settled) {\n if (err !== undefined) driver.status(500).json({ error: 'Internal Server Error' })\n else driver.status(404).json({ error: 'Not Found' })\n }\n return driver.ready\n}\n\n/**\n * The h3 v2 HTTP runtime. Pass to `bootstrap({ runtime: h3WebRuntime() })`.\n * Requires the h3 v2 peer; fails fast with guidance when v1 is installed.\n */\nexport function h3WebRuntime(options: H3WebRuntimeOptions = {}): HttpRuntime<H3AppLike> {\n const { H3, toNodeHandler, fromNodeHandler } = loadH3v2(options.h3)\n\n return {\n name: 'h3-web',\n\n createApp(_options: RuntimeAppOptions = {}): H3AppLike {\n const holder: { app?: H3AppLike } = {}\n const app = new H3({\n // Engine-level errors (middleware throws outside the kick pipeline)\n // dispatch to the kick error handler once setErrorHandler stashes it.\n onError: (error: unknown, event: H3EventLike) => {\n const mw = (holder.app as unknown as Record<symbol, ConnectMiddleware | undefined>)?.[\n ERROR_MW\n ]\n return runConnectTerminal(mw, event, error)\n },\n })\n holder.app = app\n return app\n },\n\n nodeHandler(app) {\n const state = app as unknown as Record<symbol, unknown>\n if (!state[ASSEMBLED]) {\n state[ASSEMBLED] = true\n // Terminal catch-all AFTER all mounted routes: kick's own notFound\n // handler answers unmatched paths (h3 v2's default 404 never fires).\n app.all('/**', (event: H3EventLike) => {\n const mw = state[NOTFOUND_MW] as ConnectMiddleware | undefined\n return runConnectTerminal(mw, event)\n })\n }\n const listener = toNodeHandler(app)\n return (req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void) => {\n try {\n const result = listener(req, res) as unknown\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n void (result as Promise<unknown>).catch((err) => next?.(err))\n }\n } catch (err) {\n next?.(err)\n }\n }\n },\n\n mountRoutes(app, table: RouteTable) {\n for (const { mountPath, routes } of table) {\n for (const entry of routes) {\n const url = joinPath(mountPath, entry.path)\n const hooks: WebRouteHooks = {\n onError: async (err, req, driver) => {\n const mw = (app as unknown as Record<symbol, ConnectMiddleware | undefined>)[ERROR_MW]\n if (!mw) return\n ;(mw as (e: unknown, rq: unknown, rs: unknown, n: () => void) => void)(\n err,\n req,\n driver,\n NOOP_NEXT,\n )\n },\n }\n const run = compileWebRoute(entry, hooks)\n app.on(entry.method, url, (event: H3EventLike) =>\n run({\n request: event.req,\n url: event.url,\n params: event.context.params ?? {},\n }),\n )\n }\n }\n },\n\n useConnect(app, mw: ConnectMiddleware, opts?: UseConnectOptions) {\n // Node bootstrap path only — h3 v2 bridges node middleware via\n // fromNodeHandler (the edge entry never calls useConnect).\n const handler = fromNodeHandler(mw)\n if (opts?.path !== undefined) {\n // v2 `use` is exact-match; `/**` covers the subtree like v1's prefix.\n app.use(joinPath(String(opts.path), '/**'), handler)\n } else {\n app.use(handler)\n }\n },\n\n serveStatic(app, prefix, dir) {\n // serve-static bridged like any other connect middleware (node only).\n const serveStatic = peerRequire('serve-static')\n this.useConnect(app, serveStatic(dir) as ConnectMiddleware, { path: prefix })\n },\n\n setNotFound(app, mw: ConnectMiddleware) {\n ;(app as unknown as Record<symbol, ConnectMiddleware>)[NOTFOUND_MW] = mw\n },\n\n setErrorHandler(app, mw: ConnectMiddleware) {\n ;(app as unknown as Record<symbol, ConnectMiddleware>)[ERROR_MW] = mw\n },\n\n capabilities: {\n render: false,\n uploads: true,\n connectMiddleware: true,\n nativeBodyParsing: true,\n },\n }\n}\n\n/**\n * h3-web runtime engine types for the `kick/runtime` typegen augmentation —\n * mirrors {@link import('./h3').H3RuntimeTypes} for the v1 runtime.\n */\nexport interface H3WebRuntimeTypes {\n request: Request\n response: Response\n app: unknown\n}\n\n/** Join a mount prefix and a route path into one URL, collapsing slashes. */\nfunction joinPath(mountPath: string, path: string): string {\n const a = mountPath.endsWith('/') ? mountPath.slice(0, -1) : mountPath\n if (path === '/' || path === '') return a === '' ? '/' : a\n const b = path.startsWith('/') ? path : `/${path}`\n return `${a}${b}`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"h3-web.mjs","names":[],"sources":["../../../src/http/runtimes/h3-web.ts"],"sourcesContent":["// The h3 v2 (web-standards) runtime — `@forinda/kickjs/h3-web`.\n//\n// ADDITIVE runtime: the h3 v1 runtime (`./h3.ts`, `@forinda/kickjs/h3`)\n// stays untouched so existing adopters keep working (locked decision,\n// web-standards-edge-design.md §3.1). This runtime targets h3 >= 2 — the\n// web-standard rebase where `event.req` is a WHATWG Request, handlers\n// return values/Responses, and `app.fetch(request)` is the universal entry.\n//\n// The request pipeline runs through the shared web driver pair\n// (`../web/driver`, `../web/handler`) — the same code path the\n// `@forinda/kickjs/web` edge entry uses, so node bootstrap and edge deploys\n// exercise identical request semantics.\n//\n// Known limitation vs the v1 runtime: no Vite dev-server fall-through —\n// unmatched requests are answered by the kick notFound handler (h3 v2 owns\n// the full request; there is no node `next()` bail-out mid-engine). Use the\n// v1 h3 runtime or Express for Vite-integrated dev setups.\n\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createRequire } from 'node:module'\n\nimport type {\n ConnectMiddleware,\n HttpRuntime,\n RouteTable,\n RuntimeAppOptions,\n UseConnectOptions,\n} from '../runtime'\nimport { compileWebRoute, type WebRouteHooks } from '../web/handler'\nimport { WebRequestShim, WebResponseDriver } from '../web/driver'\nimport { createLogger, describeError } from '../../core/logger'\n\nconst log = createLogger('H3WebRuntime')\n\nconst peerRequire = createRequire(import.meta.url)\n\nconst NOTFOUND_MW = Symbol('kickjs.h3web.notFoundMw')\nconst ERROR_MW = Symbol('kickjs.h3web.errorMw')\nconst ASSEMBLED = Symbol('kickjs.h3web.assembled')\nconst NOOP_NEXT = (): void => {}\n\n// Minimal structural surface of h3 v2 we depend on.\ninterface H3AppLike {\n on(method: string, path: string, handler: (event: H3EventLike) => unknown): unknown\n all(path: string, handler: (event: H3EventLike) => unknown): unknown\n use(...args: unknown[]): unknown\n fetch(request: Request): Promise<Response>\n}\ninterface H3EventLike {\n req: Request & { runtime?: { node?: { req: IncomingMessage; res: ServerResponse } } }\n url: URL\n context: { params?: Record<string, string> }\n}\n\ninterface H3v2Module {\n H3: new (config?: unknown) => H3AppLike\n toNodeHandler: (app: H3AppLike) => (req: IncomingMessage, res: ServerResponse) => void\n fromNodeHandler: (mw: unknown) => unknown\n}\n\nexport interface H3WebRuntimeOptions {\n /**\n * Pre-imported h3 v2 module. Two audiences:\n * - Edge/worker bundlers with no `createRequire` at runtime:\n * `h3WebRuntime({ h3: await import('h3') })`\n * - Tests running both h3 majors side by side via an npm alias.\n * Omitted → the peer is loaded via `createRequire` (node bootstrap path).\n */\n h3?: unknown\n}\n\n/** Resolve h3 (injected or peer-required) and fail fast unless it is v2. */\nfunction loadH3v2(injected?: unknown): H3v2Module {\n const mod = injected ?? peerRequire('h3')\n const m = mod as { __esModule?: boolean; default?: Record<string, unknown> } & Record<\n string,\n unknown\n >\n const resolved = (m && m.__esModule && m.default\n ? { ...m.default, ...m }\n : m) as unknown as H3v2Module\n if (typeof resolved.H3 !== 'function') {\n throw new Error(\n \"@forinda/kickjs: h3WebRuntime() requires h3 v2 (the web-standards line, npm dist-tag 'latest'), \" +\n 'but the installed h3 has no H3 class — that is the v1 line. ' +\n \"Either `pnpm add h3@latest` for this runtime, or keep h3 v1 and use h3Runtime() from '@forinda/kickjs/h3'.\",\n )\n }\n return resolved\n}\n\n/**\n * Bridge a connect-style terminal middleware (kick's notFound / error\n * handlers) onto the web driver pair, returning the produced Response.\n */\nasync function runConnectTerminal(\n mw: ConnectMiddleware | undefined,\n event: H3EventLike,\n err?: unknown,\n): Promise<Response> {\n const req = new WebRequestShim(event.req, event.url)\n const driver = new WebResponseDriver(event.req.signal)\n if (mw) {\n if (err !== undefined) {\n ;(mw as (e: unknown, rq: unknown, rs: unknown, n: () => void) => void)(\n err,\n req,\n driver,\n NOOP_NEXT,\n )\n } else {\n ;(mw as (rq: unknown, rs: unknown, n: () => void) => void)(req, driver, NOOP_NEXT)\n }\n }\n if (!driver.settled) {\n if (err !== undefined) {\n // The configured error handler ran but didn't settle the response\n // (or there wasn't one). Emitting a bare 500 with no log entry\n // makes this failure invisible on both sides — log it here, since\n // by definition nothing downstream will.\n log.error(err, `h3-web: error handler did not settle the response — ${describeError(err)}`)\n driver.status(500).json({\n error: 'Internal Server Error',\n ...(typeof process !== 'undefined' && process.env?.NODE_ENV === 'production'\n ? {}\n : { message: describeError(err) }),\n })\n } else {\n driver.status(404).json({ error: 'Not Found' })\n }\n }\n return driver.ready\n}\n\n/**\n * The h3 v2 HTTP runtime. Pass to `bootstrap({ runtime: h3WebRuntime() })`.\n * Requires the h3 v2 peer; fails fast with guidance when v1 is installed.\n */\nexport function h3WebRuntime(options: H3WebRuntimeOptions = {}): HttpRuntime<H3AppLike> {\n const { H3, toNodeHandler, fromNodeHandler } = loadH3v2(options.h3)\n\n return {\n name: 'h3-web',\n\n createApp(_options: RuntimeAppOptions = {}): H3AppLike {\n const holder: { app?: H3AppLike } = {}\n const app = new H3({\n // Engine-level errors (middleware throws outside the kick pipeline)\n // dispatch to the kick error handler once setErrorHandler stashes it.\n onError: (error: unknown, event: H3EventLike) => {\n const mw = (holder.app as unknown as Record<symbol, ConnectMiddleware | undefined>)?.[\n ERROR_MW\n ]\n return runConnectTerminal(mw, event, error)\n },\n })\n holder.app = app\n return app\n },\n\n nodeHandler(app) {\n const state = app as unknown as Record<symbol, unknown>\n if (!state[ASSEMBLED]) {\n state[ASSEMBLED] = true\n // Terminal catch-all AFTER all mounted routes: kick's own notFound\n // handler answers unmatched paths (h3 v2's default 404 never fires).\n app.all('/**', (event: H3EventLike) => {\n const mw = state[NOTFOUND_MW] as ConnectMiddleware | undefined\n return runConnectTerminal(mw, event)\n })\n }\n const listener = toNodeHandler(app)\n return (req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void) => {\n try {\n const result = listener(req, res) as unknown\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n void (result as Promise<unknown>).catch((err) => next?.(err))\n }\n } catch (err) {\n next?.(err)\n }\n }\n },\n\n mountRoutes(app, table: RouteTable) {\n for (const { mountPath, routes } of table) {\n for (const entry of routes) {\n const url = joinPath(mountPath, entry.path)\n const hooks: WebRouteHooks = {\n onError: async (err, req, driver) => {\n const mw = (app as unknown as Record<symbol, ConnectMiddleware | undefined>)[ERROR_MW]\n if (!mw) return\n ;(mw as (e: unknown, rq: unknown, rs: unknown, n: () => void) => void)(\n err,\n req,\n driver,\n NOOP_NEXT,\n )\n },\n }\n const run = compileWebRoute(entry, hooks)\n app.on(entry.method, url, (event: H3EventLike) =>\n run({\n request: event.req,\n url: event.url,\n params: event.context.params ?? {},\n }),\n )\n }\n }\n },\n\n useConnect(app, mw: ConnectMiddleware, opts?: UseConnectOptions) {\n // Node bootstrap path only — h3 v2 bridges node middleware via\n // fromNodeHandler (the edge entry never calls useConnect).\n const handler = fromNodeHandler(mw)\n if (opts?.path !== undefined) {\n // v2 `use` is exact-match; `/**` covers the subtree like v1's prefix.\n app.use(joinPath(String(opts.path), '/**'), handler)\n } else {\n app.use(handler)\n }\n },\n\n serveStatic(app, prefix, dir) {\n // serve-static bridged like any other connect middleware (node only).\n const serveStatic = peerRequire('serve-static')\n this.useConnect(app, serveStatic(dir) as ConnectMiddleware, { path: prefix })\n },\n\n setNotFound(app, mw: ConnectMiddleware) {\n ;(app as unknown as Record<symbol, ConnectMiddleware>)[NOTFOUND_MW] = mw\n },\n\n setErrorHandler(app, mw: ConnectMiddleware) {\n ;(app as unknown as Record<symbol, ConnectMiddleware>)[ERROR_MW] = mw\n },\n\n capabilities: {\n render: false,\n uploads: true,\n connectMiddleware: true,\n nativeBodyParsing: true,\n },\n }\n}\n\n/**\n * h3-web runtime engine types for the `kick/runtime` typegen augmentation —\n * mirrors {@link import('./h3').H3RuntimeTypes} for the v1 runtime.\n */\nexport interface H3WebRuntimeTypes {\n request: Request\n response: Response\n app: unknown\n}\n\n/** Join a mount prefix and a route path into one URL, collapsing slashes. */\nfunction joinPath(mountPath: string, path: string): string {\n const a = mountPath.endsWith('/') ? mountPath.slice(0, -1) : mountPath\n if (path === '/' || path === '') return a === '' ? '/' : a\n const b = path.startsWith('/') ? path : `/${path}`\n return `${a}${b}`\n}\n"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
 
2
- import { f as HttpRuntime } from "../../adapter-BjxEv8oW.mjs";
3
-
2
+ import { f as HttpRuntime } from "../../adapter-CbMq-NBf.mjs";
4
3
  //#region src/http/runtimes/h3.d.ts
5
4
  interface H3AppLike {
6
5
  use(arg1: unknown, arg2?: unknown): unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"h3.d.mts","names":[],"sources":["../../../src/http/runtimes/h3.ts"],"mappings":";;;;UA4CU,SAAA;EACR,GAAA,CAAI,IAAA,WAAe,IAAA;EACnB,OAAA;AAAA;;;;;;iBA2Kc,SAAA,IAAa,WAAW,CAAC,SAAA;;AA3KhC;AA2KT;;;;AAAkD;KAqI7C,SAAA;EAA0C,GAAA;AAAA,IAAiB,CAAA;AAAA,KAC3D,WAAA;EAA4C,OAAA,UAAiB,IAAA;AAAA,IAC9D,CAAA;AAAA,UAGa,cAAA;EACf,OAAA,EAAS,WAAA;EACT,QAAA,EAAU,WAAA;EACV,GAAA,EAAK,SAAA;AAAA"}
1
+ {"version":3,"file":"h3.d.mts","names":[],"sources":["../../../src/http/runtimes/h3.ts"],"mappings":";;;UA4CU;EACR,IAAI,eAAe;EACnB;;;;;;;iBA2Kc,aAAa,YAAY;;;;;;;;KAqIpC;EAA0C,WAAW;IAAM;KAC3D;EAA4C,iBAAiB,wBAAwB;IACtF;UAGa;EACf,SAAS;EACT,UAAU;EACV,KAAK"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v6.4.0
2
+ * @forinda/kickjs v6.5.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{i as requestStore,t as RequestContext}from"../../context-Cs0939u1.mjs";import{a as disposeRequestStore,i as createRequestStore,t as applyHandlerResult}from"../../reply-edT8RGBB.mjs";import{validate}from"../middleware/validate.mjs";import{t as applyUploadConfig}from"../../upload-config-C3MSF7RF.mjs";import"../middleware/upload.mjs";import{createRequire}from"node:module";const peerRequire=createRequire(import.meta.url),h3=()=>peerRequire(`h3`),NEXT=Symbol(`kickjs.h3.next`),ERROR_MW=Symbol(`kickjs.h3.errorMw`),ROUTER=Symbol(`kickjs.h3.router`),NOTFOUND_MW=Symbol(`kickjs.h3.notFoundMw`),ASSEMBLED=Symbol(`kickjs.h3.assembled`),NOOP_NEXT=()=>{},BODY_METHODS=new Set([`POST`,`PUT`,`PATCH`,`DELETE`]);var NodeResDriver=class{res;constructor(res){this.res=res}status(code){return this.res.statusCode=code,this}json(data){return this.res.headersSent||this.res.setHeader(`content-type`,`application/json; charset=utf-8`),this.res.end(JSON.stringify(data)),this}send(data){return this.res.end(data),this}type(contentType){return this.res.setHeader(`content-type`,contentType),this}setHeader(name,value){return this.res.setHeader(name,value),this}render(){throw Error(`ctx.render() is not supported on the h3 runtime (no view engine).`)}writeHead(statusCode,headers){return this.res.writeHead(statusCode,headers),this}flushHeaders(){return this.res.flushHeaders(),this}write(chunk){return this.res.write(chunk)}end(data){return this.res.end(data),this}once(event,listener){return this.res.once(event,listener),this}get headersSent(){return this.res.headersSent}};function resDriver(res){return new NodeResDriver(res)}function makeEventHandler(entry){let{getRouterParams,getQuery,readBody,readMultipartFormData}=h3(),upload=entry.meta.upload,validator=entry.meta.validation?validate(entry.meta.validation):void 0;return async event=>{let req=event.node.req,res=event.node.res;if(req.params=getRouterParams(event)??{},req.query=getQuery(event)??{},upload&&upload.mode!==`none`){let form=await readMultipartFormData(event).catch(()=>void 0),rawParts=[],fields={};for(let part of form??[])part.filename?rawParts.push({fieldname:part.name??``,filename:part.filename,mimetype:part.type??`application/octet-stream`,buffer:part.data}):part.name&&(fields[part.name]=part.data.toString(`utf-8`));let{file,files}=applyUploadConfig(rawParts,upload);req.file=file,req.files=files,req.body=fields}else BODY_METHODS.has(req.method??`GET`)&&(req.body=await readBody(event).catch(()=>void 0));let headerId=req.headers[`x-request-id`],store=createRequestStore((Array.isArray(headerId)?headerId[0]:headerId)||req.requestId);req.requestId=store.requestId,res.once(`close`,()=>disposeRequestStore(store)),await requestStore.run(store,async()=>{let ctx=new RequestContext(req,res,NOOP_NEXT,resDriver(res));validator&&await new Promise((resolve,reject)=>{validator(req,void 0,err=>err?reject(err):resolve())});for(let mw of entry.middlewares){let advanced=!1;if(await new Promise((resolve,reject)=>{Promise.resolve(mw(ctx,err=>{advanced=!0,err?reject(err):resolve()})).catch(reject)}),!advanced||res.writableEnded)return}if(entry.contributorRunner&&await entry.contributorRunner(ctx),res.writableEnded)return;let result=await entry.handler(ctx);!res.writableEnded&&!res.headersSent&&applyHandlerResult(ctx,result)})}}function h3Runtime(){return{name:`h3`,createApp(_options={}){let{createApp}=h3(),app=createApp({onError:(error,event)=>{let res=event.node.res;if(res.writableEnded)return;let state=app;if(error?.statusCode===404){let next=event.node.req[NEXT];if(next){next();return}let nf=state[NOTFOUND_MW];if(nf){nf(event.node.req,resDriver(res),NOOP_NEXT);return}}let mw=state[ERROR_MW];mw&&mw(error,event.node.req,resDriver(res),NOOP_NEXT)}});return app},nodeHandler(app){let{toNodeListener}=h3(),state=app;state[ASSEMBLED]||(state[ASSEMBLED]=!0,state[ROUTER]&&app.use(state[ROUTER]));let listener=toNodeListener(app);return(req,res,next)=>{next&&(req[NEXT]=next),Promise.resolve(listener(req,res)).catch(err=>{next&&next(err)})}},mountRoutes(app,table){let{createRouter,eventHandler}=h3(),state=app;state[ROUTER]||(state[ROUTER]=createRouter());let router=state[ROUTER];for(let{mountPath,routes}of table)for(let entry of routes){let url=joinPath(mountPath,entry.path);router[entry.method.toLowerCase()](url,eventHandler(makeEventHandler(entry)))}},useConnect(app,mw,opts){let{fromNodeMiddleware}=h3(),handler=fromNodeMiddleware(mw);opts?.path===void 0?app.use(handler):app.use(opts.path,handler)},serveStatic(app,prefix,dir){let serveStatic=peerRequire(`serve-static`);this.useConnect(app,serveStatic(dir),{path:prefix})},setNotFound(app,mw){app[NOTFOUND_MW]=mw},setErrorHandler(app,mw){app[ERROR_MW]=mw},capabilities:{render:!1,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!0}}}function joinPath(mountPath,path){let a=mountPath.endsWith(`/`)?mountPath.slice(0,-1):mountPath;return path===`/`||path===``?a===``?`/`:a:`${a}${path.startsWith(`/`)?path:`/${path}`}`}export{h3Runtime};
11
+ import{i as requestStore,t as RequestContext}from"../../context-DQaUwwQM.mjs";import{a as disposeRequestStore,i as createRequestStore,t as applyHandlerResult}from"../../reply-C1ljl6xS.mjs";import{validate}from"../middleware/validate.mjs";import{t as applyUploadConfig}from"../../upload-config-Dd_QvlpC.mjs";import"../middleware/upload.mjs";import{createRequire}from"node:module";const peerRequire=createRequire(import.meta.url),h3=()=>peerRequire(`h3`),NEXT=Symbol(`kickjs.h3.next`),ERROR_MW=Symbol(`kickjs.h3.errorMw`),ROUTER=Symbol(`kickjs.h3.router`),NOTFOUND_MW=Symbol(`kickjs.h3.notFoundMw`),ASSEMBLED=Symbol(`kickjs.h3.assembled`),NOOP_NEXT=()=>{},BODY_METHODS=new Set([`POST`,`PUT`,`PATCH`,`DELETE`]);var NodeResDriver=class{res;constructor(res){this.res=res}status(code){return this.res.statusCode=code,this}json(data){return this.res.headersSent||this.res.setHeader(`content-type`,`application/json; charset=utf-8`),this.res.end(JSON.stringify(data)),this}send(data){return this.res.end(data),this}type(contentType){return this.res.setHeader(`content-type`,contentType),this}setHeader(name,value){return this.res.setHeader(name,value),this}render(){throw Error(`ctx.render() is not supported on the h3 runtime (no view engine).`)}writeHead(statusCode,headers){return this.res.writeHead(statusCode,headers),this}flushHeaders(){return this.res.flushHeaders(),this}write(chunk){return this.res.write(chunk)}end(data){return this.res.end(data),this}once(event,listener){return this.res.once(event,listener),this}get headersSent(){return this.res.headersSent}};function resDriver(res){return new NodeResDriver(res)}function makeEventHandler(entry){let{getRouterParams,getQuery,readBody,readMultipartFormData}=h3(),upload=entry.meta.upload,validator=entry.meta.validation?validate(entry.meta.validation):void 0;return async event=>{let req=event.node.req,res=event.node.res;if(req.params=getRouterParams(event)??{},req.query=getQuery(event)??{},upload&&upload.mode!==`none`){let form=await readMultipartFormData(event).catch(()=>void 0),rawParts=[],fields={};for(let part of form??[])part.filename?rawParts.push({fieldname:part.name??``,filename:part.filename,mimetype:part.type??`application/octet-stream`,buffer:part.data}):part.name&&(fields[part.name]=part.data.toString(`utf-8`));let{file,files}=applyUploadConfig(rawParts,upload);req.file=file,req.files=files,req.body=fields}else BODY_METHODS.has(req.method??`GET`)&&(req.body=await readBody(event).catch(()=>void 0));let headerId=req.headers[`x-request-id`],store=createRequestStore((Array.isArray(headerId)?headerId[0]:headerId)||req.requestId);req.requestId=store.requestId,res.once(`close`,()=>disposeRequestStore(store)),await requestStore.run(store,async()=>{let ctx=new RequestContext(req,res,NOOP_NEXT,resDriver(res));validator&&await new Promise((resolve,reject)=>{validator(req,void 0,err=>err?reject(err):resolve())});for(let mw of entry.middlewares){let advanced=!1;if(await new Promise((resolve,reject)=>{Promise.resolve(mw(ctx,err=>{advanced=!0,err?reject(err):resolve()})).catch(reject)}),!advanced||res.writableEnded)return}if(entry.contributorRunner&&await entry.contributorRunner(ctx),res.writableEnded)return;let result=await entry.handler(ctx);!res.writableEnded&&!res.headersSent&&applyHandlerResult(ctx,result)})}}function h3Runtime(){return{name:`h3`,createApp(_options={}){let{createApp}=h3(),app=createApp({onError:(error,event)=>{let res=event.node.res;if(res.writableEnded)return;let state=app;if(error?.statusCode===404){let next=event.node.req[NEXT];if(next){next();return}let nf=state[NOTFOUND_MW];if(nf){nf(event.node.req,resDriver(res),NOOP_NEXT);return}}let mw=state[ERROR_MW];mw&&mw(error,event.node.req,resDriver(res),NOOP_NEXT)}});return app},nodeHandler(app){let{toNodeListener}=h3(),state=app;state[ASSEMBLED]||(state[ASSEMBLED]=!0,state[ROUTER]&&app.use(state[ROUTER]));let listener=toNodeListener(app);return(req,res,next)=>{next&&(req[NEXT]=next),Promise.resolve(listener(req,res)).catch(err=>{next&&next(err)})}},mountRoutes(app,table){let{createRouter,eventHandler}=h3(),state=app;state[ROUTER]||(state[ROUTER]=createRouter());let router=state[ROUTER];for(let{mountPath,routes}of table)for(let entry of routes){let url=joinPath(mountPath,entry.path);router[entry.method.toLowerCase()](url,eventHandler(makeEventHandler(entry)))}},useConnect(app,mw,opts){let{fromNodeMiddleware}=h3(),handler=fromNodeMiddleware(mw);opts?.path===void 0?app.use(handler):app.use(opts.path,handler)},serveStatic(app,prefix,dir){let serveStatic=peerRequire(`serve-static`);this.useConnect(app,serveStatic(dir),{path:prefix})},setNotFound(app,mw){app[NOTFOUND_MW]=mw},setErrorHandler(app,mw){app[ERROR_MW]=mw},capabilities:{render:!1,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!0}}}function joinPath(mountPath,path){let a=mountPath.endsWith(`/`)?mountPath.slice(0,-1):mountPath;return path===`/`||path===``?a===``?`/`:a:`${a}${path.startsWith(`/`)?path:`/${path}`}`}export{h3Runtime};
12
12
  //# sourceMappingURL=h3.mjs.map
@@ -36,7 +36,7 @@ interface ParsedQuery {
36
36
  * Without `as const`, the arrays widen to `string[]` and the union falls
37
37
  * back to `string`. This is the documented escape hatch.
38
38
  */
39
- type FieldsOf<TConfig extends QueryFieldConfig | undefined, K extends 'filterable' | 'sortable' | 'searchable'> = TConfig extends { readonly [P in K]: readonly (infer F)[] } ? F extends string ? F : string : TConfig extends {
39
+ type FieldsOf<TConfig extends QueryFieldConfig | undefined, K extends 'filterable' | 'sortable' | 'searchable'> = TConfig extends { readonly [P in K]: readonly (infer F)[]; } ? F extends string ? F : string : TConfig extends {
40
40
  columns: infer C;
41
41
  } ? K extends 'filterable' ? keyof C & string : string : string;
42
42
  /**
@@ -227,4 +227,4 @@ declare function parseQuery<TConfig extends QueryFieldConfig | undefined = undef
227
227
  declare function buildQueryParams(parsed: Partial<ParsedQuery>): Record<string, string | string[] | number>;
228
228
  //#endregion
229
229
  export { SortItem as C, QueryFieldConfig as S, TypedParsedQuery as T, FilterOperator as _, getQueryParsingDefaults as a, ParsedQuery as b, parseQuery as c, resetQueryParsingDefaults as d, setQueryParsingDefaults as f, FilterItem as g, FieldsOf as h, buildQueryParams as i, parseSearchQuery as l, FILTER_OPERATORS as m, QueryRejectReason as n, parseFilters as o, ColumnQueryFieldConfig as p, QueryRejection as r, parsePagination as s, ParseQueryOptions as t, parseSort as u, PaginatedResponse as v, StringQueryFieldConfig as w, QueryBuilderAdapter as x, PaginationParams as y };
230
- //# sourceMappingURL=index-BtWJ9SLP.d.mts.map
230
+ //# sourceMappingURL=index-BfT5KZ-M.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-BfT5KZ-M.d.mts","names":[],"sources":["../src/http/query/types.ts","../src/http/query/parse-query.ts"],"mappings":";;;KACY;cAaC,kBAAgB;UAcZ;EACf;EACA,UAAU;EACV;;UAGe;EACf;EACA;;UAGe;EACf;EACA;EACA;;;;;;;UAQe;EACf,SAAS;EACT,MAAM;EACN,YAAY;EACZ;;;;;;;;;;KAWU,SACV,gBAAgB,8BAChB,sDACE,4BAA4B,KAAK,oBAAoB,UACrD,mBACE,aAEF;EAAkB,eAAe;IAC/B,+BACQ;;;;;;;;;;;;;;;;;;UAqBG,iBACf,gBAAgB,sCACR;EACR,SAAS,MAAM;IAAe,OAAO,SAAS;;EAC9C,MAAM,MAAM;IAAa,OAAO,SAAS;;;;;;;;;;;;;KAa/B,mBAAmB,yBAAyB;UAEvC;EACf;EACA;EACA;;;;;;UAOe;EACf,SAAS;EACT,WAAW;EACX;GACC;;;UAIc,kBAAkB;EACjC,MAAM;EACN;IACE;IACA;IACA;IACA;IACA;IACA;;;;;;;;;;;;;;;;;;;;;;;;;;UA2Ba,oBAAoB,eAAe;;WAEzC;;;;;;EAOT,MAAM,QAAQ,aAAa,QAAQ,UAAU;;;;;;;;;KC7JnC;UAEK;EACf;EACA;EACA,QAAQ;;UAGO;;EAEf;;EAEA;;EAEA;;;;;;EAMA,YAAY,WAAW;;UAGf;EACR;EACA;EACA;EACA;;;;;;iBAgBc,wBACd,SAAS,QAAQ,KAAK;;iBAQR,2BAA2B,SAAS;;;;;;;iBAUpC;;iBAOA,aACd,4CACA,0BACA,UAAU,KAAK,iCACd;;iBAqCa,UACd,0CACA,0BACA,UAAU,KAAK,iCACd;;iBA8Ba,gBACd;EACE;EACA;GAEF,UAAU,KAAK,kDACd;;iBAqBa,iBACd,uBACA,UAAU,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkED,WAAW,gBAAgB,0CACzC,OAAO,qBACP,cAAc,SACd,UAAU,oBACT,iBAAiB;;iBAcJ,iBACd,QAAQ,QAAQ,eACf"}
@@ -1,8 +1,7 @@
1
1
 
2
- import { p as EnvKey } from "./decorators-wEJBqXHg.mjs";
2
+ import { p as EnvKey } from "./decorators-Bks7cgBJ.mjs";
3
3
  import { InferSchemaOutput } from "@forinda/kickjs-schema";
4
4
  import { z } from "zod";
5
-
6
5
  //#region src/config/env.d.ts
7
6
  /**
8
7
  * Build the base env schema. Memoized so repeated calls reuse one
@@ -302,4 +301,4 @@ declare function createConfigService<T extends z.ZodObject<any>>(schema: T): {
302
301
  };
303
302
  //#endregion
304
303
  export { baseEnvSchema as a, loadEnv as c, resetEnvCache as d, Env as i, loadEnvFromSchema as l, TypedConfigService as n, defineEnv as o, createConfigService as r, getEnv as s, ConfigService as t, reloadEnv as u };
305
- //# sourceMappingURL=index-D98NLfS5.d.mts.map
304
+ //# sourceMappingURL=index-M6TF75MS.d.mts.map