@carno.js/core 1.2.0 → 1.3.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/Carno.js CHANGED
@@ -39,6 +39,9 @@ class Carno {
39
39
  this.corsHandler = null;
40
40
  this.hasCors = !1;
41
41
  this.validator = null;
42
+ // WebSocket support
43
+ this._wsHandlerBuilder = null;
44
+ this._wsUpgradePaths = /* @__PURE__ */ new Set();
42
45
  // Cached lifecycle event flags - checked once at startup
43
46
  this.hasInitHooks = !1;
44
47
  this.hasBootHooks = !1;
@@ -52,7 +55,7 @@ class Carno {
52
55
  }
53
56
  /**
54
57
  * Use a Carno plugin.
55
- * Imports controllers, services, middlewares, and routes from another Carno instance.
58
+ * Imports controllers, services, middlewares, routes, and WebSocket config from another Carno instance.
56
59
  */
57
60
  use(plugin) {
58
61
  plugin._controllers.length > 0 && this._controllers.push(...plugin._controllers);
@@ -63,8 +66,29 @@ class Carno {
63
66
  plugin._services.length > 0 && this._services.push(...plugin._services), plugin.config.globalMiddlewares && this._middlewares.push(...plugin.config.globalMiddlewares), plugin._middlewares.length > 0 && this._middlewares.push(...plugin._middlewares);
64
67
  for (const [path, methods] of Object.entries(plugin.routes))
65
68
  this.routes[path] || (this.routes[path] = {}), typeof methods == "object" && methods !== null && !(methods instanceof Response) ? Object.assign(this.routes[path], methods) : this.routes[path] = methods;
69
+ if (plugin._wsHandlerBuilder) {
70
+ this._wsHandlerBuilder = plugin._wsHandlerBuilder;
71
+ for (const path of plugin._wsUpgradePaths)
72
+ this._wsUpgradePaths.add(path);
73
+ }
74
+ return this;
75
+ }
76
+ /**
77
+ * Register a WebSocket handler builder and the upgrade paths.
78
+ * Used internally by @carno.js/websocket plugin.
79
+ */
80
+ wsHandler(builder, upgradePaths) {
81
+ this._wsHandlerBuilder = builder;
82
+ for (const path of upgradePaths)
83
+ this._wsUpgradePaths.add(path);
66
84
  return this;
67
85
  }
86
+ /**
87
+ * Returns the underlying Bun server instance (available after listen()).
88
+ */
89
+ getServer() {
90
+ return this.server;
91
+ }
68
92
  findServiceInPlugin(plugin, exported) {
69
93
  return plugin._services.find(
70
94
  (s) => this.getServiceToken(s) === this.getServiceToken(exported)
@@ -155,12 +179,28 @@ class Carno {
155
179
  ...this.routes
156
180
  }
157
181
  };
182
+ if (this._wsHandlerBuilder && this._wsUpgradePaths.size > 0) {
183
+ config.websocket = this._wsHandlerBuilder(this.container);
184
+ const upgradePaths = this._wsUpgradePaths, fallback = this.handleNotFound.bind(this);
185
+ config.fetch = (req, server) => {
186
+ const pathname = new URL(req.url).pathname;
187
+ return upgradePaths.has(pathname) ? server.upgrade(req, {
188
+ data: {
189
+ id: crypto.randomUUID(),
190
+ namespace: pathname
191
+ }
192
+ }) ? void 0 : new Response("WebSocket upgrade failed", { status: 400 }) : fallback(req);
193
+ };
194
+ }
158
195
  this.server = Bun.serve(config), this.hasBootHooks && this.executeLifecycleHooks(import_Lifecycle.EventType.BOOT), this.hasShutdownHooks && this.registerShutdownHandlers(), this.config.disableStartupLog || console.log(`Carno running on port ${port}`);
159
196
  }
160
197
  bootstrap() {
161
198
  this.hasInitHooks = (0, import_Lifecycle.hasEventHandlers)(import_Lifecycle.EventType.INIT), this.hasBootHooks = (0, import_Lifecycle.hasEventHandlers)(import_Lifecycle.EventType.BOOT), this.hasShutdownHooks = (0, import_Lifecycle.hasEventHandlers)(import_Lifecycle.EventType.SHUTDOWN), this.container.register({
162
199
  token: import_Container.Container,
163
200
  useValue: this.container
201
+ }), this.container.register({
202
+ token: Carno,
203
+ useValue: this
164
204
  });
165
205
  const cacheConfig = typeof this.config.cache == "object" ? this.config.cache : {};
166
206
  this.container.register({
package/dist/Carno.mjs CHANGED
@@ -31,6 +31,9 @@ class Carno {
31
31
  this.corsHandler = null;
32
32
  this.hasCors = !1;
33
33
  this.validator = null;
34
+ // WebSocket support
35
+ this._wsHandlerBuilder = null;
36
+ this._wsUpgradePaths = /* @__PURE__ */ new Set();
34
37
  // Cached lifecycle event flags - checked once at startup
35
38
  this.hasInitHooks = !1;
36
39
  this.hasBootHooks = !1;
@@ -44,7 +47,7 @@ class Carno {
44
47
  }
45
48
  /**
46
49
  * Use a Carno plugin.
47
- * Imports controllers, services, middlewares, and routes from another Carno instance.
50
+ * Imports controllers, services, middlewares, routes, and WebSocket config from another Carno instance.
48
51
  */
49
52
  use(plugin) {
50
53
  plugin._controllers.length > 0 && this._controllers.push(...plugin._controllers);
@@ -55,8 +58,29 @@ class Carno {
55
58
  plugin._services.length > 0 && this._services.push(...plugin._services), plugin.config.globalMiddlewares && this._middlewares.push(...plugin.config.globalMiddlewares), plugin._middlewares.length > 0 && this._middlewares.push(...plugin._middlewares);
56
59
  for (const [path, methods] of Object.entries(plugin.routes))
57
60
  this.routes[path] || (this.routes[path] = {}), typeof methods == "object" && methods !== null && !(methods instanceof Response) ? Object.assign(this.routes[path], methods) : this.routes[path] = methods;
61
+ if (plugin._wsHandlerBuilder) {
62
+ this._wsHandlerBuilder = plugin._wsHandlerBuilder;
63
+ for (const path of plugin._wsUpgradePaths)
64
+ this._wsUpgradePaths.add(path);
65
+ }
66
+ return this;
67
+ }
68
+ /**
69
+ * Register a WebSocket handler builder and the upgrade paths.
70
+ * Used internally by @carno.js/websocket plugin.
71
+ */
72
+ wsHandler(builder, upgradePaths) {
73
+ this._wsHandlerBuilder = builder;
74
+ for (const path of upgradePaths)
75
+ this._wsUpgradePaths.add(path);
58
76
  return this;
59
77
  }
78
+ /**
79
+ * Returns the underlying Bun server instance (available after listen()).
80
+ */
81
+ getServer() {
82
+ return this.server;
83
+ }
60
84
  findServiceInPlugin(plugin, exported) {
61
85
  return plugin._services.find(
62
86
  (s) => this.getServiceToken(s) === this.getServiceToken(exported)
@@ -147,12 +171,28 @@ class Carno {
147
171
  ...this.routes
148
172
  }
149
173
  };
174
+ if (this._wsHandlerBuilder && this._wsUpgradePaths.size > 0) {
175
+ config.websocket = this._wsHandlerBuilder(this.container);
176
+ const upgradePaths = this._wsUpgradePaths, fallback = this.handleNotFound.bind(this);
177
+ config.fetch = (req, server) => {
178
+ const pathname = new URL(req.url).pathname;
179
+ return upgradePaths.has(pathname) ? server.upgrade(req, {
180
+ data: {
181
+ id: crypto.randomUUID(),
182
+ namespace: pathname
183
+ }
184
+ }) ? void 0 : new Response("WebSocket upgrade failed", { status: 400 }) : fallback(req);
185
+ };
186
+ }
150
187
  this.server = Bun.serve(config), this.hasBootHooks && this.executeLifecycleHooks(EventType.BOOT), this.hasShutdownHooks && this.registerShutdownHandlers(), this.config.disableStartupLog || console.log(`Carno running on port ${port}`);
151
188
  }
152
189
  bootstrap() {
153
190
  this.hasInitHooks = hasEventHandlers(EventType.INIT), this.hasBootHooks = hasEventHandlers(EventType.BOOT), this.hasShutdownHooks = hasEventHandlers(EventType.SHUTDOWN), this.container.register({
154
191
  token: Container,
155
192
  useValue: this.container
193
+ }), this.container.register({
194
+ token: Carno,
195
+ useValue: this
156
196
  });
157
197
  const cacheConfig = typeof this.config.cache == "object" ? this.config.cache : {};
158
198
  this.container.register({
package/dist/bun/index.js CHANGED
@@ -177,7 +177,7 @@ return h(${argsCode});
177
177
  return await h(${argsCode});
178
178
  }`;return{fn:Function("h",code2)(bound),isAsync:!0,isStatic:!1}}let code=`return function(c){
179
179
  return h(${argsCode});
180
- }`;return{fn:Function("h",code)(bound),isAsync:!1,isStatic:!1}}function escapeKey(key){return key.replace(/['\"\\]/g,"\\$&")}function buildArgExpression(param){let key=param.key?escapeKey(param.key):void 0;switch(param.type){case"param":return key?`c.params['${key}']`:"c.params";case"query":return key?`c.query['${key}']`:"c.query";case"body":return key?`c.body['${key}']`:"c.body";case"header":return key?`c.req.headers.get('${key}')`:"c.req.headers";case"req":return"c.req";case"ctx":return"c";case"locals":return key?`c.locals['${key}']`:"c.locals";default:return"undefined"}}function parseQueryFromURL(url){let queryStart=url.indexOf("?");if(queryStart===-1)return Object.create(null);let queryEnd=url.indexOf("#",queryStart);if(queryEnd===-1)queryEnd=url.length;return parseQuery(url,queryStart+1,queryEnd)}function parseQuery(input,startIndex,endIndex){let result=Object.create(null),flags=0,startingIndex=startIndex-1,equalityIndex=startingIndex;for(let i=startIndex;i<endIndex;i++)switch(input.charCodeAt(i)){case 38:processKeyValuePair(i),startingIndex=i,equalityIndex=i,flags=0;break;case 61:if(equalityIndex<=startingIndex)equalityIndex=i;else flags|=8;break;case 43:if(equalityIndex>startingIndex)flags|=4;else flags|=1;break;case 37:if(equalityIndex>startingIndex)flags|=8;else flags|=2;break}if(startingIndex<endIndex)processKeyValuePair(endIndex);return result;function processKeyValuePair(pairEndIndex){let hasBothKeyValuePair=equalityIndex>startingIndex,effectiveEqualityIndex=hasBothKeyValuePair?equalityIndex:pairEndIndex,keySlice=input.slice(startingIndex+1,effectiveEqualityIndex);if(!hasBothKeyValuePair&&keySlice.length===0)return;let finalKey=keySlice;if(flags&1)finalKey=finalKey.replace(/\+/g," ");if(flags&2)try{finalKey=decodeURIComponent(finalKey)}catch{}let finalValue="";if(hasBothKeyValuePair){let valueSlice=input.slice(equalityIndex+1,pairEndIndex);if(flags&4)valueSlice=valueSlice.replace(/\+/g," ");if(flags&8)try{finalValue=decodeURIComponent(valueSlice)}catch{finalValue=valueSlice}else finalValue=valueSlice}result[finalKey]=finalValue}}var EMPTY_PARAMS=Object.freeze({});class Context{req;params;locals={};_query=null;_body;_bodyParsed=!1;_url=null;_status=0;constructor(req,params=EMPTY_PARAMS){this.req=req,this.params=params}get status(){return this._status||200}set status(value){this._status=value}get url(){if(!this._url)this._url=new URL(this.req.url);return this._url}get query(){if(!this._query)this._query=parseQueryFromURL(this.req.url);return this._query}get body(){return this._body}async parseBody(){if(this._bodyParsed)return this._body;this._bodyParsed=!0;let contentType=this.req.headers.get("content-type")||"";if(contentType.includes("application/json"))this._body=await this.req.json();else if(contentType.includes("form")){let formData=await this.req.formData();this._body=Object.fromEntries(formData)}else if(contentType.includes("text"))this._body=await this.req.text();else this._body=await this.req.arrayBuffer();return this._body}get method(){return this.req.method}get headers(){return this.req.headers}get path(){return this.url.pathname}json(data,status){if(status)this.status=status;return Response.json(data,{status:this.status})}text(data,status){if(status)this.status=status;return new Response(data,{status:this.status,headers:{"Content-Type":"text/plain"}})}html(data,status){if(status)this.status=status;return new Response(data,{status:this.status,headers:{"Content-Type":"text/html"}})}redirect(url,status=302){return Response.redirect(url,status)}static createFromJob(job){let fakeRequest=new Request("http://localhost/job"),ctx=new Context(fakeRequest);return ctx.locals.job=job,ctx}}var Scope;((Scope2)=>{Scope2.SINGLETON="singleton";Scope2.REQUEST="request";Scope2.INSTANCE="instance"})(Scope||={});class Container{configs=new Map;instances=new Map;resolving=new Set;register(config){let normalized=this.normalizeConfig(config);if(this.configs.set(normalized.token,normalized),normalized.useValue!==void 0)this.instances.set(normalized.token,normalized.useValue);return this}get(token){let cached=this.instances.get(token);if(cached!==void 0)return cached;return this.resolveInternal(token).instance}has(token){return this.configs.has(token)}resolveInternal(token,requestLocals){if(requestLocals?.has(token))return{instance:requestLocals.get(token),scope:"request"};let cached=this.instances.get(token);if(cached!==void 0)return{instance:cached,scope:"singleton"};let config=this.configs.get(token);if(!config)throw Error(`Provider not found: ${token.name}`);let creation=this.createInstance(config,requestLocals);if(creation.scope==="singleton")this.instances.set(token,creation.instance);else if(creation.scope==="request"&&requestLocals)requestLocals.set(token,creation.instance);return creation}createInstance(config,requestLocals){let target=config.useClass??config.token;if(this.resolving.has(target))throw Error(`Circular dependency detected: ${target.name}`);this.resolving.add(target);try{let depsToken=this.getDependencies(target);if(depsToken.length===0)return{instance:new target,scope:config.scope||"singleton"};let args=[],effectiveScope=config.scope||"singleton";for(let depToken of depsToken){let depResult=this.resolveInternal(depToken,requestLocals);if(args.push(depResult.instance),depResult.scope==="request"&&effectiveScope==="singleton")effectiveScope="request"}return{instance:new target(...args),scope:effectiveScope}}finally{this.resolving.delete(target)}}getDependencies(target){return(Reflect.getMetadata("design:paramtypes",target)||[]).filter((t)=>t&&typeof t==="function"&&!this.isPrimitive(t))}isPrimitive(type){return type===String||type===Number||type===Boolean||type===Object||type===Array||type===Symbol}normalizeConfig(config){if(typeof config==="function")return{token:config,useClass:config,scope:"singleton"};return{...config,useClass:config.useClass??config.token,scope:config.scope??"singleton"}}clear(){this.configs.clear(),this.instances.clear()}}var DEFAULT_CORS_METHODS=["GET","HEAD","PUT","PATCH","POST","DELETE"],DEFAULT_CORS_HEADERS=["Content-Type","Authorization","X-Requested-With","Accept","Origin"];class CorsHandler{cache=new Map;methodsStr;headersStr;exposedStr;maxAgeStr;hasCredentials;isWildcard;matcher;preflightResponse=null;constructor(config){if(this.methodsStr=(config.methods||DEFAULT_CORS_METHODS).join(", "),this.headersStr=(config.allowedHeaders||DEFAULT_CORS_HEADERS).join(", "),this.exposedStr=config.exposedHeaders?.join(", ")||null,this.maxAgeStr=config.maxAge?.toString()||null,this.hasCredentials=!!config.credentials,this.isWildcard=config.origins==="*",this.matcher=this.buildMatcher(config.origins),this.isWildcard)this.preflightResponse=new Response(null,{status:204,headers:this.buildHeaders("*")})}preflight(origin){if(this.isWildcard&&this.preflightResponse)return this.preflightResponse.clone();if(!this.isAllowed(origin))return new Response(null,{status:403});return new Response(null,{status:204,headers:this.getHeaders(origin)})}apply(response,origin){if(!this.isAllowed(origin))return response;let headers=this.getHeaders(origin);for(let[key,value]of Object.entries(headers))response.headers.set(key,value);return response}isAllowed(origin){return this.matcher(origin)}getHeaders(origin){let key=this.isWildcard?"*":origin,headers=this.cache.get(key);if(!headers)headers=this.buildHeaders(origin),this.cache.set(key,headers);return headers}buildHeaders(origin){let headers={"Access-Control-Allow-Origin":this.isWildcard?"*":origin,"Access-Control-Allow-Methods":this.methodsStr,"Access-Control-Allow-Headers":this.headersStr};if(this.hasCredentials)headers["Access-Control-Allow-Credentials"]="true";if(this.exposedStr)headers["Access-Control-Expose-Headers"]=this.exposedStr;if(this.maxAgeStr)headers["Access-Control-Max-Age"]=this.maxAgeStr;return headers}buildMatcher(origins){if(origins==="*")return()=>!0;if(typeof origins==="string")return(o)=>o===origins;if(Array.isArray(origins)){let set=new Set(origins);return(o)=>set.has(o)}if(origins instanceof RegExp)return(o)=>origins.test(o);if(typeof origins==="function")return origins;return()=>!1}}class HttpException extends Error{statusCode;errors;constructor(statusCode,message,errors){super(message);this.statusCode=statusCode;this.errors=errors;this.name="HttpException"}toResponse(){let body={statusCode:this.statusCode,message:this.message,...this.errors&&{errors:this.errors}};return Response.json(body,{status:this.statusCode})}}class BadRequestException extends HttpException{constructor(message="Bad Request",errors){super(400,message,errors);this.name="BadRequestException"}}class UnauthorizedException extends HttpException{constructor(message="Unauthorized"){super(401,message);this.name="UnauthorizedException"}}class ForbiddenException extends HttpException{constructor(message="Forbidden"){super(403,message);this.name="ForbiddenException"}}class NotFoundException extends HttpException{constructor(message="Not Found"){super(404,message);this.name="NotFoundException"}}class MethodNotAllowedException extends HttpException{constructor(message="Method Not Allowed"){super(405,message);this.name="MethodNotAllowedException"}}class ConflictException extends HttpException{constructor(message="Conflict"){super(409,message);this.name="ConflictException"}}class UnprocessableEntityException extends HttpException{constructor(message="Unprocessable Entity",errors){super(422,message,errors);this.name="UnprocessableEntityException"}}class TooManyRequestsException extends HttpException{constructor(message="Too Many Requests"){super(429,message);this.name="TooManyRequestsException"}}class InternalServerErrorException extends HttpException{constructor(message="Internal Server Error"){super(500,message);this.name="InternalServerErrorException"}}class ServiceUnavailableException extends HttpException{constructor(message="Service Unavailable"){super(503,message);this.name="ServiceUnavailableException"}}var VALIDATION_SCHEMA=Symbol("turbo:validation");function Schema(schema){return(target)=>{Reflect.defineMetadata(VALIDATION_SCHEMA,schema,target)}}function getSchema(target){return Reflect.getMetadata(VALIDATION_SCHEMA,target)}class ZodAdapter{name="ZodAdapter";schemaCache=new Map;hasValidation(target){return getSchema(target)!==void 0}validate(target,value){let schema=this.getOrCacheSchema(target);if(!schema)return{success:!0,data:value};let result=schema.safeParse(value);if(result.success)return{success:!0,data:result.data};return{success:!1,errors:this.formatErrors(result.error)}}validateOrThrow(target,value){let schema=this.getOrCacheSchema(target);if(!schema)return value;let result=schema.safeParse(value);if(result.success)return result.data;let errors=this.formatErrors(result.error);throw new ValidationException(errors)}getOrCacheSchema(target){let schema=this.schemaCache.get(target);if(schema===void 0)schema=getSchema(target)??null,this.schemaCache.set(target,schema);return schema}formatErrors(zodError){return zodError.issues.map((issue)=>({path:issue.path.join("."),message:issue.message}))}}class ValidationException extends Error{errors;constructor(errors){super(`Validation failed: ${errors.map((e)=>`${e.path}: ${e.message}`).join(", ")}`);this.errors=errors;this.name="ValidationException"}toResponse(){return Response.json({statusCode:400,message:"Validation failed",errors:this.errors},{status:400})}}var EventType;((EventType2)=>{EventType2.INIT="onInit";EventType2.BOOT="onBoot";EventType2.SHUTDOWN="onShutdown"})(EventType||={});var EVENTS_META=Symbol("turbo:events"),eventRegistry=new Map;function registerEvent(type,target,methodName,priority=0){let handlers=eventRegistry.get(type);if(!handlers)handlers=[],eventRegistry.set(type,handlers);handlers.push({target,methodName,priority})}function getEventHandlers(type){return(eventRegistry.get(type)||[]).sort((a,b)=>b.priority-a.priority)}function hasEventHandlers(type){return(eventRegistry.get(type)?.length??0)>0}function OnApplicationInit(priority=0){return function(target,propertyKey){registerEvent("onInit",target.constructor,propertyKey,priority)}}function OnApplicationBoot(priority=0){return function(target,propertyKey){registerEvent("onBoot",target.constructor,propertyKey,priority)}}function OnApplicationShutdown(priority=0){return function(target,propertyKey){registerEvent("onShutdown",target.constructor,propertyKey,priority)}}class MemoryDriver{name="MemoryDriver";cache=new Map;cleanupInterval=null;constructor(cleanupIntervalMs=0){if(cleanupIntervalMs>0)this.cleanupInterval=setInterval(()=>this.cleanup(),cleanupIntervalMs)}async get(key){let entry=this.cache.get(key);if(!entry)return null;if(entry.expiresAt!==null&&Date.now()>entry.expiresAt)return this.cache.delete(key),null;return entry.value}async set(key,value,ttl){let expiresAt=ttl?Date.now()+ttl:null;return this.cache.set(key,{value,expiresAt}),!0}async del(key){return this.cache.delete(key)}async has(key){let entry=this.cache.get(key);if(!entry)return!1;if(entry.expiresAt!==null&&Date.now()>entry.expiresAt)return this.cache.delete(key),!1;return!0}async clear(){this.cache.clear()}async close(){if(this.cleanupInterval)clearInterval(this.cleanupInterval),this.cleanupInterval=null;this.cache.clear()}cleanup(){let now=Date.now();for(let[key,entry]of this.cache)if(entry.expiresAt!==null&&now>entry.expiresAt)this.cache.delete(key)}stats(){return{size:this.cache.size}}}class CacheService{driver;prefix;defaultTtl;constructor(config={}){this.driver=config.driver||new MemoryDriver,this.prefix=config.prefix||"",this.defaultTtl=config.defaultTtl}key(key){return this.prefix?`${this.prefix}:${key}`:key}async get(key){return this.driver.get(this.key(key))}async set(key,value,ttl){return this.driver.set(this.key(key),value,ttl??this.defaultTtl)}async del(key){return this.driver.del(this.key(key))}async has(key){return this.driver.has(this.key(key))}async clear(){return this.driver.clear()}async getOrSet(key,cb,ttl){let cached=await this.get(key);if(cached!==null)return cached;let value=await cb();return await this.set(key,value,ttl),value}async getMany(keys){return Promise.all(keys.map((key)=>this.get(key)))}async setMany(entries){return Promise.all(entries.map((entry)=>this.set(entry.key,entry.value,entry.ttl)))}async delMany(keys){return Promise.all(keys.map((key)=>this.del(key)))}async close(){await this.driver.close?.()}getDriver(){return this.driver}}function normalizeOptions(pathOrOptions){if(!pathOrOptions)return{};if(typeof pathOrOptions==="string")return{path:pathOrOptions};return pathOrOptions}function normalizePath(path){if(!path)return"";let normalized=path.startsWith("/")?path:"/"+path;if(normalized!=="/"&&normalized.endsWith("/"))normalized=normalized.slice(0,-1);return normalized}function Controller(pathOrOptions){return(target)=>{let options=normalizeOptions(pathOrOptions),meta={path:normalizePath(options.path||""),scope:options.scope,children:options.children};if(Reflect.defineMetadata(CONTROLLER_META,meta,target),!Reflect.hasMetadata(ROUTES_META,target))Reflect.defineMetadata(ROUTES_META,[],target)}}function createMethodDecorator(method){return function(path=""){return function(targetOrMethod,contextOrPropertyKey,descriptor){if(contextOrPropertyKey&&typeof contextOrPropertyKey==="object"&&"kind"in contextOrPropertyKey){let context=contextOrPropertyKey;return context.addInitializer(function(){let constructor2=this.constructor,routes2=Reflect.getMetadata(ROUTES_META,constructor2)||[];routes2.push({method,path:path.startsWith("/")?path:"/"+path,handlerName:String(context.name)}),Reflect.defineMetadata(ROUTES_META,routes2,constructor2)}),targetOrMethod}let constructor=targetOrMethod.constructor,propertyKey=contextOrPropertyKey,routes=Reflect.getMetadata(ROUTES_META,constructor)||[];routes.push({method,path:path.startsWith("/")?path:"/"+path,handlerName:String(propertyKey)}),Reflect.defineMetadata(ROUTES_META,routes,constructor)}}}var Get=createMethodDecorator("get"),Post=createMethodDecorator("post"),Put=createMethodDecorator("put"),Delete=createMethodDecorator("delete"),Patch=createMethodDecorator("patch"),Head=createMethodDecorator("head"),Options=createMethodDecorator("options");class DefaultRoutes{favicon(){return new Response(null,{status:204})}}__legacyDecorateClassTS([Get("/favicon.ico"),__legacyMetadataTS("design:type",Function),__legacyMetadataTS("design:paramtypes",[]),__legacyMetadataTS("design:returntype",void 0)],DefaultRoutes.prototype,"favicon",null),DefaultRoutes=__legacyDecorateClassTS([Controller()],DefaultRoutes);var DEFAULT_STATIC_ROUTES={"/health":new Response('{"status":"ok"}',{status:200,headers:{"Content-Type":"application/json"}}),"/ready":new Response('{"ready":true}',{status:200,headers:{"Content-Type":"application/json"}}),"/favicon.ico":new Response(null,{status:204})};var NOT_FOUND_RESPONSE=new Response("Not Found",{status:404}),TEXT_OPTS=Object.freeze({status:200,headers:{"Content-Type":"text/plain"}}),JSON_OPTS=Object.freeze({status:200,headers:{"Content-Type":"application/json"}}),INTERNAL_ERROR_RESPONSE=new Response('{"statusCode":500,"message":"Internal Server Error"}',{status:500,headers:{"Content-Type":"application/json"}});class Carno{config;_controllers=[];_services=[];_middlewares=[];routes={};container=new Container;corsHandler=null;hasCors=!1;validator=null;server;hasInitHooks=!1;hasBootHooks=!1;hasShutdownHooks=!1;constructor(config={}){this.config=config;if(this.config.exports=this.config.exports||[],this.config.globalMiddlewares=this.config.globalMiddlewares||[],this.config.cors)this.corsHandler=new CorsHandler(this.config.cors),this.hasCors=!0;if(this.config.validation===void 0||this.config.validation===!0)this.validator=new ZodAdapter;else if(typeof this.config.validation==="function"){let AdapterClass=this.config.validation;this.validator=new AdapterClass}else if(this.config.validation)this.validator=this.config.validation}use(plugin){if(plugin._controllers.length>0)this._controllers.push(...plugin._controllers);for(let exported of plugin.config.exports||[]){let existingService=this.findServiceInPlugin(plugin,exported),serviceToAdd=this.shouldCloneService(existingService)?{...existingService}:exported;this._services.push(serviceToAdd)}if(plugin._services.length>0)this._services.push(...plugin._services);if(plugin.config.globalMiddlewares)this._middlewares.push(...plugin.config.globalMiddlewares);if(plugin._middlewares.length>0)this._middlewares.push(...plugin._middlewares);for(let[path,methods]of Object.entries(plugin.routes)){if(!this.routes[path])this.routes[path]={};if(typeof methods==="object"&&methods!==null&&!(methods instanceof Response))Object.assign(this.routes[path],methods);else this.routes[path]=methods}return this}findServiceInPlugin(plugin,exported){return plugin._services.find((s)=>this.getServiceToken(s)===this.getServiceToken(exported))}getServiceToken(service){return service?.token||service}shouldCloneService(service){return!!(service?.useValue!==void 0||service?.useClass)}services(serviceClass){let items=Array.isArray(serviceClass)?serviceClass:[serviceClass];return this._services.push(...items),this}middlewares(handler){let items=Array.isArray(handler)?handler:[handler];return this._middlewares.push(...items),this}controllers(controllerClass){let items=Array.isArray(controllerClass)?controllerClass:[controllerClass];return this._controllers.push(...items),this}route(method,path,handler){let normalizedMethod=method.toUpperCase();if(!this.routes[path])this.routes[path]={};return this.routes[path][normalizedMethod]=handler,this}addRoutes(routes){for(let[path,methods]of Object.entries(routes)){if(!this.routes[path])this.routes[path]={};for(let[method,handler]of Object.entries(methods))this.routes[path][method.toUpperCase()]=handler}return this}get(token){return this.container.get(token)}listen(port=3000){this.bootstrap(),this.compileRoutes();let config={port,fetch:this.handleNotFound.bind(this),error:this.handleError.bind(this),routes:{...DEFAULT_STATIC_ROUTES,...this.routes}};if(this.server=Bun.serve(config),this.hasBootHooks)this.executeLifecycleHooks("onBoot");if(this.hasShutdownHooks)this.registerShutdownHandlers();if(!this.config.disableStartupLog)console.log(`Carno running on port ${port}`)}bootstrap(){this.hasInitHooks=hasEventHandlers("onInit"),this.hasBootHooks=hasEventHandlers("onBoot"),this.hasShutdownHooks=hasEventHandlers("onShutdown"),this.container.register({token:Container,useValue:this.container});let cacheConfig=typeof this.config.cache==="object"?this.config.cache:{};this.container.register({token:CacheService,useValue:new CacheService(cacheConfig)});for(let service of this._services)this.container.register(service);for(let ControllerClass of this._controllers)this.container.register(ControllerClass);if(this.hasInitHooks)this.executeLifecycleHooks("onInit");for(let service of this._services){let token=typeof service==="function"?service:service.token,serviceConfig=typeof service==="function"?null:service;if(!serviceConfig||serviceConfig.scope!=="request")this.container.get(token)}}compileRoutes(){for(let ControllerClass of this._controllers)this.compileController(ControllerClass)}compileController(ControllerClass,parentPath="",inheritedMiddlewares=[]){let meta=Reflect.getMetadata(CONTROLLER_META,ControllerClass)||{path:""},basePath=parentPath+(meta.path||""),routes=Reflect.getMetadata(ROUTES_META,ControllerClass)||[],middlewares=Reflect.getMetadata(MIDDLEWARE_META,ControllerClass)||[],instance=this.container.get(ControllerClass),controllerMiddlewares=middlewares.filter((m)=>!m.target).map((m)=>m.handler),scopedMiddlewares=[...inheritedMiddlewares,...controllerMiddlewares];for(let route of routes){let fullPath=this.normalizePath(basePath+route.path),params=Reflect.getMetadata(PARAMS_META,ControllerClass,route.handlerName)||[],routeMiddlewares=middlewares.filter((m)=>m.target===route.handlerName).map((m)=>m.handler),paramTypes=Reflect.getMetadata("design:paramtypes",ControllerClass.prototype,route.handlerName)||[],bodyDtoType=null;for(let param of params)if(param.type==="body"&&!param.key){let dtoType=paramTypes[param.index];if(dtoType&&this.validator?.hasValidation(dtoType))bodyDtoType=dtoType}let compiled=compileHandler(instance,route.handlerName,params),resolvedMiddlewares=[...this.config.globalMiddlewares||[],...this._middlewares,...scopedMiddlewares,...routeMiddlewares].map((m)=>this.resolveMiddleware(m)),hasMiddlewares=resolvedMiddlewares.length>0,method=route.method.toUpperCase();if(compiled.isStatic&&!hasMiddlewares)this.registerRoute(fullPath,method,this.createStaticResponse(compiled.staticValue));else this.registerRoute(fullPath,method,this.createHandler(compiled,params,resolvedMiddlewares,bodyDtoType))}if(meta.children)for(let ChildController of meta.children){if(!this.container.has(ChildController))this.container.register(ChildController);this.compileController(ChildController,basePath,scopedMiddlewares)}}registerRoute(path,method,handler){if(!this.routes[path])this.routes[path]={};this.routes[path][method]=handler}createStaticResponse(value){let isString=typeof value==="string",body=isString?value:JSON.stringify(value);return new Response(body,isString?TEXT_OPTS:JSON_OPTS)}createHandler(compiled,params,middlewares,bodyDtoType){let handler=compiled.fn,hasMiddlewares=middlewares.length>0,hasParams=params.length>0,applyCors=this.hasCors?this.applyCors.bind(this):null,validator=bodyDtoType?this.validator:null,hasMiddlewaresOrValidation=hasMiddlewares||!!validator;if(!hasMiddlewaresOrValidation&&!hasParams){if(compiled.isAsync)return async(req)=>{let ctx=new Context(req),result=await handler(ctx),response=this.buildResponse(result);return applyCors?applyCors(response,req):response};return(req)=>{let ctx=new Context(req),result=handler(ctx),response=this.buildResponse(result);return applyCors?applyCors(response,req):response}}if(!hasMiddlewaresOrValidation&&hasParams){if(compiled.isAsync)return async(req)=>{let ctx=new Context(req,req.params),result=await handler(ctx),response=this.buildResponse(result);return applyCors?applyCors(response,req):response};return(req)=>{let ctx=new Context(req,req.params),result=handler(ctx),response=this.buildResponse(result);return applyCors?applyCors(response,req):response}}let coreHandler=async(ctx)=>{if(validator&&bodyDtoType)await ctx.parseBody(),validator.validateOrThrow(bodyDtoType,ctx.body);return compiled.isAsync?await handler(ctx):handler(ctx)},chain=this.buildMiddlewareChain(middlewares,coreHandler,this.buildResponse.bind(this));return async(req)=>{let ctx=new Context(req,req.params||{}),response=await chain(ctx);return applyCors?applyCors(response,req):response}}resolveMiddleware(middleware){if(typeof middleware==="function"&&middleware.prototype?.handle)return{kind:"class",instance:this.container.get(middleware)};if(typeof middleware==="object"&&middleware!==null&&"handle"in middleware)return{kind:"class",instance:middleware};return{kind:"function",handler:middleware}}buildMiddlewareChain(middlewares,coreHandler,buildResponseFn){let chain=async(ctx)=>{let result=await coreHandler(ctx);return buildResponseFn(result)};for(let i=middlewares.length-1;i>=0;i--){let mw=middlewares[i],nextLayer=chain;if(mw.kind==="function")chain=async(ctx)=>{let result=await mw.handler(ctx);if(result instanceof Response)return result;return nextLayer(ctx)};else chain=async(ctx)=>{let response,result=await mw.instance.handle(ctx,async()=>{return response=await nextLayer(ctx),response});if(result instanceof Response)return result;return response??new Response(null,{status:200})}}return chain}applyCors(response,req){let origin=req.headers.get("origin");if(origin&&this.corsHandler)return this.corsHandler.apply(response,origin);return response}handleNotFound(req){if(this.hasCors&&req.method==="OPTIONS"){let origin=req.headers.get("origin");if(origin)return this.corsHandler.preflight(origin)}return NOT_FOUND_RESPONSE}buildResponse(result){if(result instanceof Response)return result;if(typeof result==="string")return new Response(result,TEXT_OPTS);if(result===void 0)return new Response(null,{status:204});return Response.json(result)}normalizePath(path){if(!path.startsWith("/"))path="/"+path;if(path!=="/"&&path.endsWith("/"))path=path.slice(0,-1);return path.replace(/\/+/g,"/")}hasParams(path){return path.includes(":")||path.includes("*")}stop(){this.server?.stop?.()}handleError(error){let response;if(error instanceof HttpException)response=error.toResponse();else if(error instanceof ValidationException)response=error.toResponse();else console.error("Unhandled error:",error),response=INTERNAL_ERROR_RESPONSE;if(this.hasCors&&this.corsHandler)return this.corsHandler.apply(response,"*");return response}executeLifecycleHooks(type){let handlers=getEventHandlers(type);for(let handler of handlers)try{let instance=this.container.has(handler.target)?this.container.get(handler.target):null;if(instance&&typeof instance[handler.methodName]==="function"){let result=instance[handler.methodName]();if(result instanceof Promise)result.catch((err)=>console.error(`Error in ${type} hook ${handler.methodName}:`,err))}}catch(err){console.error(`Error in ${type} hook ${handler.methodName}:`,err)}}registerShutdownHandlers(){let shutdown=()=>{this.executeLifecycleHooks("onShutdown"),this.stop(),process.exit(0)};process.on("SIGTERM",shutdown),process.on("SIGINT",shutdown)}}function createParamDecorator(type,key){return function(target,propertyKey,index){let params=Reflect.getMetadata(PARAMS_META,target.constructor,propertyKey)||[];params.push({type,key,index}),Reflect.defineMetadata(PARAMS_META,params,target.constructor,propertyKey)}}function Param(key){return createParamDecorator("param",key)}function Query(key){return createParamDecorator("query",key)}function Body(key){return createParamDecorator("body",key)}function Header(key){return createParamDecorator("header",key)}function Req(){return createParamDecorator("req")}function Ctx(){return createParamDecorator("ctx")}function Locals(key){return createParamDecorator("locals",key)}function Use(...middlewares){return function(target,propertyKey){let isMethod=propertyKey!==void 0,metaTarget=isMethod?target.constructor:target,existing=Reflect.getMetadata(MIDDLEWARE_META,metaTarget)||[];for(let handler of middlewares)existing.push({handler,target:isMethod?propertyKey:void 0});Reflect.defineMetadata(MIDDLEWARE_META,existing,metaTarget)}}function Service(options={}){return(target)=>{Reflect.defineMetadata(SERVICE_META,{scope:options.scope??"singleton"},target)}}function Inject(token){return(target,propertyKey,parameterIndex)=>{let existing=Reflect.getMetadata(INJECT_META,target)||new Map;existing.set(parameterIndex,token),Reflect.defineMetadata(INJECT_META,existing,target)}}var EMPTY_PARAMS2=Object.freeze({});function createNode(part){return{part,store:null,children:null,paramChild:null,wildcardStore:null}}class RadixRouter{roots={};add(method,path,store){if(path==="")path="/";if(path[0]!=="/")path="/"+path;let isWildcard=path.endsWith("*");if(isWildcard)path=path.slice(0,-1);let node=this.roots[method];if(!node)node=this.roots[method]=createNode("/");let i=0,len=path.length;while(i<len){if(path.charCodeAt(i)===58){let paramStart=i+1,paramEnd=paramStart;while(paramEnd<len&&path.charCodeAt(paramEnd)!==47)paramEnd++;let paramName=path.slice(paramStart,paramEnd);if(!node.paramChild)node.paramChild={name:paramName,store:null,child:null};if(paramEnd>=len){node.paramChild.store=store;return}if(!node.paramChild.child)node.paramChild.child=createNode(path.slice(paramEnd));node=node.paramChild.child,i=paramEnd;continue}let segmentEnd=i;while(segmentEnd<len&&path.charCodeAt(segmentEnd)!==47&&path.charCodeAt(segmentEnd)!==58)segmentEnd++;if(segmentEnd<len&&path.charCodeAt(segmentEnd)===47)segmentEnd++;let segment=path.slice(i,segmentEnd);if(segment===node.part){i=segmentEnd;continue}if(!node.children)node.children=new Map;let firstChar=segment.charCodeAt(0),child=node.children.get(firstChar);if(!child)child=createNode(segment),node.children.set(firstChar,child);node=child,i=segmentEnd}if(isWildcard)node.wildcardStore=store;else node.store=store}find(method,path){let root=this.roots[method];if(!root)return null;return this.matchPath(root,path,0,path.length)}matchPath(node,path,start,len){let partLen=node.part.length,end=start+partLen;if(partLen>1){if(end>len)return null;for(let i=1,j=start+1;i<partLen;i++,j++)if(node.part.charCodeAt(i)!==path.charCodeAt(j))return null}if(end===len){if(node.store!==null)return{store:node.store,params:EMPTY_PARAMS2};if(node.wildcardStore!==null)return{store:node.wildcardStore,params:{"*":""}};return null}if(node.children){let child=node.children.get(path.charCodeAt(end));if(child){let result=this.matchPath(child,path,end,len);if(result)return result}}if(node.paramChild){let param=node.paramChild,paramEnd=end;while(paramEnd<len&&path.charCodeAt(paramEnd)!==47)paramEnd++;if(paramEnd===end)return null;let paramValue=path.slice(end,paramEnd);if(paramEnd>=len){if(param.store!==null)return{store:param.store,params:{[param.name]:paramValue}}}else if(param.child){let result=this.matchPath(param.child,path,paramEnd,len);if(result){if(result.params===EMPTY_PARAMS2)result.params={[param.name]:paramValue};else result.params[param.name]=paramValue;return result}}}if(node.wildcardStore!==null)return{store:node.wildcardStore,params:{"*":path.slice(end)}};return null}}import{brotliCompressSync,constants}from"zlib";var DEFAULT_THRESHOLD=1024,DEFAULT_ENCODINGS=["br","gzip"],DEFAULT_COMPRESSIBLE_TYPES=["text/","application/json","application/javascript","application/xml","application/xhtml+xml","image/svg+xml"],DEFAULT_BROTLI_QUALITY=4,DEFAULT_GZIP_LEVEL=6;class CompressionMiddleware{threshold;compressibleTypes;compressors;encodingOrder;constructor(config){this.threshold=config?.threshold??DEFAULT_THRESHOLD,this.compressibleTypes=(config?.compressibleTypes??DEFAULT_COMPRESSIBLE_TYPES).map((t)=>t.toLowerCase());let encodings=config?.encodings??DEFAULT_ENCODINGS;this.encodingOrder=encodings;let brotliQuality=config?.brotliQuality??DEFAULT_BROTLI_QUALITY,gzipLevel=config?.gzipLevel??DEFAULT_GZIP_LEVEL;this.compressors=new Map;for(let enc of encodings)switch(enc){case"br":this.compressors.set("br",(data)=>{let buf=brotliCompressSync(data,{params:{[constants.BROTLI_PARAM_QUALITY]:brotliQuality}});return new Uint8Array(buf.buffer,buf.byteOffset,buf.byteLength)});break;case"gzip":this.compressors.set("gzip",(data)=>Bun.gzipSync(data,{level:gzipLevel}));break;case"deflate":this.compressors.set("deflate",(data)=>Bun.deflateSync(data));break}}async handle(ctx,next){let response=await next(),acceptEncoding=ctx.req.headers.get("accept-encoding");if(!acceptEncoding)return response;if(response.headers.get("content-encoding"))return response;let contentType=response.headers.get("content-type");if(!contentType||!this.isCompressible(contentType))return response;let encoding=this.negotiateEncoding(acceptEncoding);if(!encoding)return response;let buffer=await response.arrayBuffer();if(buffer.byteLength<this.threshold)return new Response(buffer,{status:response.status,statusText:response.statusText,headers:response.headers});let compressor=this.compressors.get(encoding),bodyBytes=new Uint8Array(buffer),compressed=compressor(bodyBytes);if(compressed.byteLength>=buffer.byteLength)return new Response(buffer,{status:response.status,statusText:response.statusText,headers:response.headers});let headers=new Headers(response.headers);return headers.set("Content-Encoding",encoding),headers.set("Content-Length",String(compressed.byteLength)),headers.set("Vary",this.buildVaryHeader(headers.get("Vary"))),new Response(compressed,{status:response.status,statusText:response.statusText,headers})}isCompressible(contentType){let lower=contentType.toLowerCase();for(let pattern of this.compressibleTypes)if(lower.includes(pattern))return!0;return!1}negotiateEncoding(acceptEncoding){let lower=acceptEncoding.toLowerCase();for(let encoding of this.encodingOrder)if(lower.includes(encoding))return encoding;return null}buildVaryHeader(existing){if(!existing)return"Accept-Encoding";if(existing.toLowerCase().includes("accept-encoding"))return existing;return`${existing}, Accept-Encoding`}}class ValibotAdapter{name="ValibotAdapter";schemaCache=new Map;valibot=null;constructor(){try{this.valibot=__require("valibot")}catch{}}ensureValibot(){if(!this.valibot)this.valibot=__require("valibot");return this.valibot}hasValidation(target){return getSchema(target)!==void 0}validate(target,value){let schema=this.getOrCacheSchema(target);if(!schema)return{success:!0,data:value};let result=this.ensureValibot().safeParse(schema,value);if(result.success)return{success:!0,data:result.output};return{success:!1,errors:this.formatErrors(result.issues)}}validateOrThrow(target,value){let result=this.validate(target,value);if(result.success)return result.data;throw new ValidationException(result.errors)}getOrCacheSchema(target){let schema=this.schemaCache.get(target);if(schema===void 0)schema=getSchema(target)??null,this.schemaCache.set(target,schema);return schema}formatErrors(issues){return issues.map((issue)=>({path:issue.path?.map((p)=>p.key).join(".")||"",message:issue.message}))}}class RedisDriver{config;name="RedisDriver";client=null;connected=!1;constructor(config={}){this.config=config}async ensureConnected(){if(this.connected)return;let url=this.config.url||`redis://${this.config.host||"localhost"}:${this.config.port||6379}`;if(typeof Bun<"u"&&Bun.redis)this.client=new Bun.redis(url);else try{let Redis=require_built3();this.client=new Redis({host:this.config.host||"localhost",port:this.config.port||6379,password:this.config.password,db:this.config.db||0})}catch{throw Error("Redis client not available. Install ioredis or use Bun with Redis support.")}this.connected=!0}async get(key){await this.ensureConnected();let value=await this.client.get(key);if(value===null)return null;try{return JSON.parse(value)}catch{return value}}async set(key,value,ttl){await this.ensureConnected();let serialized=typeof value==="string"?value:JSON.stringify(value);if(ttl)await this.client.setex(key,ttl,serialized);else await this.client.set(key,serialized);return!0}async del(key){return await this.ensureConnected(),await this.client.del(key)>0}async has(key){return await this.ensureConnected(),await this.client.exists(key)>0}async clear(){await this.ensureConnected(),await this.client.flushdb()}async close(){if(this.client&&this.connected)await this.client.quit?.(),this.connected=!1}}async function createTestHarness(options={}){let config={...options.config,disableStartupLog:!0},app=new Carno(config);if(options.plugins)for(let plugin of options.plugins)app.use(plugin);if(options.controllers)app.controllers(options.controllers);if(options.services)app.services(options.services);let port=resolvePort(options),server;if(shouldListen(options.listen))app.listen(port),server=app.server;let actualPort=server?.port??port,container=app.container,baseUrl=`http://127.0.0.1:${actualPort}`,request=async(path,init)=>{if(!server)throw Error("Server not running. Set listen: true in options.");let url=path.startsWith("http")?path:`${baseUrl}${path.startsWith("/")?path:"/"+path}`;return fetch(url,init)};return{app,container,server,port:actualPort,resolve:(token)=>container.get(token),request,get:(path,init)=>request(path,{...init,method:"GET"}),post:(path,body,init)=>request(path,{...init,method:"POST",body:body?JSON.stringify(body):void 0,headers:{"Content-Type":"application/json",...init?.headers}}),put:(path,body,init)=>request(path,{...init,method:"PUT",body:body?JSON.stringify(body):void 0,headers:{"Content-Type":"application/json",...init?.headers}}),delete:(path,init)=>request(path,{...init,method:"DELETE"}),close:async()=>{app.stop()}}}async function withTestApp(routine,options={}){let harness=await createTestHarness(options);try{await routine(harness)}finally{await harness.close()}}function shouldListen(value){return typeof value==="number"||Boolean(value)}function resolvePort(options){if(typeof options.listen==="number")return options.listen;if(typeof options.port==="number")return options.port;return 0}class Metadata{static get(key,target){return Reflect.getMetadata(key,target)}static set(key,value,target){Reflect.defineMetadata(key,value,target)}static has(key,target){return Reflect.hasMetadata(key,target)}static delete(key,target){return Reflect.deleteMetadata(key,target)}static keys(target){return Reflect.getMetadataKeys(target)}static getType(target,propertyKey){return Reflect.getMetadata("design:type",target,propertyKey)}}function isObject(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function isString(value){return typeof value==="string"}export{withTestApp,isString,isObject,getSchema,createTestHarness,ZodAdapter,ValidationException,ValibotAdapter,VALIDATION_SCHEMA,Use,UnprocessableEntityException,UnauthorizedException,TooManyRequestsException,ServiceUnavailableException,Service,Scope,Schema,Req,RedisDriver,RadixRouter,Query,Put,Post,Patch,Param,Options,OnApplicationShutdown,OnApplicationInit,OnApplicationBoot,NotFoundException,Use as Middleware,MethodNotAllowedException,Metadata,MemoryDriver,Locals,InternalServerErrorException,Inject,HttpException,Header,Head,Get,ForbiddenException,EventType,Delete,Ctx,CorsHandler,Controller,Context,Container,ConflictException,CompressionMiddleware,Carno,CacheService,Body,BadRequestException};
180
+ }`;return{fn:Function("h",code)(bound),isAsync:!1,isStatic:!1}}function escapeKey(key){return key.replace(/['\"\\]/g,"\\$&")}function buildArgExpression(param){let key=param.key?escapeKey(param.key):void 0;switch(param.type){case"param":return key?`c.params['${key}']`:"c.params";case"query":return key?`c.query['${key}']`:"c.query";case"body":return key?`c.body['${key}']`:"c.body";case"header":return key?`c.req.headers.get('${key}')`:"c.req.headers";case"req":return"c.req";case"ctx":return"c";case"locals":return key?`c.locals['${key}']`:"c.locals";default:return"undefined"}}function parseQueryFromURL(url){let queryStart=url.indexOf("?");if(queryStart===-1)return Object.create(null);let queryEnd=url.indexOf("#",queryStart);if(queryEnd===-1)queryEnd=url.length;return parseQuery(url,queryStart+1,queryEnd)}function parseQuery(input,startIndex,endIndex){let result=Object.create(null),flags=0,startingIndex=startIndex-1,equalityIndex=startingIndex;for(let i=startIndex;i<endIndex;i++)switch(input.charCodeAt(i)){case 38:processKeyValuePair(i),startingIndex=i,equalityIndex=i,flags=0;break;case 61:if(equalityIndex<=startingIndex)equalityIndex=i;else flags|=8;break;case 43:if(equalityIndex>startingIndex)flags|=4;else flags|=1;break;case 37:if(equalityIndex>startingIndex)flags|=8;else flags|=2;break}if(startingIndex<endIndex)processKeyValuePair(endIndex);return result;function processKeyValuePair(pairEndIndex){let hasBothKeyValuePair=equalityIndex>startingIndex,effectiveEqualityIndex=hasBothKeyValuePair?equalityIndex:pairEndIndex,keySlice=input.slice(startingIndex+1,effectiveEqualityIndex);if(!hasBothKeyValuePair&&keySlice.length===0)return;let finalKey=keySlice;if(flags&1)finalKey=finalKey.replace(/\+/g," ");if(flags&2)try{finalKey=decodeURIComponent(finalKey)}catch{}let finalValue="";if(hasBothKeyValuePair){let valueSlice=input.slice(equalityIndex+1,pairEndIndex);if(flags&4)valueSlice=valueSlice.replace(/\+/g," ");if(flags&8)try{finalValue=decodeURIComponent(valueSlice)}catch{finalValue=valueSlice}else finalValue=valueSlice}result[finalKey]=finalValue}}var EMPTY_PARAMS=Object.freeze({});class Context{req;params;locals={};_query=null;_body;_bodyParsed=!1;_url=null;_status=0;constructor(req,params=EMPTY_PARAMS){this.req=req,this.params=params}get status(){return this._status||200}set status(value){this._status=value}get url(){if(!this._url)this._url=new URL(this.req.url);return this._url}get query(){if(!this._query)this._query=parseQueryFromURL(this.req.url);return this._query}get body(){return this._body}async parseBody(){if(this._bodyParsed)return this._body;this._bodyParsed=!0;let contentType=this.req.headers.get("content-type")||"";if(contentType.includes("application/json"))this._body=await this.req.json();else if(contentType.includes("form")){let formData=await this.req.formData();this._body=Object.fromEntries(formData)}else if(contentType.includes("text"))this._body=await this.req.text();else this._body=await this.req.arrayBuffer();return this._body}get method(){return this.req.method}get headers(){return this.req.headers}get path(){return this.url.pathname}json(data,status){if(status)this.status=status;return Response.json(data,{status:this.status})}text(data,status){if(status)this.status=status;return new Response(data,{status:this.status,headers:{"Content-Type":"text/plain"}})}html(data,status){if(status)this.status=status;return new Response(data,{status:this.status,headers:{"Content-Type":"text/html"}})}redirect(url,status=302){return Response.redirect(url,status)}static createFromJob(job){let fakeRequest=new Request("http://localhost/job"),ctx=new Context(fakeRequest);return ctx.locals.job=job,ctx}}var Scope;((Scope2)=>{Scope2.SINGLETON="singleton";Scope2.REQUEST="request";Scope2.INSTANCE="instance"})(Scope||={});class Container{configs=new Map;instances=new Map;resolving=new Set;register(config){let normalized=this.normalizeConfig(config);if(this.configs.set(normalized.token,normalized),normalized.useValue!==void 0)this.instances.set(normalized.token,normalized.useValue);return this}get(token){let cached=this.instances.get(token);if(cached!==void 0)return cached;return this.resolveInternal(token).instance}has(token){return this.configs.has(token)}resolveInternal(token,requestLocals){if(requestLocals?.has(token))return{instance:requestLocals.get(token),scope:"request"};let cached=this.instances.get(token);if(cached!==void 0)return{instance:cached,scope:"singleton"};let config=this.configs.get(token);if(!config)throw Error(`Provider not found: ${token.name}`);let creation=this.createInstance(config,requestLocals);if(creation.scope==="singleton")this.instances.set(token,creation.instance);else if(creation.scope==="request"&&requestLocals)requestLocals.set(token,creation.instance);return creation}createInstance(config,requestLocals){let target=config.useClass??config.token;if(this.resolving.has(target))throw Error(`Circular dependency detected: ${target.name}`);this.resolving.add(target);try{let depsToken=this.getDependencies(target);if(depsToken.length===0)return{instance:new target,scope:config.scope||"singleton"};let args=[],effectiveScope=config.scope||"singleton";for(let depToken of depsToken){let depResult=this.resolveInternal(depToken,requestLocals);if(args.push(depResult.instance),depResult.scope==="request"&&effectiveScope==="singleton")effectiveScope="request"}return{instance:new target(...args),scope:effectiveScope}}finally{this.resolving.delete(target)}}getDependencies(target){return(Reflect.getMetadata("design:paramtypes",target)||[]).filter((t)=>t&&typeof t==="function"&&!this.isPrimitive(t))}isPrimitive(type){return type===String||type===Number||type===Boolean||type===Object||type===Array||type===Symbol}normalizeConfig(config){if(typeof config==="function")return{token:config,useClass:config,scope:"singleton"};return{...config,useClass:config.useClass??config.token,scope:config.scope??"singleton"}}clear(){this.configs.clear(),this.instances.clear()}}var DEFAULT_CORS_METHODS=["GET","HEAD","PUT","PATCH","POST","DELETE"],DEFAULT_CORS_HEADERS=["Content-Type","Authorization","X-Requested-With","Accept","Origin"];class CorsHandler{cache=new Map;methodsStr;headersStr;exposedStr;maxAgeStr;hasCredentials;isWildcard;matcher;preflightResponse=null;constructor(config){if(this.methodsStr=(config.methods||DEFAULT_CORS_METHODS).join(", "),this.headersStr=(config.allowedHeaders||DEFAULT_CORS_HEADERS).join(", "),this.exposedStr=config.exposedHeaders?.join(", ")||null,this.maxAgeStr=config.maxAge?.toString()||null,this.hasCredentials=!!config.credentials,this.isWildcard=config.origins==="*",this.matcher=this.buildMatcher(config.origins),this.isWildcard)this.preflightResponse=new Response(null,{status:204,headers:this.buildHeaders("*")})}preflight(origin){if(this.isWildcard&&this.preflightResponse)return this.preflightResponse.clone();if(!this.isAllowed(origin))return new Response(null,{status:403});return new Response(null,{status:204,headers:this.getHeaders(origin)})}apply(response,origin){if(!this.isAllowed(origin))return response;let headers=this.getHeaders(origin);for(let[key,value]of Object.entries(headers))response.headers.set(key,value);return response}isAllowed(origin){return this.matcher(origin)}getHeaders(origin){let key=this.isWildcard?"*":origin,headers=this.cache.get(key);if(!headers)headers=this.buildHeaders(origin),this.cache.set(key,headers);return headers}buildHeaders(origin){let headers={"Access-Control-Allow-Origin":this.isWildcard?"*":origin,"Access-Control-Allow-Methods":this.methodsStr,"Access-Control-Allow-Headers":this.headersStr};if(this.hasCredentials)headers["Access-Control-Allow-Credentials"]="true";if(this.exposedStr)headers["Access-Control-Expose-Headers"]=this.exposedStr;if(this.maxAgeStr)headers["Access-Control-Max-Age"]=this.maxAgeStr;return headers}buildMatcher(origins){if(origins==="*")return()=>!0;if(typeof origins==="string")return(o)=>o===origins;if(Array.isArray(origins)){let set=new Set(origins);return(o)=>set.has(o)}if(origins instanceof RegExp)return(o)=>origins.test(o);if(typeof origins==="function")return origins;return()=>!1}}class HttpException extends Error{statusCode;errors;constructor(statusCode,message,errors){super(message);this.statusCode=statusCode;this.errors=errors;this.name="HttpException"}toResponse(){let body={statusCode:this.statusCode,message:this.message,...this.errors&&{errors:this.errors}};return Response.json(body,{status:this.statusCode})}}class BadRequestException extends HttpException{constructor(message="Bad Request",errors){super(400,message,errors);this.name="BadRequestException"}}class UnauthorizedException extends HttpException{constructor(message="Unauthorized"){super(401,message);this.name="UnauthorizedException"}}class ForbiddenException extends HttpException{constructor(message="Forbidden"){super(403,message);this.name="ForbiddenException"}}class NotFoundException extends HttpException{constructor(message="Not Found"){super(404,message);this.name="NotFoundException"}}class MethodNotAllowedException extends HttpException{constructor(message="Method Not Allowed"){super(405,message);this.name="MethodNotAllowedException"}}class ConflictException extends HttpException{constructor(message="Conflict"){super(409,message);this.name="ConflictException"}}class UnprocessableEntityException extends HttpException{constructor(message="Unprocessable Entity",errors){super(422,message,errors);this.name="UnprocessableEntityException"}}class TooManyRequestsException extends HttpException{constructor(message="Too Many Requests"){super(429,message);this.name="TooManyRequestsException"}}class InternalServerErrorException extends HttpException{constructor(message="Internal Server Error"){super(500,message);this.name="InternalServerErrorException"}}class ServiceUnavailableException extends HttpException{constructor(message="Service Unavailable"){super(503,message);this.name="ServiceUnavailableException"}}var VALIDATION_SCHEMA=Symbol("turbo:validation");function Schema(schema){return(target)=>{Reflect.defineMetadata(VALIDATION_SCHEMA,schema,target)}}function getSchema(target){return Reflect.getMetadata(VALIDATION_SCHEMA,target)}class ZodAdapter{name="ZodAdapter";schemaCache=new Map;hasValidation(target){return getSchema(target)!==void 0}validate(target,value){let schema=this.getOrCacheSchema(target);if(!schema)return{success:!0,data:value};let result=schema.safeParse(value);if(result.success)return{success:!0,data:result.data};return{success:!1,errors:this.formatErrors(result.error)}}validateOrThrow(target,value){let schema=this.getOrCacheSchema(target);if(!schema)return value;let result=schema.safeParse(value);if(result.success)return result.data;let errors=this.formatErrors(result.error);throw new ValidationException(errors)}getOrCacheSchema(target){let schema=this.schemaCache.get(target);if(schema===void 0)schema=getSchema(target)??null,this.schemaCache.set(target,schema);return schema}formatErrors(zodError){return zodError.issues.map((issue)=>({path:issue.path.join("."),message:issue.message}))}}class ValidationException extends Error{errors;constructor(errors){super(`Validation failed: ${errors.map((e)=>`${e.path}: ${e.message}`).join(", ")}`);this.errors=errors;this.name="ValidationException"}toResponse(){return Response.json({statusCode:400,message:"Validation failed",errors:this.errors},{status:400})}}var EventType;((EventType2)=>{EventType2.INIT="onInit";EventType2.BOOT="onBoot";EventType2.SHUTDOWN="onShutdown"})(EventType||={});var EVENTS_META=Symbol("turbo:events"),eventRegistry=new Map;function registerEvent(type,target,methodName,priority=0){let handlers=eventRegistry.get(type);if(!handlers)handlers=[],eventRegistry.set(type,handlers);handlers.push({target,methodName,priority})}function getEventHandlers(type){return(eventRegistry.get(type)||[]).sort((a,b)=>b.priority-a.priority)}function hasEventHandlers(type){return(eventRegistry.get(type)?.length??0)>0}function OnApplicationInit(priority=0){return function(target,propertyKey){registerEvent("onInit",target.constructor,propertyKey,priority)}}function OnApplicationBoot(priority=0){return function(target,propertyKey){registerEvent("onBoot",target.constructor,propertyKey,priority)}}function OnApplicationShutdown(priority=0){return function(target,propertyKey){registerEvent("onShutdown",target.constructor,propertyKey,priority)}}class MemoryDriver{name="MemoryDriver";cache=new Map;cleanupInterval=null;constructor(cleanupIntervalMs=0){if(cleanupIntervalMs>0)this.cleanupInterval=setInterval(()=>this.cleanup(),cleanupIntervalMs)}async get(key){let entry=this.cache.get(key);if(!entry)return null;if(entry.expiresAt!==null&&Date.now()>entry.expiresAt)return this.cache.delete(key),null;return entry.value}async set(key,value,ttl){let expiresAt=ttl?Date.now()+ttl:null;return this.cache.set(key,{value,expiresAt}),!0}async del(key){return this.cache.delete(key)}async has(key){let entry=this.cache.get(key);if(!entry)return!1;if(entry.expiresAt!==null&&Date.now()>entry.expiresAt)return this.cache.delete(key),!1;return!0}async clear(){this.cache.clear()}async close(){if(this.cleanupInterval)clearInterval(this.cleanupInterval),this.cleanupInterval=null;this.cache.clear()}cleanup(){let now=Date.now();for(let[key,entry]of this.cache)if(entry.expiresAt!==null&&now>entry.expiresAt)this.cache.delete(key)}stats(){return{size:this.cache.size}}}class CacheService{driver;prefix;defaultTtl;constructor(config={}){this.driver=config.driver||new MemoryDriver,this.prefix=config.prefix||"",this.defaultTtl=config.defaultTtl}key(key){return this.prefix?`${this.prefix}:${key}`:key}async get(key){return this.driver.get(this.key(key))}async set(key,value,ttl){return this.driver.set(this.key(key),value,ttl??this.defaultTtl)}async del(key){return this.driver.del(this.key(key))}async has(key){return this.driver.has(this.key(key))}async clear(){return this.driver.clear()}async getOrSet(key,cb,ttl){let cached=await this.get(key);if(cached!==null)return cached;let value=await cb();return await this.set(key,value,ttl),value}async getMany(keys){return Promise.all(keys.map((key)=>this.get(key)))}async setMany(entries){return Promise.all(entries.map((entry)=>this.set(entry.key,entry.value,entry.ttl)))}async delMany(keys){return Promise.all(keys.map((key)=>this.del(key)))}async close(){await this.driver.close?.()}getDriver(){return this.driver}}function normalizeOptions(pathOrOptions){if(!pathOrOptions)return{};if(typeof pathOrOptions==="string")return{path:pathOrOptions};return pathOrOptions}function normalizePath(path){if(!path)return"";let normalized=path.startsWith("/")?path:"/"+path;if(normalized!=="/"&&normalized.endsWith("/"))normalized=normalized.slice(0,-1);return normalized}function Controller(pathOrOptions){return(target)=>{let options=normalizeOptions(pathOrOptions),meta={path:normalizePath(options.path||""),scope:options.scope,children:options.children};if(Reflect.defineMetadata(CONTROLLER_META,meta,target),!Reflect.hasMetadata(ROUTES_META,target))Reflect.defineMetadata(ROUTES_META,[],target)}}function createMethodDecorator(method){return function(path=""){return function(targetOrMethod,contextOrPropertyKey,descriptor){if(contextOrPropertyKey&&typeof contextOrPropertyKey==="object"&&"kind"in contextOrPropertyKey){let context=contextOrPropertyKey;return context.addInitializer(function(){let constructor2=this.constructor,routes2=Reflect.getMetadata(ROUTES_META,constructor2)||[];routes2.push({method,path:path.startsWith("/")?path:"/"+path,handlerName:String(context.name)}),Reflect.defineMetadata(ROUTES_META,routes2,constructor2)}),targetOrMethod}let constructor=targetOrMethod.constructor,propertyKey=contextOrPropertyKey,routes=Reflect.getMetadata(ROUTES_META,constructor)||[];routes.push({method,path:path.startsWith("/")?path:"/"+path,handlerName:String(propertyKey)}),Reflect.defineMetadata(ROUTES_META,routes,constructor)}}}var Get=createMethodDecorator("get"),Post=createMethodDecorator("post"),Put=createMethodDecorator("put"),Delete=createMethodDecorator("delete"),Patch=createMethodDecorator("patch"),Head=createMethodDecorator("head"),Options=createMethodDecorator("options");class DefaultRoutes{favicon(){return new Response(null,{status:204})}}__legacyDecorateClassTS([Get("/favicon.ico"),__legacyMetadataTS("design:type",Function),__legacyMetadataTS("design:paramtypes",[]),__legacyMetadataTS("design:returntype",void 0)],DefaultRoutes.prototype,"favicon",null),DefaultRoutes=__legacyDecorateClassTS([Controller()],DefaultRoutes);var DEFAULT_STATIC_ROUTES={"/health":new Response('{"status":"ok"}',{status:200,headers:{"Content-Type":"application/json"}}),"/ready":new Response('{"ready":true}',{status:200,headers:{"Content-Type":"application/json"}}),"/favicon.ico":new Response(null,{status:204})};var NOT_FOUND_RESPONSE=new Response("Not Found",{status:404}),TEXT_OPTS=Object.freeze({status:200,headers:{"Content-Type":"text/plain"}}),JSON_OPTS=Object.freeze({status:200,headers:{"Content-Type":"application/json"}}),INTERNAL_ERROR_RESPONSE=new Response('{"statusCode":500,"message":"Internal Server Error"}',{status:500,headers:{"Content-Type":"application/json"}});class Carno{config;_controllers=[];_services=[];_middlewares=[];routes={};container=new Container;corsHandler=null;hasCors=!1;validator=null;server;_wsHandlerBuilder=null;_wsUpgradePaths=new Set;hasInitHooks=!1;hasBootHooks=!1;hasShutdownHooks=!1;constructor(config={}){this.config=config;if(this.config.exports=this.config.exports||[],this.config.globalMiddlewares=this.config.globalMiddlewares||[],this.config.cors)this.corsHandler=new CorsHandler(this.config.cors),this.hasCors=!0;if(this.config.validation===void 0||this.config.validation===!0)this.validator=new ZodAdapter;else if(typeof this.config.validation==="function"){let AdapterClass=this.config.validation;this.validator=new AdapterClass}else if(this.config.validation)this.validator=this.config.validation}use(plugin){if(plugin._controllers.length>0)this._controllers.push(...plugin._controllers);for(let exported of plugin.config.exports||[]){let existingService=this.findServiceInPlugin(plugin,exported),serviceToAdd=this.shouldCloneService(existingService)?{...existingService}:exported;this._services.push(serviceToAdd)}if(plugin._services.length>0)this._services.push(...plugin._services);if(plugin.config.globalMiddlewares)this._middlewares.push(...plugin.config.globalMiddlewares);if(plugin._middlewares.length>0)this._middlewares.push(...plugin._middlewares);for(let[path,methods]of Object.entries(plugin.routes)){if(!this.routes[path])this.routes[path]={};if(typeof methods==="object"&&methods!==null&&!(methods instanceof Response))Object.assign(this.routes[path],methods);else this.routes[path]=methods}if(plugin._wsHandlerBuilder){this._wsHandlerBuilder=plugin._wsHandlerBuilder;for(let path of plugin._wsUpgradePaths)this._wsUpgradePaths.add(path)}return this}wsHandler(builder,upgradePaths){this._wsHandlerBuilder=builder;for(let path of upgradePaths)this._wsUpgradePaths.add(path);return this}getServer(){return this.server}findServiceInPlugin(plugin,exported){return plugin._services.find((s)=>this.getServiceToken(s)===this.getServiceToken(exported))}getServiceToken(service){return service?.token||service}shouldCloneService(service){return!!(service?.useValue!==void 0||service?.useClass)}services(serviceClass){let items=Array.isArray(serviceClass)?serviceClass:[serviceClass];return this._services.push(...items),this}middlewares(handler){let items=Array.isArray(handler)?handler:[handler];return this._middlewares.push(...items),this}controllers(controllerClass){let items=Array.isArray(controllerClass)?controllerClass:[controllerClass];return this._controllers.push(...items),this}route(method,path,handler){let normalizedMethod=method.toUpperCase();if(!this.routes[path])this.routes[path]={};return this.routes[path][normalizedMethod]=handler,this}addRoutes(routes){for(let[path,methods]of Object.entries(routes)){if(!this.routes[path])this.routes[path]={};for(let[method,handler]of Object.entries(methods))this.routes[path][method.toUpperCase()]=handler}return this}get(token){return this.container.get(token)}listen(port=3000){this.bootstrap(),this.compileRoutes();let config={port,fetch:this.handleNotFound.bind(this),error:this.handleError.bind(this),routes:{...DEFAULT_STATIC_ROUTES,...this.routes}};if(this._wsHandlerBuilder&&this._wsUpgradePaths.size>0){config.websocket=this._wsHandlerBuilder(this.container);let upgradePaths=this._wsUpgradePaths,fallback=this.handleNotFound.bind(this);config.fetch=(req,server)=>{let pathname=new URL(req.url).pathname;if(upgradePaths.has(pathname)){if(server.upgrade(req,{data:{id:crypto.randomUUID(),namespace:pathname}}))return;return new Response("WebSocket upgrade failed",{status:400})}return fallback(req)}}if(this.server=Bun.serve(config),this.hasBootHooks)this.executeLifecycleHooks("onBoot");if(this.hasShutdownHooks)this.registerShutdownHandlers();if(!this.config.disableStartupLog)console.log(`Carno running on port ${port}`)}bootstrap(){this.hasInitHooks=hasEventHandlers("onInit"),this.hasBootHooks=hasEventHandlers("onBoot"),this.hasShutdownHooks=hasEventHandlers("onShutdown"),this.container.register({token:Container,useValue:this.container}),this.container.register({token:Carno,useValue:this});let cacheConfig=typeof this.config.cache==="object"?this.config.cache:{};this.container.register({token:CacheService,useValue:new CacheService(cacheConfig)});for(let service of this._services)this.container.register(service);for(let ControllerClass of this._controllers)this.container.register(ControllerClass);if(this.hasInitHooks)this.executeLifecycleHooks("onInit");for(let service of this._services){let token=typeof service==="function"?service:service.token,serviceConfig=typeof service==="function"?null:service;if(!serviceConfig||serviceConfig.scope!=="request")this.container.get(token)}}compileRoutes(){for(let ControllerClass of this._controllers)this.compileController(ControllerClass)}compileController(ControllerClass,parentPath="",inheritedMiddlewares=[]){let meta=Reflect.getMetadata(CONTROLLER_META,ControllerClass)||{path:""},basePath=parentPath+(meta.path||""),routes=Reflect.getMetadata(ROUTES_META,ControllerClass)||[],middlewares=Reflect.getMetadata(MIDDLEWARE_META,ControllerClass)||[],instance=this.container.get(ControllerClass),controllerMiddlewares=middlewares.filter((m)=>!m.target).map((m)=>m.handler),scopedMiddlewares=[...inheritedMiddlewares,...controllerMiddlewares];for(let route of routes){let fullPath=this.normalizePath(basePath+route.path),params=Reflect.getMetadata(PARAMS_META,ControllerClass,route.handlerName)||[],routeMiddlewares=middlewares.filter((m)=>m.target===route.handlerName).map((m)=>m.handler),paramTypes=Reflect.getMetadata("design:paramtypes",ControllerClass.prototype,route.handlerName)||[],bodyDtoType=null;for(let param of params)if(param.type==="body"&&!param.key){let dtoType=paramTypes[param.index];if(dtoType&&this.validator?.hasValidation(dtoType))bodyDtoType=dtoType}let compiled=compileHandler(instance,route.handlerName,params),resolvedMiddlewares=[...this.config.globalMiddlewares||[],...this._middlewares,...scopedMiddlewares,...routeMiddlewares].map((m)=>this.resolveMiddleware(m)),hasMiddlewares=resolvedMiddlewares.length>0,method=route.method.toUpperCase();if(compiled.isStatic&&!hasMiddlewares)this.registerRoute(fullPath,method,this.createStaticResponse(compiled.staticValue));else this.registerRoute(fullPath,method,this.createHandler(compiled,params,resolvedMiddlewares,bodyDtoType))}if(meta.children)for(let ChildController of meta.children){if(!this.container.has(ChildController))this.container.register(ChildController);this.compileController(ChildController,basePath,scopedMiddlewares)}}registerRoute(path,method,handler){if(!this.routes[path])this.routes[path]={};this.routes[path][method]=handler}createStaticResponse(value){let isString=typeof value==="string",body=isString?value:JSON.stringify(value);return new Response(body,isString?TEXT_OPTS:JSON_OPTS)}createHandler(compiled,params,middlewares,bodyDtoType){let handler=compiled.fn,hasMiddlewares=middlewares.length>0,hasParams=params.length>0,applyCors=this.hasCors?this.applyCors.bind(this):null,validator=bodyDtoType?this.validator:null,hasMiddlewaresOrValidation=hasMiddlewares||!!validator;if(!hasMiddlewaresOrValidation&&!hasParams){if(compiled.isAsync)return async(req)=>{let ctx=new Context(req),result=await handler(ctx),response=this.buildResponse(result);return applyCors?applyCors(response,req):response};return(req)=>{let ctx=new Context(req),result=handler(ctx),response=this.buildResponse(result);return applyCors?applyCors(response,req):response}}if(!hasMiddlewaresOrValidation&&hasParams){if(compiled.isAsync)return async(req)=>{let ctx=new Context(req,req.params),result=await handler(ctx),response=this.buildResponse(result);return applyCors?applyCors(response,req):response};return(req)=>{let ctx=new Context(req,req.params),result=handler(ctx),response=this.buildResponse(result);return applyCors?applyCors(response,req):response}}let coreHandler=async(ctx)=>{if(validator&&bodyDtoType)await ctx.parseBody(),validator.validateOrThrow(bodyDtoType,ctx.body);return compiled.isAsync?await handler(ctx):handler(ctx)},chain=this.buildMiddlewareChain(middlewares,coreHandler,this.buildResponse.bind(this));return async(req)=>{let ctx=new Context(req,req.params||{}),response=await chain(ctx);return applyCors?applyCors(response,req):response}}resolveMiddleware(middleware){if(typeof middleware==="function"&&middleware.prototype?.handle)return{kind:"class",instance:this.container.get(middleware)};if(typeof middleware==="object"&&middleware!==null&&"handle"in middleware)return{kind:"class",instance:middleware};return{kind:"function",handler:middleware}}buildMiddlewareChain(middlewares,coreHandler,buildResponseFn){let chain=async(ctx)=>{let result=await coreHandler(ctx);return buildResponseFn(result)};for(let i=middlewares.length-1;i>=0;i--){let mw=middlewares[i],nextLayer=chain;if(mw.kind==="function")chain=async(ctx)=>{let result=await mw.handler(ctx);if(result instanceof Response)return result;return nextLayer(ctx)};else chain=async(ctx)=>{let response,result=await mw.instance.handle(ctx,async()=>{return response=await nextLayer(ctx),response});if(result instanceof Response)return result;return response??new Response(null,{status:200})}}return chain}applyCors(response,req){let origin=req.headers.get("origin");if(origin&&this.corsHandler)return this.corsHandler.apply(response,origin);return response}handleNotFound(req){if(this.hasCors&&req.method==="OPTIONS"){let origin=req.headers.get("origin");if(origin)return this.corsHandler.preflight(origin)}return NOT_FOUND_RESPONSE}buildResponse(result){if(result instanceof Response)return result;if(typeof result==="string")return new Response(result,TEXT_OPTS);if(result===void 0)return new Response(null,{status:204});return Response.json(result)}normalizePath(path){if(!path.startsWith("/"))path="/"+path;if(path!=="/"&&path.endsWith("/"))path=path.slice(0,-1);return path.replace(/\/+/g,"/")}hasParams(path){return path.includes(":")||path.includes("*")}stop(){this.server?.stop?.()}handleError(error){let response;if(error instanceof HttpException)response=error.toResponse();else if(error instanceof ValidationException)response=error.toResponse();else console.error("Unhandled error:",error),response=INTERNAL_ERROR_RESPONSE;if(this.hasCors&&this.corsHandler)return this.corsHandler.apply(response,"*");return response}executeLifecycleHooks(type){let handlers=getEventHandlers(type);for(let handler of handlers)try{let instance=this.container.has(handler.target)?this.container.get(handler.target):null;if(instance&&typeof instance[handler.methodName]==="function"){let result=instance[handler.methodName]();if(result instanceof Promise)result.catch((err)=>console.error(`Error in ${type} hook ${handler.methodName}:`,err))}}catch(err){console.error(`Error in ${type} hook ${handler.methodName}:`,err)}}registerShutdownHandlers(){let shutdown=()=>{this.executeLifecycleHooks("onShutdown"),this.stop(),process.exit(0)};process.on("SIGTERM",shutdown),process.on("SIGINT",shutdown)}}function createParamDecorator(type,key){return function(target,propertyKey,index){let params=Reflect.getMetadata(PARAMS_META,target.constructor,propertyKey)||[];params.push({type,key,index}),Reflect.defineMetadata(PARAMS_META,params,target.constructor,propertyKey)}}function Param(key){return createParamDecorator("param",key)}function Query(key){return createParamDecorator("query",key)}function Body(key){return createParamDecorator("body",key)}function Header(key){return createParamDecorator("header",key)}function Req(){return createParamDecorator("req")}function Ctx(){return createParamDecorator("ctx")}function Locals(key){return createParamDecorator("locals",key)}function Use(...middlewares){return function(target,propertyKey){let isMethod=propertyKey!==void 0,metaTarget=isMethod?target.constructor:target,existing=Reflect.getMetadata(MIDDLEWARE_META,metaTarget)||[];for(let handler of middlewares)existing.push({handler,target:isMethod?propertyKey:void 0});Reflect.defineMetadata(MIDDLEWARE_META,existing,metaTarget)}}function Service(options={}){return(target)=>{Reflect.defineMetadata(SERVICE_META,{scope:options.scope??"singleton"},target)}}function Inject(token){return(target,propertyKey,parameterIndex)=>{let existing=Reflect.getMetadata(INJECT_META,target)||new Map;existing.set(parameterIndex,token),Reflect.defineMetadata(INJECT_META,existing,target)}}var EMPTY_PARAMS2=Object.freeze({});function createNode(part){return{part,store:null,children:null,paramChild:null,wildcardStore:null}}class RadixRouter{roots={};add(method,path,store){if(path==="")path="/";if(path[0]!=="/")path="/"+path;let isWildcard=path.endsWith("*");if(isWildcard)path=path.slice(0,-1);let node=this.roots[method];if(!node)node=this.roots[method]=createNode("/");let i=0,len=path.length;while(i<len){if(path.charCodeAt(i)===58){let paramStart=i+1,paramEnd=paramStart;while(paramEnd<len&&path.charCodeAt(paramEnd)!==47)paramEnd++;let paramName=path.slice(paramStart,paramEnd);if(!node.paramChild)node.paramChild={name:paramName,store:null,child:null};if(paramEnd>=len){node.paramChild.store=store;return}if(!node.paramChild.child)node.paramChild.child=createNode(path.slice(paramEnd));node=node.paramChild.child,i=paramEnd;continue}let segmentEnd=i;while(segmentEnd<len&&path.charCodeAt(segmentEnd)!==47&&path.charCodeAt(segmentEnd)!==58)segmentEnd++;if(segmentEnd<len&&path.charCodeAt(segmentEnd)===47)segmentEnd++;let segment=path.slice(i,segmentEnd);if(segment===node.part){i=segmentEnd;continue}if(!node.children)node.children=new Map;let firstChar=segment.charCodeAt(0),child=node.children.get(firstChar);if(!child)child=createNode(segment),node.children.set(firstChar,child);node=child,i=segmentEnd}if(isWildcard)node.wildcardStore=store;else node.store=store}find(method,path){let root=this.roots[method];if(!root)return null;return this.matchPath(root,path,0,path.length)}matchPath(node,path,start,len){let partLen=node.part.length,end=start+partLen;if(partLen>1){if(end>len)return null;for(let i=1,j=start+1;i<partLen;i++,j++)if(node.part.charCodeAt(i)!==path.charCodeAt(j))return null}if(end===len){if(node.store!==null)return{store:node.store,params:EMPTY_PARAMS2};if(node.wildcardStore!==null)return{store:node.wildcardStore,params:{"*":""}};return null}if(node.children){let child=node.children.get(path.charCodeAt(end));if(child){let result=this.matchPath(child,path,end,len);if(result)return result}}if(node.paramChild){let param=node.paramChild,paramEnd=end;while(paramEnd<len&&path.charCodeAt(paramEnd)!==47)paramEnd++;if(paramEnd===end)return null;let paramValue=path.slice(end,paramEnd);if(paramEnd>=len){if(param.store!==null)return{store:param.store,params:{[param.name]:paramValue}}}else if(param.child){let result=this.matchPath(param.child,path,paramEnd,len);if(result){if(result.params===EMPTY_PARAMS2)result.params={[param.name]:paramValue};else result.params[param.name]=paramValue;return result}}}if(node.wildcardStore!==null)return{store:node.wildcardStore,params:{"*":path.slice(end)}};return null}}import{brotliCompressSync,constants}from"zlib";var DEFAULT_THRESHOLD=1024,DEFAULT_ENCODINGS=["br","gzip"],DEFAULT_COMPRESSIBLE_TYPES=["text/","application/json","application/javascript","application/xml","application/xhtml+xml","image/svg+xml"],DEFAULT_BROTLI_QUALITY=4,DEFAULT_GZIP_LEVEL=6;class CompressionMiddleware{threshold;compressibleTypes;compressors;encodingOrder;constructor(config){this.threshold=config?.threshold??DEFAULT_THRESHOLD,this.compressibleTypes=(config?.compressibleTypes??DEFAULT_COMPRESSIBLE_TYPES).map((t)=>t.toLowerCase());let encodings=config?.encodings??DEFAULT_ENCODINGS;this.encodingOrder=encodings;let brotliQuality=config?.brotliQuality??DEFAULT_BROTLI_QUALITY,gzipLevel=config?.gzipLevel??DEFAULT_GZIP_LEVEL;this.compressors=new Map;for(let enc of encodings)switch(enc){case"br":this.compressors.set("br",(data)=>{let buf=brotliCompressSync(data,{params:{[constants.BROTLI_PARAM_QUALITY]:brotliQuality}});return new Uint8Array(buf.buffer,buf.byteOffset,buf.byteLength)});break;case"gzip":this.compressors.set("gzip",(data)=>Bun.gzipSync(data,{level:gzipLevel}));break;case"deflate":this.compressors.set("deflate",(data)=>Bun.deflateSync(data));break}}async handle(ctx,next){let response=await next(),acceptEncoding=ctx.req.headers.get("accept-encoding");if(!acceptEncoding)return response;if(response.headers.get("content-encoding"))return response;let contentType=response.headers.get("content-type");if(!contentType||!this.isCompressible(contentType))return response;let encoding=this.negotiateEncoding(acceptEncoding);if(!encoding)return response;let buffer=await response.arrayBuffer();if(buffer.byteLength<this.threshold)return new Response(buffer,{status:response.status,statusText:response.statusText,headers:response.headers});let compressor=this.compressors.get(encoding),bodyBytes=new Uint8Array(buffer),compressed=compressor(bodyBytes);if(compressed.byteLength>=buffer.byteLength)return new Response(buffer,{status:response.status,statusText:response.statusText,headers:response.headers});let headers=new Headers(response.headers);return headers.set("Content-Encoding",encoding),headers.set("Content-Length",String(compressed.byteLength)),headers.set("Vary",this.buildVaryHeader(headers.get("Vary"))),new Response(compressed,{status:response.status,statusText:response.statusText,headers})}isCompressible(contentType){let lower=contentType.toLowerCase();for(let pattern of this.compressibleTypes)if(lower.includes(pattern))return!0;return!1}negotiateEncoding(acceptEncoding){let lower=acceptEncoding.toLowerCase();for(let encoding of this.encodingOrder)if(lower.includes(encoding))return encoding;return null}buildVaryHeader(existing){if(!existing)return"Accept-Encoding";if(existing.toLowerCase().includes("accept-encoding"))return existing;return`${existing}, Accept-Encoding`}}class ValibotAdapter{name="ValibotAdapter";schemaCache=new Map;valibot=null;constructor(){try{this.valibot=__require("valibot")}catch{}}ensureValibot(){if(!this.valibot)this.valibot=__require("valibot");return this.valibot}hasValidation(target){return getSchema(target)!==void 0}validate(target,value){let schema=this.getOrCacheSchema(target);if(!schema)return{success:!0,data:value};let result=this.ensureValibot().safeParse(schema,value);if(result.success)return{success:!0,data:result.output};return{success:!1,errors:this.formatErrors(result.issues)}}validateOrThrow(target,value){let result=this.validate(target,value);if(result.success)return result.data;throw new ValidationException(result.errors)}getOrCacheSchema(target){let schema=this.schemaCache.get(target);if(schema===void 0)schema=getSchema(target)??null,this.schemaCache.set(target,schema);return schema}formatErrors(issues){return issues.map((issue)=>({path:issue.path?.map((p)=>p.key).join(".")||"",message:issue.message}))}}class RedisDriver{config;name="RedisDriver";client=null;connected=!1;constructor(config={}){this.config=config}async ensureConnected(){if(this.connected)return;let url=this.config.url||`redis://${this.config.host||"localhost"}:${this.config.port||6379}`;if(typeof Bun<"u"&&Bun.redis)this.client=new Bun.redis(url);else try{let Redis=require_built3();this.client=new Redis({host:this.config.host||"localhost",port:this.config.port||6379,password:this.config.password,db:this.config.db||0})}catch{throw Error("Redis client not available. Install ioredis or use Bun with Redis support.")}this.connected=!0}async get(key){await this.ensureConnected();let value=await this.client.get(key);if(value===null)return null;try{return JSON.parse(value)}catch{return value}}async set(key,value,ttl){await this.ensureConnected();let serialized=typeof value==="string"?value:JSON.stringify(value);if(ttl)await this.client.setex(key,ttl,serialized);else await this.client.set(key,serialized);return!0}async del(key){return await this.ensureConnected(),await this.client.del(key)>0}async has(key){return await this.ensureConnected(),await this.client.exists(key)>0}async clear(){await this.ensureConnected(),await this.client.flushdb()}async close(){if(this.client&&this.connected)await this.client.quit?.(),this.connected=!1}}async function createTestHarness(options={}){let config={...options.config,disableStartupLog:!0},app=new Carno(config);if(options.plugins)for(let plugin of options.plugins)app.use(plugin);if(options.controllers)app.controllers(options.controllers);if(options.services)app.services(options.services);let port=resolvePort(options),server;if(shouldListen(options.listen))app.listen(port),server=app.server;let actualPort=server?.port??port,container=app.container,baseUrl=`http://127.0.0.1:${actualPort}`,request=async(path,init)=>{if(!server)throw Error("Server not running. Set listen: true in options.");let url=path.startsWith("http")?path:`${baseUrl}${path.startsWith("/")?path:"/"+path}`;return fetch(url,init)};return{app,container,server,port:actualPort,resolve:(token)=>container.get(token),request,get:(path,init)=>request(path,{...init,method:"GET"}),post:(path,body,init)=>request(path,{...init,method:"POST",body:body?JSON.stringify(body):void 0,headers:{"Content-Type":"application/json",...init?.headers}}),put:(path,body,init)=>request(path,{...init,method:"PUT",body:body?JSON.stringify(body):void 0,headers:{"Content-Type":"application/json",...init?.headers}}),delete:(path,init)=>request(path,{...init,method:"DELETE"}),close:async()=>{app.stop()}}}async function withTestApp(routine,options={}){let harness=await createTestHarness(options);try{await routine(harness)}finally{await harness.close()}}function shouldListen(value){return typeof value==="number"||Boolean(value)}function resolvePort(options){if(typeof options.listen==="number")return options.listen;if(typeof options.port==="number")return options.port;return 0}class Metadata{static get(key,target){return Reflect.getMetadata(key,target)}static set(key,value,target){Reflect.defineMetadata(key,value,target)}static has(key,target){return Reflect.hasMetadata(key,target)}static delete(key,target){return Reflect.deleteMetadata(key,target)}static keys(target){return Reflect.getMetadataKeys(target)}static getType(target,propertyKey){return Reflect.getMetadata("design:type",target,propertyKey)}}function isObject(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function isString(value){return typeof value==="string"}export{withTestApp,isString,isObject,getSchema,createTestHarness,ZodAdapter,ValidationException,ValibotAdapter,VALIDATION_SCHEMA,Use,UnprocessableEntityException,UnauthorizedException,TooManyRequestsException,ServiceUnavailableException,Service,Scope,Schema,Req,RedisDriver,RadixRouter,Query,Put,Post,Patch,Param,Options,OnApplicationShutdown,OnApplicationInit,OnApplicationBoot,NotFoundException,Use as Middleware,MethodNotAllowedException,Metadata,MemoryDriver,Locals,InternalServerErrorException,Inject,HttpException,Header,Head,Get,ForbiddenException,EventType,Delete,Ctx,CorsHandler,Controller,Context,Container,ConflictException,CompressionMiddleware,Carno,CacheService,Body,BadRequestException};
181
181
 
182
- //# debugId=767E88F5346E809564756E2164756E21
182
+ //# debugId=98409ACCA4BC4E0864756E2164756E21
183
183
  //# sourceMappingURL=index.js.map