@forinda/kickjs 5.5.0 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/{application-Zt956jNS.mjs → application-YJZ2frWX.mjs} +3 -3
  2. package/dist/{application-Zt956jNS.mjs.map → application-YJZ2frWX.mjs.map} +1 -1
  3. package/dist/{bootstrap-CmFQq1ar.mjs → bootstrap-UCZoqbbW.mjs} +3 -3
  4. package/dist/{bootstrap-CmFQq1ar.mjs.map → bootstrap-UCZoqbbW.mjs.map} +1 -1
  5. package/dist/config/index.mjs +2 -2
  6. package/dist/{config-DDz5LuWw.mjs → config-D6VaNpwo.mjs} +3 -3
  7. package/dist/{config-DDz5LuWw.mjs.map → config-D6VaNpwo.mjs.map} +1 -1
  8. package/dist/{container-DrioVLNv.mjs → container-D0v17gF8.mjs} +3 -3
  9. package/dist/{container-DrioVLNv.mjs.map → container-D0v17gF8.mjs.map} +1 -1
  10. package/dist/context-CbX_T0XC.mjs +12 -0
  11. package/dist/context-CbX_T0XC.mjs.map +1 -0
  12. package/dist/{context-BAdAV-zO.d.mts → context-aocg9gO-.d.mts} +36 -1
  13. package/dist/context-aocg9gO-.d.mts.map +1 -0
  14. package/dist/core/adapter.mjs +1 -1
  15. package/dist/core/app-module.mjs +1 -1
  16. package/dist/core/cache.mjs +1 -1
  17. package/dist/core/container.mjs +2 -2
  18. package/dist/core/cron.mjs +2 -2
  19. package/dist/core/decorators.mjs +2 -2
  20. package/dist/core/errors.mjs +1 -1
  21. package/dist/core/interfaces.mjs +1 -1
  22. package/dist/core/logger.mjs +1 -1
  23. package/dist/core/path.mjs +1 -1
  24. package/dist/core/plugin.mjs +1 -1
  25. package/dist/core/reactivity.mjs +1 -1
  26. package/dist/{core-MvABl8Cl.mjs → core-DvTHNOay.mjs} +3 -3
  27. package/dist/{core-MvABl8Cl.mjs.map → core-DvTHNOay.mjs.map} +1 -1
  28. package/dist/{env-CObTuGZ3.mjs → env-yTMEMpV6.mjs} +3 -3
  29. package/dist/{env-CObTuGZ3.mjs.map → env-yTMEMpV6.mjs.map} +1 -1
  30. package/dist/http/application.mjs +2 -2
  31. package/dist/http/bootstrap.mjs +2 -2
  32. package/dist/http/context.d.mts +1 -1
  33. package/dist/http/context.mjs +2 -2
  34. package/dist/http/middleware/cors.mjs +1 -1
  35. package/dist/http/middleware/csrf.mjs +1 -1
  36. package/dist/http/middleware/error-handler.mjs +2 -2
  37. package/dist/http/middleware/helmet.mjs +1 -1
  38. package/dist/http/middleware/rate-limit.mjs +1 -1
  39. package/dist/http/middleware/request-id.mjs +1 -1
  40. package/dist/http/middleware/request-logger.mjs +2 -2
  41. package/dist/http/middleware/session.mjs +1 -1
  42. package/dist/http/middleware/spa.mjs +2 -2
  43. package/dist/http/middleware/upload.mjs +1 -1
  44. package/dist/http/middleware/validate.mjs +2 -2
  45. package/dist/http/middleware/views.d.mts +1 -1
  46. package/dist/http/middleware/views.mjs +2 -2
  47. package/dist/http/query/index.mjs +2 -2
  48. package/dist/http/router-builder.mjs +2 -2
  49. package/dist/index.d.mts +2 -2
  50. package/dist/index.mjs +2 -2
  51. package/dist/{metadata-WTmSU6Sf.mjs → metadata-IHiynOCv.mjs} +2 -2
  52. package/dist/{metadata-WTmSU6Sf.mjs.map → metadata-IHiynOCv.mjs.map} +1 -1
  53. package/dist/{query-BmO0aBf6.mjs → query-CQU-Hyiv.mjs} +2 -2
  54. package/dist/{query-BmO0aBf6.mjs.map → query-CQU-Hyiv.mjs.map} +1 -1
  55. package/dist/{views-1e3jQr5C.d.mts → views-BMhrZsH9.d.mts} +2 -2
  56. package/dist/{views-1e3jQr5C.d.mts.map → views-BMhrZsH9.d.mts.map} +1 -1
  57. package/package.json +1 -1
  58. package/dist/context-B4BeGq1B.mjs +0 -12
  59. package/dist/context-B4BeGq1B.mjs.map +0 -1
  60. package/dist/context-BAdAV-zO.d.mts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{createLogger}from"../../core/logger.mjs";import"../../core-MvABl8Cl.mjs";import{HttpException}from"../../core/errors.mjs";const log=createLogger(`ErrorHandler`);function notFoundHandler(){return(_req,res,_next)=>{res.status(404).json({message:`Not Found`})}}function errorHandler(){let isProduction=process.env.NODE_ENV===`production`;return(err,req,res,_next)=>{if(res.headersSent){log.warn(`Error after headers sent: ${err?.message||`Unknown`}`);return}if(err?.name===`ZodError`){let firstIssue=err.issues?.[0];return res.status(422).json({message:firstIssue?.message||`Validation failed`,...isProduction?{}:{errors:err.issues}})}if(err instanceof HttpException){if(err.status>=500&&log.error(err,err.message),err.headers)for(let[k,v]of Object.entries(err.headers))res.setHeader(k,v);let exposeDetails=!isProduction&&err.details!==void 0;return res.status(err.status).json({message:err.message,...exposeDetails?{errors:err.details}:{}})}let status=err.status||err.statusCode||500;log.error(err,`${req.method} ${req.originalUrl} — ${err.message||`Unhandled error`}`);let message=status===500?`Internal Server Error`:err.message||`Error`;res.status(status).json({message})}}export{errorHandler,notFoundHandler};
11
+ import{createLogger}from"../../core/logger.mjs";import"../../core-DvTHNOay.mjs";import{HttpException}from"../../core/errors.mjs";const log=createLogger(`ErrorHandler`);function notFoundHandler(){return(_req,res,_next)=>{res.status(404).json({message:`Not Found`})}}function errorHandler(){let isProduction=process.env.NODE_ENV===`production`;return(err,req,res,_next)=>{if(res.headersSent){log.warn(`Error after headers sent: ${err?.message||`Unknown`}`);return}if(err?.name===`ZodError`){let firstIssue=err.issues?.[0];return res.status(422).json({message:firstIssue?.message||`Validation failed`,...isProduction?{}:{errors:err.issues}})}if(err instanceof HttpException){if(err.status>=500&&log.error(err,err.message),err.headers)for(let[k,v]of Object.entries(err.headers))res.setHeader(k,v);let exposeDetails=!isProduction&&err.details!==void 0;return res.status(err.status).json({message:err.message,...exposeDetails?{errors:err.details}:{}})}let status=err.status||err.statusCode||500;log.error(err,`${req.method} ${req.originalUrl} — ${err.message||`Unhandled error`}`);let message=status===500?`Internal Server Error`:err.message||`Error`;res.status(status).json({message})}}export{errorHandler,notFoundHandler};
12
12
  //# sourceMappingURL=error-handler.mjs.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{createLogger}from"../../core/logger.mjs";import"../../core-MvABl8Cl.mjs";function requestLogger(options={}){let log=createLogger(options.name??`HTTP`),level=options.level??`info`,skip=options.skip??[];return(req,res,next)=>{if(skip.some(prefix=>req.path.startsWith(prefix)))return next();let start=Date.now();res.on(`finish`,()=>{let duration=Date.now()-start,requestId=req.requestId||req.headers[`x-request-id`]||`-`,status=res.statusCode;log[level](`${req.method} ${req.originalUrl} ${status} ${duration}ms ${requestId}`)}),next()}}export{requestLogger};
11
+ import{createLogger}from"../../core/logger.mjs";import"../../core-DvTHNOay.mjs";function requestLogger(options={}){let log=createLogger(options.name??`HTTP`),level=options.level??`info`,skip=options.skip??[];return(req,res,next)=>{if(skip.some(prefix=>req.path.startsWith(prefix)))return next();let start=Date.now();res.on(`finish`,()=>{let duration=Date.now()-start,requestId=req.requestId||req.headers[`x-request-id`]||`-`,status=res.statusCode;log[level](`${req.method} ${req.originalUrl} ${status} ${duration}ms ${requestId}`)}),next()}}export{requestLogger};
12
12
  //# sourceMappingURL=request-logger.mjs.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{h as __require}from"../../container-DrioVLNv.mjs";import{Logger}from"../../core/logger.mjs";import"../../core-MvABl8Cl.mjs";import{existsSync,readFileSync}from"node:fs";import{join,resolve}from"node:path";const log=Logger.for(`SpaAdapter`);var SpaAdapter=class{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{h as __require}from"../../container-D0v17gF8.mjs";import{Logger}from"../../core/logger.mjs";import"../../core-DvTHNOay.mjs";import{existsSync,readFileSync}from"node:fs";import{join,resolve}from"node:path";const log=Logger.for(`SpaAdapter`);var SpaAdapter=class{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,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import"../../core-MvABl8Cl.mjs";import{HttpException,HttpStatus}from"../../core/errors.mjs";function toValidationException(error,message,useFirstIssueMessage){let issues=error.issues||[],details=issues.map(i=>({field:i.path?.join(`.`)??``,message:i.message})),finalMessage=useFirstIssueMessage?issues[0]?.message??message:message;return new HttpException(HttpStatus.UNPROCESSABLE_ENTITY,finalMessage,details)}function assignReqProperty(req,key,value){Object.defineProperty(req,key,{value,writable:!0,configurable:!0,enumerable:!0})}function validate(schema){return(req,_res,next)=>{try{if(schema.body){let result=schema.body.safeParse(req.body);if(!result.success)return next(toValidationException(result.error,`Validation failed`,!0));req.body=result.data}if(schema.query){let result=schema.query.safeParse(req.query);if(!result.success)return next(toValidationException(result.error,`Invalid query parameters`,!1));assignReqProperty(req,`query`,result.data)}if(schema.params){let result=schema.params.safeParse(req.params);if(!result.success)return next(toValidationException(result.error,`Invalid path parameters`,!1));assignReqProperty(req,`params`,result.data)}next()}catch(err){next(err)}}}export{validate};
11
+ import"../../core-DvTHNOay.mjs";import{HttpException,HttpStatus}from"../../core/errors.mjs";function toValidationException(error,message,useFirstIssueMessage){let issues=error.issues||[],details=issues.map(i=>({field:i.path?.join(`.`)??``,message:i.message})),finalMessage=useFirstIssueMessage?issues[0]?.message??message:message;return new HttpException(HttpStatus.UNPROCESSABLE_ENTITY,finalMessage,details)}function assignReqProperty(req,key,value){Object.defineProperty(req,key,{value,writable:!0,configurable:!0,enumerable:!0})}function validate(schema){return(req,_res,next)=>{try{if(schema.body){let result=schema.body.safeParse(req.body);if(!result.success)return next(toValidationException(result.error,`Validation failed`,!0));req.body=result.data}if(schema.query){let result=schema.query.safeParse(req.query);if(!result.success)return next(toValidationException(result.error,`Invalid query parameters`,!1));assignReqProperty(req,`query`,result.data)}if(schema.params){let result=schema.params.safeParse(req.params);if(!result.success)return next(toValidationException(result.error,`Invalid path parameters`,!1));assignReqProperty(req,`params`,result.data)}next()}catch(err){next(err)}}}export{validate};
12
12
  //# sourceMappingURL=validate.mjs.map
@@ -1,3 +1,3 @@
1
1
 
2
- import { n as ViewAdapterOptions, t as ViewAdapter } from "../../views-1e3jQr5C.mjs";
2
+ import { n as ViewAdapterOptions, t as ViewAdapter } from "../../views-BMhrZsH9.mjs";
3
3
  export { ViewAdapter, ViewAdapterOptions };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{Logger}from"../../core/logger.mjs";import{n as defineAdapter}from"../../core-MvABl8Cl.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};
