@forinda/kickjs 6.3.1 → 6.5.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.
- package/dist/{adapter-stSpsUHs.d.mts → adapter-CEt1MEdF.d.mts} +126 -10
- package/dist/adapter-CEt1MEdF.d.mts.map +1 -0
- package/dist/{app-module-BwYtACD6.d.mts → app-module-BSpHKlhE.d.mts} +149 -28
- package/dist/app-module-BSpHKlhE.d.mts.map +1 -0
- package/dist/{application-C_KlrNoH.d.mts → application-Bzdzl5wP.d.mts} +3 -3
- package/dist/{application-C_KlrNoH.d.mts.map → application-Bzdzl5wP.d.mts.map} +1 -1
- package/dist/application-C-r79XqD.mjs +12 -0
- package/dist/application-C-r79XqD.mjs.map +1 -0
- package/dist/{bootstrap-BJB_prf2.mjs → bootstrap-C1VGQQVF.mjs} +3 -3
- package/dist/{bootstrap-BJB_prf2.mjs.map → bootstrap-C1VGQQVF.mjs.map} +1 -1
- package/dist/{bootstrap-BgffgFNX.d.mts → bootstrap-DYexY169.d.mts} +2 -2
- package/dist/{bootstrap-BgffgFNX.d.mts.map → bootstrap-DYexY169.d.mts.map} +1 -1
- package/dist/config/index.mjs +2 -2
- package/dist/{config-CZB3bZ5C.mjs → config-BeIZJBXA.mjs} +3 -3
- package/dist/{config-CZB3bZ5C.mjs.map → config-BeIZJBXA.mjs.map} +1 -1
- package/dist/container-CBaL5lus.mjs +117 -0
- package/dist/container-CBaL5lus.mjs.map +1 -0
- package/dist/context-BS5o1IuU.mjs +12 -0
- package/dist/context-BS5o1IuU.mjs.map +1 -0
- package/dist/context-errors-CwD27Vbj.mjs +13 -0
- package/dist/context-errors-CwD27Vbj.mjs.map +1 -0
- package/dist/contributor-runner-Cu13cDQp.mjs +12 -0
- package/dist/contributor-runner-Cu13cDQp.mjs.map +1 -0
- package/dist/core/adapter.d.mts +1 -1
- package/dist/core/adapter.mjs +1 -1
- package/dist/core/app-module.d.mts +1 -1
- package/dist/core/app-module.mjs +1 -1
- package/dist/core/cache.mjs +1 -1
- package/dist/core/container.mjs +2 -2
- package/dist/core/cron.mjs +2 -2
- package/dist/core/decorators.mjs +2 -2
- package/dist/core/errors.mjs +1 -1
- package/dist/core/interfaces.mjs +1 -1
- package/dist/core/logger.d.mts +2 -2
- package/dist/core/logger.mjs +2 -2
- package/dist/core/logger.mjs.map +1 -1
- package/dist/core/path.mjs +1 -1
- package/dist/core/plugin.d.mts +1 -1
- package/dist/core/plugin.mjs +1 -1
- package/dist/core/reactivity.mjs +1 -1
- package/dist/core-TDR0XGzO.mjs +12 -0
- package/dist/core-TDR0XGzO.mjs.map +1 -0
- package/dist/{csrf-BXorNCE1.mjs → csrf-DNh_7aOk.mjs} +2 -2
- package/dist/{csrf-BXorNCE1.mjs.map → csrf-DNh_7aOk.mjs.map} +1 -1
- package/dist/{disposables-Dt1t6RE0.mjs → disposables-mnjJuaVx.mjs} +2 -2
- package/dist/{disposables-Dt1t6RE0.mjs.map → disposables-mnjJuaVx.mjs.map} +1 -1
- package/dist/{env-C_gXlHfU.mjs → env-Dgtjncd5.mjs} +3 -3
- package/dist/{env-C_gXlHfU.mjs.map → env-Dgtjncd5.mjs.map} +1 -1
- package/dist/{error-handler-3ih4E-QZ.d.mts → error-handler-DR01K_rI.d.mts} +5 -1
- package/dist/error-handler-DR01K_rI.d.mts.map +1 -0
- package/dist/handler-UvmjQGYc.mjs +13 -0
- package/dist/handler-UvmjQGYc.mjs.map +1 -0
- package/dist/http/application.d.mts +1 -1
- package/dist/http/application.mjs +2 -2
- package/dist/http/bootstrap.d.mts +1 -1
- package/dist/http/bootstrap.mjs +2 -2
- package/dist/http/context.d.mts +2 -2
- package/dist/http/context.mjs +2 -2
- package/dist/http/middleware/cors.mjs +1 -1
- package/dist/http/middleware/csrf.mjs +2 -2
- package/dist/http/middleware/error-handler.d.mts +1 -1
- package/dist/http/middleware/error-handler.mjs +3 -2
- package/dist/http/middleware/error-handler.mjs.map +1 -1
- package/dist/http/middleware/helmet.mjs +1 -1
- package/dist/http/middleware/rate-limit.mjs +2 -2
- package/dist/http/middleware/request-id.mjs +1 -1
- package/dist/http/middleware/request-logger.mjs +2 -2
- package/dist/http/middleware/session.mjs +2 -2
- package/dist/http/middleware/spa.d.mts +1 -1
- package/dist/http/middleware/spa.mjs +2 -2
- package/dist/http/middleware/upload.mjs +2 -2
- package/dist/http/middleware/validate.mjs +1 -1
- package/dist/http/middleware/views.d.mts +1 -1
- package/dist/http/middleware/views.mjs +2 -2
- package/dist/http/query/index.mjs +2 -2
- package/dist/http/router-builder.d.mts +2 -2
- package/dist/http/router-builder.mjs +2 -2
- package/dist/http/router-builder.mjs.map +1 -1
- package/dist/http/runtimes/fastify.d.mts +1 -1
- package/dist/http/runtimes/fastify.mjs +2 -2
- package/dist/http/runtimes/h3-web.d.mts +1 -1
- package/dist/http/runtimes/h3-web.d.mts.map +1 -1
- package/dist/http/runtimes/h3-web.mjs +2 -2
- package/dist/http/runtimes/h3-web.mjs.map +1 -1
- package/dist/http/runtimes/h3.d.mts +1 -1
- package/dist/http/runtimes/h3.mjs +2 -2
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/kv-stores-O8HUSwpv.mjs +12 -0
- package/dist/kv-stores-O8HUSwpv.mjs.map +1 -0
- package/dist/kv-stores-duYHIEr1.d.mts +188 -0
- package/dist/kv-stores-duYHIEr1.d.mts.map +1 -0
- package/dist/{logger-COlEkn_C.d.mts → logger-DA43DvNY.d.mts} +18 -3
- package/dist/logger-DA43DvNY.d.mts.map +1 -0
- package/dist/{metadata-JG-zrMEb.mjs → metadata-CUDfhy4e.mjs} +2 -2
- package/dist/{metadata-JG-zrMEb.mjs.map → metadata-CUDfhy4e.mjs.map} +1 -1
- package/dist/{module-registry-Bp2f2NWM.mjs → module-registry-Mv5FqVlq.mjs} +2 -2
- package/dist/{module-registry-Bp2f2NWM.mjs.map → module-registry-Mv5FqVlq.mjs.map} +1 -1
- package/dist/{query-C7YPGlW6.mjs → query-gMAVXzx2.mjs} +2 -2
- package/dist/{query-C7YPGlW6.mjs.map → query-gMAVXzx2.mjs.map} +1 -1
- package/dist/{reply-ym0sPcEJ.mjs → reply-BMBvs-FB.mjs} +3 -3
- package/dist/{reply-ym0sPcEJ.mjs.map → reply-BMBvs-FB.mjs.map} +1 -1
- package/dist/{rolldown-runtime-BsVplZDg.mjs → rolldown-runtime-CuwksmWW.mjs} +1 -1
- package/dist/{router-builder-BK7yPm89.d.mts → router-builder-GWgxoOdC.d.mts} +14 -3
- package/dist/router-builder-GWgxoOdC.d.mts.map +1 -0
- package/dist/{spa-BwO1qOuA.d.mts → spa-DWzSWAzx.d.mts} +2 -2
- package/dist/{spa-BwO1qOuA.d.mts.map → spa-DWzSWAzx.d.mts.map} +1 -1
- package/dist/{upload-config-BHIV2SGy.mjs → upload-config-CFfHvoHF.mjs} +2 -2
- package/dist/{upload-config-BHIV2SGy.mjs.map → upload-config-CFfHvoHF.mjs.map} +1 -1
- package/dist/{views-w0EPrhui.d.mts → views-Kp8Z3AO1.d.mts} +4 -4
- package/dist/{views-w0EPrhui.d.mts.map → views-Kp8Z3AO1.d.mts.map} +1 -1
- package/dist/web.d.mts +11 -4
- package/dist/web.d.mts.map +1 -1
- package/dist/web.mjs +2 -2
- package/dist/web.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/adapter-stSpsUHs.d.mts.map +0 -1
- package/dist/app-module-BwYtACD6.d.mts.map +0 -1
- package/dist/application-ClWQpWUz.mjs +0 -12
- package/dist/application-ClWQpWUz.mjs.map +0 -1
- package/dist/container-BkAX8ftP.mjs +0 -108
- package/dist/container-BkAX8ftP.mjs.map +0 -1
- package/dist/context-BADwflt3.mjs +0 -12
- package/dist/context-BADwflt3.mjs.map +0 -1
- package/dist/contributor-runner-cYobgGDc.mjs +0 -13
- package/dist/contributor-runner-cYobgGDc.mjs.map +0 -1
- package/dist/core-BnRYGmHr.mjs +0 -12
- package/dist/core-BnRYGmHr.mjs.map +0 -1
- package/dist/error-handler-3ih4E-QZ.d.mts.map +0 -1
- package/dist/handler-Cm6IG_Ld.mjs +0 -12
- package/dist/handler-Cm6IG_Ld.mjs.map +0 -1
- package/dist/logger-COlEkn_C.d.mts.map +0 -1
- package/dist/reply-2CvBP_Sl.d.mts +0 -63
- package/dist/reply-2CvBP_Sl.d.mts.map +0 -1
- package/dist/router-builder-BK7yPm89.d.mts.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs v6.
|
|
2
|
+
* @forinda/kickjs v6.5.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{i as requestStore,t as RequestContext}from"../../context-
|
|
11
|
+
import{i as requestStore,t as RequestContext}from"../../context-BS5o1IuU.mjs";import{a as disposeRequestStore,i as createRequestStore,t as applyHandlerResult}from"../../reply-BMBvs-FB.mjs";import{validate}from"../middleware/validate.mjs";import{t as applyUploadConfig}from"../../upload-config-CFfHvoHF.mjs";import"../middleware/upload.mjs";import{createRequire}from"node:module";const peerRequire=createRequire(import.meta.url),loadPeer=name=>{let mod=peerRequire(name);return mod&&mod.__esModule&&mod.default?mod.default:mod.default??mod},QUEUE=Symbol(`kickjs.fastify.connectQueue`),READY=Symbol(`kickjs.fastify.ready`),NEXT=Symbol(`kickjs.fastify.next`),NEEDS_MULTIPART=Symbol(`kickjs.fastify.needsMultipart`),NOOP_NEXT=()=>{};var FastifyReplyDriver=class{reply;constructor(reply){this.reply=reply}status(code){return this.reply.code(code),this}json(data){return this.reply.send(data),this}send(data){return this.reply.send(data),this}type(contentType){return this.reply.type(contentType),this}setHeader(name,value){return this.reply.header(name,value),this}render(){throw Error(`ctx.render() is not supported on the Fastify runtime (no view engine).`)}writeHead(statusCode,headers){return this.reply.hijack(),this.reply.raw.writeHead(statusCode,headers),this}flushHeaders(){return this.reply.raw.flushHeaders?.(),this}write(chunk){return this.reply.raw.write(chunk)}end(data){return this.reply.raw.end(data),this}once(event,listener){return this.reply.raw.once(event,listener),this}get headersSent(){return this.reply.sent||this.reply.raw.headersSent}};function replyDriver(reply){return new FastifyReplyDriver(reply)}function routeHandler(entry){let validator=entry.meta.validation?validate(entry.meta.validation):void 0;return async(request,reply)=>{let raw=request.raw;if(raw.body=request.body,raw.params=request.params,raw.query=request.query,entry.meta.upload&&entry.meta.upload.mode!==`none`){if(typeof request.parts!=`function`)throw Error(`@forinda/kickjs: @FileUpload on the Fastify runtime requires '@fastify/multipart'.
|
|
12
12
|
Install it as a peer dependency: pnpm add @fastify/multipart.`);let rawParts=[],fields={};for await(let part of request.parts())part.type===`file`&&part.toBuffer?rawParts.push({fieldname:part.fieldname,filename:part.filename??``,mimetype:part.mimetype??`application/octet-stream`,buffer:await part.toBuffer()}):fields[part.fieldname]=part.value;let{file,files}=applyUploadConfig(rawParts,entry.meta.upload);raw.file=file,raw.files=files,raw.body={...raw.body,...fields}}let headerId=raw.headers[`x-request-id`],store=createRequestStore((Array.isArray(headerId)?headerId[0]:headerId)||raw.requestId);raw.requestId=store.requestId,reply.raw.once(`close`,()=>disposeRequestStore(store)),await requestStore.run(store,async()=>{let ctx=new RequestContext(raw,reply,NOOP_NEXT,replyDriver(reply));validator&&await new Promise((resolve,reject)=>{validator(raw,void 0,err=>err?reject(err):resolve())});for(let mw of entry.middlewares){let advanced=!1;if(await new Promise((resolve,reject)=>{Promise.resolve(mw(ctx,err=>{advanced=!0,err?reject(err):resolve()})).catch(reject)}),!advanced||reply.sent)return}if(entry.contributorRunner&&await entry.contributorRunner(ctx),reply.sent)return;let result=await entry.handler(ctx);reply.sent||applyHandlerResult(ctx,result)})}}function fastifyRuntime(){return{name:`fastify`,createApp(options={}){let app=loadPeer(`fastify`)({logger:!1,routerOptions:{ignoreTrailingSlash:!0},trustProxy:typeof options.trustProxy==`function`?!1:options.trustProxy});return app[QUEUE]=[],app},nodeHandler(app){let state=app;state[READY]||(state[READY]=(async()=>{if(state[NEEDS_MULTIPART]){let multipart=loadPeer(`@fastify/multipart`);await app.register(multipart,{limits:{fileSize:2**53-1}})}let middie=loadPeer(`@fastify/middie`);await app.register(middie);for(let{mw,opts}of state[QUEUE]??[])opts?.path===void 0?app.use(mw):app.use(opts.path,mw);await app.ready()})());let readyP=state[READY];return(req,res,next)=>{next&&(req[NEXT]=next),readyP.then(()=>app.routing(req,res)).catch(err=>{next&&next(err)})}},mountRoutes(app,table){for(let{mountPath,routes}of table)for(let entry of routes){entry.meta.upload&&(app[NEEDS_MULTIPART]=!0);let url=joinPath(mountPath,entry.path);app.route({method:entry.method,url,handler:routeHandler(entry)})}},useConnect(app,mw,opts){app[QUEUE]?.push({mw,opts})},serveStatic(app,prefix,dir){let serveStatic=loadPeer(`serve-static`);this.useConnect(app,serveStatic(dir),{path:prefix})},setNotFound(app,mw){app.setNotFoundHandler((request,reply)=>{let next=request.raw[NEXT];if(next){reply.hijack(),next();return}mw(request.raw,replyDriver(reply),NOOP_NEXT)})},setErrorHandler(app,mw){app.setErrorHandler((err,request,reply)=>{mw(err,request.raw,replyDriver(reply),NOOP_NEXT)})},capabilities:{render:!1,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!0}}}function joinPath(mountPath,path){let a=mountPath.endsWith(`/`)?mountPath.slice(0,-1):mountPath;return path===`/`||path===``?a===``?`/`:a:`${a}${path.startsWith(`/`)?path:`/${path}`}`}export{fastifyRuntime};
|
|
13
13
|
//# sourceMappingURL=fastify.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"h3-web.d.mts","names":[],"sources":["../../../src/http/runtimes/h3-web.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"h3-web.d.mts","names":[],"sources":["../../../src/http/runtimes/h3-web.ts"],"mappings":";;;;;UA0CU,SAAA;EACR,EAAA,CAAG,MAAA,UAAgB,IAAA,UAAc,OAAA,GAAU,KAAA,EAAO,WAAA;EAClD,GAAA,CAAI,IAAA,UAAc,OAAA,GAAU,KAAA,EAAO,WAAA;EACnC,GAAA,IAAO,IAAA;EACP,KAAA,CAAM,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,QAAA;AAAA;AAAA,UAEzB,WAAA;EACR,GAAA,EAAK,OAAA;IAAY,OAAA;MAAY,IAAA;QAAS,GAAA,EAAK,eAAA;QAAiB,GAAA,EAAK,cAAA;MAAA;IAAA;EAAA;EACjE,GAAA,EAAK,GAAA;EACL,OAAA;IAAW,MAAA,GAAS,MAAA;EAAA;AAAA;AAAA,UASL,mBAAA;EAhBa;;;;;;;EAwB5B,EAAE;AAAA;;AAtBuC;AAAA;;iBA4F3B,YAAA,CAAa,OAAA,GAAS,mBAAA,GAA2B,WAAA,CAAY,SAAA;;;;;UAiH5D,iBAAA;EACf,OAAA,EAAS,OAAA;EACT,QAAA,EAAU,QAAQ;EAClB,GAAA;AAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs v6.
|
|
2
|
+
* @forinda/kickjs v6.5.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{n as WebRequestShim,r as WebResponseDriver,t as compileWebRoute}from"../../handler-
|
|
11
|
+
import{createLogger,describeError}from"../../core/logger.mjs";import{n as WebRequestShim,r as WebResponseDriver,t as compileWebRoute}from"../../handler-UvmjQGYc.mjs";import{createRequire}from"node:module";const log=createLogger(`H3WebRuntime`),peerRequire=createRequire(import.meta.url),NOTFOUND_MW=Symbol(`kickjs.h3web.notFoundMw`),ERROR_MW=Symbol(`kickjs.h3web.errorMw`),ASSEMBLED=Symbol(`kickjs.h3web.assembled`),NOOP_NEXT=()=>{};function loadH3v2(injected){let m=injected??peerRequire(`h3`),resolved=m&&m.__esModule&&m.default?{...m.default,...m}:m;if(typeof resolved.H3!=`function`)throw Error("@forinda/kickjs: h3WebRuntime() requires h3 v2 (the web-standards line, npm dist-tag 'latest'), but the installed h3 has no H3 class — that is the v1 line. Either `pnpm add h3@latest` for this runtime, or keep h3 v1 and use h3Runtime() from '@forinda/kickjs/h3'.");return resolved}async function runConnectTerminal(mw,event,err){let req=new WebRequestShim(event.req,event.url),driver=new WebResponseDriver(event.req.signal);return mw&&(err===void 0?mw(req,driver,NOOP_NEXT):mw(err,req,driver,NOOP_NEXT)),driver.settled||(err===void 0?driver.status(404).json({error:`Not Found`}):(log.error(err,`h3-web: error handler did not settle the response — ${describeError(err)}`),driver.status(500).json({error:`Internal Server Error`,...typeof process<`u`&&process.env?.NODE_ENV===`production`?{}:{message:describeError(err)}}))),driver.ready}function h3WebRuntime(options={}){let{H3,toNodeHandler,fromNodeHandler}=loadH3v2(options.h3);return{name:`h3-web`,createApp(_options={}){let holder={},app=new H3({onError:(error,event)=>{let mw=holder.app?.[ERROR_MW];return runConnectTerminal(mw,event,error)}});return holder.app=app,app},nodeHandler(app){let state=app;state[ASSEMBLED]||(state[ASSEMBLED]=!0,app.all(`/**`,event=>{let mw=state[NOTFOUND_MW];return runConnectTerminal(mw,event)}));let listener=toNodeHandler(app);return(req,res,next)=>{try{let result=listener(req,res);result&&typeof result.then==`function`&&result.catch(err=>next?.(err))}catch(err){next?.(err)}}},mountRoutes(app,table){for(let{mountPath,routes}of table)for(let entry of routes){let url=joinPath(mountPath,entry.path),run=compileWebRoute(entry,{onError:async(err,req,driver)=>{let mw=app[ERROR_MW];mw&&mw(err,req,driver,NOOP_NEXT)}});app.on(entry.method,url,event=>run({request:event.req,url:event.url,params:event.context.params??{}}))}},useConnect(app,mw,opts){let handler=fromNodeHandler(mw);opts?.path===void 0?app.use(handler):app.use(joinPath(String(opts.path),`/**`),handler)},serveStatic(app,prefix,dir){let serveStatic=peerRequire(`serve-static`);this.useConnect(app,serveStatic(dir),{path:prefix})},setNotFound(app,mw){app[NOTFOUND_MW]=mw},setErrorHandler(app,mw){app[ERROR_MW]=mw},capabilities:{render:!1,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!0}}}function joinPath(mountPath,path){let a=mountPath.endsWith(`/`)?mountPath.slice(0,-1):mountPath;return path===`/`||path===``?a===``?`/`:a:`${a}${path.startsWith(`/`)?path:`/${path}`}`}export{h3WebRuntime};
|
|
12
12
|
//# sourceMappingURL=h3-web.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"h3-web.mjs","names":[],"sources":["../../../src/http/runtimes/h3-web.ts"],"sourcesContent":["// The h3 v2 (web-standards) runtime — `@forinda/kickjs/h3-web`.\n//\n// ADDITIVE runtime: the h3 v1 runtime (`./h3.ts`, `@forinda/kickjs/h3`)\n// stays untouched so existing adopters keep working (locked decision,\n// web-standards-edge-design.md §3.1). This runtime targets h3 >= 2 — the\n// web-standard rebase where `event.req` is a WHATWG Request, handlers\n// return values/Responses, and `app.fetch(request)` is the universal entry.\n//\n// The request pipeline runs through the shared web driver pair\n// (`../web/driver`, `../web/handler`) — the same code path the\n// `@forinda/kickjs/web` edge entry uses, so node bootstrap and edge deploys\n// exercise identical request semantics.\n//\n// Known limitation vs the v1 runtime: no Vite dev-server fall-through —\n// unmatched requests are answered by the kick notFound handler (h3 v2 owns\n// the full request; there is no node `next()` bail-out mid-engine). Use the\n// v1 h3 runtime or Express for Vite-integrated dev setups.\n\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createRequire } from 'node:module'\n\nimport type {\n ConnectMiddleware,\n HttpRuntime,\n RouteTable,\n RuntimeAppOptions,\n UseConnectOptions,\n} from '../runtime'\nimport { compileWebRoute, type WebRouteHooks } from '../web/handler'\nimport { WebRequestShim, WebResponseDriver } from '../web/driver'\n\nconst peerRequire = createRequire(import.meta.url)\n\nconst NOTFOUND_MW = Symbol('kickjs.h3web.notFoundMw')\nconst ERROR_MW = Symbol('kickjs.h3web.errorMw')\nconst ASSEMBLED = Symbol('kickjs.h3web.assembled')\nconst NOOP_NEXT = (): void => {}\n\n// Minimal structural surface of h3 v2 we depend on.\ninterface H3AppLike {\n on(method: string, path: string, handler: (event: H3EventLike) => unknown): unknown\n all(path: string, handler: (event: H3EventLike) => unknown): unknown\n use(...args: unknown[]): unknown\n fetch(request: Request): Promise<Response>\n}\ninterface H3EventLike {\n req: Request & { runtime?: { node?: { req: IncomingMessage; res: ServerResponse } } }\n url: URL\n context: { params?: Record<string, string> }\n}\n\ninterface H3v2Module {\n H3: new (config?: unknown) => H3AppLike\n toNodeHandler: (app: H3AppLike) => (req: IncomingMessage, res: ServerResponse) => void\n fromNodeHandler: (mw: unknown) => unknown\n}\n\nexport interface H3WebRuntimeOptions {\n /**\n * Pre-imported h3 v2 module. Two audiences:\n * - Edge/worker bundlers with no `createRequire` at runtime:\n * `h3WebRuntime({ h3: await import('h3') })`\n * - Tests running both h3 majors side by side via an npm alias.\n * Omitted → the peer is loaded via `createRequire` (node bootstrap path).\n */\n h3?: unknown\n}\n\n/** Resolve h3 (injected or peer-required) and fail fast unless it is v2. */\nfunction loadH3v2(injected?: unknown): H3v2Module {\n const mod = injected ?? peerRequire('h3')\n const m = mod as { __esModule?: boolean; default?: Record<string, unknown> } & Record<\n string,\n unknown\n >\n const resolved = (m && m.__esModule && m.default\n ? { ...m.default, ...m }\n : m) as unknown as H3v2Module\n if (typeof resolved.H3 !== 'function') {\n throw new Error(\n \"@forinda/kickjs: h3WebRuntime() requires h3 v2 (the web-standards line, npm dist-tag 'latest'), \" +\n 'but the installed h3 has no H3 class — that is the v1 line. ' +\n \"Either `pnpm add h3@latest` for this runtime, or keep h3 v1 and use h3Runtime() from '@forinda/kickjs/h3'.\",\n )\n }\n return resolved\n}\n\n/**\n * Bridge a connect-style terminal middleware (kick's notFound / error\n * handlers) onto the web driver pair, returning the produced Response.\n */\nasync function runConnectTerminal(\n mw: ConnectMiddleware | undefined,\n event: H3EventLike,\n err?: unknown,\n): Promise<Response> {\n const req = new WebRequestShim(event.req, event.url)\n const driver = new WebResponseDriver(event.req.signal)\n if (mw) {\n if (err !== undefined) {\n ;(mw as (e: unknown, rq: unknown, rs: unknown, n: () => void) => void)(\n err,\n req,\n driver,\n NOOP_NEXT,\n )\n } else {\n ;(mw as (rq: unknown, rs: unknown, n: () => void) => void)(req, driver, NOOP_NEXT)\n }\n }\n if (!driver.settled) {\n if (err !== undefined) driver.status(500).json({ error: 'Internal Server Error' })\n else driver.status(404).json({ error: 'Not Found' })\n }\n return driver.ready\n}\n\n/**\n * The h3 v2 HTTP runtime. Pass to `bootstrap({ runtime: h3WebRuntime() })`.\n * Requires the h3 v2 peer; fails fast with guidance when v1 is installed.\n */\nexport function h3WebRuntime(options: H3WebRuntimeOptions = {}): HttpRuntime<H3AppLike> {\n const { H3, toNodeHandler, fromNodeHandler } = loadH3v2(options.h3)\n\n return {\n name: 'h3-web',\n\n createApp(_options: RuntimeAppOptions = {}): H3AppLike {\n const holder: { app?: H3AppLike } = {}\n const app = new H3({\n // Engine-level errors (middleware throws outside the kick pipeline)\n // dispatch to the kick error handler once setErrorHandler stashes it.\n onError: (error: unknown, event: H3EventLike) => {\n const mw = (holder.app as unknown as Record<symbol, ConnectMiddleware | undefined>)?.[\n ERROR_MW\n ]\n return runConnectTerminal(mw, event, error)\n },\n })\n holder.app = app\n return app\n },\n\n nodeHandler(app) {\n const state = app as unknown as Record<symbol, unknown>\n if (!state[ASSEMBLED]) {\n state[ASSEMBLED] = true\n // Terminal catch-all AFTER all mounted routes: kick's own notFound\n // handler answers unmatched paths (h3 v2's default 404 never fires).\n app.all('/**', (event: H3EventLike) => {\n const mw = state[NOTFOUND_MW] as ConnectMiddleware | undefined\n return runConnectTerminal(mw, event)\n })\n }\n const listener = toNodeHandler(app)\n return (req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void) => {\n try {\n const result = listener(req, res) as unknown\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n void (result as Promise<unknown>).catch((err) => next?.(err))\n }\n } catch (err) {\n next?.(err)\n }\n }\n },\n\n mountRoutes(app, table: RouteTable) {\n for (const { mountPath, routes } of table) {\n for (const entry of routes) {\n const url = joinPath(mountPath, entry.path)\n const hooks: WebRouteHooks = {\n onError: async (err, req, driver) => {\n const mw = (app as unknown as Record<symbol, ConnectMiddleware | undefined>)[ERROR_MW]\n if (!mw) return\n ;(mw as (e: unknown, rq: unknown, rs: unknown, n: () => void) => void)(\n err,\n req,\n driver,\n NOOP_NEXT,\n )\n },\n }\n const run = compileWebRoute(entry, hooks)\n app.on(entry.method, url, (event: H3EventLike) =>\n run({\n request: event.req,\n url: event.url,\n params: event.context.params ?? {},\n }),\n )\n }\n }\n },\n\n useConnect(app, mw: ConnectMiddleware, opts?: UseConnectOptions) {\n // Node bootstrap path only — h3 v2 bridges node middleware via\n // fromNodeHandler (the edge entry never calls useConnect).\n const handler = fromNodeHandler(mw)\n if (opts?.path !== undefined) {\n // v2 `use` is exact-match; `/**` covers the subtree like v1's prefix.\n app.use(joinPath(String(opts.path), '/**'), handler)\n } else {\n app.use(handler)\n }\n },\n\n serveStatic(app, prefix, dir) {\n // serve-static bridged like any other connect middleware (node only).\n const serveStatic = peerRequire('serve-static')\n this.useConnect(app, serveStatic(dir) as ConnectMiddleware, { path: prefix })\n },\n\n setNotFound(app, mw: ConnectMiddleware) {\n ;(app as unknown as Record<symbol, ConnectMiddleware>)[NOTFOUND_MW] = mw\n },\n\n setErrorHandler(app, mw: ConnectMiddleware) {\n ;(app as unknown as Record<symbol, ConnectMiddleware>)[ERROR_MW] = mw\n },\n\n capabilities: {\n render: false,\n uploads: true,\n connectMiddleware: true,\n nativeBodyParsing: true,\n },\n }\n}\n\n/**\n * h3-web runtime engine types for the `kick/runtime` typegen augmentation —\n * mirrors {@link import('./h3').H3RuntimeTypes} for the v1 runtime.\n */\nexport interface H3WebRuntimeTypes {\n request: Request\n response: Response\n app: unknown\n}\n\n/** Join a mount prefix and a route path into one URL, collapsing slashes. */\nfunction joinPath(mountPath: string, path: string): string {\n const a = mountPath.endsWith('/') ? mountPath.slice(0, -1) : mountPath\n if (path === '/' || path === '') return a === '' ? '/' : a\n const b = path.startsWith('/') ? path : `/${path}`\n return `${a}${b}`\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"h3-web.mjs","names":[],"sources":["../../../src/http/runtimes/h3-web.ts"],"sourcesContent":["// The h3 v2 (web-standards) runtime — `@forinda/kickjs/h3-web`.\n//\n// ADDITIVE runtime: the h3 v1 runtime (`./h3.ts`, `@forinda/kickjs/h3`)\n// stays untouched so existing adopters keep working (locked decision,\n// web-standards-edge-design.md §3.1). This runtime targets h3 >= 2 — the\n// web-standard rebase where `event.req` is a WHATWG Request, handlers\n// return values/Responses, and `app.fetch(request)` is the universal entry.\n//\n// The request pipeline runs through the shared web driver pair\n// (`../web/driver`, `../web/handler`) — the same code path the\n// `@forinda/kickjs/web` edge entry uses, so node bootstrap and edge deploys\n// exercise identical request semantics.\n//\n// Known limitation vs the v1 runtime: no Vite dev-server fall-through —\n// unmatched requests are answered by the kick notFound handler (h3 v2 owns\n// the full request; there is no node `next()` bail-out mid-engine). Use the\n// v1 h3 runtime or Express for Vite-integrated dev setups.\n\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { createRequire } from 'node:module'\n\nimport type {\n ConnectMiddleware,\n HttpRuntime,\n RouteTable,\n RuntimeAppOptions,\n UseConnectOptions,\n} from '../runtime'\nimport { compileWebRoute, type WebRouteHooks } from '../web/handler'\nimport { WebRequestShim, WebResponseDriver } from '../web/driver'\nimport { createLogger, describeError } from '../../core/logger'\n\nconst log = createLogger('H3WebRuntime')\n\nconst peerRequire = createRequire(import.meta.url)\n\nconst NOTFOUND_MW = Symbol('kickjs.h3web.notFoundMw')\nconst ERROR_MW = Symbol('kickjs.h3web.errorMw')\nconst ASSEMBLED = Symbol('kickjs.h3web.assembled')\nconst NOOP_NEXT = (): void => {}\n\n// Minimal structural surface of h3 v2 we depend on.\ninterface H3AppLike {\n on(method: string, path: string, handler: (event: H3EventLike) => unknown): unknown\n all(path: string, handler: (event: H3EventLike) => unknown): unknown\n use(...args: unknown[]): unknown\n fetch(request: Request): Promise<Response>\n}\ninterface H3EventLike {\n req: Request & { runtime?: { node?: { req: IncomingMessage; res: ServerResponse } } }\n url: URL\n context: { params?: Record<string, string> }\n}\n\ninterface H3v2Module {\n H3: new (config?: unknown) => H3AppLike\n toNodeHandler: (app: H3AppLike) => (req: IncomingMessage, res: ServerResponse) => void\n fromNodeHandler: (mw: unknown) => unknown\n}\n\nexport interface H3WebRuntimeOptions {\n /**\n * Pre-imported h3 v2 module. Two audiences:\n * - Edge/worker bundlers with no `createRequire` at runtime:\n * `h3WebRuntime({ h3: await import('h3') })`\n * - Tests running both h3 majors side by side via an npm alias.\n * Omitted → the peer is loaded via `createRequire` (node bootstrap path).\n */\n h3?: unknown\n}\n\n/** Resolve h3 (injected or peer-required) and fail fast unless it is v2. */\nfunction loadH3v2(injected?: unknown): H3v2Module {\n const mod = injected ?? peerRequire('h3')\n const m = mod as { __esModule?: boolean; default?: Record<string, unknown> } & Record<\n string,\n unknown\n >\n const resolved = (m && m.__esModule && m.default\n ? { ...m.default, ...m }\n : m) as unknown as H3v2Module\n if (typeof resolved.H3 !== 'function') {\n throw new Error(\n \"@forinda/kickjs: h3WebRuntime() requires h3 v2 (the web-standards line, npm dist-tag 'latest'), \" +\n 'but the installed h3 has no H3 class — that is the v1 line. ' +\n \"Either `pnpm add h3@latest` for this runtime, or keep h3 v1 and use h3Runtime() from '@forinda/kickjs/h3'.\",\n )\n }\n return resolved\n}\n\n/**\n * Bridge a connect-style terminal middleware (kick's notFound / error\n * handlers) onto the web driver pair, returning the produced Response.\n */\nasync function runConnectTerminal(\n mw: ConnectMiddleware | undefined,\n event: H3EventLike,\n err?: unknown,\n): Promise<Response> {\n const req = new WebRequestShim(event.req, event.url)\n const driver = new WebResponseDriver(event.req.signal)\n if (mw) {\n if (err !== undefined) {\n ;(mw as (e: unknown, rq: unknown, rs: unknown, n: () => void) => void)(\n err,\n req,\n driver,\n NOOP_NEXT,\n )\n } else {\n ;(mw as (rq: unknown, rs: unknown, n: () => void) => void)(req, driver, NOOP_NEXT)\n }\n }\n if (!driver.settled) {\n if (err !== undefined) {\n // The configured error handler ran but didn't settle the response\n // (or there wasn't one). Emitting a bare 500 with no log entry\n // makes this failure invisible on both sides — log it here, since\n // by definition nothing downstream will.\n log.error(err, `h3-web: error handler did not settle the response — ${describeError(err)}`)\n driver.status(500).json({\n error: 'Internal Server Error',\n ...(typeof process !== 'undefined' && process.env?.NODE_ENV === 'production'\n ? {}\n : { message: describeError(err) }),\n })\n } else {\n driver.status(404).json({ error: 'Not Found' })\n }\n }\n return driver.ready\n}\n\n/**\n * The h3 v2 HTTP runtime. Pass to `bootstrap({ runtime: h3WebRuntime() })`.\n * Requires the h3 v2 peer; fails fast with guidance when v1 is installed.\n */\nexport function h3WebRuntime(options: H3WebRuntimeOptions = {}): HttpRuntime<H3AppLike> {\n const { H3, toNodeHandler, fromNodeHandler } = loadH3v2(options.h3)\n\n return {\n name: 'h3-web',\n\n createApp(_options: RuntimeAppOptions = {}): H3AppLike {\n const holder: { app?: H3AppLike } = {}\n const app = new H3({\n // Engine-level errors (middleware throws outside the kick pipeline)\n // dispatch to the kick error handler once setErrorHandler stashes it.\n onError: (error: unknown, event: H3EventLike) => {\n const mw = (holder.app as unknown as Record<symbol, ConnectMiddleware | undefined>)?.[\n ERROR_MW\n ]\n return runConnectTerminal(mw, event, error)\n },\n })\n holder.app = app\n return app\n },\n\n nodeHandler(app) {\n const state = app as unknown as Record<symbol, unknown>\n if (!state[ASSEMBLED]) {\n state[ASSEMBLED] = true\n // Terminal catch-all AFTER all mounted routes: kick's own notFound\n // handler answers unmatched paths (h3 v2's default 404 never fires).\n app.all('/**', (event: H3EventLike) => {\n const mw = state[NOTFOUND_MW] as ConnectMiddleware | undefined\n return runConnectTerminal(mw, event)\n })\n }\n const listener = toNodeHandler(app)\n return (req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void) => {\n try {\n const result = listener(req, res) as unknown\n if (result && typeof (result as Promise<unknown>).then === 'function') {\n void (result as Promise<unknown>).catch((err) => next?.(err))\n }\n } catch (err) {\n next?.(err)\n }\n }\n },\n\n mountRoutes(app, table: RouteTable) {\n for (const { mountPath, routes } of table) {\n for (const entry of routes) {\n const url = joinPath(mountPath, entry.path)\n const hooks: WebRouteHooks = {\n onError: async (err, req, driver) => {\n const mw = (app as unknown as Record<symbol, ConnectMiddleware | undefined>)[ERROR_MW]\n if (!mw) return\n ;(mw as (e: unknown, rq: unknown, rs: unknown, n: () => void) => void)(\n err,\n req,\n driver,\n NOOP_NEXT,\n )\n },\n }\n const run = compileWebRoute(entry, hooks)\n app.on(entry.method, url, (event: H3EventLike) =>\n run({\n request: event.req,\n url: event.url,\n params: event.context.params ?? {},\n }),\n )\n }\n }\n },\n\n useConnect(app, mw: ConnectMiddleware, opts?: UseConnectOptions) {\n // Node bootstrap path only — h3 v2 bridges node middleware via\n // fromNodeHandler (the edge entry never calls useConnect).\n const handler = fromNodeHandler(mw)\n if (opts?.path !== undefined) {\n // v2 `use` is exact-match; `/**` covers the subtree like v1's prefix.\n app.use(joinPath(String(opts.path), '/**'), handler)\n } else {\n app.use(handler)\n }\n },\n\n serveStatic(app, prefix, dir) {\n // serve-static bridged like any other connect middleware (node only).\n const serveStatic = peerRequire('serve-static')\n this.useConnect(app, serveStatic(dir) as ConnectMiddleware, { path: prefix })\n },\n\n setNotFound(app, mw: ConnectMiddleware) {\n ;(app as unknown as Record<symbol, ConnectMiddleware>)[NOTFOUND_MW] = mw\n },\n\n setErrorHandler(app, mw: ConnectMiddleware) {\n ;(app as unknown as Record<symbol, ConnectMiddleware>)[ERROR_MW] = mw\n },\n\n capabilities: {\n render: false,\n uploads: true,\n connectMiddleware: true,\n nativeBodyParsing: true,\n },\n }\n}\n\n/**\n * h3-web runtime engine types for the `kick/runtime` typegen augmentation —\n * mirrors {@link import('./h3').H3RuntimeTypes} for the v1 runtime.\n */\nexport interface H3WebRuntimeTypes {\n request: Request\n response: Response\n app: unknown\n}\n\n/** Join a mount prefix and a route path into one URL, collapsing slashes. */\nfunction joinPath(mountPath: string, path: string): string {\n const a = mountPath.endsWith('/') ? mountPath.slice(0, -1) : mountPath\n if (path === '/' || path === '') return a === '' ? '/' : a\n const b = path.startsWith('/') ? path : `/${path}`\n return `${a}${b}`\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs v6.
|
|
2
|
+
* @forinda/kickjs v6.5.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{i as requestStore,t as RequestContext}from"../../context-
|
|
11
|
+
import{i as requestStore,t as RequestContext}from"../../context-BS5o1IuU.mjs";import{a as disposeRequestStore,i as createRequestStore,t as applyHandlerResult}from"../../reply-BMBvs-FB.mjs";import{validate}from"../middleware/validate.mjs";import{t as applyUploadConfig}from"../../upload-config-CFfHvoHF.mjs";import"../middleware/upload.mjs";import{createRequire}from"node:module";const peerRequire=createRequire(import.meta.url),h3=()=>peerRequire(`h3`),NEXT=Symbol(`kickjs.h3.next`),ERROR_MW=Symbol(`kickjs.h3.errorMw`),ROUTER=Symbol(`kickjs.h3.router`),NOTFOUND_MW=Symbol(`kickjs.h3.notFoundMw`),ASSEMBLED=Symbol(`kickjs.h3.assembled`),NOOP_NEXT=()=>{},BODY_METHODS=new Set([`POST`,`PUT`,`PATCH`,`DELETE`]);var NodeResDriver=class{res;constructor(res){this.res=res}status(code){return this.res.statusCode=code,this}json(data){return this.res.headersSent||this.res.setHeader(`content-type`,`application/json; charset=utf-8`),this.res.end(JSON.stringify(data)),this}send(data){return this.res.end(data),this}type(contentType){return this.res.setHeader(`content-type`,contentType),this}setHeader(name,value){return this.res.setHeader(name,value),this}render(){throw Error(`ctx.render() is not supported on the h3 runtime (no view engine).`)}writeHead(statusCode,headers){return this.res.writeHead(statusCode,headers),this}flushHeaders(){return this.res.flushHeaders(),this}write(chunk){return this.res.write(chunk)}end(data){return this.res.end(data),this}once(event,listener){return this.res.once(event,listener),this}get headersSent(){return this.res.headersSent}};function resDriver(res){return new NodeResDriver(res)}function makeEventHandler(entry){let{getRouterParams,getQuery,readBody,readMultipartFormData}=h3(),upload=entry.meta.upload,validator=entry.meta.validation?validate(entry.meta.validation):void 0;return async event=>{let req=event.node.req,res=event.node.res;if(req.params=getRouterParams(event)??{},req.query=getQuery(event)??{},upload&&upload.mode!==`none`){let form=await readMultipartFormData(event).catch(()=>void 0),rawParts=[],fields={};for(let part of form??[])part.filename?rawParts.push({fieldname:part.name??``,filename:part.filename,mimetype:part.type??`application/octet-stream`,buffer:part.data}):part.name&&(fields[part.name]=part.data.toString(`utf-8`));let{file,files}=applyUploadConfig(rawParts,upload);req.file=file,req.files=files,req.body=fields}else BODY_METHODS.has(req.method??`GET`)&&(req.body=await readBody(event).catch(()=>void 0));let headerId=req.headers[`x-request-id`],store=createRequestStore((Array.isArray(headerId)?headerId[0]:headerId)||req.requestId);req.requestId=store.requestId,res.once(`close`,()=>disposeRequestStore(store)),await requestStore.run(store,async()=>{let ctx=new RequestContext(req,res,NOOP_NEXT,resDriver(res));validator&&await new Promise((resolve,reject)=>{validator(req,void 0,err=>err?reject(err):resolve())});for(let mw of entry.middlewares){let advanced=!1;if(await new Promise((resolve,reject)=>{Promise.resolve(mw(ctx,err=>{advanced=!0,err?reject(err):resolve()})).catch(reject)}),!advanced||res.writableEnded)return}if(entry.contributorRunner&&await entry.contributorRunner(ctx),res.writableEnded)return;let result=await entry.handler(ctx);!res.writableEnded&&!res.headersSent&&applyHandlerResult(ctx,result)})}}function h3Runtime(){return{name:`h3`,createApp(_options={}){let{createApp}=h3(),app=createApp({onError:(error,event)=>{let res=event.node.res;if(res.writableEnded)return;let state=app;if(error?.statusCode===404){let next=event.node.req[NEXT];if(next){next();return}let nf=state[NOTFOUND_MW];if(nf){nf(event.node.req,resDriver(res),NOOP_NEXT);return}}let mw=state[ERROR_MW];mw&&mw(error,event.node.req,resDriver(res),NOOP_NEXT)}});return app},nodeHandler(app){let{toNodeListener}=h3(),state=app;state[ASSEMBLED]||(state[ASSEMBLED]=!0,state[ROUTER]&&app.use(state[ROUTER]));let listener=toNodeListener(app);return(req,res,next)=>{next&&(req[NEXT]=next),Promise.resolve(listener(req,res)).catch(err=>{next&&next(err)})}},mountRoutes(app,table){let{createRouter,eventHandler}=h3(),state=app;state[ROUTER]||(state[ROUTER]=createRouter());let router=state[ROUTER];for(let{mountPath,routes}of table)for(let entry of routes){let url=joinPath(mountPath,entry.path);router[entry.method.toLowerCase()](url,eventHandler(makeEventHandler(entry)))}},useConnect(app,mw,opts){let{fromNodeMiddleware}=h3(),handler=fromNodeMiddleware(mw);opts?.path===void 0?app.use(handler):app.use(opts.path,handler)},serveStatic(app,prefix,dir){let serveStatic=peerRequire(`serve-static`);this.useConnect(app,serveStatic(dir),{path:prefix})},setNotFound(app,mw){app[NOTFOUND_MW]=mw},setErrorHandler(app,mw){app[ERROR_MW]=mw},capabilities:{render:!1,uploads:!0,connectMiddleware:!0,nativeBodyParsing:!0}}}function joinPath(mountPath,path){let a=mountPath.endsWith(`/`)?mountPath.slice(0,-1):mountPath;return path===`/`||path===``?a===``?`/`:a:`${a}${path.startsWith(`/`)?path:`/${path}`}`}export{h3Runtime};
|
|
12
12
|
//# sourceMappingURL=h3.mjs.map
|
package/dist/index.d.mts
CHANGED
|
@@ -3,29 +3,29 @@ import { a as METADATA, c as Scope, i as Constructor, l as ServiceOptions, n as
|
|
|
3
3
|
import { a as tokenName, c as createToken, i as KickJsRegistry, l as isInjectionToken, n as ContainerChangeEvent, o as INJECTION_TOKEN, r as ContainerChangeListener, s as InjectionToken, t as Container } from "./container-iED1swkd.mjs";
|
|
4
4
|
import { A as Service, B as resolveAsset, C as Post, D as Put, F as ASSET_MANIFEST_VERSION, I as KickAssets, L as UnknownAssetError, M as normalizeApiQueryParamsConfig, N as withBuilder, O as Repository, P as ASSETS, R as assets, S as Patch, T as PreDestroy, V as useAssets, _ as Get, a as Autowired, b as Middleware, c as ColumnApiQueryParamsConfig, d as Delete, f as Env, g as FileUploadConfig, h as FileUpload, i as AssetKey, j as Value, k as RouteDefinition, l as Component, m as FileTypeFilter, n as ApiQueryParamsConfig, o as BaseUploadOptions, p as EnvKey, r as Asset, s as Builder, t as ApiQueryParams, u as Controller, v as Inject, w as PostConstruct, x as MiddlewareHandler, y as Injectable, z as clearAssetCache } from "./decorators-wEJBqXHg.mjs";
|
|
5
5
|
import { a as baseEnvSchema, c as loadEnv, d as resetEnvCache, l as loadEnvFromSchema, n as TypedConfigService, o as defineEnv, r as createConfigService, s as getEnv, t as ConfigService, u as reloadEnv } from "./index-D98NLfS5.mjs";
|
|
6
|
-
import { _ as ExecutionContext, a as AnyContributorRegistration, c as ContextDecoratorTarget, d as ContributorRegistrations, f as DepValue, g as ContextMeta, h as ContextKeys, i as ModuleRoutes, l as ContextMetaKey, m as defineContextDecorator, n as AppModuleClass, o as ContextDecorator, p as ResolvedDeps, r as AppModuleEntry, s as ContextDecoratorSpec, t as AppModule, u as ContributorRegistration, v as MetaValue } from "./app-module-
|
|
7
|
-
import { $ as
|
|
6
|
+
import { _ as ExecutionContext, a as AnyContributorRegistration, c as ContextDecoratorTarget, d as ContributorRegistrations, f as DepValue, g as ContextMeta, h as ContextKeys, i as ModuleRoutes, l as ContextMetaKey, m as defineContextDecorator, n as AppModuleClass, o as ContextDecorator, p as ResolvedDeps, r as AppModuleEntry, s as ContextDecoratorSpec, t as AppModule, u as ContributorRegistration, v as MetaValue } from "./app-module-BSpHKlhE.mjs";
|
|
7
|
+
import { $ as MissingContextValueError, A as GroupAssetKeysOptions, At as defineModule, B as MountCycleError, Bt as KickJsPluginName, C as Ctx, Ct as Disposable, D as SseHandler, Dt as defineModules, E as RouteShape, Et as ModuleList, F as AdapterFactory, Ft as definePlugin, G as runContributors, Gt as IntrospectionSnapshot, H as MountSortItem, Ht as defineAugmentation, I as DefineAdapterOptions, It as KickPlugin, J as ContributorSource, K as BuildPipelineOptions, L as defineAdapter, Lt as ModuleRegistry, M as groupAssetKeys, Mt as DefinePluginOptions, N as stripExt, Nt as PluginAsyncOptions, O as AssetKeyPair, Ot as DefineModuleOptions, P as AdapterAsyncOptions, Pt as PluginFactory, Q as DuplicateContributorError, R as DuplicateMountNameError, Rt as MutableModuleRegistry, S as UseConnectOptions, St as formatKickError, T as RequestContext, Tt as registerDisposable, U as mountSort, Ut as IntrospectFn, V as MountKind, Vt as KickJsPluginRegistry, W as RunContributorsOptions, Wt as IntrospectionKind, X as buildPipeline, Y as SourcedRegistration, Z as ContributorCycleError, _ as RouteTable, _t as setInMetaRecord, a as MiddlewarePath, at as CircuitOpenError, b as RuntimeResponse, bt as KickError, c as AdapterHttp, ct as getMetaMap, d as ExpressRuntimeTypes, dt as getMethodMetaOrUndefined, et as MissingContributorError, f as HttpRuntime, ft as hasClassMeta, g as RouteMethod, gt as setInMetaMap, h as RouteMeta, ht as setClassMeta, i as AppAdapterClass, it as CircuitBreakerStats, j as GroupAssetKeysResult, jt as BuildContext, k as AssetKeyStrategy, kt as ModuleFactory, l as ConnectMiddleware, lt as getMetaRecord, m as RouteEntry, mt as pushMethodMeta, n as AdapterMiddleware, nt as CircuitBreakerOptions, o as MiddlewarePhase, ot as getClassMeta, p as KickRuntimeRegister, pt as pushClassMeta, q as ContributorPipeline, r as AppAdapter, rt as CircuitBreakerState, s as ActiveRuntime, st as getClassMetaOrUndefined, t as AdapterContext, tt as CircuitBreaker, u as CtxHandler, ut as getMethodMeta, v as RuntimeAppOptions, vt as setMethodMeta, w as DeepReadonly, wt as disposeAll, x as RuntimeTypeMap, xt as KickErrorInit, y as RuntimeCapabilities, yt as FormatKickErrorOptions, z as MissingMountDepError, zt as AugmentationMeta } from "./adapter-CEt1MEdF.mjs";
|
|
8
8
|
import { i as getCronJobs, n as Cron, r as CronJobMeta, t as CRON_META } from "./cron-DOOtTY4q.mjs";
|
|
9
9
|
import { a as Cacheable, c as setCacheProvider, i as CacheProvider, n as CacheEvict, o as MemoryCacheProvider, r as CacheOptions, s as getCacheProvider } from "./cache-CwZzIH0D.mjs";
|
|
10
|
-
import { i as createLogger, n as Logger, r as LoggerProvider, t as ConsoleLoggerProvider } from "./logger-
|
|
10
|
+
import { a as describeError, i as createLogger, n as Logger, r as LoggerProvider, t as ConsoleLoggerProvider } from "./logger-DA43DvNY.mjs";
|
|
11
11
|
import { a as ProblemException, c as defaultProblemTitle, i as ProblemDetails, l as normalizeProblem, n as HttpStatus, o as Problems, r as HttpStatusCode, s as ValidationError, t as HttpException } from "./errors-n0H96eEz.mjs";
|
|
12
12
|
import { n as normalizePath, t as joinPaths } from "./path-BVA9MfN1.mjs";
|
|
13
13
|
import { a as isRef, c as toRefs, i as computed, l as unref, n as Ref, o as reactive, r as WatchOptions, s as ref, t as ComputedRef, u as watch } from "./reactivity-XL0fFDvG.mjs";
|
|
14
14
|
import { C as SortItem, S as QueryFieldConfig, T as TypedParsedQuery, _ as FilterOperator, a as getQueryParsingDefaults, b as ParsedQuery, c as parseQuery, d as resetQueryParsingDefaults, f as setQueryParsingDefaults, g as FilterItem, h as FieldsOf, i as buildQueryParams, l as parseSearchQuery, m as FILTER_OPERATORS, n as QueryRejectReason, o as parseFilters, r as QueryRejection, s as parsePagination, t as ParseQueryOptions, u as parseSort, v as PaginatedResponse, x as QueryBuilderAdapter, y as PaginationParams } from "./index-BtWJ9SLP.mjs";
|
|
15
|
-
import { n as ApplicationOptions, r as MiddlewareEntry, t as Application } from "./application-
|
|
16
|
-
import { t as bootstrap } from "./bootstrap-
|
|
15
|
+
import { n as ApplicationOptions, r as MiddlewareEntry, t as Application } from "./application-Bzdzl5wP.mjs";
|
|
16
|
+
import { t as bootstrap } from "./bootstrap-DYexY169.mjs";
|
|
17
17
|
import { n as cors, t as CorsOptions } from "./cors-C4aSCD3J.mjs";
|
|
18
18
|
import { n as csrf, t as CsrfOptions } from "./csrf-7nUXGTuz.mjs";
|
|
19
|
-
import { n as notFoundHandler, t as errorHandler } from "./error-handler-
|
|
19
|
+
import { n as notFoundHandler, t as errorHandler } from "./error-handler-DR01K_rI.mjs";
|
|
20
20
|
import { n as helmet, t as HelmetOptions } from "./helmet-CKzZzeNl.mjs";
|
|
21
21
|
import { n as RateLimitStore, r as rateLimit, t as RateLimitOptions } from "./rate-limit-oLL5EPUf.mjs";
|
|
22
22
|
import { n as requestId, t as REQUEST_ID_HEADER } from "./request-id-IDobbxU-.mjs";
|
|
23
23
|
import { n as requestLogger, t as RequestLoggerOptions } from "./request-logger-DZVW6VNI.mjs";
|
|
24
24
|
import { a as session, i as SessionStore, n as SessionData, r as SessionOptions, t as Session } from "./session-DSRA-PUX.mjs";
|
|
25
|
-
import { n as SpaAdapterOptions, t as SpaAdapter } from "./spa-
|
|
25
|
+
import { n as SpaAdapterOptions, t as SpaAdapter } from "./spa-DWzSWAzx.mjs";
|
|
26
26
|
import { i as upload, l as resolveMimeTypes, n as buildUploadMiddleware, r as cleanupFiles, t as UploadOptions } from "./upload-CIZc803m.mjs";
|
|
27
27
|
import { n as validate } from "./validate-DoZDOhCi.mjs";
|
|
28
|
-
import { _ as ClusterOptions, a as TraceContextOptions, c as requestScopeMiddleware, d as getRequestValue, f as requestStore, g as defineHttpContextDecorator, h as materializeRouter, i as TraceContext, l as RequestStore, m as expressRuntime, n as ViewAdapterOptions, o as parseTraceparent, p as buildRoutes, r as TRACEPARENT_HEADER, s as traceContext, t as ViewAdapter, u as getRequestStore, v as isClusterPrimary } from "./views-
|
|
29
|
-
import { a as reply, i as
|
|
30
|
-
import {
|
|
31
|
-
export { ASSETS, ASSET_MANIFEST_VERSION, type ActiveRuntime, type AdapterAsyncOptions, type AdapterContext, type AdapterFactory, type AdapterHttp, type AdapterMiddleware, type AnyContributorRegistration, ApiQueryParams, type ApiQueryParamsConfig, type ApiQueryParamsConfig as QueryParamsConfig, type AppAdapter, type AppAdapterClass, type AppModule, type AppModuleClass, type AppModuleEntry, Application, type ApplicationOptions, Asset, type AssetKey, type AssetKeyPair, type AssetKeyStrategy, type AugmentationMeta, Autowired, type BaseUploadOptions, type BuildContext, type BuildPipelineOptions, type BuildRoutesOptions, type Buildable, Builder, type BuilderOf, CRON_META, CacheEvict, type CacheOptions, type CacheProvider, Cacheable, CircuitBreaker, type CircuitBreakerOptions, type CircuitBreakerState, type CircuitBreakerStats, CircuitOpenError, type ClassKind, type ClusterOptions, type ColumnApiQueryParamsConfig, Component, type ComputedRef, ConfigService, type ConnectMiddleware, ConsoleLoggerProvider, type Constructor, Container, type ContainerChangeEvent, type ContainerChangeListener, type ContextDecorator, type ContextDecoratorSpec, type ContextDecoratorTarget, type ContextKeys, type ContextMeta, type ContextMetaKey, ContributorCycleError, type ContributorPipeline, type ContributorRegistration, type ContributorRegistrations, type ContributorSource, Controller, type CorsOptions, Cron, type CronJobMeta, type CsrfOptions, type Ctx, type CtxHandler, type DeepReadonly, type DefineAdapterOptions, type DefineModuleOptions, type DefinePluginOptions, Delete, type DepValue, type Disposable, DuplicateContributorError, DuplicateMountNameError, type Env, type EnvKey, type ExecutionContext, type ExpressRuntimeTypes, FILTER_OPERATORS, type FieldsOf, type FileTypeFilter, FileUpload, type FileUploadConfig, type FilterItem, type FilterOperator, type FormatKickErrorOptions, Get, type GroupAssetKeysOptions, type GroupAssetKeysResult, type HelmetOptions, HttpException, type HttpRuntime, HttpStatus, type HttpStatusCode, INJECTION_TOKEN, type InferHandlerResponse, Inject, Injectable, type InjectionToken, type IntrospectFn, type IntrospectionKind, type IntrospectionSnapshot, type KickAssets, KickError, type KickErrorInit, type KickJsPluginName, type KickJsPluginRegistry, type KickJsRegistry, type KickPlugin, type KickRuntimeRegister, Logger, type LoggerProvider, METADATA, type MaybePromise, MemoryCacheProvider, type MetaValue, Middleware, type MiddlewareEntry, type MiddlewareHandler, type MiddlewarePath, type MiddlewarePhase, MissingContributorError, MissingMountDepError, type ModuleFactory, ModuleList, type ModuleRegistry, type ModuleRoutes, MountCycleError, type MountKind, type MountSortItem, MutableModuleRegistry, type PaginatedResponse, type PaginationParams, type ParseQueryOptions, type ParsedQuery, Patch, type PluginAsyncOptions, type PluginFactory, Post, PostConstruct, type PostConstructStatus, PreDestroy, type ProblemDetails, ProblemException, Problems, Put, type QueryBuilderAdapter, type QueryFieldConfig, type QueryRejectReason, type QueryRejection, REQUEST_ID_HEADER, type RateLimitOptions, type RateLimitStore, type Ref, type Reply, Repository, RequestContext, type RequestLoggerOptions, type RequestStore, type ResolvedDeps, type RouteDefinition, type RouteEntry, type RouteMeta, type RouteMethod, type RouteShape, type RouteTable, type RunContributorsOptions, type RuntimeAppOptions, type RuntimeCapabilities, type RuntimeResponse, type RuntimeTypeMap, Scope, Service, type ServiceOptions, type Session, type SessionData, type SessionOptions, type SessionStore, type SortItem, type SourcedRegistration, SpaAdapter, type SpaAdapterOptions, type SseHandler, TRACEPARENT_HEADER, type TraceContext, type TraceContextOptions, type TypedConfigService, type TypedParsedQuery, UnknownAssetError, type UploadOptions, type UseConnectOptions, type ValidationError, Value, ViewAdapter, type ViewAdapterOptions, type WatchOptions, applyHandlerResult, assets, baseEnvSchema, bootstrap, buildPipeline, buildQueryParams, buildRouteTable, buildRoutes, buildUploadMiddleware, cleanupFiles, clearAssetCache, computed, cors, createConfigService, createLogger, createToken, csrf, defaultProblemTitle, defineAdapter, defineAugmentation, defineContextDecorator, defineEnv, defineHttpContextDecorator, defineModule, defineModules, definePlugin, disposeAll, errorHandler, expressRuntime, formatKickError, getCacheProvider, getClassMeta, getClassMetaOrUndefined, getCronJobs, getEnv, getMetaMap, getMetaRecord, getMethodMeta, getMethodMetaOrUndefined, getQueryParsingDefaults, getRequestStore, getRequestValue, groupAssetKeys, hasClassMeta, helmet, isClusterPrimary, isInjectionToken, isRef, isReply, joinPaths, loadEnv, loadEnvFromSchema, materializeRouter, mountSort, normalizeApiQueryParamsConfig, normalizePath, normalizeProblem, notFoundHandler, parseFilters, parsePagination, parseQuery, parseSearchQuery, parseSort, parseTraceparent, pushClassMeta, pushMethodMeta, rateLimit, reactive, ref, registerDisposable, reloadEnv, reply, requestId, requestLogger, requestScopeMiddleware, requestStore, resetEnvCache, resetQueryParsingDefaults, resolveAsset, resolveMimeTypes, runContributors, session, setCacheProvider, setClassMeta, setInMetaMap, setInMetaRecord, setMethodMeta, setQueryParsingDefaults, stripExt, toRefs, tokenName, traceContext, unref, upload, useAssets, validate, watch, withBuilder };
|
|
28
|
+
import { _ as ClusterOptions, a as TraceContextOptions, c as requestScopeMiddleware, d as getRequestValue, f as requestStore, g as defineHttpContextDecorator, h as materializeRouter, i as TraceContext, l as RequestStore, m as expressRuntime, n as ViewAdapterOptions, o as parseTraceparent, p as buildRoutes, r as TRACEPARENT_HEADER, s as traceContext, t as ViewAdapter, u as getRequestStore, v as isClusterPrimary } from "./views-Kp8Z3AO1.mjs";
|
|
29
|
+
import { a as KvSessionStoreOptions, c as InferHandlerResponse, d as isReply, f as reply, i as KvSessionStore, l as Reply, n as KvRateLimitStore, o as RateLimitGuardOptions, r as KvRateLimitStoreOptions, s as rateLimitGuard, t as KvLike, u as applyHandlerResult } from "./kv-stores-duYHIEr1.mjs";
|
|
30
|
+
import { i as buildRouteTable, t as BuildRoutesOptions } from "./router-builder-GWgxoOdC.mjs";
|
|
31
|
+
export { ASSETS, ASSET_MANIFEST_VERSION, type ActiveRuntime, type AdapterAsyncOptions, type AdapterContext, type AdapterFactory, type AdapterHttp, type AdapterMiddleware, type AnyContributorRegistration, ApiQueryParams, type ApiQueryParamsConfig, type ApiQueryParamsConfig as QueryParamsConfig, type AppAdapter, type AppAdapterClass, type AppModule, type AppModuleClass, type AppModuleEntry, Application, type ApplicationOptions, Asset, type AssetKey, type AssetKeyPair, type AssetKeyStrategy, type AugmentationMeta, Autowired, type BaseUploadOptions, type BuildContext, type BuildPipelineOptions, type BuildRoutesOptions, type Buildable, Builder, type BuilderOf, CRON_META, CacheEvict, type CacheOptions, type CacheProvider, Cacheable, CircuitBreaker, type CircuitBreakerOptions, type CircuitBreakerState, type CircuitBreakerStats, CircuitOpenError, type ClassKind, type ClusterOptions, type ColumnApiQueryParamsConfig, Component, type ComputedRef, ConfigService, type ConnectMiddleware, ConsoleLoggerProvider, type Constructor, Container, type ContainerChangeEvent, type ContainerChangeListener, type ContextDecorator, type ContextDecoratorSpec, type ContextDecoratorTarget, type ContextKeys, type ContextMeta, type ContextMetaKey, ContributorCycleError, type ContributorPipeline, type ContributorRegistration, type ContributorRegistrations, type ContributorSource, Controller, type CorsOptions, Cron, type CronJobMeta, type CsrfOptions, type Ctx, type CtxHandler, type DeepReadonly, type DefineAdapterOptions, type DefineModuleOptions, type DefinePluginOptions, Delete, type DepValue, type Disposable, DuplicateContributorError, DuplicateMountNameError, type Env, type EnvKey, type ExecutionContext, type ExpressRuntimeTypes, FILTER_OPERATORS, type FieldsOf, type FileTypeFilter, FileUpload, type FileUploadConfig, type FilterItem, type FilterOperator, type FormatKickErrorOptions, Get, type GroupAssetKeysOptions, type GroupAssetKeysResult, type HelmetOptions, HttpException, type HttpRuntime, HttpStatus, type HttpStatusCode, INJECTION_TOKEN, type InferHandlerResponse, Inject, Injectable, type InjectionToken, type IntrospectFn, type IntrospectionKind, type IntrospectionSnapshot, type KickAssets, KickError, type KickErrorInit, type KickJsPluginName, type KickJsPluginRegistry, type KickJsRegistry, type KickPlugin, type KickRuntimeRegister, type KvLike, KvRateLimitStore, type KvRateLimitStoreOptions, KvSessionStore, type KvSessionStoreOptions, Logger, type LoggerProvider, METADATA, type MaybePromise, MemoryCacheProvider, type MetaValue, Middleware, type MiddlewareEntry, type MiddlewareHandler, type MiddlewarePath, type MiddlewarePhase, MissingContextValueError, MissingContributorError, MissingMountDepError, type ModuleFactory, ModuleList, type ModuleRegistry, type ModuleRoutes, MountCycleError, type MountKind, type MountSortItem, MutableModuleRegistry, type PaginatedResponse, type PaginationParams, type ParseQueryOptions, type ParsedQuery, Patch, type PluginAsyncOptions, type PluginFactory, Post, PostConstruct, type PostConstructStatus, PreDestroy, type ProblemDetails, ProblemException, Problems, Put, type QueryBuilderAdapter, type QueryFieldConfig, type QueryRejectReason, type QueryRejection, REQUEST_ID_HEADER, type RateLimitGuardOptions, type RateLimitOptions, type RateLimitStore, type Ref, type Reply, Repository, RequestContext, type RequestLoggerOptions, type RequestStore, type ResolvedDeps, type RouteDefinition, type RouteEntry, type RouteMeta, type RouteMethod, type RouteShape, type RouteTable, type RunContributorsOptions, type RuntimeAppOptions, type RuntimeCapabilities, type RuntimeResponse, type RuntimeTypeMap, Scope, Service, type ServiceOptions, type Session, type SessionData, type SessionOptions, type SessionStore, type SortItem, type SourcedRegistration, SpaAdapter, type SpaAdapterOptions, type SseHandler, TRACEPARENT_HEADER, type TraceContext, type TraceContextOptions, type TypedConfigService, type TypedParsedQuery, UnknownAssetError, type UploadOptions, type UseConnectOptions, type ValidationError, Value, ViewAdapter, type ViewAdapterOptions, type WatchOptions, applyHandlerResult, assets, baseEnvSchema, bootstrap, buildPipeline, buildQueryParams, buildRouteTable, buildRoutes, buildUploadMiddleware, cleanupFiles, clearAssetCache, computed, cors, createConfigService, createLogger, createToken, csrf, defaultProblemTitle, defineAdapter, defineAugmentation, defineContextDecorator, defineEnv, defineHttpContextDecorator, defineModule, defineModules, definePlugin, describeError, disposeAll, errorHandler, expressRuntime, formatKickError, getCacheProvider, getClassMeta, getClassMetaOrUndefined, getCronJobs, getEnv, getMetaMap, getMetaRecord, getMethodMeta, getMethodMetaOrUndefined, getQueryParsingDefaults, getRequestStore, getRequestValue, groupAssetKeys, hasClassMeta, helmet, isClusterPrimary, isInjectionToken, isRef, isReply, joinPaths, loadEnv, loadEnvFromSchema, materializeRouter, mountSort, normalizeApiQueryParamsConfig, normalizePath, normalizeProblem, notFoundHandler, parseFilters, parsePagination, parseQuery, parseSearchQuery, parseSort, parseTraceparent, pushClassMeta, pushMethodMeta, rateLimit, rateLimitGuard, reactive, ref, registerDisposable, reloadEnv, reply, requestId, requestLogger, requestScopeMiddleware, requestStore, resetEnvCache, resetQueryParsingDefaults, resolveAsset, resolveMimeTypes, runContributors, session, setCacheProvider, setClassMeta, setInMetaMap, setInMetaRecord, setMethodMeta, setQueryParsingDefaults, stripExt, toRefs, tokenName, traceContext, unref, upload, useAssets, validate, watch, withBuilder };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs v6.
|
|
2
|
+
* @forinda/kickjs v6.5.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{METADATA,Scope}from"./core/interfaces.mjs";import{a as isInjectionToken,c as
|
|
11
|
+
import{METADATA,Scope}from"./core/interfaces.mjs";import{a as isInjectionToken,c as KickError,i as createToken,l as formatKickError,n as tokenName,r as INJECTION_TOKEN,t as Container}from"./container-CBaL5lus.mjs";import{ConsoleLoggerProvider,Logger,createLogger,describeError}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-CUDfhy4e.mjs";import{ApiQueryParams,Asset,Autowired,Builder,Component,Controller,Delete,FileUpload,Get,Inject,Injectable,Middleware,Patch,Post,PostConstruct,PreDestroy,Put,Repository,Service,Value,normalizeApiQueryParamsConfig,withBuilder}from"./core/decorators.mjs";import{S as defineModule,_ as defineContextDecorator,a as clearAssetCache,b as ModuleList,c as groupAssetKeys,d as defineAdapter,f as definePlugin,g as mountSort,h as MountCycleError,i as assets,l as stripExt,m as MissingMountDepError,n as ASSET_MANIFEST_VERSION,o as resolveAsset,p as DuplicateMountNameError,r as UnknownAssetError,s as useAssets,t as ASSETS,u as defineAugmentation,v as CircuitBreaker,x as defineModules,y as CircuitOpenError}from"./core-TDR0XGzO.mjs";import{t as MutableModuleRegistry}from"./module-registry-Mv5FqVlq.mjs";import{CRON_META,Cron,getCronJobs}from"./core/cron.mjs";import{CacheEvict,Cacheable,MemoryCacheProvider,getCacheProvider,setCacheProvider}from"./core/cache.mjs";import{r as registerDisposable,t as disposeAll}from"./disposables-mnjJuaVx.mjs";import{HttpException,HttpStatus,ProblemException,Problems,defaultProblemTitle,normalizeProblem}from"./core/errors.mjs";import{joinPaths,normalizePath}from"./core/path.mjs";import{computed,isRef,reactive,ref,toRefs,unref,watch}from"./core/reactivity.mjs";import{i as MissingContributorError,n as DuplicateContributorError,r as MissingContextValueError,t as ContributorCycleError}from"./context-errors-CwD27Vbj.mjs";import{n as buildPipeline,t as runContributors}from"./contributor-runner-Cu13cDQp.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-BS5o1IuU.mjs";import{n as isReply,r as reply,s as requestScopeMiddleware,t as applyHandlerResult}from"./reply-BMBvs-FB.mjs";import{buildRouteTable}from"./http/router-builder.mjs";import{a as parseQuery,c as resetQueryParsingDefaults,i as parsePagination,l as setQueryParsingDefaults,n as getQueryParsingDefaults,o as parseSearchQuery,r as parseFilters,s as parseSort,t as buildQueryParams,u as FILTER_OPERATORS}from"./query-gMAVXzx2.mjs";import{validate}from"./http/middleware/validate.mjs";import{r as resolveMimeTypes}from"./upload-config-CFfHvoHF.mjs";import{buildUploadMiddleware,cleanupFiles,upload}from"./http/middleware/upload.mjs";import{i as materializeRouter,n as buildRoutes,r as expressRuntime,t as Application}from"./application-C-r79XqD.mjs";import{a as loadEnvFromSchema,i as loadEnv,n as defineEnv,o as reloadEnv,r as getEnv,s as resetEnvCache,t as baseEnvSchema}from"./env-Dgtjncd5.mjs";import{n as isClusterPrimary,t as bootstrap}from"./bootstrap-C1VGQQVF.mjs";import{n as randomHex,t as csrf}from"./csrf-DNh_7aOk.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{n as KvSessionStore,r as rateLimitGuard,t as KvRateLimitStore}from"./kv-stores-O8HUSwpv.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-BeIZJBXA.mjs";function defineHttpContextDecoratorImpl(spec){return defineContextDecorator(spec)}const defineHttpContextDecorator=Object.freeze(Object.assign(defineHttpContextDecoratorImpl,{withParams:()=>spec=>defineHttpContextDecoratorImpl(spec)})),TRACEPARENT_HEADER=`traceparent`,TRACEPARENT_RE=/^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;function generateTraceId(){return randomHex(16)}function generateSpanId(){return randomHex(8)}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,KickError,KvRateLimitStore,KvSessionStore,Logger,METADATA,MemoryCacheProvider,Middleware,MissingContextValueError,MissingContributorError,MissingMountDepError,ModuleList,MountCycleError,MutableModuleRegistry,Patch,Post,PostConstruct,PreDestroy,ProblemException,Problems,Put,REQUEST_ID_HEADER,Repository,RequestContext,Scope,Service,SpaAdapter,TRACEPARENT_HEADER,UnknownAssetError,Value,ViewAdapter,applyHandlerResult,assets,baseEnvSchema,bootstrap,buildPipeline,buildQueryParams,buildRouteTable,buildRoutes,buildUploadMiddleware,cleanupFiles,clearAssetCache,computed,cors,createConfigService,createLogger,createToken,csrf,defaultProblemTitle,defineAdapter,defineAugmentation,defineContextDecorator,defineEnv,defineHttpContextDecorator,defineModule,defineModules,definePlugin,describeError,disposeAll,errorHandler,expressRuntime,formatKickError,getCacheProvider,getClassMeta,getClassMetaOrUndefined,getCronJobs,getEnv,getMetaMap,getMetaRecord,getMethodMeta,getMethodMetaOrUndefined,getQueryParsingDefaults,getRequestStore,getRequestValue,groupAssetKeys,hasClassMeta,helmet,isClusterPrimary,isInjectionToken,isRef,isReply,joinPaths,loadEnv,loadEnvFromSchema,materializeRouter,mountSort,normalizeApiQueryParamsConfig,normalizePath,normalizeProblem,notFoundHandler,parseFilters,parsePagination,parseQuery,parseSearchQuery,parseSort,parseTraceparent,pushClassMeta,pushMethodMeta,rateLimit,rateLimitGuard,reactive,ref,registerDisposable,reloadEnv,reply,requestId,requestLogger,requestScopeMiddleware,requestStore,resetEnvCache,resetQueryParsingDefaults,resolveAsset,resolveMimeTypes,runContributors,session,setCacheProvider,setClassMeta,setInMetaMap,setInMetaRecord,setMethodMeta,setQueryParsingDefaults,stripExt,toRefs,tokenName,traceContext,unref,upload,useAssets,validate,watch,withBuilder};
|
|
12
12
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/http/define-http-context-decorator.ts","../src/http/middleware/trace-context.ts"],"sourcesContent":["/**\n * HTTP-flavoured wrapper around {@link defineContextDecorator}.\n *\n * The core factory leaves `Ctx` defaulted to {@link ExecutionContext} so the\n * Context Contributor pipeline stays transport-agnostic — future WebSocket,\n * queue, and cron contexts share the same primitive. The cost is that the\n * vast majority of contributors (which are HTTP) have to spell the fourth\n * generic by hand to get `ctx.req` / `ctx.headers` typing:\n *\n * ```ts\n * // Verbose: spell every generic, including the params shape `P`.\n * defineContextDecorator<'tenant', Record<string, never>, Record<string, never>, RequestContext>({\n * key: 'tenant',\n * resolve: (ctx) => ({ id: ctx.req.headers['x-tenant-id'] as string }),\n * })\n * ```\n *\n * `defineHttpContextDecorator` removes that ceremony — the spec is typed\n * against `RequestContext` from the start, so resolvers can read `ctx.req`,\n * `ctx.headers`, `ctx.params`, etc. without a cast or generic dance:\n *\n * ```ts\n * import { defineHttpContextDecorator } from '@forinda/kickjs'\n *\n * const LoadTenant = defineHttpContextDecorator({\n * key: 'tenant',\n * resolve: (ctx) => ({ id: ctx.req.headers['x-tenant-id'] as string }),\n * })\n *\n * // Parameterised — the positional form forces you to also spell `K`\n * // and `D`, which loses automatic `deps` inference. Prefer the curried\n * // `.withParams<P>()` entry point below.\n * const LoadTenantParameterised = defineHttpContextDecorator<\n * 'tenant',\n * Record<string, never>,\n * { source: 'header' | 'subdomain' }\n * >({\n * key: 'tenant',\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, _deps, params) => {\n * // params.source narrows inside `if` branches.\n * },\n * })\n *\n * // Parameterised + deps — only `P` is spelled; `K` and `D` infer from\n * // the spec, so `deps.repo` is fully typed in the resolver.\n * const LoadTenantWithRepo = defineHttpContextDecorator.withParams<{\n * source: 'header' | 'subdomain'\n * }>()({\n * key: 'tenant',\n * deps: { repo: TENANT_REPO },\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, { repo }, params) =>\n * repo.findFor(ctx.req.headers['x-tenant-id'] as string, params.source),\n * })\n * ```\n *\n * Use {@link defineContextDecorator} directly when authoring contributors\n * for non-HTTP transports or when you want to share one contributor across\n * HTTP + WS + queue (the resolver then has to stick to the\n * `ExecutionContext` surface — `ctx.get` / `ctx.set` / `ctx.requestId`).\n */\n\nimport {\n defineContextDecorator,\n type ContextDecorator,\n type ContextDecoratorSpec,\n type DepValue,\n} from '../core/context-decorator'\nimport type { RequestContext } from './context'\n\n/**\n * Curried form returned by {@link DefineHttpContextDecoratorFn.withParams}.\n * Spell only the per-call params shape `P`; `K` and `D` are inferred\n * from the spec, and `Ctx` is locked to `RequestContext`.\n */\nexport interface DefineHttpContextDecoratorWithParams<P extends Record<string, unknown>> {\n <K extends string, D extends Record<string, DepValue> = Record<string, never>>(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext>\n}\n\n/**\n * Public shape of {@link defineHttpContextDecorator} — the positional\n * call signature plus the curried `.withParams<P>()` helper that fixes\n * partial-inference for the parameterised case.\n */\nexport interface DefineHttpContextDecoratorFn {\n <\n K extends string,\n D extends Record<string, DepValue> = Record<string, never>,\n P extends Record<string, unknown> = Record<string, never>,\n >(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext>\n\n /**\n * Curried entry point. Spell only `P`; `K` and `D` infer from the\n * spec. `Ctx` is locked to `RequestContext`.\n *\n * ```ts\n * const LoadTenant = defineHttpContextDecorator.withParams<{\n * source: 'header' | 'subdomain'\n * }>()({\n * key: 'tenant',\n * deps: { repo: TENANT_REPO }, // D inferred\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, { repo }, params) => repo.findFor(ctx, params),\n * })\n * ```\n */\n withParams<P extends Record<string, unknown>>(): DefineHttpContextDecoratorWithParams<P>\n}\n\nfunction defineHttpContextDecoratorImpl<\n K extends string,\n D extends Record<string, DepValue> = Record<string, never>,\n P extends Record<string, unknown> = Record<string, never>,\n>(spec: ContextDecoratorSpec<K, D, P, RequestContext>): ContextDecorator<K, D, P, RequestContext> {\n return defineContextDecorator<K, D, P, RequestContext>(spec)\n}\n\nexport const defineHttpContextDecorator: DefineHttpContextDecoratorFn = Object.freeze(\n Object.assign(defineHttpContextDecoratorImpl, {\n withParams: <P extends Record<string, unknown>>(): DefineHttpContextDecoratorWithParams<P> => {\n return <K extends string, D extends Record<string, DepValue> = Record<string, never>>(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext> => defineHttpContextDecoratorImpl<K, D, P>(spec)\n },\n }),\n) as DefineHttpContextDecoratorFn\n","import type { Request, Response, NextFunction } from 'express'\nimport { requestStore } from '../request-store'\n\nimport { randomHex } from '../../core/web-crypto'\n\n/**\n * W3C Trace Context header name.\n * @see https://www.w3.org/TR/trace-context/#traceparent-header\n */\nexport const TRACEPARENT_HEADER = 'traceparent'\n\n/** Regex for a valid W3C traceparent: {version}-{trace-id}-{parent-id}-{flags} */\nconst TRACEPARENT_RE = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/\n\nexport interface TraceContextOptions {\n /**\n * When true, respond with a `traceresponse` header containing the trace ID.\n * Useful for debugging. Default: false.\n */\n propagateResponse?: boolean\n}\n\n/**\n * Parsed W3C trace context fields.\n */\nexport interface TraceContext {\n /** W3C traceparent version (usually '00') */\n version: string\n /** 32-hex-char distributed trace ID */\n traceId: string\n /** 16-hex-char parent span ID */\n parentSpanId: string\n /** 2-hex-char trace flags */\n flags: string\n}\n\n/**\n * Generate a random 32-hex-character trace ID.\n */\nfunction generateTraceId(): string {\n return randomHex(16)\n}\n\n/**\n * Generate a random 16-hex-character span ID.\n */\nfunction generateSpanId(): string {\n return randomHex(8)\n}\n\n/**\n * Parse a W3C traceparent header value.\n * Returns null if the format is invalid.\n */\nexport function parseTraceparent(header: string): TraceContext | null {\n const match = TRACEPARENT_RE.exec(header.trim().toLowerCase())\n if (!match) return null\n\n const version = match[1]\n const traceId = match[2]\n const parentSpanId = match[3]\n const flags = match[4]\n\n // W3C spec: version ff is invalid, all-zero trace-id and parent-id are invalid\n if (\n version === 'ff' ||\n traceId === '00000000000000000000000000000000' ||\n parentSpanId === '0000000000000000'\n ) {\n return null\n }\n\n return { version, traceId, parentSpanId, flags }\n}\n\n/**\n * Middleware that extracts W3C `traceparent` header and stores trace context\n * in the request's AsyncLocalStorage store.\n *\n * If no valid `traceparent` header is present, a new traceId is generated\n * so that every request always has a trace ID available for correlation.\n *\n * Must be mounted **after** `requestScopeMiddleware()` (which creates the store).\n *\n * @example\n * ```ts\n * bootstrap({\n * middleware: [\n * requestScopeMiddleware(),\n * traceContext(), // extracts or generates traceId\n * requestLogger(),\n * express.json(),\n * ],\n * })\n * ```\n */\nexport function traceContext(options: TraceContextOptions = {}) {\n return (req: Request, res: Response, next: NextFunction) => {\n const store = requestStore.getStore()\n\n const traceparentHeader = req.headers[TRACEPARENT_HEADER]\n const raw = Array.isArray(traceparentHeader) ? traceparentHeader[0] : traceparentHeader\n const parsed = raw ? parseTraceparent(raw) : null\n\n const traceId = parsed?.traceId ?? generateTraceId()\n const spanId = generateSpanId()\n\n // Attach to the request store so logger + downstream code can access it\n if (store) {\n store.values.set('traceId', traceId)\n store.values.set('spanId', spanId)\n if (parsed) {\n store.values.set('parentSpanId', parsed.parentSpanId)\n store.values.set('traceFlags', parsed.flags)\n store.values.set('traceVersion', parsed.version)\n }\n }\n\n // Also expose on req for convenience\n ;(req as any).traceId = traceId\n ;(req as any).spanId = spanId\n\n if (options.propagateResponse) {\n res.setHeader('traceresponse', traceId)\n }\n\n next()\n }\n}\n"],"mappings":";;;;;;;;;;8sHAkHA,SAAS,+BAIP,KAAgG,CAChG,OAAO,uBAAgD,IAAI,CAC7D,CAEA,MAAa,2BAA2D,OAAO,OAC7E,OAAO,OAAO,+BAAgC,CAC5C,eAEI,MAC8C,+BAAwC,IAAI,CAEhG,CAAC,CACH,ECzHa,mBAAqB,cAG5B,eAAiB,8DA2BvB,SAAS,iBAA0B,CACjC,OAAO,UAAU,EAAE,CACrB,CAKA,SAAS,gBAAyB,CAChC,OAAO,UAAU,CAAC,CACpB,CAMA,SAAgB,iBAAiB,OAAqC,CACpE,IAAM,MAAQ,eAAe,KAAK,OAAO,KAAK,CAAC,CAAC,YAAY,CAAC,EAC7D,GAAI,CAAC,MAAO,OAAO,KAEnB,IAAM,QAAU,MAAM,GAChB,QAAU,MAAM,GAChB,aAAe,MAAM,GACrB,MAAQ,MAAM,GAWpB,OAPE,UAAY,MACZ,UAAY,oCACZ,eAAiB,mBAEV,KAGF,CAAE,QAAS,QAAS,aAAc,KAAM,CACjD,CAuBA,SAAgB,aAAa,QAA+B,CAAC,EAAG,CAC9D,OAAQ,IAAc,IAAe,OAAuB,CAC1D,IAAM,MAAQ,aAAa,SAAS,EAE9B,kBAAoB,IAAI,QAAQ,oBAChC,IAAM,MAAM,QAAQ,iBAAiB,EAAI,kBAAkB,GAAK,kBAChE,OAAS,IAAM,iBAAiB,GAAG,EAAI,KAEvC,QAAU,QAAQ,SAAW,gBAAgB,EAC7C,OAAS,eAAe,EAG1B,QACF,MAAM,OAAO,IAAI,UAAW,OAAO,EACnC,MAAM,OAAO,IAAI,SAAU,MAAM,EAC7B,SACF,MAAM,OAAO,IAAI,eAAgB,OAAO,YAAY,EACpD,MAAM,OAAO,IAAI,aAAc,OAAO,KAAK,EAC3C,MAAM,OAAO,IAAI,eAAgB,OAAO,OAAO,IAKlD,IAAa,QAAU,QACvB,IAAa,OAAS,OAEnB,QAAQ,mBACV,IAAI,UAAU,gBAAiB,OAAO,EAGxC,KAAK,CACP,CACF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/http/define-http-context-decorator.ts","../src/http/middleware/trace-context.ts"],"sourcesContent":["/**\n * HTTP-flavoured wrapper around {@link defineContextDecorator}.\n *\n * The core factory leaves `Ctx` defaulted to {@link ExecutionContext} so the\n * Context Contributor pipeline stays transport-agnostic — future WebSocket,\n * queue, and cron contexts share the same primitive. The cost is that the\n * vast majority of contributors (which are HTTP) have to spell the fourth\n * generic by hand to get `ctx.req` / `ctx.headers` typing:\n *\n * ```ts\n * // Verbose: spell every generic, including the params shape `P`.\n * defineContextDecorator<'tenant', Record<string, never>, Record<string, never>, RequestContext>({\n * key: 'tenant',\n * resolve: (ctx) => ({ id: ctx.req.headers['x-tenant-id'] as string }),\n * })\n * ```\n *\n * `defineHttpContextDecorator` removes that ceremony — the spec is typed\n * against `RequestContext` from the start, so resolvers can read `ctx.req`,\n * `ctx.headers`, `ctx.params`, etc. without a cast or generic dance:\n *\n * ```ts\n * import { defineHttpContextDecorator } from '@forinda/kickjs'\n *\n * const LoadTenant = defineHttpContextDecorator({\n * key: 'tenant',\n * resolve: (ctx) => ({ id: ctx.req.headers['x-tenant-id'] as string }),\n * })\n *\n * // Parameterised — the positional form forces you to also spell `K`\n * // and `D`, which loses automatic `deps` inference. Prefer the curried\n * // `.withParams<P>()` entry point below.\n * const LoadTenantParameterised = defineHttpContextDecorator<\n * 'tenant',\n * Record<string, never>,\n * { source: 'header' | 'subdomain' }\n * >({\n * key: 'tenant',\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, _deps, params) => {\n * // params.source narrows inside `if` branches.\n * },\n * })\n *\n * // Parameterised + deps — only `P` is spelled; `K` and `D` infer from\n * // the spec, so `deps.repo` is fully typed in the resolver.\n * const LoadTenantWithRepo = defineHttpContextDecorator.withParams<{\n * source: 'header' | 'subdomain'\n * }>()({\n * key: 'tenant',\n * deps: { repo: TENANT_REPO },\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, { repo }, params) =>\n * repo.findFor(ctx.req.headers['x-tenant-id'] as string, params.source),\n * })\n * ```\n *\n * Use {@link defineContextDecorator} directly when authoring contributors\n * for non-HTTP transports or when you want to share one contributor across\n * HTTP + WS + queue (the resolver then has to stick to the\n * `ExecutionContext` surface — `ctx.get` / `ctx.set` / `ctx.requestId`).\n */\n\nimport {\n defineContextDecorator,\n type ContextDecorator,\n type ContextDecoratorSpec,\n type DepValue,\n} from '../core/context-decorator'\nimport type { RequestContext } from './context'\n\n/**\n * Curried form returned by {@link DefineHttpContextDecoratorFn.withParams}.\n * Spell only the per-call params shape `P`; `K` and `D` are inferred\n * from the spec, and `Ctx` is locked to `RequestContext`.\n */\nexport interface DefineHttpContextDecoratorWithParams<P extends Record<string, unknown>> {\n <K extends string, D extends Record<string, DepValue> = Record<string, never>>(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext>\n}\n\n/**\n * Public shape of {@link defineHttpContextDecorator} — the positional\n * call signature plus the curried `.withParams<P>()` helper that fixes\n * partial-inference for the parameterised case.\n */\nexport interface DefineHttpContextDecoratorFn {\n <\n K extends string,\n D extends Record<string, DepValue> = Record<string, never>,\n P extends Record<string, unknown> = Record<string, never>,\n >(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext>\n\n /**\n * Curried entry point. Spell only `P`; `K` and `D` infer from the\n * spec. `Ctx` is locked to `RequestContext`.\n *\n * ```ts\n * const LoadTenant = defineHttpContextDecorator.withParams<{\n * source: 'header' | 'subdomain'\n * }>()({\n * key: 'tenant',\n * deps: { repo: TENANT_REPO }, // D inferred\n * paramDefaults: { source: 'header' },\n * resolve: (ctx, { repo }, params) => repo.findFor(ctx, params),\n * })\n * ```\n */\n withParams<P extends Record<string, unknown>>(): DefineHttpContextDecoratorWithParams<P>\n}\n\nfunction defineHttpContextDecoratorImpl<\n K extends string,\n D extends Record<string, DepValue> = Record<string, never>,\n P extends Record<string, unknown> = Record<string, never>,\n>(spec: ContextDecoratorSpec<K, D, P, RequestContext>): ContextDecorator<K, D, P, RequestContext> {\n return defineContextDecorator<K, D, P, RequestContext>(spec)\n}\n\nexport const defineHttpContextDecorator: DefineHttpContextDecoratorFn = Object.freeze(\n Object.assign(defineHttpContextDecoratorImpl, {\n withParams: <P extends Record<string, unknown>>(): DefineHttpContextDecoratorWithParams<P> => {\n return <K extends string, D extends Record<string, DepValue> = Record<string, never>>(\n spec: ContextDecoratorSpec<K, D, P, RequestContext>,\n ): ContextDecorator<K, D, P, RequestContext> => defineHttpContextDecoratorImpl<K, D, P>(spec)\n },\n }),\n) as DefineHttpContextDecoratorFn\n","import type { Request, Response, NextFunction } from 'express'\nimport { requestStore } from '../request-store'\n\nimport { randomHex } from '../../core/web-crypto'\n\n/**\n * W3C Trace Context header name.\n * @see https://www.w3.org/TR/trace-context/#traceparent-header\n */\nexport const TRACEPARENT_HEADER = 'traceparent'\n\n/** Regex for a valid W3C traceparent: {version}-{trace-id}-{parent-id}-{flags} */\nconst TRACEPARENT_RE = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/\n\nexport interface TraceContextOptions {\n /**\n * When true, respond with a `traceresponse` header containing the trace ID.\n * Useful for debugging. Default: false.\n */\n propagateResponse?: boolean\n}\n\n/**\n * Parsed W3C trace context fields.\n */\nexport interface TraceContext {\n /** W3C traceparent version (usually '00') */\n version: string\n /** 32-hex-char distributed trace ID */\n traceId: string\n /** 16-hex-char parent span ID */\n parentSpanId: string\n /** 2-hex-char trace flags */\n flags: string\n}\n\n/**\n * Generate a random 32-hex-character trace ID.\n */\nfunction generateTraceId(): string {\n return randomHex(16)\n}\n\n/**\n * Generate a random 16-hex-character span ID.\n */\nfunction generateSpanId(): string {\n return randomHex(8)\n}\n\n/**\n * Parse a W3C traceparent header value.\n * Returns null if the format is invalid.\n */\nexport function parseTraceparent(header: string): TraceContext | null {\n const match = TRACEPARENT_RE.exec(header.trim().toLowerCase())\n if (!match) return null\n\n const version = match[1]\n const traceId = match[2]\n const parentSpanId = match[3]\n const flags = match[4]\n\n // W3C spec: version ff is invalid, all-zero trace-id and parent-id are invalid\n if (\n version === 'ff' ||\n traceId === '00000000000000000000000000000000' ||\n parentSpanId === '0000000000000000'\n ) {\n return null\n }\n\n return { version, traceId, parentSpanId, flags }\n}\n\n/**\n * Middleware that extracts W3C `traceparent` header and stores trace context\n * in the request's AsyncLocalStorage store.\n *\n * If no valid `traceparent` header is present, a new traceId is generated\n * so that every request always has a trace ID available for correlation.\n *\n * Must be mounted **after** `requestScopeMiddleware()` (which creates the store).\n *\n * @example\n * ```ts\n * bootstrap({\n * middleware: [\n * requestScopeMiddleware(),\n * traceContext(), // extracts or generates traceId\n * requestLogger(),\n * express.json(),\n * ],\n * })\n * ```\n */\nexport function traceContext(options: TraceContextOptions = {}) {\n return (req: Request, res: Response, next: NextFunction) => {\n const store = requestStore.getStore()\n\n const traceparentHeader = req.headers[TRACEPARENT_HEADER]\n const raw = Array.isArray(traceparentHeader) ? traceparentHeader[0] : traceparentHeader\n const parsed = raw ? parseTraceparent(raw) : null\n\n const traceId = parsed?.traceId ?? generateTraceId()\n const spanId = generateSpanId()\n\n // Attach to the request store so logger + downstream code can access it\n if (store) {\n store.values.set('traceId', traceId)\n store.values.set('spanId', spanId)\n if (parsed) {\n store.values.set('parentSpanId', parsed.parentSpanId)\n store.values.set('traceFlags', parsed.flags)\n store.values.set('traceVersion', parsed.version)\n }\n }\n\n // Also expose on req for convenience\n ;(req as any).traceId = traceId\n ;(req as any).spanId = spanId\n\n if (options.propagateResponse) {\n res.setHeader('traceresponse', traceId)\n }\n\n next()\n }\n}\n"],"mappings":";;;;;;;;;;y4HAkHA,SAAS,+BAIP,KAAgG,CAChG,OAAO,uBAAgD,IAAI,CAC7D,CAEA,MAAa,2BAA2D,OAAO,OAC7E,OAAO,OAAO,+BAAgC,CAC5C,eAEI,MAC8C,+BAAwC,IAAI,CAEhG,CAAC,CACH,ECzHa,mBAAqB,cAG5B,eAAiB,8DA2BvB,SAAS,iBAA0B,CACjC,OAAO,UAAU,EAAE,CACrB,CAKA,SAAS,gBAAyB,CAChC,OAAO,UAAU,CAAC,CACpB,CAMA,SAAgB,iBAAiB,OAAqC,CACpE,IAAM,MAAQ,eAAe,KAAK,OAAO,KAAK,CAAC,CAAC,YAAY,CAAC,EAC7D,GAAI,CAAC,MAAO,OAAO,KAEnB,IAAM,QAAU,MAAM,GAChB,QAAU,MAAM,GAChB,aAAe,MAAM,GACrB,MAAQ,MAAM,GAWpB,OAPE,UAAY,MACZ,UAAY,oCACZ,eAAiB,mBAEV,KAGF,CAAE,QAAS,QAAS,aAAc,KAAM,CACjD,CAuBA,SAAgB,aAAa,QAA+B,CAAC,EAAG,CAC9D,OAAQ,IAAc,IAAe,OAAuB,CAC1D,IAAM,MAAQ,aAAa,SAAS,EAE9B,kBAAoB,IAAI,QAAQ,oBAChC,IAAM,MAAM,QAAQ,iBAAiB,EAAI,kBAAkB,GAAK,kBAChE,OAAS,IAAM,iBAAiB,GAAG,EAAI,KAEvC,QAAU,QAAQ,SAAW,gBAAgB,EAC7C,OAAS,eAAe,EAG1B,QACF,MAAM,OAAO,IAAI,UAAW,OAAO,EACnC,MAAM,OAAO,IAAI,SAAU,MAAM,EAC7B,SACF,MAAM,OAAO,IAAI,eAAgB,OAAO,YAAY,EACpD,MAAM,OAAO,IAAI,aAAc,OAAO,KAAK,EAC3C,MAAM,OAAO,IAAI,eAAgB,OAAO,OAAO,IAKlD,IAAa,QAAU,QACvB,IAAa,OAAS,OAEnB,QAAQ,mBACV,IAAI,UAAU,gBAAiB,OAAO,EAGxC,KAAK,CACP,CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @forinda/kickjs v6.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
|
+
var LazyMemoryStore=class{windowMs;hits=new Map;constructor(windowMs){this.windowMs=windowMs}async increment(key){let now=Date.now(),entry=this.hits.get(key);if(entry&&entry.reset>now)return entry.hits++,{totalHits:entry.hits,resetTime:new Date(entry.reset)};if(this.hits.size>1e4)for(let[k,e]of this.hits)e.reset<=now&&this.hits.delete(k);let fresh={hits:1,reset:now+this.windowMs};return this.hits.set(key,fresh),{totalHits:1,resetTime:new Date(fresh.reset)}}async decrement(key){let entry=this.hits.get(key);entry&&entry.hits>0&&entry.hits--}async reset(key){this.hits.delete(key)}};function defaultKey(ctx){let reqIp=ctx.req.ip;if(typeof reqIp==`string`&&reqIp.length>0)return reqIp;let h=ctx.headers,first=v=>typeof v==`string`&&v.length>0?v.split(`,`)[0].trim():void 0;return first(h[`cf-connecting-ip`])??first(h[`x-forwarded-for`])??first(h[`x-real-ip`])??`global`}function rateLimitGuard(options={}){let max=options.max??100,windowMs=options.windowMs??6e4,message=options.message??`Too Many Requests`,statusCode=options.statusCode??429,keyGenerator=options.keyGenerator??defaultKey,sendHeaders=options.headers??!0,store=options.store??new LazyMemoryStore(windowMs);return async(ctx,next)=>{if(options.skip?.(ctx))return next();let{totalHits,resetTime}=await store.increment(keyGenerator(ctx)),remaining=Math.max(0,max-totalHits);if(sendHeaders&&(ctx.setHeader(`X-RateLimit-Limit`,String(max)),ctx.setHeader(`X-RateLimit-Remaining`,String(remaining)),ctx.setHeader(`X-RateLimit-Reset`,String(Math.ceil(resetTime.getTime()/1e3)))),totalHits>max){let retryAfter=Math.max(1,Math.ceil((resetTime.getTime()-Date.now())/1e3));sendHeaders&&ctx.setHeader(`Retry-After`,String(retryAfter)),ctx.json({message},statusCode);return}next()}}function kvTtlSeconds(ms){return Math.max(60,Math.ceil(ms/1e3))}var KvRateLimitStore=class{kv;prefix;windowMs;constructor(kv,options){this.kv=kv,this.windowMs=options.windowMs,this.prefix=options.prefix??`rl:`}async increment(key){let now=Date.now(),entry=await this.read(key);if(entry&&entry.reset>now)return entry.hits++,await this.write(key,entry),{totalHits:entry.hits,resetTime:new Date(entry.reset)};let fresh={hits:1,reset:now+this.windowMs};return await this.write(key,fresh),{totalHits:1,resetTime:new Date(fresh.reset)}}async decrement(key){let entry=await this.read(key);entry&&entry.hits>0&&(entry.hits--,await this.write(key,entry))}async reset(key){await this.kv.delete(this.prefix+key)}async read(key){let raw=await this.kv.get(this.prefix+key);if(raw===null)return null;try{return JSON.parse(raw)}catch{return null}}async write(key,entry){await this.kv.put(this.prefix+key,JSON.stringify(entry),{expirationTtl:kvTtlSeconds(Math.max(entry.reset-Date.now(),1e3))})}},KvSessionStore=class{kv;prefix;constructor(kv,options={}){this.kv=kv,this.prefix=options.prefix??`sess:`}async get(sid){let raw=await this.kv.get(this.prefix+sid);if(raw===null)return null;try{return JSON.parse(raw)}catch{return null}}async set(sid,data,maxAge){await this.kv.put(this.prefix+sid,JSON.stringify(data),{expirationTtl:kvTtlSeconds(maxAge)})}async destroy(sid){await this.kv.delete(this.prefix+sid)}async touch(sid,maxAge){let data=await this.get(sid);data!==null&&await this.set(sid,data,maxAge)}};export{KvSessionStore as n,rateLimitGuard as r,KvRateLimitStore as t};
|
|
12
|
+
//# sourceMappingURL=kv-stores-O8HUSwpv.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-stores-O8HUSwpv.mjs","names":[],"sources":["../src/http/middleware/rate-limit-guard.ts","../src/http/middleware/kv-stores.ts"],"sourcesContent":["// Ctx-style rate limiter — runs on BOTH pipelines: the node runtimes (via\n// `@Middleware()` / class middleware) and the `@forinda/kickjs/web` fetch\n// entry (via `@Middleware()` or `createWebApp({ middleware })`), unlike the\n// connect-style `rateLimit()` which is node-only. Zero runtime imports —\n// part of the edge purity graph.\nimport type { MiddlewareHandler } from '../../core/decorators'\nimport type { RequestContext } from '../context'\nimport type { RateLimitStore } from './rate-limit'\n\nexport interface RateLimitGuardOptions {\n /** Maximum number of requests per window (default: 100). */\n max?: number\n /** Time window in milliseconds (default: 60_000). */\n windowMs?: number\n /** Response message when the limit is exceeded (default: 'Too Many Requests'). */\n message?: string\n /** HTTP status code when the limit is exceeded (default: 429). */\n statusCode?: number\n /**\n * Rate-limit key per request. Default: `cf-connecting-ip`, then the first\n * hop of `x-forwarded-for`, then `x-real-ip`, then `'global'`. Return a\n * user/tenant id here for authenticated quotas.\n */\n keyGenerator?: (ctx: RequestContext) => string\n /** Send `X-RateLimit-*` / `Retry-After` headers (default: true). */\n headers?: boolean\n /**\n * Counter backend. Defaults to a per-isolate in-memory map — fine for a\n * single node process, useless on edge where isolates recycle: pass a\n * `KvRateLimitStore` (or Redis-backed store) there.\n */\n store?: RateLimitStore\n /** Skip limiting for a request (health checks, allowlists). */\n skip?: (ctx: RequestContext) => boolean\n}\n\n/** Timer-free in-memory store: sweeps expired entries when the map grows. */\nclass LazyMemoryStore implements RateLimitStore {\n private readonly hits = new Map<string, { hits: number; reset: number }>()\n\n constructor(private readonly windowMs: number) {}\n\n async increment(key: string): Promise<{ totalHits: number; resetTime: Date }> {\n const now = Date.now()\n const entry = this.hits.get(key)\n if (entry && entry.reset > now) {\n entry.hits++\n return { totalHits: entry.hits, resetTime: new Date(entry.reset) }\n }\n // ponytail: sweep-on-growth instead of a cleanup interval — no timer to\n // dispose, edge-safe. Bound is 10k live keys before a full sweep.\n if (this.hits.size > 10_000) {\n for (const [k, e] of this.hits) if (e.reset <= now) this.hits.delete(k)\n }\n const fresh = { hits: 1, reset: now + this.windowMs }\n this.hits.set(key, fresh)\n return { totalHits: 1, resetTime: new Date(fresh.reset) }\n }\n\n async decrement(key: string): Promise<void> {\n const entry = this.hits.get(key)\n if (entry && entry.hits > 0) entry.hits--\n }\n\n async reset(key: string): Promise<void> {\n this.hits.delete(key)\n }\n}\n\nfunction defaultKey(ctx: RequestContext): string {\n // Prefer the runtime-computed client IP (Express derives req.ip from the\n // app's `trust proxy` setting) — raw forwarded headers are client-spoofable\n // on node deployments that don't normalize them. Header fallbacks cover\n // runtimes without an `ip` field (the web/edge entry).\n const reqIp = (ctx.req as { ip?: unknown }).ip\n if (typeof reqIp === 'string' && reqIp.length > 0) return reqIp\n const h = ctx.headers\n const first = (v: unknown): string | undefined =>\n typeof v === 'string' && v.length > 0 ? v.split(',')[0].trim() : undefined\n return (\n first(h['cf-connecting-ip']) ?? first(h['x-forwarded-for']) ?? first(h['x-real-ip']) ?? 'global'\n )\n}\n\n/**\n * Rate limiting as a `(ctx, next)` middleware.\n *\n * ```ts\n * // Edge (Cloudflare Workers) — app-wide:\n * const app = createWebApp({\n * h3, modules,\n * middleware: [rateLimitGuard({ max: 60, windowMs: 60_000, store: new KvRateLimitStore(env.KV, { windowMs: 60_000 }) })],\n * })\n *\n * // Any runtime — per controller/route:\n * @Middleware(rateLimitGuard({ max: 10 }))\n * @Post('/login')\n * ```\n */\nexport function rateLimitGuard(options: RateLimitGuardOptions = {}): MiddlewareHandler {\n const max = options.max ?? 100\n const windowMs = options.windowMs ?? 60_000\n const message = options.message ?? 'Too Many Requests'\n const statusCode = options.statusCode ?? 429\n const keyGenerator = options.keyGenerator ?? defaultKey\n const sendHeaders = options.headers ?? true\n const store = options.store ?? new LazyMemoryStore(windowMs)\n\n return async (ctx: RequestContext, next: () => void): Promise<void> => {\n if (options.skip?.(ctx)) return next()\n\n const { totalHits, resetTime } = await store.increment(keyGenerator(ctx))\n const remaining = Math.max(0, max - totalHits)\n\n if (sendHeaders) {\n ctx.setHeader('X-RateLimit-Limit', String(max))\n ctx.setHeader('X-RateLimit-Remaining', String(remaining))\n ctx.setHeader('X-RateLimit-Reset', String(Math.ceil(resetTime.getTime() / 1000)))\n }\n\n if (totalHits > max) {\n const retryAfter = Math.max(1, Math.ceil((resetTime.getTime() - Date.now()) / 1000))\n if (sendHeaders) ctx.setHeader('Retry-After', String(retryAfter))\n ctx.json({ message }, statusCode)\n return\n }\n\n next()\n }\n}\n","// Edge-safe store adapters over a minimal KV surface. Zero runtime imports —\n// this file must stay importable from the `@forinda/kickjs/web` purity graph.\nimport type { RateLimitStore } from './rate-limit'\nimport type { SessionData, SessionStore } from './session'\n\n/**\n * The minimal async KV surface both stores need. Structurally compatible\n * with a Cloudflare Workers `KVNamespace` binding — pass it straight in:\n *\n * ```ts\n * export default {\n * fetch(req: Request, env: Env) {\n * const store = new KvRateLimitStore(env.MY_KV, { windowMs: 60_000 })\n * // ...\n * },\n * }\n * ```\n *\n * Anything with the same shape works too (Deno KV wrapper, Upstash Redis\n * REST client adapter, a Map-backed fake in tests).\n */\nexport interface KvLike {\n get(key: string): Promise<string | null>\n put(key: string, value: string, options?: { expirationTtl?: number }): Promise<void>\n delete(key: string): Promise<void>\n}\n\n/** Cloudflare KV rejects TTLs under 60 seconds — clamp up, never down. */\nfunction kvTtlSeconds(ms: number): number {\n return Math.max(60, Math.ceil(ms / 1000))\n}\n\nexport interface KvRateLimitStoreOptions {\n /** Window length in ms — must match the `windowMs` given to the limiter. */\n windowMs: number\n /** Key prefix in the KV namespace (default `'rl:'`). */\n prefix?: string\n}\n\n/**\n * {@link RateLimitStore} over a {@link KvLike} namespace, for edge deployments\n * where in-memory counters reset on every isolate.\n *\n * KV is eventually consistent and this is a read-modify-write counter, so\n * limiting is **approximate** under concurrency — bursts racing across\n * isolates can each read the same count. That is the accepted trade-off for\n * abuse throttling; use a Durable Object / Redis store when you need an\n * exact ceiling (billing quotas, strict per-key locks).\n */\nexport class KvRateLimitStore implements RateLimitStore {\n private readonly prefix: string\n private readonly windowMs: number\n\n constructor(\n private readonly kv: KvLike,\n options: KvRateLimitStoreOptions,\n ) {\n this.windowMs = options.windowMs\n this.prefix = options.prefix ?? 'rl:'\n }\n\n async increment(key: string): Promise<{ totalHits: number; resetTime: Date }> {\n const now = Date.now()\n const entry = await this.read(key)\n if (entry && entry.reset > now) {\n entry.hits++\n await this.write(key, entry)\n return { totalHits: entry.hits, resetTime: new Date(entry.reset) }\n }\n const fresh = { hits: 1, reset: now + this.windowMs }\n await this.write(key, fresh)\n return { totalHits: 1, resetTime: new Date(fresh.reset) }\n }\n\n async decrement(key: string): Promise<void> {\n const entry = await this.read(key)\n if (entry && entry.hits > 0) {\n entry.hits--\n await this.write(key, entry)\n }\n }\n\n async reset(key: string): Promise<void> {\n await this.kv.delete(this.prefix + key)\n }\n\n private async read(key: string): Promise<{ hits: number; reset: number } | null> {\n const raw = await this.kv.get(this.prefix + key)\n if (raw === null) return null\n try {\n return JSON.parse(raw) as { hits: number; reset: number }\n } catch {\n return null\n }\n }\n\n private async write(key: string, entry: { hits: number; reset: number }): Promise<void> {\n await this.kv.put(this.prefix + key, JSON.stringify(entry), {\n expirationTtl: kvTtlSeconds(Math.max(entry.reset - Date.now(), 1000)),\n })\n }\n}\n\nexport interface KvSessionStoreOptions {\n /** Key prefix in the KV namespace (default `'sess:'`). */\n prefix?: string\n}\n\n/**\n * {@link SessionStore} over a {@link KvLike} namespace. Plug into the node\n * `session()` middleware today (e.g. backed by Upstash/Cloudflare KV via\n * REST) so sessions survive restarts and horizontal scaling; TTL expiry is\n * delegated to the KV layer via `expirationTtl`.\n *\n * Cost note: `touch()` is a get + put round-trip (KvLike has no TTL-only\n * refresh), so `session({ rolling: true })` doubles KV operations per\n * request — per-operation-billed platforms may prefer `rolling: false`.\n */\nexport class KvSessionStore implements SessionStore {\n private readonly prefix: string\n\n constructor(\n private readonly kv: KvLike,\n options: KvSessionStoreOptions = {},\n ) {\n this.prefix = options.prefix ?? 'sess:'\n }\n\n async get(sid: string): Promise<SessionData | null> {\n const raw = await this.kv.get(this.prefix + sid)\n if (raw === null) return null\n try {\n return JSON.parse(raw) as SessionData\n } catch {\n return null\n }\n }\n\n async set(sid: string, data: SessionData, maxAge: number): Promise<void> {\n await this.kv.put(this.prefix + sid, JSON.stringify(data), {\n expirationTtl: kvTtlSeconds(maxAge),\n })\n }\n\n async destroy(sid: string): Promise<void> {\n await this.kv.delete(this.prefix + sid)\n }\n\n async touch(sid: string, maxAge: number): Promise<void> {\n const data = await this.get(sid)\n if (data !== null) await this.set(sid, data, maxAge)\n }\n}\n"],"mappings":"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"}
|