11
+ import{Logger}from"../../core/logger.mjs";import{n as defineAdapter}from"../../core-DvTHNOay.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
12
  //# sourceMappingURL=views.mjs.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
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 parseSearchQuery,i as parseQuery,n as parseFilters,o as parseSort,r as parsePagination,s as FILTER_OPERATORS,t as buildQueryParams}from"../../query-BmO0aBf6.mjs";export{FILTER_OPERATORS,buildQueryParams,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort};
11
+ import{a as parseSearchQuery,i as parseQuery,n as parseFilters,o as parseSort,r as parsePagination,s as FILTER_OPERATORS,t as buildQueryParams}from"../../query-CQU-Hyiv.mjs";export{FILTER_OPERATORS,buildQueryParams,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
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 Container}from"../container-DrioVLNv.mjs";import{METADATA}from"../core/interfaces.mjs";import{a as getMethodMeta,o as getMethodMetaOrUndefined,t as getClassMeta}from"../metadata-WTmSU6Sf.mjs";import{c as runContributors,l as buildPipeline}from"../core-MvABl8Cl.mjs";import{t as RequestContext}from"../context-B4BeGq1B.mjs";import{validate}from"./middleware/validate.mjs";import{buildUploadMiddleware}from"./middleware/upload.mjs";import{Router}from"express";let _externalContributorSources=[];function _setExternalContributorSources(sources){_externalContributorSources=sources}function buildRoutes(controllerClass,options={}){let router=Router(),container=Container.getInstance(),externalSources=options.externalSources??_externalContributorSources,routes=getClassMeta(METADATA.ROUTES,controllerClass,[]),classMiddlewares=getClassMeta(METADATA.CLASS_MIDDLEWARES,controllerClass,[]),classContributors=getClassMeta(METADATA.CLASS_CONTRIBUTORS,controllerClass,[]);for(let route of routes){let method=route.method.toLowerCase(),fullPath=route.path||`/`,methodMiddlewares=getMethodMeta(METADATA.METHOD_MIDDLEWARES,controllerClass,route.handlerName,[]),handlers=[];route.validation&&handlers.push(validate(route.validation));let fileUploadConfig=getMethodMetaOrUndefined(METADATA.FILE_UPLOAD,controllerClass,route.handlerName);fileUploadConfig&&handlers.push(buildUploadMiddleware(fileUploadConfig));for(let mw of[...classMiddlewares,...methodMiddlewares])handlers.push((req,res,next)=>{let ctx=new RequestContext(req,res,next);Promise.resolve(mw(ctx,next)).catch(next)});let methodContributors=getMethodMeta(METADATA.METHOD_CONTRIBUTORS,controllerClass,route.handlerName,[]);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:`${route.method.toUpperCase()} ${fullPath}`});handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{await runContributors({pipeline,ctx,container}),next()}catch(err){next(err)}})}handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{await container.resolve(controllerClass)[route.handlerName](ctx)}catch(err){next(err)}}),router[method](fullPath,...handlers)}return router}export{_setExternalContributorSources,buildRoutes};
11
+ import{t as Container}from"../container-D0v17gF8.mjs";import{METADATA}from"../core/interfaces.mjs";import{a as getMethodMeta,o as getMethodMetaOrUndefined,t as getClassMeta}from"../metadata-IHiynOCv.mjs";import{c as runContributors,l as buildPipeline}from"../core-DvTHNOay.mjs";import{t as RequestContext}from"../context-CbX_T0XC.mjs";import{validate}from"./middleware/validate.mjs";import{buildUploadMiddleware}from"./middleware/upload.mjs";import{Router}from"express";let _externalContributorSources=[];function _setExternalContributorSources(sources){_externalContributorSources=sources}function buildRoutes(controllerClass,options={}){let router=Router(),container=Container.getInstance(),externalSources=options.externalSources??_externalContributorSources,routes=getClassMeta(METADATA.ROUTES,controllerClass,[]),classMiddlewares=getClassMeta(METADATA.CLASS_MIDDLEWARES,controllerClass,[]),classContributors=getClassMeta(METADATA.CLASS_CONTRIBUTORS,controllerClass,[]);for(let route of routes){let method=route.method.toLowerCase(),fullPath=route.path||`/`,methodMiddlewares=getMethodMeta(METADATA.METHOD_MIDDLEWARES,controllerClass,route.handlerName,[]),handlers=[];route.validation&&handlers.push(validate(route.validation));let fileUploadConfig=getMethodMetaOrUndefined(METADATA.FILE_UPLOAD,controllerClass,route.handlerName);fileUploadConfig&&handlers.push(buildUploadMiddleware(fileUploadConfig));for(let mw of[...classMiddlewares,...methodMiddlewares])handlers.push((req,res,next)=>{let ctx=new RequestContext(req,res,next);Promise.resolve(mw(ctx,next)).catch(next)});let methodContributors=getMethodMeta(METADATA.METHOD_CONTRIBUTORS,controllerClass,route.handlerName,[]);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:`${route.method.toUpperCase()} ${fullPath}`});handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{await runContributors({pipeline,ctx,container}),next()}catch(err){next(err)}})}handlers.push(async(req,res,next)=>{let ctx=new RequestContext(req,res,next);try{await container.resolve(controllerClass)[route.handlerName](ctx)}catch(err){next(err)}}),router[method](fullPath,...handlers)}return router}export{_setExternalContributorSources,buildRoutes};
12
12
  //# sourceMappingURL=router-builder.mjs.map
package/dist/index.d.mts CHANGED
@@ -18,7 +18,7 @@ import { $ as PluginFactory, A as CircuitBreakerStats, B as pushMethodMeta, C as
18
18
  import { n as ApplicationOptions, r as MiddlewareEntry, t as Application } from "./application-CaEYca9E.mjs";
19
19
  import { t as bootstrap } from "./bootstrap-Db8k2-21.mjs";
20
20
  import { _ as SortItem, a as parseSearchQuery, c as FILTER_OPERATORS, d as FilterOperator, f as PaginatedResponse, g as QueryFieldConfig, h as QueryBuilderAdapter, i as parseQuery, l as FieldsOf, m as ParsedQuery, n as parseFilters, o as parseSort, p as PaginationParams, r as parsePagination, t as buildQueryParams, u as FilterItem, y as TypedParsedQuery } from "./index-B9cyOQAe.mjs";
21
- import { i as RouteShape, n as DeepReadonly, r as RequestContext, t as Ctx } from "./context-BAdAV-zO.mjs";
21
+ import { i as RouteShape, n as DeepReadonly, r as RequestContext, t as Ctx } from "./context-aocg9gO-.mjs";
22
22
  import { n as cors, t as CorsOptions } from "./cors-BfgqfLPd.mjs";
23
23
  import { n as csrf, t as CsrfOptions } from "./csrf-DiK9zept.mjs";
24
24
  import { n as notFoundHandler, t as errorHandler } from "./error-handler-Xud_3Lah.mjs";
@@ -30,6 +30,6 @@ import { a as session, i as SessionStore, n as SessionData, r as SessionOptions,
30
30
  import { n as SpaAdapterOptions, t as SpaAdapter } from "./spa-CreyQc4N.mjs";
31
31
  import { a as upload, i as resolveMimeTypes, n as buildUploadMiddleware, r as cleanupFiles, t as UploadOptions } from "./upload-tGMrlhEB.mjs";
32
32
  import { n as validate } from "./validate-HwAyWTwH.mjs";
33
- import { a as TraceContextOptions, c as requestScopeMiddleware, d as getRequestValue, f as requestStore, h as isClusterPrimary, i as TraceContext, l as RequestStore, m as ClusterOptions, n as ViewAdapterOptions, o as parseTraceparent, p as defineHttpContextDecorator, r as TRACEPARENT_HEADER, s as traceContext, t as ViewAdapter, u as getRequestStore } from "./views-1e3jQr5C.mjs";
33
+ import { a as TraceContextOptions, c as requestScopeMiddleware, d as getRequestValue, f as requestStore, h as isClusterPrimary, i as TraceContext, l as RequestStore, m as ClusterOptions, n as ViewAdapterOptions, o as parseTraceparent, p as defineHttpContextDecorator, r as TRACEPARENT_HEADER, s as traceContext, t as ViewAdapter, u as getRequestStore } from "./views-BMhrZsH9.mjs";
34
34
  import { r as buildRoutes } from "./router-builder-CaQ629tw.mjs";
35
35
  export { ASSETS, ASSET_MANIFEST_VERSION, AdapterAsyncOptions, AdapterContext, AdapterFactory, AdapterMiddleware, AnyContributorRegistration, ApiQueryParams, ApiQueryParamsConfig, ApiQueryParamsConfig as QueryParamsConfig, AppAdapter, AppAdapterClass, AppModule, AppModuleClass, AppModuleEntry, Application, ApplicationOptions, Asset, AssetKey, AssetKeyPair, AssetKeyStrategy, AugmentationMeta, Autowired, BaseUploadOptions, BuildContext, BuildPipelineOptions, Buildable, Builder, BuilderOf, CRON_META, CacheEvict, CacheOptions, CacheProvider, Cacheable, CircuitBreaker, CircuitBreakerOptions, CircuitBreakerState, CircuitBreakerStats, CircuitOpenError, ClassKind, ClusterOptions, ColumnApiQueryParamsConfig, Component, ComputedRef, ConfigService, ConsoleLoggerProvider, Constructor, Container, ContainerChangeEvent, ContainerChangeListener, ContextDecorator, ContextDecoratorSpec, ContextMeta, ContextMetaKey, ContributorCycleError, ContributorPipeline, ContributorRegistration, ContributorRegistrations, ContributorSource, Controller, CorsOptions, Cron, CronJobMeta, CsrfOptions, Ctx, DeepReadonly, DefineAdapterOptions, DefineModuleOptions, DefinePluginOptions, Delete, DepValue, DuplicateContributorError, DuplicateMountNameError, Env, EnvKey, ExecutionContext, FILTER_OPERATORS, FieldsOf, FileTypeFilter, FileUpload, FileUploadConfig, FilterItem, FilterOperator, Get, GroupAssetKeysOptions, GroupAssetKeysResult, HelmetOptions, HttpException, HttpStatus, HttpStatusCode, INJECTION_TOKEN, Inject, Injectable, InjectionToken, KickAssets, KickJsPluginName, KickJsPluginRegistry, KickJsRegistry, KickPlugin, Logger, LoggerProvider, METADATA, MaybePromise, MemoryCacheProvider, MetaValue, Middleware, MiddlewareEntry, MiddlewareHandler, MiddlewarePhase, MissingContributorError, MissingMountDepError, ModuleFactory, ModuleList, ModuleRegistry, ModuleRoutes, MountCycleError, MountKind, MountSortItem, MutableModuleRegistry, PaginatedResponse, PaginationParams, ParsedQuery, Patch, PluginAsyncOptions, PluginFactory, Post, PostConstruct, PostConstructStatus, Put, QueryBuilderAdapter, QueryFieldConfig, REQUEST_ID_HEADER, RateLimitOptions, RateLimitStore, Ref, Repository, RequestContext, RequestLoggerOptions, RequestStore, ResolvedDeps, RouteDefinition, RouteShape, RunContributorsOptions, Scope, Service, ServiceOptions, Session, SessionData, SessionOptions, SessionStore, SortItem, SourcedRegistration, SpaAdapter, SpaAdapterOptions, TRACEPARENT_HEADER, TraceContext, TraceContextOptions, TypedConfigService, TypedParsedQuery, UnknownAssetError, UploadOptions, ValidationError, Value, ViewAdapter, ViewAdapterOptions, WatchOptions, assets, baseEnvSchema, bootstrap, buildPipeline, buildQueryParams, buildRoutes, buildUploadMiddleware, cleanupFiles, clearAssetCache, computed, cors, createConfigService, createLogger, createToken, csrf, defineAdapter, defineAugmentation, defineContextDecorator, defineEnv, defineHttpContextDecorator, defineModule, defineModules, definePlugin, errorHandler, getCacheProvider, getClassMeta, getClassMetaOrUndefined, getCronJobs, getEnv, getMetaMap, getMetaRecord, getMethodMeta, getMethodMetaOrUndefined, getRequestStore, getRequestValue, groupAssetKeys, hasClassMeta, helmet, isClusterPrimary, isInjectionToken, isRef, joinPaths, loadEnv, mountSort, normalizeApiQueryParamsConfig, normalizePath, notFoundHandler, parseFilters, parsePagination, parseQuery, parseSearchQuery, parseSort, parseTraceparent, pushClassMeta, pushMethodMeta, rateLimit, reactive, ref, reloadEnv, requestId, requestLogger, requestScopeMiddleware, requestStore, resetEnvCache, resolveAsset, resolveMimeTypes, rootLogger, runContributors, session, setCacheProvider, setClassMeta, setInMetaMap, setInMetaRecord, setMethodMeta, stripExt, toRefs, tokenName, traceContext, unref, upload, useAssets, validate, watch, withBuilder };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,5 +8,5 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{a as UnknownAssetError,c as resolveAsset,d as stripExt,f as INJECTION_TOKEN,i as ASSET_MANIFEST_VERSION,l as useAssets,m as isInjectionToken,n as tokenName,o as assets,p as createToken,r as ASSETS,s as clearAssetCache,t as Container,u as groupAssetKeys}from"./container-DrioVLNv.mjs";import{METADATA,Scope}from"./core/interfaces.mjs";import{ConsoleLoggerProvider,Logger,createLogger,rootLogger}from"./core/logger.mjs";import{a as getMethodMeta,c as pushClassMeta,d as setInMetaMap,f as setInMetaRecord,i as getMetaRecord,l as pushMethodMeta,n as getClassMetaOrUndefined,o as getMethodMetaOrUndefined,p as setMethodMeta,r as getMetaMap,s as hasClassMeta,t as getClassMeta,u as setClassMeta}from"./metadata-WTmSU6Sf.mjs";import{ApiQueryParams,Asset,Autowired,Builder,Component,Controller,Delete,FileUpload,Get,Inject,Injectable,Middleware,Patch,Post,PostConstruct,Put,Repository,Service,Value,normalizeApiQueryParamsConfig,withBuilder}from"./core/decorators.mjs";import{_ as ModuleList,a as MissingMountDepError,c as runContributors,d as DuplicateContributorError,f as MissingContributorError,g as MutableModuleRegistry,h as CircuitOpenError,i as DuplicateMountNameError,l as buildPipeline,m as CircuitBreaker,n as defineAdapter,o as MountCycleError,p as defineContextDecorator,r as definePlugin,s as mountSort,t as defineAugmentation,u as ContributorCycleError,v as defineModules,y as defineModule}from"./core-MvABl8Cl.mjs";import{CRON_META,Cron,getCronJobs}from"./core/cron.mjs";import{CacheEvict,Cacheable,MemoryCacheProvider,getCacheProvider,setCacheProvider}from"./core/cache.mjs";import{HttpException,HttpStatus}from"./core/errors.mjs";import{joinPaths,normalizePath}from"./core/path.mjs";import{computed,isRef,reactive,ref,toRefs,unref,watch}from"./core/reactivity.mjs";import{REQUEST_ID_HEADER,requestId}from"./http/middleware/request-id.mjs";import{errorHandler,notFoundHandler}from"./http/middleware/error-handler.mjs";import{i as requestStore,n as getRequestStore,r as getRequestValue,t as RequestContext}from"./context-B4BeGq1B.mjs";import{n as requestScopeMiddleware,t as Application}from"./application-Zt956jNS.mjs";import{a as parseSearchQuery,i as parseQuery,n as parseFilters,o as parseSort,r as parsePagination,s as FILTER_OPERATORS,t as buildQueryParams}from"./query-BmO0aBf6.mjs";import{validate}from"./http/middleware/validate.mjs";import{buildUploadMiddleware,cleanupFiles,resolveMimeTypes,upload}from"./http/middleware/upload.mjs";import{buildRoutes}from"./http/router-builder.mjs";import{a as reloadEnv,i as loadEnv,n as defineEnv,o as resetEnvCache,r as getEnv,t as baseEnvSchema}from"./env-CObTuGZ3.mjs";import{n as isClusterPrimary,t as bootstrap}from"./bootstrap-CmFQq1ar.mjs";import{csrf}from"./http/middleware/csrf.mjs";import{requestLogger}from"./http/middleware/request-logger.mjs";import{helmet}from"./http/middleware/helmet.mjs";import{cors}from"./http/middleware/cors.mjs";import{rateLimit}from"./http/middleware/rate-limit.mjs";import{session}from"./http/middleware/session.mjs";import{ViewAdapter}from"./http/middleware/views.mjs";import{SpaAdapter}from"./http/middleware/spa.mjs";import{n as createConfigService,t as ConfigService}from"./config-DDz5LuWw.mjs";import crypto from"node:crypto";function defineHttpContextDecorator(spec){return defineContextDecorator(spec)}const TRACEPARENT_HEADER=`traceparent`,TRACEPARENT_RE=/^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;function generateTraceId(){return crypto.randomBytes(16).toString(`hex`)}function generateSpanId(){return crypto.randomBytes(8).toString(`hex`)}function parseTraceparent(header){let match=TRACEPARENT_RE.exec(header.trim().toLowerCase());if(!match)return null;let version=match[1],traceId=match[2],parentSpanId=match[3],flags=match[4];return version===`ff`||traceId===`00000000000000000000000000000000`||parentSpanId===`0000000000000000`?null:{version,traceId,parentSpanId,flags}}function traceContext(options={}){return(req,res,next)=>{let store=requestStore.getStore(),traceparentHeader=req.headers[TRACEPARENT_HEADER],raw=Array.isArray(traceparentHeader)?traceparentHeader[0]:traceparentHeader,parsed=raw?parseTraceparent(raw):null,traceId=parsed?.traceId??generateTraceId(),spanId=generateSpanId();store&&(store.values.set(`traceId`,traceId),store.values.set(`spanId`,spanId),parsed&&(store.values.set(`parentSpanId`,parsed.parentSpanId),store.values.set(`traceFlags`,parsed.flags),store.values.set(`traceVersion`,parsed.version))),req.traceId=traceId,req.spanId=spanId,options.propagateResponse&&res.setHeader(`traceresponse`,traceId),next()}}export{ASSETS,ASSET_MANIFEST_VERSION,ApiQueryParams,Application,Asset,Autowired,Builder,CRON_META,CacheEvict,Cacheable,CircuitBreaker,CircuitOpenError,Component,ConfigService,ConsoleLoggerProvider,Container,ContributorCycleError,Controller,Cron,Delete,DuplicateContributorError,DuplicateMountNameError,FILTER_OPERATORS,FileUpload,Get,HttpException,HttpStatus,INJECTION_TOKEN,Inject,Injectable,Logger,METADATA,MemoryCacheProvider,Middleware,MissingContributorError,MissingMountDepError,ModuleList,MountCycleError,MutableModuleRegistry,Patch,Post,PostConstruct,Put,REQUEST_ID_HEADER,Repository,RequestContext,Scope,Service,SpaAdapter,TRACEPARENT_HEADER,UnknownAssetError,Value,ViewAdapter,assets,baseEnvSchema,bootstrap,buildPipeline,buildQueryParams,buildRoutes,buildUploadMiddleware,cleanupFiles,clearAssetCache,computed,cors,createConfigService,createLogger,createToken,csrf,defineAdapter,defineAugmentation,defineContextDecorator,defineEnv,defineHttpContextDecorator,defineModule,defineModules,definePlugin,errorHandler,getCacheProvider,getClassMeta,getClassMetaOrUndefined,getCronJobs,getEnv,getMetaMap,getMetaRecord,getMethodMeta,getMethodMetaOrUndefined,getRequestStore,getRequestValue,groupAssetKeys,hasClassMeta,helmet,isClusterPrimary,isInjectionToken,isRef,joinPaths,loadEnv,mountSort,normalizeApiQueryParamsConfig,normalizePath,notFoundHandler,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort,parseTraceparent,pushClassMeta,pushMethodMeta,rateLimit,reactive,ref,reloadEnv,requestId,requestLogger,requestScopeMiddleware,requestStore,resetEnvCache,resolveAsset,resolveMimeTypes,rootLogger,runContributors,session,setCacheProvider,setClassMeta,setInMetaMap,setInMetaRecord,setMethodMeta,stripExt,toRefs,tokenName,traceContext,unref,upload,useAssets,validate,watch,withBuilder};
11
+ import{a as UnknownAssetError,c as resolveAsset,d as stripExt,f as INJECTION_TOKEN,i as ASSET_MANIFEST_VERSION,l as useAssets,m as isInjectionToken,n as tokenName,o as assets,p as createToken,r as ASSETS,s as clearAssetCache,t as Container,u as groupAssetKeys}from"./container-D0v17gF8.mjs";import{METADATA,Scope}from"./core/interfaces.mjs";import{ConsoleLoggerProvider,Logger,createLogger,rootLogger}from"./core/logger.mjs";import{a as getMethodMeta,c as pushClassMeta,d as setInMetaMap,f as setInMetaRecord,i as getMetaRecord,l as pushMethodMeta,n as getClassMetaOrUndefined,o as getMethodMetaOrUndefined,p as setMethodMeta,r as getMetaMap,s as hasClassMeta,t as getClassMeta,u as setClassMeta}from"./metadata-IHiynOCv.mjs";import{ApiQueryParams,Asset,Autowired,Builder,Component,Controller,Delete,FileUpload,Get,Inject,Injectable,Middleware,Patch,Post,PostConstruct,Put,Repository,Service,Value,normalizeApiQueryParamsConfig,withBuilder}from"./core/decorators.mjs";import{_ as ModuleList,a as MissingMountDepError,c as runContributors,d as DuplicateContributorError,f as MissingContributorError,g as MutableModuleRegistry,h as CircuitOpenError,i as DuplicateMountNameError,l as buildPipeline,m as CircuitBreaker,n as defineAdapter,o as MountCycleError,p as defineContextDecorator,r as definePlugin,s as mountSort,t as defineAugmentation,u as ContributorCycleError,v as defineModules,y as defineModule}from"./core-DvTHNOay.mjs";import{CRON_META,Cron,getCronJobs}from"./core/cron.mjs";import{CacheEvict,Cacheable,MemoryCacheProvider,getCacheProvider,setCacheProvider}from"./core/cache.mjs";import{HttpException,HttpStatus}from"./core/errors.mjs";import{joinPaths,normalizePath}from"./core/path.mjs";import{computed,isRef,reactive,ref,toRefs,unref,watch}from"./core/reactivity.mjs";import{REQUEST_ID_HEADER,requestId}from"./http/middleware/request-id.mjs";import{errorHandler,notFoundHandler}from"./http/middleware/error-handler.mjs";import{i as requestStore,n as getRequestStore,r as getRequestValue,t as RequestContext}from"./context-CbX_T0XC.mjs";import{n as requestScopeMiddleware,t as Application}from"./application-YJZ2frWX.mjs";import{a as parseSearchQuery,i as parseQuery,n as parseFilters,o as parseSort,r as parsePagination,s as FILTER_OPERATORS,t as buildQueryParams}from"./query-CQU-Hyiv.mjs";import{validate}from"./http/middleware/validate.mjs";import{buildUploadMiddleware,cleanupFiles,resolveMimeTypes,upload}from"./http/middleware/upload.mjs";import{buildRoutes}from"./http/router-builder.mjs";import{a as reloadEnv,i as loadEnv,n as defineEnv,o as resetEnvCache,r as getEnv,t as baseEnvSchema}from"./env-yTMEMpV6.mjs";import{n as isClusterPrimary,t as bootstrap}from"./bootstrap-UCZoqbbW.mjs";import{csrf}from"./http/middleware/csrf.mjs";import{requestLogger}from"./http/middleware/request-logger.mjs";import{helmet}from"./http/middleware/helmet.mjs";import{cors}from"./http/middleware/cors.mjs";import{rateLimit}from"./http/middleware/rate-limit.mjs";import{session}from"./http/middleware/session.mjs";import{ViewAdapter}from"./http/middleware/views.mjs";import{SpaAdapter}from"./http/middleware/spa.mjs";import{n as createConfigService,t as ConfigService}from"./config-D6VaNpwo.mjs";import crypto from"node:crypto";function defineHttpContextDecorator(spec){return defineContextDecorator(spec)}const TRACEPARENT_HEADER=`traceparent`,TRACEPARENT_RE=/^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;function generateTraceId(){return crypto.randomBytes(16).toString(`hex`)}function generateSpanId(){return crypto.randomBytes(8).toString(`hex`)}function parseTraceparent(header){let match=TRACEPARENT_RE.exec(header.trim().toLowerCase());if(!match)return null;let version=match[1],traceId=match[2],parentSpanId=match[3],flags=match[4];return version===`ff`||traceId===`00000000000000000000000000000000`||parentSpanId===`0000000000000000`?null:{version,traceId,parentSpanId,flags}}function traceContext(options={}){return(req,res,next)=>{let store=requestStore.getStore(),traceparentHeader=req.headers[TRACEPARENT_HEADER],raw=Array.isArray(traceparentHeader)?traceparentHeader[0]:traceparentHeader,parsed=raw?parseTraceparent(raw):null,traceId=parsed?.traceId??generateTraceId(),spanId=generateSpanId();store&&(store.values.set(`traceId`,traceId),store.values.set(`spanId`,spanId),parsed&&(store.values.set(`parentSpanId`,parsed.parentSpanId),store.values.set(`traceFlags`,parsed.flags),store.values.set(`traceVersion`,parsed.version))),req.traceId=traceId,req.spanId=spanId,options.propagateResponse&&res.setHeader(`traceresponse`,traceId),next()}}export{ASSETS,ASSET_MANIFEST_VERSION,ApiQueryParams,Application,Asset,Autowired,Builder,CRON_META,CacheEvict,Cacheable,CircuitBreaker,CircuitOpenError,Component,ConfigService,ConsoleLoggerProvider,Container,ContributorCycleError,Controller,Cron,Delete,DuplicateContributorError,DuplicateMountNameError,FILTER_OPERATORS,FileUpload,Get,HttpException,HttpStatus,INJECTION_TOKEN,Inject,Injectable,Logger,METADATA,MemoryCacheProvider,Middleware,MissingContributorError,MissingMountDepError,ModuleList,MountCycleError,MutableModuleRegistry,Patch,Post,PostConstruct,Put,REQUEST_ID_HEADER,Repository,RequestContext,Scope,Service,SpaAdapter,TRACEPARENT_HEADER,UnknownAssetError,Value,ViewAdapter,assets,baseEnvSchema,bootstrap,buildPipeline,buildQueryParams,buildRoutes,buildUploadMiddleware,cleanupFiles,clearAssetCache,computed,cors,createConfigService,createLogger,createToken,csrf,defineAdapter,defineAugmentation,defineContextDecorator,defineEnv,defineHttpContextDecorator,defineModule,defineModules,definePlugin,errorHandler,getCacheProvider,getClassMeta,getClassMetaOrUndefined,getCronJobs,getEnv,getMetaMap,getMetaRecord,getMethodMeta,getMethodMetaOrUndefined,getRequestStore,getRequestValue,groupAssetKeys,hasClassMeta,helmet,isClusterPrimary,isInjectionToken,isRef,joinPaths,loadEnv,mountSort,normalizeApiQueryParamsConfig,normalizePath,notFoundHandler,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort,parseTraceparent,pushClassMeta,pushMethodMeta,rateLimit,reactive,ref,reloadEnv,requestId,requestLogger,requestScopeMiddleware,requestStore,resetEnvCache,resolveAsset,resolveMimeTypes,rootLogger,runContributors,session,setCacheProvider,setClassMeta,setInMetaMap,setInMetaRecord,setMethodMeta,stripExt,toRefs,tokenName,traceContext,unref,upload,useAssets,validate,watch,withBuilder};
12
12
  //# sourceMappingURL=index.mjs.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -9,4 +9,4 @@
9
9
  * @license MIT
10
10
  */
11
11
  import"reflect-metadata";function setClassMeta(key,value,target){Reflect.defineMetadata(key,value,target)}function setMethodMeta(key,value,target,method){Reflect.defineMetadata(key,value,target,method)}function getClassMeta(key,target,fallback){return Reflect.getMetadata(key,target)??fallback}function getMethodMeta(key,target,method,fallback){return Reflect.getMetadata(key,target,method)??fallback}function hasClassMeta(key,target){return Reflect.hasMetadata(key,target)}function getClassMetaOrUndefined(key,target){return Reflect.getMetadata(key,target)}function getMethodMetaOrUndefined(key,target,method){return Reflect.getMetadata(key,target,method)}function pushClassMeta(key,target,...items){let existing=Reflect.getOwnMetadata(key,target)??[];Reflect.defineMetadata(key,[...existing,...items],target)}function pushMethodMeta(key,target,method,...items){let existing=Reflect.getOwnMetadata(key,target,method)??[];Reflect.defineMetadata(key,[...existing,...items],target,method)}function setInMetaMap(key,target,mapKey,mapValue){let inherited=Reflect.getOwnMetadata(key,target),map=inherited?new Map(inherited):new Map;map.set(mapKey,mapValue),Reflect.defineMetadata(key,map,target)}function getMetaMap(key,target){return Reflect.getMetadata(key,target)??new Map}function setInMetaRecord(key,target,recKey,recValue){let inherited=Reflect.getOwnMetadata(key,target),rec=inherited?{...inherited}:{};rec[recKey]=recValue,Reflect.defineMetadata(key,rec,target)}function getMetaRecord(key,target){return Reflect.getMetadata(key,target)??{}}export{getMethodMeta as a,pushClassMeta as c,setInMetaMap as d,setInMetaRecord as f,getMetaRecord as i,pushMethodMeta as l,getClassMetaOrUndefined as n,getMethodMetaOrUndefined as o,setMethodMeta as p,getMetaMap as r,hasClassMeta as s,getClassMeta as t,setClassMeta as u};
12
- //# sourceMappingURL=metadata-WTmSU6Sf.mjs.map
12
+ //# sourceMappingURL=metadata-IHiynOCv.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"metadata-WTmSU6Sf.mjs","names":[],"sources":["../src/core/metadata.ts"],"sourcesContent":["import 'reflect-metadata'\nimport type { InjectionToken } from './token'\n\n/**\n * Typed utilities for reflect-metadata operations.\n *\n * Replaces scattered Reflect.defineMetadata/getMetadata calls with a single\n * source of truth. If we later move to Stage 3 decorators or a WeakMap-based\n * store, only this file changes — all 24+ consumer files stay untouched.\n *\n * Keys may be symbols, strings, or `InjectionToken<T>` objects produced by\n * `createToken()`. Using `createToken` is the preferred pattern because the\n * token's phantom type flows through the helpers, enabling type-safe reads.\n */\n\ntype MetaKey = symbol | string | InjectionToken<unknown>\n\n// ── Setters ─────────────────────────────────────────────────────\n\n/** Set metadata on a class (or prototype for property decorators) */\nexport function setClassMeta<T>(key: MetaKey, value: T, target: object): void {\n Reflect.defineMetadata(key, value, target)\n}\n\n/** Set metadata on a specific method */\nexport function setMethodMeta<T>(key: MetaKey, value: T, target: object, method: string): void {\n Reflect.defineMetadata(key, value, target, method)\n}\n\n// ── Getters ─────────────────────────────────────────────────────\n\n/** Get metadata from a class, returning `fallback` if not set */\nexport function getClassMeta<T>(key: MetaKey, target: object, fallback: T): T {\n return (Reflect.getMetadata(key, target) as T) ?? fallback\n}\n\n/** Get metadata from a specific method, returning `fallback` if not set */\nexport function getMethodMeta<T>(key: MetaKey, target: object, method: string, fallback: T): T {\n return (Reflect.getMetadata(key, target, method) as T) ?? fallback\n}\n\n/** Check if metadata exists on a class */\nexport function hasClassMeta(key: MetaKey, target: object): boolean {\n return Reflect.hasMetadata(key, target)\n}\n\n/** Get metadata from a class with no default (returns undefined if absent) */\nexport function getClassMetaOrUndefined<T>(key: MetaKey, target: object): T | undefined {\n return Reflect.getMetadata(key, target) as T | undefined\n}\n\n/** Get metadata from a method with no default (returns undefined if absent) */\nexport function getMethodMetaOrUndefined<T>(\n key: MetaKey,\n target: object,\n method: string,\n): T | undefined {\n return Reflect.getMetadata(key, target, method) as T | undefined\n}\n\n// ── Accumulate: Arrays ──────────────────────────────────────────\n\n/** Append items to an array stored in class metadata */\nexport function pushClassMeta<T>(key: MetaKey, target: object, ...items: T[]): void {\n const existing: T[] = Reflect.getOwnMetadata(key, target) ?? []\n Reflect.defineMetadata(key, [...existing, ...items], target)\n}\n\n/** Append items to an array stored in method metadata */\nexport function pushMethodMeta<T>(\n key: MetaKey,\n target: object,\n method: string,\n ...items: T[]\n): void {\n const existing: T[] = Reflect.getOwnMetadata(key, target, method) ?? []\n Reflect.defineMetadata(key, [...existing, ...items], target, method)\n}\n\n// ── Accumulate: Maps ────────────────────────────────────────────\n\n/** Set a key/value in a Map stored in class metadata */\nexport function setInMetaMap<K, V>(key: MetaKey, target: object, mapKey: K, mapValue: V): void {\n const inherited: Map<K, V> | undefined = Reflect.getOwnMetadata(key, target)\n const map = inherited ? new Map(inherited) : new Map<K, V>()\n map.set(mapKey, mapValue)\n Reflect.defineMetadata(key, map, target)\n}\n\n/** Get a Map from class metadata (returns empty Map if absent) */\nexport function getMetaMap<K, V>(key: MetaKey, target: object): Map<K, V> {\n return Reflect.getMetadata(key, target) ?? new Map()\n}\n\n// ── Accumulate: Records ─────────────────────────────────────────\n\n/** Set a key/value in a Record stored in class metadata */\nexport function setInMetaRecord<V>(\n key: MetaKey,\n target: object,\n recKey: string | number,\n recValue: V,\n): void {\n const inherited: Record<string | number, V> | undefined = Reflect.getOwnMetadata(key, target)\n const rec = inherited ? { ...inherited } : {}\n rec[recKey] = recValue\n Reflect.defineMetadata(key, rec, target)\n}\n\n/** Get a Record from class metadata (returns empty object if absent) */\nexport function getMetaRecord<V>(key: MetaKey, target: object): Record<string | number, V> {\n return Reflect.getMetadata(key, target) ?? {}\n}\n"],"mappings":";;;;;;;;;;yBAoBA,SAAgB,aAAgB,IAAc,MAAU,OAAsB,CAC5E,QAAQ,eAAe,IAAK,MAAO,OAAO,CAI5C,SAAgB,cAAiB,IAAc,MAAU,OAAgB,OAAsB,CAC7F,QAAQ,eAAe,IAAK,MAAO,OAAQ,OAAO,CAMpD,SAAgB,aAAgB,IAAc,OAAgB,SAAgB,CAC5E,OAAQ,QAAQ,YAAY,IAAK,OAAO,EAAU,SAIpD,SAAgB,cAAiB,IAAc,OAAgB,OAAgB,SAAgB,CAC7F,OAAQ,QAAQ,YAAY,IAAK,OAAQ,OAAO,EAAU,SAI5D,SAAgB,aAAa,IAAc,OAAyB,CAClE,OAAO,QAAQ,YAAY,IAAK,OAAO,CAIzC,SAAgB,wBAA2B,IAAc,OAA+B,CACtF,OAAO,QAAQ,YAAY,IAAK,OAAO,CAIzC,SAAgB,yBACd,IACA,OACA,OACe,CACf,OAAO,QAAQ,YAAY,IAAK,OAAQ,OAAO,CAMjD,SAAgB,cAAiB,IAAc,OAAgB,GAAG,MAAkB,CAClF,IAAM,SAAgB,QAAQ,eAAe,IAAK,OAAO,EAAI,EAAE,CAC/D,QAAQ,eAAe,IAAK,CAAC,GAAG,SAAU,GAAG,MAAM,CAAE,OAAO,CAI9D,SAAgB,eACd,IACA,OACA,OACA,GAAG,MACG,CACN,IAAM,SAAgB,QAAQ,eAAe,IAAK,OAAQ,OAAO,EAAI,EAAE,CACvE,QAAQ,eAAe,IAAK,CAAC,GAAG,SAAU,GAAG,MAAM,CAAE,OAAQ,OAAO,CAMtE,SAAgB,aAAmB,IAAc,OAAgB,OAAW,SAAmB,CAC7F,IAAM,UAAmC,QAAQ,eAAe,IAAK,OAAO,CACtE,IAAM,UAAY,IAAI,IAAI,UAAU,CAAG,IAAI,IACjD,IAAI,IAAI,OAAQ,SAAS,CACzB,QAAQ,eAAe,IAAK,IAAK,OAAO,CAI1C,SAAgB,WAAiB,IAAc,OAA2B,CACxE,OAAO,QAAQ,YAAY,IAAK,OAAO,EAAI,IAAI,IAMjD,SAAgB,gBACd,IACA,OACA,OACA,SACM,CACN,IAAM,UAAoD,QAAQ,eAAe,IAAK,OAAO,CACvF,IAAM,UAAY,CAAE,GAAG,UAAW,CAAG,EAAE,CAC7C,IAAI,QAAU,SACd,QAAQ,eAAe,IAAK,IAAK,OAAO,CAI1C,SAAgB,cAAiB,IAAc,OAA4C,CACzF,OAAO,QAAQ,YAAY,IAAK,OAAO,EAAI,EAAE"}
1
+ {"version":3,"file":"metadata-IHiynOCv.mjs","names":[],"sources":["../src/core/metadata.ts"],"sourcesContent":["import 'reflect-metadata'\nimport type { InjectionToken } from './token'\n\n/**\n * Typed utilities for reflect-metadata operations.\n *\n * Replaces scattered Reflect.defineMetadata/getMetadata calls with a single\n * source of truth. If we later move to Stage 3 decorators or a WeakMap-based\n * store, only this file changes — all 24+ consumer files stay untouched.\n *\n * Keys may be symbols, strings, or `InjectionToken<T>` objects produced by\n * `createToken()`. Using `createToken` is the preferred pattern because the\n * token's phantom type flows through the helpers, enabling type-safe reads.\n */\n\ntype MetaKey = symbol | string | InjectionToken<unknown>\n\n// ── Setters ─────────────────────────────────────────────────────\n\n/** Set metadata on a class (or prototype for property decorators) */\nexport function setClassMeta<T>(key: MetaKey, value: T, target: object): void {\n Reflect.defineMetadata(key, value, target)\n}\n\n/** Set metadata on a specific method */\nexport function setMethodMeta<T>(key: MetaKey, value: T, target: object, method: string): void {\n Reflect.defineMetadata(key, value, target, method)\n}\n\n// ── Getters ─────────────────────────────────────────────────────\n\n/** Get metadata from a class, returning `fallback` if not set */\nexport function getClassMeta<T>(key: MetaKey, target: object, fallback: T): T {\n return (Reflect.getMetadata(key, target) as T) ?? fallback\n}\n\n/** Get metadata from a specific method, returning `fallback` if not set */\nexport function getMethodMeta<T>(key: MetaKey, target: object, method: string, fallback: T): T {\n return (Reflect.getMetadata(key, target, method) as T) ?? fallback\n}\n\n/** Check if metadata exists on a class */\nexport function hasClassMeta(key: MetaKey, target: object): boolean {\n return Reflect.hasMetadata(key, target)\n}\n\n/** Get metadata from a class with no default (returns undefined if absent) */\nexport function getClassMetaOrUndefined<T>(key: MetaKey, target: object): T | undefined {\n return Reflect.getMetadata(key, target) as T | undefined\n}\n\n/** Get metadata from a method with no default (returns undefined if absent) */\nexport function getMethodMetaOrUndefined<T>(\n key: MetaKey,\n target: object,\n method: string,\n): T | undefined {\n return Reflect.getMetadata(key, target, method) as T | undefined\n}\n\n// ── Accumulate: Arrays ──────────────────────────────────────────\n\n/** Append items to an array stored in class metadata */\nexport function pushClassMeta<T>(key: MetaKey, target: object, ...items: T[]): void {\n const existing: T[] = Reflect.getOwnMetadata(key, target) ?? []\n Reflect.defineMetadata(key, [...existing, ...items], target)\n}\n\n/** Append items to an array stored in method metadata */\nexport function pushMethodMeta<T>(\n key: MetaKey,\n target: object,\n method: string,\n ...items: T[]\n): void {\n const existing: T[] = Reflect.getOwnMetadata(key, target, method) ?? []\n Reflect.defineMetadata(key, [...existing, ...items], target, method)\n}\n\n// ── Accumulate: Maps ────────────────────────────────────────────\n\n/** Set a key/value in a Map stored in class metadata */\nexport function setInMetaMap<K, V>(key: MetaKey, target: object, mapKey: K, mapValue: V): void {\n const inherited: Map<K, V> | undefined = Reflect.getOwnMetadata(key, target)\n const map = inherited ? new Map(inherited) : new Map<K, V>()\n map.set(mapKey, mapValue)\n Reflect.defineMetadata(key, map, target)\n}\n\n/** Get a Map from class metadata (returns empty Map if absent) */\nexport function getMetaMap<K, V>(key: MetaKey, target: object): Map<K, V> {\n return Reflect.getMetadata(key, target) ?? new Map()\n}\n\n// ── Accumulate: Records ─────────────────────────────────────────\n\n/** Set a key/value in a Record stored in class metadata */\nexport function setInMetaRecord<V>(\n key: MetaKey,\n target: object,\n recKey: string | number,\n recValue: V,\n): void {\n const inherited: Record<string | number, V> | undefined = Reflect.getOwnMetadata(key, target)\n const rec = inherited ? { ...inherited } : {}\n rec[recKey] = recValue\n Reflect.defineMetadata(key, rec, target)\n}\n\n/** Get a Record from class metadata (returns empty object if absent) */\nexport function getMetaRecord<V>(key: MetaKey, target: object): Record<string | number, V> {\n return Reflect.getMetadata(key, target) ?? {}\n}\n"],"mappings":";;;;;;;;;;yBAoBA,SAAgB,aAAgB,IAAc,MAAU,OAAsB,CAC5E,QAAQ,eAAe,IAAK,MAAO,OAAO,CAI5C,SAAgB,cAAiB,IAAc,MAAU,OAAgB,OAAsB,CAC7F,QAAQ,eAAe,IAAK,MAAO,OAAQ,OAAO,CAMpD,SAAgB,aAAgB,IAAc,OAAgB,SAAgB,CAC5E,OAAQ,QAAQ,YAAY,IAAK,OAAO,EAAU,SAIpD,SAAgB,cAAiB,IAAc,OAAgB,OAAgB,SAAgB,CAC7F,OAAQ,QAAQ,YAAY,IAAK,OAAQ,OAAO,EAAU,SAI5D,SAAgB,aAAa,IAAc,OAAyB,CAClE,OAAO,QAAQ,YAAY,IAAK,OAAO,CAIzC,SAAgB,wBAA2B,IAAc,OAA+B,CACtF,OAAO,QAAQ,YAAY,IAAK,OAAO,CAIzC,SAAgB,yBACd,IACA,OACA,OACe,CACf,OAAO,QAAQ,YAAY,IAAK,OAAQ,OAAO,CAMjD,SAAgB,cAAiB,IAAc,OAAgB,GAAG,MAAkB,CAClF,IAAM,SAAgB,QAAQ,eAAe,IAAK,OAAO,EAAI,EAAE,CAC/D,QAAQ,eAAe,IAAK,CAAC,GAAG,SAAU,GAAG,MAAM,CAAE,OAAO,CAI9D,SAAgB,eACd,IACA,OACA,OACA,GAAG,MACG,CACN,IAAM,SAAgB,QAAQ,eAAe,IAAK,OAAQ,OAAO,EAAI,EAAE,CACvE,QAAQ,eAAe,IAAK,CAAC,GAAG,SAAU,GAAG,MAAM,CAAE,OAAQ,OAAO,CAMtE,SAAgB,aAAmB,IAAc,OAAgB,OAAW,SAAmB,CAC7F,IAAM,UAAmC,QAAQ,eAAe,IAAK,OAAO,CACtE,IAAM,UAAY,IAAI,IAAI,UAAU,CAAG,IAAI,IACjD,IAAI,IAAI,OAAQ,SAAS,CACzB,QAAQ,eAAe,IAAK,IAAK,OAAO,CAI1C,SAAgB,WAAiB,IAAc,OAA2B,CACxE,OAAO,QAAQ,YAAY,IAAK,OAAO,EAAI,IAAI,IAMjD,SAAgB,gBACd,IACA,OACA,OACA,SACM,CACN,IAAM,UAAoD,QAAQ,eAAe,IAAK,OAAO,CACvF,IAAM,UAAY,CAAE,GAAG,UAAW,CAAG,EAAE,CAC7C,IAAI,QAAU,SACd,QAAQ,eAAe,IAAK,IAAK,OAAO,CAI1C,SAAgB,cAAiB,IAAc,OAA4C,CACzF,OAAO,QAAQ,YAAY,IAAK,OAAO,EAAI,EAAE"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs v5.5.0
2
+ * @forinda/kickjs v5.6.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -9,4 +9,4 @@
9
9
  * @license MIT
10
10
  */
11
11
  const FILTER_OPERATORS=new Set([`eq`,`neq`,`gt`,`gte`,`lt`,`lte`,`between`,`in`,`contains`,`starts`,`ends`]);function parseFilters(filterParam,allowedFields){if(!filterParam)return[];let items=Array.isArray(filterParam)?filterParam:[filterParam],results=[],allowed=allowedFields?new Set(allowedFields):null;for(let item of items){let firstColon=item.indexOf(`:`);if(firstColon===-1)continue;let secondColon=item.indexOf(`:`,firstColon+1);if(secondColon===-1)continue;let field=item.slice(0,firstColon),operator=item.slice(firstColon+1,secondColon),value=item.slice(secondColon+1);!field||!value||FILTER_OPERATORS.has(operator)&&(allowed&&!allowed.has(field)||results.push({field,operator,value}))}return results}function parseSort(sortParam,allowedFields){if(!sortParam)return[];let items=Array.isArray(sortParam)?sortParam:[sortParam],results=[],allowed=allowedFields?new Set(allowedFields):null;for(let item of items){let lastColon=item.lastIndexOf(`:`);if(lastColon===-1)continue;let field=item.slice(0,lastColon),dir=item.slice(lastColon+1).toLowerCase();field&&(dir!==`asc`&&dir!==`desc`||allowed&&!allowed.has(field)||results.push({field,direction:dir}))}return results}function parsePagination(params){let page=typeof params.page==`string`?Number.parseInt(params.page,10):params.page??1,limit=typeof params.limit==`string`?Number.parseInt(params.limit,10):params.limit??20;return(Number.isNaN(page)||page<1)&&(page=1),(Number.isNaN(limit)||limit<1)&&(limit=20),limit>100&&(limit=100),{page,limit,offset:(page-1)*limit}}function parseSearchQuery(q){return q?q.trim().slice(0,200):``}function normalizeFieldConfig(config){if(config)return`columns`in config&&config.columns&&typeof config.columns==`object`?{filterable:Object.keys(config.columns),sortable:config.sortable?Object.keys(config.sortable):void 0,searchable:void 0}:config}function parseQuery(query,fieldConfig){let normalized=normalizeFieldConfig(fieldConfig);return{filters:parseFilters(query.filter,normalized?.filterable),sort:parseSort(query.sort,normalized?.sortable),pagination:parsePagination({page:query.page,limit:query.limit}),search:parseSearchQuery(query.q)}}function buildQueryParams(parsed){let params={};return parsed.filters?.length&&(params.filter=parsed.filters.map(f=>`${f.field}:${f.operator}:${f.value}`)),parsed.sort?.length&&(params.sort=parsed.sort.map(s=>`${s.field}:${s.direction}`)),parsed.pagination&&(params.page=parsed.pagination.page,params.limit=parsed.pagination.limit),parsed.search&&(params.q=parsed.search),params}export{parseSearchQuery as a,parseQuery as i,parseFilters as n,parseSort as o,parsePagination as r,FILTER_OPERATORS as s,buildQueryParams as t};
12
- //# sourceMappingURL=query-BmO0aBf6.mjs.map
12
+ //# sourceMappingURL=query-CQU-Hyiv.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"query-BmO0aBf6.mjs","names":[],"sources":["../src/http/query/types.ts","../src/http/query/parse-query.ts"],"sourcesContent":["/** Supported filter operators for query string parsing */\nexport type FilterOperator =\n | 'eq'\n | 'neq'\n | 'gt'\n | 'gte'\n | 'lt'\n | 'lte'\n | 'between'\n | 'in'\n | 'contains'\n | 'starts'\n | 'ends'\n\nexport const FILTER_OPERATORS = new Set<string>([\n 'eq',\n 'neq',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'between',\n 'in',\n 'contains',\n 'starts',\n 'ends',\n])\n\nexport interface FilterItem {\n field: string\n operator: FilterOperator\n value: string\n}\n\nexport interface SortItem {\n field: string\n direction: 'asc' | 'desc'\n}\n\nexport interface PaginationParams {\n page: number\n limit: number\n offset: number\n}\n\n/**\n * The result of parsing a query string. ORM-agnostic — pass this to\n * a query builder adapter (Drizzle, Prisma, Sequelize, etc.) to produce\n * database-specific query objects.\n */\nexport interface ParsedQuery {\n filters: FilterItem[]\n sort: SortItem[]\n pagination: PaginationParams\n search: string\n}\n\n/**\n * Helper that extracts the literal field union from a `QueryFieldConfig`\n * key. When the user passes `{ filterable: ['status', 'category'] } as const`,\n * `FieldsOf<typeof config, 'filterable'>` resolves to `'status' | 'category'`.\n *\n * Without `as const`, the arrays widen to `string[]` and the union falls\n * back to `string`. This is the documented escape hatch.\n */\nexport type FieldsOf<\n TConfig extends QueryFieldConfig | undefined,\n K extends 'filterable' | 'sortable' | 'searchable',\n> = TConfig extends { readonly [P in K]: readonly (infer F)[] }\n ? F extends string\n ? F\n : string\n : TConfig extends { columns: infer C }\n ? K extends 'filterable'\n ? keyof C & string\n : string\n : string\n\n/**\n * Narrowed `ParsedQuery` whose `filters[].field` and `sort[].field`\n * unions are constrained to the literal whitelist passed to\n * `ctx.qs()` / `parseQuery()`. Extends `ParsedQuery`, so any function\n * accepting `ParsedQuery` accepts a `TypedParsedQuery<X>` automatically\n * (subtype assignment, no codegen, backward compatible).\n *\n * @example\n * ```ts\n * const parsed = ctx.qs({\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt'],\n * } as const)\n * parsed.filters[0]?.field // 'status' | 'priority'\n * parsed.sort[0]?.field // 'createdAt'\n * ```\n */\nexport interface TypedParsedQuery<\n TConfig extends QueryFieldConfig | undefined,\n> extends ParsedQuery {\n filters: Array<FilterItem & { field: FieldsOf<TConfig, 'filterable'> }>\n sort: Array<SortItem & { field: FieldsOf<TConfig, 'sortable'> }>\n}\n\n/**\n * Restrict which fields can be filtered, sorted, or searched.\n * Fields not in the allow-list are silently ignored.\n *\n * Accepts two forms:\n * 1. **String arrays** (standard): `{ filterable: ['name', 'status'], sortable: ['createdAt'] }`\n * 2. **Column-object maps** (Drizzle-style): `{ columns: { name: col, status: col }, sortable: { createdAt: col } }`\n *\n * When column-object maps are provided, `Object.keys()` is used to derive the field names.\n */\nexport type QueryFieldConfig = StringQueryFieldConfig | ColumnQueryFieldConfig\n\nexport interface StringQueryFieldConfig {\n filterable?: string[]\n sortable?: string[]\n searchable?: string[]\n}\n\n/**\n * Column-object-based field config (e.g., from DrizzleQueryParamsConfig).\n * Keys are the field names used in query strings; values are ORM column references.\n */\nexport interface ColumnQueryFieldConfig {\n columns: Record<string, any>\n sortable?: Record<string, any>\n searchColumns?: any[]\n [key: string]: any\n}\n\n/** Standardized paginated response shape */\nexport interface PaginatedResponse<T = any> {\n data: T[]\n meta: {\n page: number\n limit: number\n total: number\n totalPages: number\n hasNext: boolean\n hasPrev: boolean\n }\n}\n\n/**\n * Interface for ORM-specific query builder adapters.\n * Implement this to translate a `ParsedQuery` into your ORM's query format.\n *\n * @example\n * ```ts\n * // Drizzle adapter\n * class DrizzleQueryAdapter implements QueryBuilderAdapter<DrizzleQueryResult> {\n * build(parsed: ParsedQuery, config: DrizzleConfig): DrizzleQueryResult {\n * // Convert filters → Drizzle SQL conditions\n * // Convert sort → Drizzle orderBy\n * return { where, orderBy, limit, offset }\n * }\n * }\n *\n * // Prisma adapter\n * class PrismaQueryAdapter implements QueryBuilderAdapter<PrismaQueryResult> {\n * build(parsed: ParsedQuery, config: PrismaConfig): PrismaQueryResult {\n * return { where, orderBy, skip, take }\n * }\n * }\n * ```\n */\nexport interface QueryBuilderAdapter<TResult = any, TConfig = any> {\n /** Human-readable name for debugging */\n readonly name: string\n\n /**\n * Convert a ParsedQuery into an ORM-specific query object.\n * @param parsed - The ORM-agnostic parsed query\n * @param config - ORM-specific configuration (column maps, search columns, etc.)\n */\n build(parsed: ParsedQuery, config: TConfig): TResult\n}\n","import {\n FILTER_OPERATORS,\n type FilterItem,\n type FilterOperator,\n type SortItem,\n type PaginationParams,\n type ParsedQuery,\n type QueryFieldConfig,\n type StringQueryFieldConfig,\n type TypedParsedQuery,\n} from './types'\n\nconst MAX_SEARCH_LENGTH = 200\nconst DEFAULT_PAGE = 1\nconst DEFAULT_LIMIT = 20\nconst MAX_LIMIT = 100\n\n// ── Individual parsers ──────────────────────────────────────────────────\n\n/** Parse filter strings like \"status:eq:active\" into structured objects */\nexport function parseFilters(\n filterParam: string | string[] | undefined,\n allowedFields?: string[],\n): FilterItem[] {\n if (!filterParam) return []\n\n const items = Array.isArray(filterParam) ? filterParam : [filterParam]\n const results: FilterItem[] = []\n const allowed = allowedFields ? new Set(allowedFields) : null\n\n for (const item of items) {\n // Split on first two colons only — value may contain colons (e.g. timestamps)\n const firstColon = item.indexOf(':')\n if (firstColon === -1) continue\n\n const secondColon = item.indexOf(':', firstColon + 1)\n if (secondColon === -1) continue\n\n const field = item.slice(0, firstColon)\n const operator = item.slice(firstColon + 1, secondColon)\n const value = item.slice(secondColon + 1)\n\n if (!field || !value) continue\n if (!FILTER_OPERATORS.has(operator)) continue\n if (allowed && !allowed.has(field)) continue\n\n results.push({ field, operator: operator as FilterOperator, value })\n }\n\n return results\n}\n\n/** Parse sort strings like \"firstName:asc\" into structured objects */\nexport function parseSort(\n sortParam: string | string[] | undefined,\n allowedFields?: string[],\n): SortItem[] {\n if (!sortParam) return []\n\n const items = Array.isArray(sortParam) ? sortParam : [sortParam]\n const results: SortItem[] = []\n const allowed = allowedFields ? new Set(allowedFields) : null\n\n for (const item of items) {\n // Split on last colon so field names with colons work\n const lastColon = item.lastIndexOf(':')\n if (lastColon === -1) continue\n\n const field = item.slice(0, lastColon)\n const dir = item.slice(lastColon + 1).toLowerCase()\n\n if (!field) continue\n if (dir !== 'asc' && dir !== 'desc') continue\n if (allowed && !allowed.has(field)) continue\n\n results.push({ field, direction: dir })\n }\n\n return results\n}\n\n/** Parse page/limit into pagination with computed offset */\nexport function parsePagination(params: {\n page?: string | number\n limit?: string | number\n}): PaginationParams {\n let page =\n typeof params.page === 'string'\n ? Number.parseInt(params.page, 10)\n : (params.page ?? DEFAULT_PAGE)\n let limit =\n typeof params.limit === 'string'\n ? Number.parseInt(params.limit, 10)\n : (params.limit ?? DEFAULT_LIMIT)\n\n if (Number.isNaN(page) || page < 1) page = DEFAULT_PAGE\n if (Number.isNaN(limit) || limit < 1) limit = DEFAULT_LIMIT\n if (limit > MAX_LIMIT) limit = MAX_LIMIT\n\n return { page, limit, offset: (page - 1) * limit }\n}\n\n/** Sanitize and truncate search query */\nexport function parseSearchQuery(q: string | undefined): string {\n if (!q) return ''\n return q.trim().slice(0, MAX_SEARCH_LENGTH)\n}\n\n// ── Config normalizer ───────────────────────────────────────────────────\n\n/**\n * Normalize a QueryFieldConfig into the string-based form.\n * Detects column-object-based configs (DrizzleQueryParamsConfig) by checking\n * for a `columns` property and extracts `Object.keys()`.\n */\nfunction normalizeFieldConfig(config?: QueryFieldConfig): StringQueryFieldConfig | undefined {\n if (!config) return undefined\n\n // Column-object-based config (e.g., DrizzleQueryParamsConfig)\n if ('columns' in config && config.columns && typeof config.columns === 'object') {\n return {\n filterable: Object.keys(config.columns),\n sortable: config.sortable ? Object.keys(config.sortable) : undefined,\n searchable: undefined, // searchColumns are Column objects, not used for string filtering\n }\n }\n\n // Already string-based\n return config as StringQueryFieldConfig\n}\n\n// ── Combined parser ─────────────────────────────────────────────────────\n\n/**\n * Parse a raw Express query object into a structured, ORM-agnostic ParsedQuery.\n *\n * @param query - Raw query string object from `req.query` or Zod-validated object\n * @param fieldConfig - Optional field restrictions (whitelist filterable/sortable/searchable).\n * Accepts both string-based configs (`{ filterable, sortable, searchable }`) and\n * column-object configs (`{ columns, sortable, searchColumns }`).\n *\n * @example\n * ```ts\n * // String-based config\n * const parsed = parseQuery(ctx.query, {\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt', 'title'],\n * })\n *\n * // Column-object config (DrizzleQueryParamsConfig)\n * const parsed = parseQuery(ctx.query, TASK_QUERY_CONFIG)\n * ```\n *\n * Query string format:\n * - Filters: `?filter=field:operator:value` (repeatable)\n * - Sort: `?sort=field:asc|desc` (repeatable)\n * - Pagination: `?page=1&limit=20`\n * - Search: `?q=search+term`\n *\n * Filter operators: eq, neq, gt, gte, lt, lte, between, in, contains, starts, ends\n */\nexport function parseQuery<TConfig extends QueryFieldConfig | undefined = undefined>(\n query: Record<string, any>,\n fieldConfig?: TConfig,\n): TypedParsedQuery<TConfig> {\n const normalized = normalizeFieldConfig(fieldConfig)\n return {\n filters: parseFilters(query.filter, normalized?.filterable),\n sort: parseSort(query.sort, normalized?.sortable),\n pagination: parsePagination({ page: query.page, limit: query.limit }),\n search: parseSearchQuery(query.q),\n } as TypedParsedQuery<TConfig>\n}\n\n// ── Query URL builder (for client-side or testing) ──────────────────────\n\n/** Convert ParsedQuery back into query string parameters */\nexport function buildQueryParams(\n parsed: Partial<ParsedQuery>,\n): Record<string, string | string[] | number> {\n const params: Record<string, string | string[] | number> = {}\n\n if (parsed.filters?.length) {\n params.filter = parsed.filters.map((f) => `${f.field}:${f.operator}:${f.value}`)\n }\n\n if (parsed.sort?.length) {\n params.sort = parsed.sort.map((s) => `${s.field}:${s.direction}`)\n }\n\n if (parsed.pagination) {\n params.page = parsed.pagination.page\n params.limit = parsed.pagination.limit\n }\n\n if (parsed.search) {\n params.q = parsed.search\n }\n\n return params\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"query-CQU-Hyiv.mjs","names":[],"sources":["../src/http/query/types.ts","../src/http/query/parse-query.ts"],"sourcesContent":["/** Supported filter operators for query string parsing */\nexport type FilterOperator =\n | 'eq'\n | 'neq'\n | 'gt'\n | 'gte'\n | 'lt'\n | 'lte'\n | 'between'\n | 'in'\n | 'contains'\n | 'starts'\n | 'ends'\n\nexport const FILTER_OPERATORS = new Set<string>([\n 'eq',\n 'neq',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'between',\n 'in',\n 'contains',\n 'starts',\n 'ends',\n])\n\nexport interface FilterItem {\n field: string\n operator: FilterOperator\n value: string\n}\n\nexport interface SortItem {\n field: string\n direction: 'asc' | 'desc'\n}\n\nexport interface PaginationParams {\n page: number\n limit: number\n offset: number\n}\n\n/**\n * The result of parsing a query string. ORM-agnostic — pass this to\n * a query builder adapter (Drizzle, Prisma, Sequelize, etc.) to produce\n * database-specific query objects.\n */\nexport interface ParsedQuery {\n filters: FilterItem[]\n sort: SortItem[]\n pagination: PaginationParams\n search: string\n}\n\n/**\n * Helper that extracts the literal field union from a `QueryFieldConfig`\n * key. When the user passes `{ filterable: ['status', 'category'] } as const`,\n * `FieldsOf<typeof config, 'filterable'>` resolves to `'status' | 'category'`.\n *\n * Without `as const`, the arrays widen to `string[]` and the union falls\n * back to `string`. This is the documented escape hatch.\n */\nexport type FieldsOf<\n TConfig extends QueryFieldConfig | undefined,\n K extends 'filterable' | 'sortable' | 'searchable',\n> = TConfig extends { readonly [P in K]: readonly (infer F)[] }\n ? F extends string\n ? F\n : string\n : TConfig extends { columns: infer C }\n ? K extends 'filterable'\n ? keyof C & string\n : string\n : string\n\n/**\n * Narrowed `ParsedQuery` whose `filters[].field` and `sort[].field`\n * unions are constrained to the literal whitelist passed to\n * `ctx.qs()` / `parseQuery()`. Extends `ParsedQuery`, so any function\n * accepting `ParsedQuery` accepts a `TypedParsedQuery<X>` automatically\n * (subtype assignment, no codegen, backward compatible).\n *\n * @example\n * ```ts\n * const parsed = ctx.qs({\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt'],\n * } as const)\n * parsed.filters[0]?.field // 'status' | 'priority'\n * parsed.sort[0]?.field // 'createdAt'\n * ```\n */\nexport interface TypedParsedQuery<\n TConfig extends QueryFieldConfig | undefined,\n> extends ParsedQuery {\n filters: Array<FilterItem & { field: FieldsOf<TConfig, 'filterable'> }>\n sort: Array<SortItem & { field: FieldsOf<TConfig, 'sortable'> }>\n}\n\n/**\n * Restrict which fields can be filtered, sorted, or searched.\n * Fields not in the allow-list are silently ignored.\n *\n * Accepts two forms:\n * 1. **String arrays** (standard): `{ filterable: ['name', 'status'], sortable: ['createdAt'] }`\n * 2. **Column-object maps** (Drizzle-style): `{ columns: { name: col, status: col }, sortable: { createdAt: col } }`\n *\n * When column-object maps are provided, `Object.keys()` is used to derive the field names.\n */\nexport type QueryFieldConfig = StringQueryFieldConfig | ColumnQueryFieldConfig\n\nexport interface StringQueryFieldConfig {\n filterable?: string[]\n sortable?: string[]\n searchable?: string[]\n}\n\n/**\n * Column-object-based field config (e.g., from DrizzleQueryParamsConfig).\n * Keys are the field names used in query strings; values are ORM column references.\n */\nexport interface ColumnQueryFieldConfig {\n columns: Record<string, any>\n sortable?: Record<string, any>\n searchColumns?: any[]\n [key: string]: any\n}\n\n/** Standardized paginated response shape */\nexport interface PaginatedResponse<T = any> {\n data: T[]\n meta: {\n page: number\n limit: number\n total: number\n totalPages: number\n hasNext: boolean\n hasPrev: boolean\n }\n}\n\n/**\n * Interface for ORM-specific query builder adapters.\n * Implement this to translate a `ParsedQuery` into your ORM's query format.\n *\n * @example\n * ```ts\n * // Drizzle adapter\n * class DrizzleQueryAdapter implements QueryBuilderAdapter<DrizzleQueryResult> {\n * build(parsed: ParsedQuery, config: DrizzleConfig): DrizzleQueryResult {\n * // Convert filters → Drizzle SQL conditions\n * // Convert sort → Drizzle orderBy\n * return { where, orderBy, limit, offset }\n * }\n * }\n *\n * // Prisma adapter\n * class PrismaQueryAdapter implements QueryBuilderAdapter<PrismaQueryResult> {\n * build(parsed: ParsedQuery, config: PrismaConfig): PrismaQueryResult {\n * return { where, orderBy, skip, take }\n * }\n * }\n * ```\n */\nexport interface QueryBuilderAdapter<TResult = any, TConfig = any> {\n /** Human-readable name for debugging */\n readonly name: string\n\n /**\n * Convert a ParsedQuery into an ORM-specific query object.\n * @param parsed - The ORM-agnostic parsed query\n * @param config - ORM-specific configuration (column maps, search columns, etc.)\n */\n build(parsed: ParsedQuery, config: TConfig): TResult\n}\n","import {\n FILTER_OPERATORS,\n type FilterItem,\n type FilterOperator,\n type SortItem,\n type PaginationParams,\n type ParsedQuery,\n type QueryFieldConfig,\n type StringQueryFieldConfig,\n type TypedParsedQuery,\n} from './types'\n\nconst MAX_SEARCH_LENGTH = 200\nconst DEFAULT_PAGE = 1\nconst DEFAULT_LIMIT = 20\nconst MAX_LIMIT = 100\n\n// ── Individual parsers ──────────────────────────────────────────────────\n\n/** Parse filter strings like \"status:eq:active\" into structured objects */\nexport function parseFilters(\n filterParam: string | string[] | undefined,\n allowedFields?: string[],\n): FilterItem[] {\n if (!filterParam) return []\n\n const items = Array.isArray(filterParam) ? filterParam : [filterParam]\n const results: FilterItem[] = []\n const allowed = allowedFields ? new Set(allowedFields) : null\n\n for (const item of items) {\n // Split on first two colons only — value may contain colons (e.g. timestamps)\n const firstColon = item.indexOf(':')\n if (firstColon === -1) continue\n\n const secondColon = item.indexOf(':', firstColon + 1)\n if (secondColon === -1) continue\n\n const field = item.slice(0, firstColon)\n const operator = item.slice(firstColon + 1, secondColon)\n const value = item.slice(secondColon + 1)\n\n if (!field || !value) continue\n if (!FILTER_OPERATORS.has(operator)) continue\n if (allowed && !allowed.has(field)) continue\n\n results.push({ field, operator: operator as FilterOperator, value })\n }\n\n return results\n}\n\n/** Parse sort strings like \"firstName:asc\" into structured objects */\nexport function parseSort(\n sortParam: string | string[] | undefined,\n allowedFields?: string[],\n): SortItem[] {\n if (!sortParam) return []\n\n const items = Array.isArray(sortParam) ? sortParam : [sortParam]\n const results: SortItem[] = []\n const allowed = allowedFields ? new Set(allowedFields) : null\n\n for (const item of items) {\n // Split on last colon so field names with colons work\n const lastColon = item.lastIndexOf(':')\n if (lastColon === -1) continue\n\n const field = item.slice(0, lastColon)\n const dir = item.slice(lastColon + 1).toLowerCase()\n\n if (!field) continue\n if (dir !== 'asc' && dir !== 'desc') continue\n if (allowed && !allowed.has(field)) continue\n\n results.push({ field, direction: dir })\n }\n\n return results\n}\n\n/** Parse page/limit into pagination with computed offset */\nexport function parsePagination(params: {\n page?: string | number\n limit?: string | number\n}): PaginationParams {\n let page =\n typeof params.page === 'string'\n ? Number.parseInt(params.page, 10)\n : (params.page ?? DEFAULT_PAGE)\n let limit =\n typeof params.limit === 'string'\n ? Number.parseInt(params.limit, 10)\n : (params.limit ?? DEFAULT_LIMIT)\n\n if (Number.isNaN(page) || page < 1) page = DEFAULT_PAGE\n if (Number.isNaN(limit) || limit < 1) limit = DEFAULT_LIMIT\n if (limit > MAX_LIMIT) limit = MAX_LIMIT\n\n return { page, limit, offset: (page - 1) * limit }\n}\n\n/** Sanitize and truncate search query */\nexport function parseSearchQuery(q: string | undefined): string {\n if (!q) return ''\n return q.trim().slice(0, MAX_SEARCH_LENGTH)\n}\n\n// ── Config normalizer ───────────────────────────────────────────────────\n\n/**\n * Normalize a QueryFieldConfig into the string-based form.\n * Detects column-object-based configs (DrizzleQueryParamsConfig) by checking\n * for a `columns` property and extracts `Object.keys()`.\n */\nfunction normalizeFieldConfig(config?: QueryFieldConfig): StringQueryFieldConfig | undefined {\n if (!config) return undefined\n\n // Column-object-based config (e.g., DrizzleQueryParamsConfig)\n if ('columns' in config && config.columns && typeof config.columns === 'object') {\n return {\n filterable: Object.keys(config.columns),\n sortable: config.sortable ? Object.keys(config.sortable) : undefined,\n searchable: undefined, // searchColumns are Column objects, not used for string filtering\n }\n }\n\n // Already string-based\n return config as StringQueryFieldConfig\n}\n\n// ── Combined parser ─────────────────────────────────────────────────────\n\n/**\n * Parse a raw Express query object into a structured, ORM-agnostic ParsedQuery.\n *\n * @param query - Raw query string object from `req.query` or Zod-validated object\n * @param fieldConfig - Optional field restrictions (whitelist filterable/sortable/searchable).\n * Accepts both string-based configs (`{ filterable, sortable, searchable }`) and\n * column-object configs (`{ columns, sortable, searchColumns }`).\n *\n * @example\n * ```ts\n * // String-based config\n * const parsed = parseQuery(ctx.query, {\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt', 'title'],\n * })\n *\n * // Column-object config (DrizzleQueryParamsConfig)\n * const parsed = parseQuery(ctx.query, TASK_QUERY_CONFIG)\n * ```\n *\n * Query string format:\n * - Filters: `?filter=field:operator:value` (repeatable)\n * - Sort: `?sort=field:asc|desc` (repeatable)\n * - Pagination: `?page=1&limit=20`\n * - Search: `?q=search+term`\n *\n * Filter operators: eq, neq, gt, gte, lt, lte, between, in, contains, starts, ends\n */\nexport function parseQuery<TConfig extends QueryFieldConfig | undefined = undefined>(\n query: Record<string, any>,\n fieldConfig?: TConfig,\n): TypedParsedQuery<TConfig> {\n const normalized = normalizeFieldConfig(fieldConfig)\n return {\n filters: parseFilters(query.filter, normalized?.filterable),\n sort: parseSort(query.sort, normalized?.sortable),\n pagination: parsePagination({ page: query.page, limit: query.limit }),\n search: parseSearchQuery(query.q),\n } as TypedParsedQuery<TConfig>\n}\n\n// ── Query URL builder (for client-side or testing) ──────────────────────\n\n/** Convert ParsedQuery back into query string parameters */\nexport function buildQueryParams(\n parsed: Partial<ParsedQuery>,\n): Record<string, string | string[] | number> {\n const params: Record<string, string | string[] | number> = {}\n\n if (parsed.filters?.length) {\n params.filter = parsed.filters.map((f) => `${f.field}:${f.operator}:${f.value}`)\n }\n\n if (parsed.sort?.length) {\n params.sort = parsed.sort.map((s) => `${s.field}:${s.direction}`)\n }\n\n if (parsed.pagination) {\n params.page = parsed.pagination.page\n params.limit = parsed.pagination.limit\n }\n\n if (parsed.search) {\n params.q = parsed.search\n }\n\n return params\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  import { r as MetaValue } from "./execution-context-DKnt61pY.mjs";
3
3
  import { n as ContextDecorator, r as ContextDecoratorSpec, s as DepValue } from "./context-decorator-BgdDd-6X.mjs";
4
4
  import { c as AdapterFactory } from "./index-D-Wb23e6.mjs";
5
- import { r as RequestContext } from "./context-BAdAV-zO.mjs";
5
+ import { r as RequestContext } from "./context-aocg9gO-.mjs";
6
6
  import { NextFunction, Request, Response } from "express";
7
7
  import { AsyncLocalStorage } from "node:async_hooks";
8
8
 
@@ -214,4 +214,4 @@ interface ViewAdapterOptions {
214
214
  declare const ViewAdapter: AdapterFactory<ViewAdapterOptions, unknown>;
215
215
  //#endregion
216
216
  export { TraceContextOptions as a, requestScopeMiddleware as c, getRequestValue as d, requestStore as f, isClusterPrimary as h, TraceContext as i, RequestStore as l, ClusterOptions as m, ViewAdapterOptions as n, parseTraceparent as o, defineHttpContextDecorator as p, TRACEPARENT_HEADER as r, traceContext as s, ViewAdapter as t, getRequestStore as u };
217
- //# sourceMappingURL=views-1e3jQr5C.d.mts.map
217
+ //# sourceMappingURL=views-BMhrZsH9.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"views-1e3jQr5C.d.mts","names":[],"sources":["../src/http/cluster.ts","../src/http/define-http-context-decorator.ts","../src/http/request-store.ts","../src/http/middleware/request-scope.ts","../src/http/middleware/trace-context.ts","../src/http/middleware/views.ts"],"mappings":";;;;;;;;;UAOiB,cAAA;;EAEf,OAAA;;EAEA,uBAAA;;EAEA,YAAA;;EAEA,WAAA;;EAEA,aAAA;AAAA;;iBAIc,gBAAA,CAAA;;;iBCoCA,0BAAA,6BAEJ,MAAA,SAAe,QAAA,IAAY,MAAA,2BAC3B,MAAA,oBAA0B,MAAA,gBAAA,CACpC,IAAA,EAAM,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,cAAA,IAAkB,gBAAA,CAAiB,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,cAAA;;;;UCzDjE,YAAA;;EAEf,SAAA;;;;;;EAMA,SAAA,EAAW,GAAA;;;;;;;;;;;;AFLb;;;;;;;;;EE0BE,MAAA,EAAQ,GAAA;AAAA;AFZV;AAAA,cEgBa,YAAA,EAAY,iBAAA,CAAA,YAAA;;;;;iBAMT,eAAA,CAAA,GAAmB,YAAA;;ADcnC;;;;;;;;;;;;;;;;;;;;;;;;;iBCsBgB,eAAA,kBAAA,CAAkC,GAAA,EAAK,CAAA,GAAI,SAAA,CAAU,CAAA;;;;;;;;;;;iBCrDrD,sBAAA,CAAA,IAAsB,GAAA,EACnB,OAAA,EAAO,IAAA,EAAQ,QAAA,EAAQ,IAAA,EAAQ,YAAA;;;;;;;cCnBrC,kBAAA;AAAA,UAKI,mBAAA;;;;;EAKf,iBAAA;AAAA;;;;UAMe,YAAA;;EAEf,OAAA;;EAEA,OAAA;;EAEA,YAAA;EJvBe;EIyBf,KAAA;AAAA;;;;;iBAqBc,gBAAA,CAAiB,MAAA,WAAiB,YAAA;;;;AJhClD;;;;;;;;ACoCA;;;;;;;;;;iBGsCgB,YAAA,CAAa,OAAA,GAAS,mBAAA,IAC5B,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA;;;UC3F5B,kBAAA;;;;;;;;;;;;;;;EAef,MAAA;;EAGA,GAAA;;EAGA,QAAA;;EAGA,MAAA;AAAA;ALtBF;;;;;;;;;;;AAcA;;;;;;;;ACoCA;;;;;;;;;;ADlDA,cKsDa,WAAA,EAAW,cAAA,CAAA,kBAAA"}
1
+ {"version":3,"file":"views-BMhrZsH9.d.mts","names":[],"sources":["../src/http/cluster.ts","../src/http/define-http-context-decorator.ts","../src/http/request-store.ts","../src/http/middleware/request-scope.ts","../src/http/middleware/trace-context.ts","../src/http/middleware/views.ts"],"mappings":";;;;;;;;;UAOiB,cAAA;;EAEf,OAAA;;EAEA,uBAAA;;EAEA,YAAA;;EAEA,WAAA;;EAEA,aAAA;AAAA;;iBAIc,gBAAA,CAAA;;;iBCoCA,0BAAA,6BAEJ,MAAA,SAAe,QAAA,IAAY,MAAA,2BAC3B,MAAA,oBAA0B,MAAA,gBAAA,CACpC,IAAA,EAAM,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,cAAA,IAAkB,gBAAA,CAAiB,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,cAAA;;;;UCzDjE,YAAA;;EAEf,SAAA;;;;;;EAMA,SAAA,EAAW,GAAA;;;;;;;;;;;;AFLb;;;;;;;;;EE0BE,MAAA,EAAQ,GAAA;AAAA;AFZV;AAAA,cEgBa,YAAA,EAAY,iBAAA,CAAA,YAAA;;;;;iBAMT,eAAA,CAAA,GAAmB,YAAA;;ADcnC;;;;;;;;;;;;;;;;;;;;;;;;;iBCsBgB,eAAA,kBAAA,CAAkC,GAAA,EAAK,CAAA,GAAI,SAAA,CAAU,CAAA;;;;;;;;;;;iBCrDrD,sBAAA,CAAA,IAAsB,GAAA,EACnB,OAAA,EAAO,IAAA,EAAQ,QAAA,EAAQ,IAAA,EAAQ,YAAA;;;;;;;cCnBrC,kBAAA;AAAA,UAKI,mBAAA;;;;;EAKf,iBAAA;AAAA;;;;UAMe,YAAA;;EAEf,OAAA;;EAEA,OAAA;;EAEA,YAAA;EJvBe;EIyBf,KAAA;AAAA;;;;;iBAqBc,gBAAA,CAAiB,MAAA,WAAiB,YAAA;;;;AJhClD;;;;;;;;ACoCA;;;;;;;;;;iBGsCgB,YAAA,CAAa,OAAA,GAAS,mBAAA,IAC5B,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,IAAA,EAAM,YAAA;;;UC3F5B,kBAAA;;;;;;;;;;;;;;;EAef,MAAA;;EAGA,GAAA;;EAGA,QAAA;;EAGA,MAAA;AAAA;ALtBF;;;;;;;;;;;AAcA;;;;;;;;ACoCA;;;;;;;;;;ADlDA,cKsDa,WAAA,EAAW,cAAA,CAAA,kBAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forinda/kickjs",
3
- "version": "5.5.0",
3
+ "version": "5.6.0",
4
4
  "description": "A production-grade, decorator-driven Node.js framework built on Express 5 and TypeScript.",
5
5
  "keywords": [
6
6
  "kickjs",
@@ -1,12 +0,0 @@
1
- /**
2
- * @forinda/kickjs v5.5.0
3
- *
4
- * Copyright (c) Felix Orinda
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- *
9
- * @license MIT
10
- */
11
- import{i as parseQuery}from"./query-BmO0aBf6.mjs";import{AsyncLocalStorage}from"node:async_hooks";const requestStore=new AsyncLocalStorage;function getRequestStore(){let store=requestStore.getStore();if(!store)throw Error(`No active request context. REQUEST-scoped services can only be resolved during an HTTP request.`);return store}function getRequestValue(key){return requestStore.getStore()?.values.get(key)}var RequestContext=class{constructor(req,res,next){this.req=req,this.res=res,this.next=next}metadataReadOnly(){return requestStore.getStore()?.values??null}metadataForWrite(){let store=requestStore.getStore();if(store)return store.values;throw Error(`RequestContext.set called outside an AsyncLocalStorage frame. Mount requestScopeMiddleware() before constructing RequestContext, or wrap test setup in requestStore.run({ requestId, instances: new Map(), values: new Map() }, () => ...).`)}get body(){return this.req.body}get params(){return this.req.params}get query(){return this.req.query}get headers(){return this.req.headers}get requestId(){return this.req.requestId??this.req.headers[`x-request-id`]}get session(){return this.req.session}get user(){return this.metadataReadOnly()?.get(`user`)??this.req.user}get tenantId(){return this.user?.tenantId}get roles(){let u=this.user;return u?.tenantRoles??u?.roles??[]}qs(fieldConfig){return parseQuery(this.req.query,fieldConfig)}get file(){return this.req.file}get files(){let f=this.req.files;return Array.isArray(f)?f:void 0}get(key){return this.metadataReadOnly()?.get(key)}set(key,value){this.metadataForWrite().set(key,value)}json(data,status=200){return this.res.status(status).json(data)}created(data){return this.res.status(201).json(data)}noContent(){return this.res.status(204).end()}notFound(message=`Not Found`){return this.res.status(404).json({message})}badRequest(message){return this.res.status(400).json({message})}html(content,status=200){return this.res.status(status).type(`html`).send(content)}download(buffer,filename,contentType=`application/octet-stream`){return this.res.setHeader(`Content-Disposition`,`attachment; filename="${filename}"`),this.res.setHeader(`Content-Type`,contentType),this.res.send(buffer)}render(template,data={}){return this.res.render(template,data)}async paginate(fetcher,fieldConfig){let parsed=this.qs(fieldConfig),{data,total}=await fetcher(parsed),{page,limit}=parsed.pagination,totalPages=Math.ceil(total/limit)||1,response={data,meta:{page,limit,total,totalPages,hasNext:page<totalPages,hasPrev:page>1}};return this.json(response)}sse(){this.res.writeHead(200,{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`,"X-Accel-Buffering":`no`}),this.res.flushHeaders();let closeCallbacks=[];return this.req.on(`close`,()=>{for(let cb of closeCallbacks)cb()}),{send:(data,event,id)=>{id&&this.res.write(`id: ${id}\n`),event&&this.res.write(`event: ${event}\n`),this.res.write(`data: ${JSON.stringify(data)}\n\n`)},comment:text=>{this.res.write(`: ${text}\n\n`)},onClose:fn=>{closeCallbacks.push(fn)},close:()=>{this.res.end()}}}};export{requestStore as i,getRequestStore as n,getRequestValue as r,RequestContext as t};
12
- //# sourceMappingURL=context-B4BeGq1B.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context-B4BeGq1B.mjs","names":[],"sources":["../src/http/request-store.ts","../src/http/context.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\nimport type { MetaValue } from '../core/execution-context'\n\n/** Per-request storage for REQUEST-scoped DI and request context propagation */\nexport interface RequestStore {\n /** Unique request identifier */\n requestId: string\n /**\n * Per-request singleton cache for REQUEST-scoped services. Keys are\n * heterogeneous (class constructors, `InjectionToken` objects, raw\n * strings), so the map type stays `Map<any, any>`.\n */\n instances: Map<any, any>\n /**\n * Per-request key/value bag. Canonical backing store for\n * `RequestContext.set/get` and the Context Contributor pipeline (#107)\n * since Phase 3. Anything written here is visible to every concrete\n * execution context constructed for the same request, regardless of\n * which middleware/contributor wrote it.\n *\n * Keys are constrained to `string` per the locked decision in\n * `architecture.md` §20.12 (#4); the value type is `unknown` because\n * the concrete shape at each key is determined by the consumer's\n * `ContextMeta` augmentation, not by the store itself.\n *\n * Prefer the {@link getRequestValue} helper over reaching into this\n * map directly — it threads the augmented `ContextMeta` shape through\n * `MetaValue<K>` so consumers get typed reads without an `as` cast.\n * Writes should flow through `ctx.set(key, value)` or a Context\n * Contributor's return value — see the `setRequestValue` note below\n * for why this package intentionally does NOT ship a service-level\n * write helper.\n */\n values: Map<string, unknown>\n}\n\n/** AsyncLocalStorage instance shared across the framework */\nexport const requestStore = new AsyncLocalStorage<RequestStore>()\n\n/**\n * Get the current request store. Throws if called outside a request context.\n * Used by the Container to resolve REQUEST-scoped dependencies.\n */\nexport function getRequestStore(): RequestStore {\n const store = requestStore.getStore()\n if (!store) {\n throw new Error(\n 'No active request context. REQUEST-scoped services can only be resolved during an HTTP request.',\n )\n }\n return store\n}\n\n/**\n * Read a per-request `ContextMeta`-keyed value from outside a controller\n * (services, repositories, anywhere with no `ctx` reference). Returns\n * the augmented type for the key — `MetaValue<K>` — so consumers get the\n * same static safety as `ctx.get(key)` without holding the context.\n *\n * Returns `undefined` outside an active request frame (background jobs,\n * startup code, tests without `requestScopeMiddleware()`) — the helper\n * is intentionally null-tolerant so calling it from a service that\n * runs in both request and non-request paths doesn't throw.\n *\n * @example\n * ```ts\n * declare module '@forinda/kickjs' {\n * interface ContextMeta { tenant: { id: string; name: string } }\n * }\n *\n * @Service()\n * class AuditService {\n * log(action: string) {\n * const tenant = getRequestValue('tenant') // typed: { id; name } | undefined\n * db.audit.insert({ action, tenantId: tenant?.id })\n * }\n * }\n * ```\n */\nexport function getRequestValue<K extends string>(key: K): MetaValue<K> | undefined {\n return requestStore.getStore()?.values.get(key) as MetaValue<K> | undefined\n}\n\n// Intentionally NO `setRequestValue` export.\n//\n// Writes are deliberately context-scoped — only the controlled write\n// surfaces should mutate the per-request Map:\n//\n// 1. A Context Contributor's `resolve()` / `onError()` return value\n// (the runner does `ctx.set(reg.key, value)` on the contributor's\n// behalf). This is the canonical way to populate a key.\n// 2. A controller / middleware / contributor that holds a\n// `RequestContext` instance can call `ctx.set(key, value)`\n// directly. The write is auditable inline at the call site.\n//\n// Letting arbitrary services reach in and mutate the store from\n// anywhere produces \"spooky action at a distance\" — keys appear in\n// the request bag without an obvious source, and tracing which\n// service polluted what becomes a grep exercise. Services that need\n// to publish per-request state should return the value to the caller\n// (controller or contributor) and let *that* layer write it via\n// `ctx.set`. If a future need genuinely requires a service-level\n// write surface, expose a narrow function (`recordTrace`,\n// `markStartTime`) on a service that captures the side effect, not a\n// generic `setRequestValue`.\n","/// <reference types=\"multer\" />\nimport type { Request, Response, NextFunction } from 'express'\nimport { type ContextMeta, type ExecutionContext, type MetaValue } from '../core/execution-context'\nimport { requestStore } from './request-store'\nimport {\n parseQuery,\n type QueryFieldConfig,\n type PaginatedResponse,\n type TypedParsedQuery,\n} from './query'\n\n/**\n * Recursively marks every property of `T` as `readonly`. Preserves\n * `Function`, `Date`, and `RegExp` unchanged, and converts `Map` / `Set`\n * to `ReadonlyMap` / `ReadonlySet` so their mutating methods (`set`,\n * `delete`, `add`, `clear`) are removed from the public surface.\n * Tuples keep their positional element types — only their per-element\n * mutability is locked.\n *\n * Used to seal typed request data (`body`, `params`, `query`) so\n * adopters can't mutate Zod-validated payloads in place — the contract\n * is \"compute new values, stash via `ctx.set`\", not \"edit the request\n * bag\".\n *\n * Type-only. No runtime cost. `DeepReadonly<any>` is `any` (TS\n * distributes conditional types over `any`), so adopters who haven't\n * typed their `Ctx` get no protection but no breakage either.\n */\nexport type DeepReadonly<T> = T extends (...args: never[]) => unknown\n ? T\n : T extends Date | RegExp\n ? T\n : T extends Map<infer K, infer V>\n ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>\n : T extends Set<infer V>\n ? ReadonlySet<DeepReadonly<V>>\n : T extends readonly (infer U)[]\n ? // Distinguish a regular array from a tuple. A regular `T[]` widens\n // its `length` to `number`; a tuple's `length` is a literal\n // (e.g. `2`). The literal branch maps over `keyof T` so each\n // positional slot keeps its own type — `DeepReadonly<[1, 2]>` →\n // `readonly [1, 2]`, not `readonly (1 | 2)[]`.\n number extends T['length']\n ? ReadonlyArray<DeepReadonly<U>>\n : { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T extends object\n ? { readonly [K in keyof T]: DeepReadonly<T[K]> }\n : T\n\n/**\n * Re-export the augmentable {@link ContextMeta} registry from core/.\n * Declared in `core/execution-context.ts` so non-HTTP transports\n * (WS/queue/cron, V2) share one declaration site. Apps still augment\n * via the canonical `declare module '@forinda/kickjs'` form.\n */\nexport type { ContextMeta } from '../core/execution-context'\n\n/**\n * The shape of a single route entry in `KickRoutes`. Generated by\n * `kick typegen` and consumed via the `Ctx<T>` helper. Each field is\n * `unknown` by default and is filled in as the typegen learns more\n * about the route (URL params from the path, body/query from validation\n * schemas, etc.).\n */\nexport interface RouteShape {\n params?: unknown\n body?: unknown\n query?: unknown\n response?: unknown\n}\n\n/**\n * Unified request/response abstraction passed to every controller method.\n * Shields handlers from raw Express objects and provides convenience methods.\n *\n * Implements {@link ExecutionContext} so the transport-agnostic Context\n * Contributor pipeline (#107) can run against this concrete HTTP context\n * the same way it will run against future WS / queue / cron contexts.\n */\nexport class RequestContext<TBody = any, TParams = any, TQuery = any> implements ExecutionContext {\n constructor(\n public readonly req: Request,\n public readonly res: Response,\n public readonly next: NextFunction,\n ) {}\n\n /**\n * Read-side accessor for the per-request metadata map.\n *\n * Returns the AsyncLocalStorage map (`requestStore.getStore().values`)\n * shared with the Context Contributor pipeline when an ALS frame is\n * active. Returns `null` outside any frame — read-side callers\n * (`ctx.get`, `ctx.user`, `ctx.tenantId`, `ctx.roles`) treat that as\n * \"no metadata\" and fall through to their `req.*` defaults. Reads\n * never throw because outside an ALS frame nobody could have written\n * via `ctx.set` anyway, so an empty answer is the truthful one.\n */\n private metadataReadOnly(): Map<string, any> | null {\n return (requestStore.getStore()?.values as Map<string, any> | undefined) ?? null\n }\n\n /**\n * Write-side accessor for the per-request metadata map.\n *\n * Returns the same map as {@link metadataReadOnly} when an ALS frame\n * is active. Throws otherwise — writing to a fallback map outside\n * the ALS frame would silently lose data because other call sites\n * (downstream contributors, request-logger, etc.) read from the\n * canonical store and would never see it. Mount\n * `requestScopeMiddleware()` (Application does this for you in\n * `'auto'` mode) or wrap your test setup in\n * `requestStore.run({...}, () => …)` to fix.\n */\n private metadataForWrite(): Map<string, any> {\n const store = requestStore.getStore()\n if (store) return store.values as Map<string, any>\n throw new Error(\n 'RequestContext.set called outside an AsyncLocalStorage frame. ' +\n 'Mount requestScopeMiddleware() before constructing RequestContext, ' +\n 'or wrap test setup in requestStore.run({ requestId, instances: new Map(), values: new Map() }, () => ...).',\n )\n }\n\n // ── Request Data ────────────────────────────────────────────────────\n\n /**\n * Parsed request body. Returned as `DeepReadonly<TBody>` so adopters\n * can't mutate Zod-validated payloads in place — compute new values\n * and stash them via `ctx.set('key', value)` or a Context Contributor\n * instead. Reach for `ctx.req.body` if you genuinely need the raw,\n * mutable Express handle (rare).\n */\n get body(): DeepReadonly<TBody> {\n return this.req.body as DeepReadonly<TBody>\n }\n\n /** Path parameters parsed from the route — read-only, see {@link body}. */\n get params(): DeepReadonly<TParams> {\n return this.req.params as DeepReadonly<TParams>\n }\n\n /** Parsed query string — read-only, see {@link body}. */\n get query(): DeepReadonly<TQuery> {\n return this.req.query as DeepReadonly<TQuery>\n }\n\n /** Inbound HTTP headers — read-only, see {@link body}. */\n get headers(): Readonly<Request['headers']> {\n return this.req.headers\n }\n\n get requestId(): string | undefined {\n return (this.req as any).requestId ?? (this.req.headers['x-request-id'] as string | undefined)\n }\n\n /** Session data (requires session middleware) */\n get session(): any {\n return (this.req as any).session\n }\n\n /**\n * The authenticated user set by AuthAdapter.\n * Reads from the per-request metadata store first (`ctx.set('user', ...)`),\n * then falls back to `req.user` (set directly by AuthAdapter middleware).\n *\n * Extend the `AuthUser` interface via module augmentation in `@forinda/kickjs-auth`\n * to get full typing.\n *\n * @example\n * ```ts\n * @Get('/me')\n * @Authenticated()\n * getProfile(ctx: RequestContext) {\n * const user = ctx.user\n * if (!user) return ctx.notFound()\n * return ctx.json({ user })\n * }\n * ```\n */\n get user(): MetaValue<'user', Record<string, any>> | undefined {\n return this.metadataReadOnly()?.get('user') ?? (this.req as any).user\n }\n\n /**\n * Tenant identifier for multi-tenant apps. Reads `user.tenantId` —\n * populated either by `AuthAdapter.testMode({ tenantId })`, a tenant\n * resolver middleware, or manually on the strategy's `mapPayload`.\n *\n * Augment `AuthUser` (or `ContextMeta['user']`) to widen the return\n * type if your user shape guarantees a non-`string | undefined` id.\n */\n get tenantId(): string | undefined {\n const u = this.user as Record<string, any> | undefined\n return u?.tenantId\n }\n\n /**\n * Effective role list for the current user. Prefers `user.tenantRoles`\n * (populated by `AuthAdapterOptions.roleResolver` under a tenant) and\n * falls back to `user.roles`. Returns an empty array when the user is\n * absent or the shape has neither field.\n */\n get roles(): string[] {\n const u = this.user as Record<string, any> | undefined\n return u?.tenantRoles ?? u?.roles ?? []\n }\n\n // ── Query String Parsing ───────────────────────────────────────────\n\n /**\n * Parse the request query string into structured filters, sort, pagination, and search.\n * Pass the result to an ORM query builder adapter (Drizzle, Prisma, Sequelize, etc.).\n *\n * Generic over the field config: when passed an inline literal (or\n * `as const`), `filters[].field` and `sort[].field` are narrowed to\n * the exact whitelisted field names. Pass nothing or a non-literal\n * config and you get the loose `string` fallback.\n *\n * @param fieldConfig - Optional whitelist for filterable, sortable, and searchable fields\n *\n * @example\n * ```ts\n * @Get('/')\n * async list(ctx: RequestContext) {\n * const parsed = ctx.qs({\n * filterable: ['status', 'priority'],\n * sortable: ['createdAt', 'title'],\n * } as const)\n * parsed.filters[0].field // typed as 'status' | 'priority'\n * parsed.sort[0].field // typed as 'createdAt' | 'title'\n * }\n * ```\n */\n qs<TConfig extends QueryFieldConfig | undefined = undefined>(\n fieldConfig?: TConfig,\n ): TypedParsedQuery<TConfig> {\n return parseQuery<TConfig>(this.req.query as Record<string, any>, fieldConfig)\n }\n\n // ── File Uploads ────────────────────────────────────────────────────\n\n /**\n * Single uploaded file (requires `@FileUpload({ mode: 'single' })`).\n * Returned as `DeepReadonly<Express.Multer.File>` — file metadata is\n * set by the upload middleware and shouldn't be mutated downstream.\n */\n get file(): DeepReadonly<Express.Multer.File> | undefined {\n return this.req.file\n }\n\n /**\n * Array of uploaded files (requires `@FileUpload({ mode: 'array' })`).\n * Returns a `ReadonlyArray<DeepReadonly<Express.Multer.File>>` — both\n * the list (`push`/`pop`) and each file's fields (`size`, `originalname`,\n * etc.) are locked. Use `ctx.req.files` if you genuinely need the\n * mutable Express handle.\n */\n get files(): ReadonlyArray<DeepReadonly<Express.Multer.File>> | undefined {\n // multer's `Request['files']` is a union — array (mode: 'array' /\n // 'any') or `{ [fieldname]: File[] }` (mode: 'fields'). The\n // `@FileUpload({ mode: 'array' })` decorator only sets the array\n // shape, but `Request['files']` is the broad union. Narrow back to\n // `File[]` here for the common-case getter; adopters using\n // `mode: 'fields'` can read `ctx.req.files` directly for the\n // dictionary shape.\n const f = this.req.files\n return Array.isArray(f) ? f : undefined\n }\n\n // ── Metadata Store ──────────────────────────────────────────────────\n\n /**\n * Read a value from the per-request metadata store.\n *\n * When `ContextMeta` has been augmented with a matching key, the return\n * type is inferred automatically. For ad-hoc keys, pass a generic:\n * `ctx.get<MyType>('custom')`.\n */\n get<K extends string>(key: K): MetaValue<K> | undefined {\n return this.metadataReadOnly()?.get(key) as MetaValue<K> | undefined\n }\n\n /**\n * Write a value to the per-request metadata store. Throws when no\n * AsyncLocalStorage frame is active — see {@link metadataForWrite}\n * for why a silent fallback is the wrong default.\n */\n set<K extends string>(key: K, value: MetaValue<K>): void {\n this.metadataForWrite().set(key, value)\n }\n\n // ── Response Helpers ────────────────────────────────────────────────\n\n json(data: any, status = 200) {\n return this.res.status(status).json(data)\n }\n\n created(data: any) {\n return this.res.status(201).json(data)\n }\n\n noContent() {\n return this.res.status(204).end()\n }\n\n notFound(message = 'Not Found') {\n return this.res.status(404).json({ message })\n }\n\n badRequest(message: string) {\n return this.res.status(400).json({ message })\n }\n\n html(content: string, status = 200) {\n return this.res.status(status).type('html').send(content)\n }\n\n download(buffer: Buffer, filename: string, contentType = 'application/octet-stream') {\n this.res.setHeader('Content-Disposition', `attachment; filename=\"${filename}\"`)\n this.res.setHeader('Content-Type', contentType)\n return this.res.send(buffer)\n }\n\n /**\n * Render a template using the registered view engine (EJS, Pug, Handlebars, etc.).\n * Requires a ViewAdapter to be configured in bootstrap().\n *\n * @param template - Template name (without extension, relative to viewsDir)\n * @param data - Data to pass to the template\n *\n * @example\n * ```ts\n * ctx.render('dashboard', { user, title: 'Dashboard' })\n * ctx.render('emails/welcome', { name: 'Alice' })\n * ```\n */\n render(template: string, data: Record<string, any> = {}) {\n return this.res.render(template, data)\n }\n\n /**\n * Parse query params and return a standardized paginated response.\n * Calls `ctx.qs()` internally, then wraps your data with pagination meta.\n *\n * @param fetcher - Async function that receives ParsedQuery and returns `{ data, total }`\n * @param fieldConfig - Optional whitelist for filterable, sortable, searchable fields\n *\n * @example\n * ```ts\n * @Get('/')\n * async list(ctx: RequestContext) {\n * return ctx.paginate(\n * async (parsed) => {\n * const data = await db.select().from(users)\n * .where(query.where).limit(parsed.pagination.limit)\n * .offset(parsed.pagination.offset).all()\n * const total = await db.select({ count: count() }).from(users).get()\n * return { data, total: total?.count ?? 0 }\n * },\n * { filterable: ['name', 'role'], sortable: ['createdAt'] },\n * )\n * }\n * ```\n */\n async paginate<T, TConfig extends QueryFieldConfig | undefined = undefined>(\n fetcher: (parsed: TypedParsedQuery<TConfig>) => Promise<{ data: T[]; total: number }>,\n fieldConfig?: TConfig,\n ) {\n const parsed = this.qs<TConfig>(fieldConfig)\n const { data, total } = await fetcher(parsed)\n const { page, limit } = parsed.pagination\n const totalPages = Math.ceil(total / limit) || 1\n\n const response: PaginatedResponse<T> = {\n data,\n meta: {\n page,\n limit,\n total,\n totalPages,\n hasNext: page < totalPages,\n hasPrev: page > 1,\n },\n }\n\n return this.json(response)\n }\n\n // ── Server-Sent Events ──────────────────────────────────────────────\n\n /**\n * Start an SSE (Server-Sent Events) stream.\n * Sets the correct headers and returns helpers to send events.\n *\n * @example\n * ```ts\n * @Get('/events')\n * async stream(ctx: RequestContext) {\n * const sse = ctx.sse()\n *\n * const interval = setInterval(() => {\n * sse.send({ time: new Date().toISOString() }, 'tick')\n * }, 1000)\n *\n * sse.onClose(() => clearInterval(interval))\n * }\n * ```\n */\n sse() {\n this.res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n })\n this.res.flushHeaders()\n\n const closeCallbacks: Array<() => void> = []\n\n this.req.on('close', () => {\n for (const cb of closeCallbacks) cb()\n })\n\n return {\n /** Send an SSE event with optional event name and id */\n send: (data: any, event?: string, id?: string) => {\n if (id) this.res.write(`id: ${id}\\n`)\n if (event) this.res.write(`event: ${event}\\n`)\n this.res.write(`data: ${JSON.stringify(data)}\\n\\n`)\n },\n /** Send a comment (keeps connection alive) */\n comment: (text: string) => {\n this.res.write(`: ${text}\\n\\n`)\n },\n /** Register a callback when the client disconnects */\n onClose: (fn: () => void) => {\n closeCallbacks.push(fn)\n },\n /** End the SSE stream */\n close: () => {\n this.res.end()\n },\n }\n }\n}\n\n/**\n * Short alias for `RequestContext` parameterised by a route shape.\n *\n * Use with `kick typegen`'s generated `KickRoutes` namespace for\n * fully-typed `ctx.params`, `ctx.body`, and `ctx.query`. Metadata\n * typing (`ctx.get()`, `ctx.set()`, `ctx.user`) comes from\n * `ContextMeta` augmentation and works on all `RequestContext` instances.\n *\n * @example\n * ```ts\n * @Get('/:id')\n * getUser(ctx: Ctx<KickRoutes.UserController['getUser']>) {\n * ctx.params.id // typed from RouteShape\n * ctx.body // typed from RouteShape\n * ctx.user // typed from ContextMeta\n * ctx.get('tenant') // typed from ContextMeta\n * }\n * ```\n */\nexport type Ctx<TRoute extends RouteShape = RouteShape> = RequestContext<\n TRoute extends { body: infer B } ? B : any,\n TRoute extends { params: infer P } ? P : any,\n TRoute extends { query: infer Q } ? Q : any\n>\n\n/**\n * Global ambient registry of typed routes, populated by `kick typegen`.\n *\n * Each interface inside `KickRoutes` corresponds to a controller class;\n * each property is one route method on that controller. The values\n * conform to `RouteShape`.\n *\n * Empty by default — declarations come from `.kickjs/types/routes.d.ts`\n * generated alongside the rest of the typegen output.\n */\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace KickRoutes {}\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"context-BAdAV-zO.d.mts","names":[],"sources":["../src/http/context.ts"],"mappings":"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"}