elysia 0.3.1 → 0.3.2

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/index.js CHANGED
@@ -1 +1 @@
1
- import{nanoid as e}from"nanoid";import{Raikiri as t}from"raikiri";import{parse as r}from"fast-querystring";import{mapResponse as s,mapEarlyResponse as a}from"./handler";import{permission as n}from"./fn";import{SCHEMA as i,EXPOSED as o,DEFS as h,clone as l,mergeHook as u,mergeDeep as d,createValidationError as f,getSchemaValidator as m,getResponseSchemaValidator as c,mapPathnameAndQueryRegEx as p}from"./utils";import{registerSchemaPath as v}from"./schema";import{mapErrorCode as g,mapErrorStatus as y}from"./error";import{runFn as b}from"./fn";import{deserialize as w}from"superjson";export default class H{store={};meta={[i]:Object.create(null),[h]:Object.create(null),[o]:Object.create(null)};decorators={[i]:this.meta[i],[h]:this.meta[h],store:this.store};event={start:[],request:[],parse:[],transform:[],beforeHandle:[],afterHandle:[],error:[],stop:[]};server=null;$schema=null;router=new t;routes=[];lazyLoadModules=[];constructor(e){this.config={fn:"/~fn",...e}}_addHandler(e,t,r,s){t=t.startsWith("/")?t:`/${t}`,this.routes.push({method:e,path:t,handler:r,hooks:u(l(this.event),s)});let a=this.meta[h],n=m(s?.schema?.body??this.$schema?.body,a),o=m(s?.schema?.headers??this.$schema?.headers,a,!0),d=m(s?.schema?.params??this.$schema?.params,a),f=m(s?.schema?.query??this.$schema?.query,a),p=c(s?.schema?.response??this.$schema?.response,a);v({schema:this.meta[i],contentType:s?.schema?.contentType,hook:s,method:e,path:t,models:this.meta[h]});let g=u(l(this.event),s);g.schema||g.transform.length||g.beforeHandle.length||g.error.length||g.afterHandle.length||(g=void 0);let y={handle:r,hooks:g,validator:n||o||d||f||p?{body:n,header:o,params:d,query:f,response:p}:void 0};this.router.add(e,t,y)}onStart(e){return this.event.start.push(e),this}onRequest(e){return this.event.request.push(e),this}onParse(e){return this.event.parse.splice(this.event.parse.length-1,0,e),this}onTransform(e){return this.event.transform.push(e),this}onBeforeHandle(e){return this.event.beforeHandle.push(e),this}onAfterHandle(e){return this.event.afterHandle.push(e),this}onError(e){return this.event.error.push(e),this}onStop(e){return this.event.stop.push(e),this}on(e,t){switch(e){case"start":this.event.start.push(t);break;case"request":this.event.request.push(t);break;case"parse":this.event.parse.push(t);break;case"transform":this.event.transform.push(t);break;case"beforeHandle":this.event.beforeHandle.push(t);break;case"afterHandle":this.event.afterHandle.push(t);break;case"error":this.event.error.push(t);break;case"stop":this.event.stop.push(t)}return this}group(e,t){let r=new H;r.store=this.store;let s=t(r);return s.event.request.length&&(this.event.request=[...this.event.request,...s.event.request]),this.setModel(s.meta[h]),Object.values(r.routes).forEach(({method:t,path:r,handler:a,hooks:n})=>{"/"===r?this._addHandler(t,e,a,u(n,{error:s.event.error})):this._addHandler(t,`${e}${r}`,a,u(n,{error:s.event.error}))}),this}guard(e,t){let r=new H;r.store=this.store;let s=t(r);return s.event.request.length&&(this.event.request=[...this.event.request,...s.event.request]),this.setModel(s.meta[h]),Object.values(r.routes).forEach(({method:t,path:r,handler:s,hooks:a})=>{this._addHandler(t,r,s,u(e,a))}),this}use(e){if(e instanceof Promise)return this.lazyLoadModules.push(e.then(e=>"function"==typeof e?e(this):e.default(this))),this;let t=e(this);return t instanceof Promise?(this.lazyLoadModules.push(t),this):t}get(e,t,r){return this._addHandler("GET",e,t,r),this}post(e,t,r){return this._addHandler("POST",e,t,r),this}put(e,t,r){return this._addHandler("PUT",e,t,r),this}patch(e,t,r){return this._addHandler("PATCH",e,t,r),this}delete(e,t,r){return this._addHandler("DELETE",e,t,r),this}options(e,t,r){return this._addHandler("OPTIONS",e,t,r),this}all(e,t,r){return this._addHandler("ALL",e,t,r),this}head(e,t,r){return this._addHandler("HEAD",e,t,r),this}trace(e,t,r){return this._addHandler("TRACE",e,t,r),this}connect(e,t,r){return this._addHandler("CONNECT",e,t,r),this}ws(t,r){if(!this.wsRouter)throw Error("Can't find WebSocket. Please register WebSocket plugin first by importing 'elysia/ws'");return this.wsRouter.add("subscribe",t,r),this.get(t,t=>{if(!this.server.upgrade(t.request,{headers:"function"==typeof r.headers?r.headers(t):r.headers,data:{...t,id:e(),message:m(r.schema?.body,this.meta[h]),transformMessage:r.transform?Array.isArray(r.transformMessage)?r.transformMessage:[r.transformMessage]:[]}}))return t.set.status=400,"Expected a websocket connection"},{beforeHandle:r.beforeHandle,transform:r.transform,schema:{headers:r.schema?.headers,params:r.schema?.params,query:r.schema?.query}}),this}route(e,t,r,s){return this._addHandler(e,t,r,s),this}state(e,t){return e in this.store||(this.store[e]=t),this}decorate(e,t){return e in this.decorators||(this.decorators[e]=t),this}derive(e){return"AsyncFunction"===e.constructor.name?this.onTransform(async t=>{Object.assign(t,await e(t))}):this.onTransform(t=>{Object.assign(t,e(t))})}fn(e){return 0===Object.keys(this.meta[o]).length&&this.post(this.config.fn??"/~fn",async e=>b(e,this.meta[o])),this.meta[o]=d(this.meta[o],"function"==typeof e?e({...this.decorators,store:this.store,permission:n}):e),this}schema(e){let t=this.meta[h];return this.$schema={body:m(e.body,t),headers:m(e?.headers,t,!0),params:m(e?.params,t),query:m(e?.query,t),response:m(e?.response,t)},this}handle=async e=>{let t;let n=this.decorators;n.request=e,n.set={status:200,headers:{}};try{let i;for(let e=0;e<this.event.request.length;e++){let t=this.event.request[e](n);if(t instanceof Promise&&(t=await t),t=a(t,n.set))return t}let o=e.url.match(p);if(!o)throw Error("NOT_FOUND");let h=this.router.match(e.method,o[1])??this.router.match("ALL",o[1]);if(!h?.store)throw Error("NOT_FOUND");let{hooks:l,validator:u}=h.store;if(l?.error&&(t=l?.error),"GET"!==e.method){let t=e.headers.get("content-type");if(t){let s=t.indexOf(";");-1!==s&&(t=t.slice(0,s));for(let e=0;e<this.event.parse.length;e++){let r=this.event.parse[e](n,t);if(r instanceof Promise&&(r=await r),void 0!==r){i=r;break}}if(void 0===i)switch(t){case"application/json":i=await e.json();break;case"text/plain":i=await e.text();break;case"application/x-www-form-urlencoded":i=r(await e.text());break;case"multipart/form-data":i={},await e.formData().then(e=>{for(let t of e.keys()){if(t in i)continue;let r=e.getAll(t);1===r.length?i[t]=r[0]:i[t]=r}});break;case"elysia/fn":i=w(await e.json())}}}if(n.body=i,n.params=h.params,o[2]?n.query=r(o[2]):n.query={},l)for(let e=0;e<l.transform.length;e++){let t=l.transform[e](n);t instanceof Promise&&await t}if(u){if(l?.error&&(t=l?.error),u.headers){let t={};for(let r in e.headers)t[r]=e.headers.get(r);if(!1===u.headers.Check(t))throw f("header",u.headers,t)}if(!1===u.params?.Check(n.params))throw f("params",u.params,n.params);if(!1===u.query?.Check(n.query))throw f("query",u.query,n.query);if(!1===u.body?.Check(i))throw f("body",u.body,i)}if(l)for(let e=0;e<l.beforeHandle.length;e++){let t=l.beforeHandle[e](n);if(t instanceof Promise&&(t=await t),null!=t){for(let e=0;e<l.afterHandle.length;e++){let r=l.afterHandle[e](n,t);r instanceof Promise&&(r=await r),r&&(t=r)}let e=a(t,n.set);if(e)return e}}let d=h.store.handle(n);if(d instanceof Promise&&(d=await d),!1===u?.response?.Check(d))throw l?.error&&(t=l.error),f("response",u.response,d);if(l)for(let e=0;e<l.afterHandle.length;e++){let t=l.afterHandle[e](n,d);t instanceof Promise&&(t=await t);let r=a(t,n.set);if(r)return r}return s(d,n.set)}catch(s){let r=n.set;if((!r.status||r.status<300)&&(r.status=500),t){let n=g(s.message);for(let i=0;i<t.length;i++){let o=t[i]({request:e,error:s,set:r,code:n});o instanceof Promise&&(o=await o);let h=a(o,r);if(h)return h}}return this.handleError(e,s,r)}};async handleError(e,t,r={headers:{}}){for(let a=0;a<this.event.error.length;a++){let n=this.event.error[a]({request:e,code:g(t.message),error:t,set:r});if(n instanceof Promise&&(n=await n),null!=n)return s(n,r)}return new Response("string"==typeof t.cause?t.cause:t.message,{headers:r.headers,status:y(g(t.message))})}listen=(e,t)=>{if(!Bun)throw Error("Bun to run");if("string"==typeof e&&Number.isNaN(e=+e))throw Error("Port must be a numeric value");let r=this.handle,s="object"==typeof e?{...this.config.serve,...e,fetch:r}:{...this.config.serve,port:e,fetch:r},a=`$$Elysia:${s.port}`;globalThis[a]?(this.server=globalThis[a],this.server.reload(s)):globalThis[a]=this.server=Bun.serve(s);for(let e=0;e<this.event.start.length;e++)this.event.start[e](this);return t&&t(this.server),Promise.all(this.lazyLoadModules).then(()=>{this.server.pendingRequests||Bun.gc(!0)}),this};stop=async()=>{if(!this.server)throw Error("Elysia isn't running. Call `app.listen` to start the server.");this.server.stop();for(let e=0;e<this.event.stop.length;e++)await this.event.stop[e](this)};get modules(){return Promise.all(this.lazyLoadModules)}setModel(e){return Object.entries(e).forEach(([e,t])=>{e in this.meta[h]||(this.meta[h][e]=t)}),this}}export{t}from"./custom-types";export{ws}from"./ws";export{SCHEMA,DEFS,EXPOSED,createValidationError,getSchemaValidator,mergeDeep,mergeHook,mergeObjectArray,mapPathnameAndQueryRegEx}from"./utils";export{H as Elysia,n as permission};
1
+ import{nanoid as e}from"nanoid";import{Raikiri as t}from"raikiri";import{parse as r}from"fast-querystring";import{mapResponse as s,mapEarlyResponse as a}from"./handler";import{permission as o}from"./fn";import{SCHEMA as i,EXPOSED as n,DEFS as h,clone as l,mergeHook as u,mergeDeep as d,createValidationError as f,getSchemaValidator as m,getResponseSchemaValidator as c,mapPathnameAndQueryRegEx as p}from"./utils";import{registerSchemaPath as g}from"./schema";import{mapErrorCode as v,mapErrorStatus as w}from"./error";import{runFn as y}from"./fn";import{deserialize as b}from"superjson";import{ws as H}from"./ws";export default class q{store={};meta={[i]:Object.create(null),[h]:Object.create(null),[n]:Object.create(null)};decorators={[i]:this.meta[i],[h]:this.meta[h],store:this.store};event={start:[],request:[],parse:[],transform:[],beforeHandle:[],afterHandle:[],error:[],stop:[]};server=null;$schema=null;router=new t;routes=[];lazyLoadModules=[];constructor(e){this.config={fn:"/~fn",...e}}_addHandler(e,t,r,s){t=t.startsWith("/")?t:`/${t}`,this.routes.push({method:e,path:t,handler:r,hooks:u({...this.event},s)});let a=this.meta[h],o=m(s?.schema?.body??this.$schema?.body,a),n=m(s?.schema?.headers??this.$schema?.headers,a,!0),d=m(s?.schema?.params??this.$schema?.params,a),f=m(s?.schema?.query??this.$schema?.query,a),p=c(s?.schema?.response??this.$schema?.response,a);g({schema:this.meta[i],contentType:s?.schema?.contentType,hook:s,method:e,path:t,models:this.meta[h]});let v=u(l(this.event),s);v.schema||v.transform.length||v.beforeHandle.length||v.error.length||v.afterHandle.length||(v=void 0);let w={handle:r,hooks:v,validator:o||n||d||f||p?{body:o,header:n,params:d,query:f,response:p}:void 0};this.router.add(e,t,w)}onStart(e){return this.event.start.push(e),this}onRequest(e){return this.event.request.push(e),this}onParse(e){return this.event.parse.splice(this.event.parse.length-1,0,e),this}onTransform(e){return this.event.transform.push(e),this}onBeforeHandle(e){return this.event.beforeHandle.push(e),this}onAfterHandle(e){return this.event.afterHandle.push(e),this}onError(e){return this.event.error.push(e),this}onStop(e){return this.event.stop.push(e),this}on(e,t){switch(e){case"start":this.event.start.push(t);break;case"request":this.event.request.push(t);break;case"parse":this.event.parse.push(t);break;case"transform":this.event.transform.push(t);break;case"beforeHandle":this.event.beforeHandle.push(t);break;case"afterHandle":this.event.afterHandle.push(t);break;case"error":this.event.error.push(t);break;case"stop":this.event.stop.push(t)}return this}group(e,t){let r=new q;r.store=this.store,this.wsRouter&&r.use(H());let s=t(r);return s.event.request.length&&(this.event.request=[...this.event.request,...s.event.request]),this.setModel(s.meta[h]),Object.values(r.routes).forEach(({method:t,path:a,handler:o,hooks:i})=>{let n="/"===a?e:`${e}${a}`,h=r.wsRouter?.match("subscribe",n);if(h){let e=r.wsRouter.history.find(([e,t])=>a===t);if(!e)return;return this.ws(n,e[2])}this._addHandler(t,n,o,u(i,{error:s.event.error}))}),r.wsRouter&&this.wsRouter&&r.wsRouter.history.forEach(([t,r,s])=>{"/"===r?this.wsRouter?.add(t,e,s):this.wsRouter?.add(t,`${e}${r}`,s)}),this}guard(e,t){let r=new q;r.store=this.store,this.wsRouter&&r.use(H());let s=t(r);return s.event.request.length&&(this.event.request=[...this.event.request,...s.event.request]),this.setModel(s.meta[h]),Object.values(r.routes).forEach(({method:t,path:s,handler:a,hooks:o})=>{let i=r.wsRouter?.match("subscribe",s);if(i){let e=r.wsRouter.history.find(([e,t])=>s===t);if(!e)return;return this.ws(s,e[2])}this._addHandler(t,s,a,u(e,o))}),r.wsRouter&&this.wsRouter&&r.wsRouter.history.forEach(([e,t,r])=>{this.wsRouter?.add(e,t,r)}),this}use(e){if(e instanceof Promise)return this.lazyLoadModules.push(e.then(e=>"function"==typeof e?e(this):e.default(this))),this;let t=e(this);return t instanceof Promise?(this.lazyLoadModules.push(t),this):t}get(e,t,r){return this._addHandler("GET",e,t,r),this}post(e,t,r){return this._addHandler("POST",e,t,r),this}put(e,t,r){return this._addHandler("PUT",e,t,r),this}patch(e,t,r){return this._addHandler("PATCH",e,t,r),this}delete(e,t,r){return this._addHandler("DELETE",e,t,r),this}options(e,t,r){return this._addHandler("OPTIONS",e,t,r),this}all(e,t,r){return this._addHandler("ALL",e,t,r),this}head(e,t,r){return this._addHandler("HEAD",e,t,r),this}trace(e,t,r){return this._addHandler("TRACE",e,t,r),this}connect(e,t,r){return this._addHandler("CONNECT",e,t,r),this}ws(t,r){if(!this.wsRouter)throw Error("Can't find WebSocket. Please register WebSocket plugin first by importing 'elysia/ws'");return this.wsRouter.add("subscribe",t,r),this.get(t,t=>{if(console.log("Got",t.request.url),!this.server?.upgrade(t.request,{headers:"function"==typeof r.headers?r.headers(t):r.headers,data:{...t,id:e(),message:m(r.schema?.body,this.meta[h]),transformMessage:r.transform?Array.isArray(r.transformMessage)?r.transformMessage:[r.transformMessage]:[]}}))return t.set.status=400,"Expected a websocket connection"},{beforeHandle:r.beforeHandle,transform:r.transform,schema:{headers:r.schema?.headers,params:r.schema?.params,query:r.schema?.query}}),this}route(e,t,r,s){return this._addHandler(e,t,r,s),this}state(e,t){return e in this.store||(this.store[e]=t),this}decorate(e,t){return e in this.decorators||(this.decorators[e]=t),this}derive(e){return"AsyncFunction"===e.constructor.name?this.onTransform(async t=>{Object.assign(t,await e(t))}):this.onTransform(t=>{Object.assign(t,e(t))})}fn(e){return 0===Object.keys(this.meta[n]).length&&this.post(this.config.fn??"/~fn",async e=>y(e,this.meta[n])),this.meta[n]=d(this.meta[n],"function"==typeof e?e({...this.decorators,store:this.store,permission:o}):e),this}schema(e){let t=this.meta[h];return this.$schema={body:m(e.body,t),headers:m(e?.headers,t,!0),params:m(e?.params,t),query:m(e?.query,t),response:m(e?.response,t)},this}handle=async e=>{let t;let o=this.decorators;o.request=e,o.set={status:200,headers:{}};try{let i;for(let e=0;e<this.event.request.length;e++){let t=this.event.request[e](o);if(t instanceof Promise&&(t=await t),t=a(t,o.set))return t}let n=e.url.match(p);if(!n)throw Error("NOT_FOUND");let h=this.router.match(e.method,n[1])??this.router.match("ALL",n[1]);if(!h?.store)throw Error("NOT_FOUND");let{hooks:l,validator:u}=h.store;if(l?.error&&(t=l?.error),"GET"!==e.method){let t=e.headers.get("content-type");if(t){let s=t.indexOf(";");-1!==s&&(t=t.slice(0,s));for(let e=0;e<this.event.parse.length;e++){let r=this.event.parse[e](o,t);if(r instanceof Promise&&(r=await r),void 0!==r){i=r;break}}if(void 0===i)switch(t){case"application/json":i=await e.json();break;case"text/plain":i=await e.text();break;case"application/x-www-form-urlencoded":i=r(await e.text());break;case"multipart/form-data":i={},await e.formData().then(e=>{for(let t of e.keys()){if(t in i)continue;let r=e.getAll(t);1===r.length?i[t]=r[0]:i[t]=r}});break;case"elysia/fn":i=b(await e.json())}}}if(o.body=i,o.params=h.params,n[2]?o.query=r(n[2]):o.query={},l)for(let e=0;e<l.transform.length;e++){let t=l.transform[e](o);t instanceof Promise&&await t}if(u){if(l?.error&&(t=l?.error),u.headers){let t={};for(let r in e.headers)t[r]=e.headers.get(r);if(!1===u.headers.Check(t))throw f("header",u.headers,t)}if(!1===u.params?.Check(o.params))throw f("params",u.params,o.params);if(!1===u.query?.Check(o.query))throw f("query",u.query,o.query);if(!1===u.body?.Check(i))throw f("body",u.body,i)}if(l)for(let e=0;e<l.beforeHandle.length;e++){let t=l.beforeHandle[e](o);if(t instanceof Promise&&(t=await t),null!=t){for(let e=0;e<l.afterHandle.length;e++){let r=l.afterHandle[e](o,t);r instanceof Promise&&(r=await r),r&&(t=r)}let e=a(t,o.set);if(e)return e}}let d=h.store.handle(o);if(d instanceof Promise&&(d=await d),!1===u?.response?.Check(d))throw l?.error&&(t=l.error),f("response",u.response,d);if(l)for(let e=0;e<l.afterHandle.length;e++){let t=l.afterHandle[e](o,d);t instanceof Promise&&(t=await t);let r=a(t,o.set);if(r)return r}return s(d,o.set)}catch(s){let r=o.set;if((!r.status||r.status<300)&&(r.status=500),t){let o=v(s.message);for(let i=0;i<t.length;i++){let n=t[i]({request:e,error:s,set:r,code:o});n instanceof Promise&&(n=await n);let h=a(n,r);if(h)return h}}return this.handleError(e,s,r)}};async handleError(e,t,r={headers:{}}){for(let a=0;a<this.event.error.length;a++){let o=this.event.error[a]({request:e,code:v(t.message),error:t,set:r});if(o instanceof Promise&&(o=await o),null!=o)return s(o,r)}return new Response("string"==typeof t.cause?t.cause:t.message,{headers:r.headers,status:w(v(t.message))})}listen=(e,t)=>{if(!Bun)throw Error("Bun to run");if("string"==typeof e&&Number.isNaN(e=+e))throw Error("Port must be a numeric value");let r=this.handle,s="object"==typeof e?{...this.config.serve,...e,fetch:r}:{...this.config.serve,port:e,fetch:r},a=`$$Elysia:${s.port}`;globalThis[a]?(this.server=globalThis[a],this.server.reload(s)):globalThis[a]=this.server=Bun.serve(s);for(let e=0;e<this.event.start.length;e++)this.event.start[e](this);return t&&t(this.server),Promise.all(this.lazyLoadModules).then(()=>{this.server.pendingRequests||Bun.gc(!0)}),this};stop=async()=>{if(!this.server)throw Error("Elysia isn't running. Call `app.listen` to start the server.");this.server.stop();for(let e=0;e<this.event.stop.length;e++)await this.event.stop[e](this)};get modules(){return Promise.all(this.lazyLoadModules)}setModel(e){return Object.entries(e).forEach(([e,t])=>{e in this.meta[h]||(this.meta[h][e]=t)}),this}}export{t}from"./custom-types";export{ws}from"./ws";export{SCHEMA,DEFS,EXPOSED,createValidationError,getSchemaValidator,mergeDeep,mergeHook,mergeObjectArray,mapPathnameAndQueryRegEx}from"./utils";export{q as Elysia,o as permission};
package/dist/schema.js CHANGED
@@ -1 +1 @@
1
- import{Kind as e}from"@sinclair/typebox";export const toOpenAPIPath=e=>e.split("/").map(e=>e.startsWith(":")?`{${e.slice(1,e.length)}}`:e).join("/");export const mapProperties=(e,t,r)=>{if(void 0===t)return[];if("string"==typeof t){if(t in r)t=r[t];else throw Error(`Can't find model ${t}`)}return Object.entries(t?.properties??[]).map(([r,o])=>({...o,in:e,name:r,type:o?.type,required:t.required?.includes(r)??!1}))};let t=(e,t)=>{let r={};for(let o of e)r[o]={schema:"string"==typeof t?{$ref:`#/components/schemas/${t}`}:{...t}};return r};export const registerSchemaPath=({schema:r,contentType:o=["application/json","multipart/form-data","text/plain"],path:s,method:i,hook:n,models:p})=>{s=toOpenAPIPath(s);let a="string"==typeof o?[o]:o??["application/json"],c=n?.schema?.body,l=n?.schema?.params,m=n?.schema?.headers,f=n?.schema?.query,h=n?.schema?.response;if("object"==typeof h){if(e in h){let{type:e,properties:r,required:o,...s}=h;h={200:{...s,description:s.description,content:t(a,"object"===e||"array"===e?{type:e,properties:r,required:o}:h)}}}else Object.entries(h).forEach(([e,r])=>{if("string"==typeof r){let{type:o,properties:s,required:i,...n}=p[r];h[e]={...n,description:n.description,content:t(a,r)}}else{let{type:o,properties:s,required:i,...n}=r;h[e]={...n,description:n.description,content:t(a,{type:o,properties:s,required:i})}}})}else if("string"==typeof h){let{type:e,properties:r,required:o,...s}=p[h];h={200:{...s,content:t(a,h)}}}let d=[...mapProperties("header",m,p),...mapProperties("path",l,p),...mapProperties("query",f,p)];r[s]={...r[s]?r[s]:{},[i.toLowerCase()]:{...m||l||f||c?{parameters:d}:{},...h?{responses:h}:{},...n?.schema?.detail,...c?{requestBody:{content:t(a,"string"==typeof c?{$ref:`#/components/schemas/${c}`}:c)}}:null}}};
1
+ import{Kind as e}from"@sinclair/typebox";import t from"lodash.clonedeep";export const toOpenAPIPath=e=>e.split("/").map(e=>e.startsWith(":")?`{${e.slice(1,e.length)}}`:e).join("/");export const mapProperties=(e,t,r)=>{if(void 0===t)return[];if("string"==typeof t){if(t in r)t=r[t];else throw Error(`Can't find model ${t}`)}return Object.entries(t?.properties??[]).map(([r,o])=>({...o,in:e,name:r,type:o?.type,required:t.required?.includes(r)??!1}))};let r=(e,t)=>{let r={};for(let o of e)r[o]={schema:"string"==typeof t?{$ref:`#/components/schemas/${t}`}:{...t}};return r};export const registerSchemaPath=({schema:o,contentType:s=["application/json","multipart/form-data","text/plain"],path:i,method:n,hook:p,models:a})=>{p&&(p=t(p)),i=toOpenAPIPath(i);let c="string"==typeof s?[s]:s??["application/json"],l=p?.schema?.body,m=p?.schema?.params,f=p?.schema?.headers,h=p?.schema?.query,d=p?.schema?.response;if("object"==typeof d){if(e in d){let{type:e,properties:t,required:o,...s}=d;d={200:{...s,description:s.description,content:r(c,"object"===e||"array"===e?{type:e,properties:t,required:o}:d)}}}else Object.entries(d).forEach(([e,t])=>{if("string"==typeof t){let{type:o,properties:s,required:i,...n}=a[t];d[e]={...n,description:n.description,content:r(c,t)}}else{let{type:o,properties:s,required:i,...n}=t;d[e]={...n,description:n.description,content:r(c,{type:o,properties:s,required:i})}}})}else if("string"==typeof d){let{type:e,properties:t,required:o,...s}=a[d];d={200:{...s,content:r(c,d)}}}let y=[...mapProperties("header",f,a),...mapProperties("path",m,a),...mapProperties("query",h,a)];o[i]={...o[i]?o[i]:{},[n.toLowerCase()]:{...f||m||h||l?{parameters:y}:{},...d?{responses:d}:{},...p?.schema?.detail,...l?{requestBody:{content:r(c,"string"==typeof l?{$ref:`#/components/schemas/${l}`}:l)}}:null}}};
package/dist/utils.d.ts CHANGED
@@ -11,4 +11,4 @@ export declare const mapPathnameAndQueryRegEx: RegExp;
11
11
  export declare const mergeDeep: <A extends Object = Object, B extends Object = Object>(target: A, source: B) => DeepMergeTwoTypes<A, B>;
12
12
  export declare const createValidationError: (type: string, validator: TypeCheck<any>, value: any) => Error;
13
13
  export declare const getSchemaValidator: (s: TSchema | string | undefined, models: Record<string, TSchema>, additionalProperties?: boolean) => TypeCheck<TSchema> | undefined;
14
- export declare const getResponseSchemaValidator: (s: TypedSchema['response'] | undefined, models: Record<string, TSchema>, additionalProperties?: boolean) => TSchema | Record<string, string | TSchema> | TypeCheck<TSchema> | undefined;
14
+ export declare const getResponseSchemaValidator: (s: TypedSchema['response'] | undefined, models: Record<string, TSchema>, additionalProperties?: boolean) => string | TSchema | Record<string, string | TSchema> | TypeCheck<TSchema> | undefined;
package/dist/utils.js CHANGED
@@ -1 +1 @@
1
- import{Kind as e,Type as r}from"@sinclair/typebox";import{TypeCompiler as t}from"@sinclair/typebox/compiler";export const SCHEMA=Symbol("schema");export const DEFS=Symbol("definitions");export const EXPOSED=Symbol("exposed");export const mergeObjectArray=(e,r)=>[...Array.isArray(e)?e:[e],...Array.isArray(r)?r:[r]];export const mergeHook=(e,r)=>{let t="schema"in e?e.schema:null,o=r&&"schema"in r?r.schema:null;return{schema:t||o?{body:o?.body??t?.body,header:o?.headers??t?.headers,params:o?.params??t?.params,query:o?.query??t?.query,response:o?.response??t?.response,detail:mergeDeep(o?.detail??{},t?.detail??{})}:void 0,transform:mergeObjectArray(e.transform??[],r?.transform??[]),beforeHandle:mergeObjectArray(e.beforeHandle??[],r?.beforeHandle??[]),afterHandle:mergeObjectArray(e.afterHandle??[],r?.afterHandle??[]),error:mergeObjectArray(e.error??[],r?.error??[])}};export const clone=e=>e;export const mapPathnameAndQueryRegEx=/:\/\/[^/]+([^#?]+)(?:\?([^#]+))?/;let o=e=>e&&"object"==typeof e&&!Array.isArray(e);export const mergeDeep=(e,r)=>{let t=Object.assign({},e);return o(e)&&o(r)&&Object.keys(r).forEach(a=>{o(r[a])&&a in e?t[a]=mergeDeep(e[a],r[a]):Object.assign(t,{[a]:r[a]})}),t};export const createValidationError=(e,r,t)=>{let o=r.Errors(t).next().value;return Error("VALIDATION",{cause:`Invalid ${e}: '${o?.path?.slice(1)||"root"}'. ${o.message}`})};export const getSchemaValidator=(e,r,o=!1)=>{if(!e||"string"==typeof e&&!(e in r))return;let a="string"==typeof e?r[e]:e;return"object"===a.type&&"additionalProperties"in a==!1&&(a.additionalProperties=o),t.Compile(a)};export const getResponseSchemaValidator=(o,a,n=!1)=>{if(!o||"string"==typeof o&&!(o in a))return;let s="string"==typeof o?a[o]:o,i=e in s?s:r.Union(Object.keys(s).map(e=>{let r=s[e];if("string"==typeof r){if(r in a){let e=a[r];return e}return}return r}).filter(e=>e));"object"===i.type&&"additionalProperties"in i==!1&&(i.additionalProperties=n);try{return t.Compile(i)}catch(e){return s}};
1
+ import{Kind as e,Type as r}from"@sinclair/typebox";import{TypeCompiler as t}from"@sinclair/typebox/compiler";export const SCHEMA=Symbol("schema");export const DEFS=Symbol("definitions");export const EXPOSED=Symbol("exposed");export const mergeObjectArray=(e,r)=>[...Array.isArray(e)?e:[e],...Array.isArray(r)?r:[r]];export const mergeHook=(e,r)=>{let t="schema"in e?e.schema:null,o=r&&"schema"in r?r.schema:null;return{schema:t||o?{body:o?.body??t?.body,header:o?.headers??t?.headers,params:o?.params??t?.params,query:o?.query??t?.query,response:o?.response??t?.response,detail:mergeDeep(o?.detail??{},t?.detail??{})}:void 0,transform:mergeObjectArray(e.transform??[],r?.transform??[]),beforeHandle:mergeObjectArray(e.beforeHandle??[],r?.beforeHandle??[]),afterHandle:mergeObjectArray(e.afterHandle??[],r?.afterHandle??[]),error:mergeObjectArray(e.error??[],r?.error??[])}};export const clone=e=>e;export const mapPathnameAndQueryRegEx=/:\/\/[^/]+([^#?]+)(?:\?([^#]+))?/;let o=e=>e&&"object"==typeof e&&!Array.isArray(e);export const mergeDeep=(e,r)=>{let t=Object.assign({},e);return o(e)&&o(r)&&Object.keys(r).forEach(a=>{o(r[a])&&a in e?t[a]=mergeDeep(e[a],r[a]):Object.assign(t,{[a]:r[a]})}),t};export const createValidationError=(e,r,t)=>{let o=r.Errors(t).next().value;return Error("VALIDATION",{cause:`Invalid ${e}: '${o?.path?.slice(1)||"root"}'. ${o.message}`})};export const getSchemaValidator=(e,r,o=!1)=>{if(!e||"string"==typeof e&&!(e in r))return;let a="string"==typeof e?r[e]:e;return"object"===a.type&&"additionalProperties"in a==!1&&(a.additionalProperties=o),t.Compile(a)};export const getResponseSchemaValidator=(o,a,n=!1)=>{if(!o||"string"==typeof o&&!(o in a))return;let s="string"==typeof o?a[o]:o,i=e in s?s:r.Union(Object.keys(s).map(e=>{let r=s[e];if("string"==typeof r){if(r in a){let e=a[r];return e}return}return r}).filter(e=>e).reduce((e,r)=>e.concat(r),[]));"object"===i.type&&"additionalProperties"in i==!1&&(i.additionalProperties=n);try{return t.Compile(i)}catch(e){return console.log(e),o}};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "elysia",
3
3
  "description": "Fast, and friendly Bun web framework",
4
- "version": "0.3.1",
4
+ "version": "0.3.2",
5
5
  "author": {
6
6
  "name": "saltyAom",
7
7
  "url": "https://github.com/SaltyAom",
@@ -93,6 +93,7 @@
93
93
  "dependencies": {
94
94
  "@sinclair/typebox": "^0.25.24",
95
95
  "fast-querystring": "^1.1.1",
96
+ "lodash.clonedeep": "^4.5.0",
96
97
  "nanoid": "^4.0.1",
97
98
  "openapi-types": "^12.1.0",
98
99
  "raikiri": "0.1.0-beta.1",
@@ -101,6 +102,7 @@
101
102
  "devDependencies": {
102
103
  "@swc/cli": "^0.1.60",
103
104
  "@swc/core": "^1.3.32",
105
+ "@types/lodash.clonedeep": "^4.5.7",
104
106
  "@types/node": "^18.11.18",
105
107
  "@typescript-eslint/eslint-plugin": "^5.48.2",
106
108
  "@typescript-eslint/parser": "^5.48.2",
package/src/index.ts CHANGED
@@ -59,7 +59,7 @@ import type {
59
59
  TypedWSRouteToEden
60
60
  } from './types'
61
61
  import { type TSchema } from '@sinclair/typebox'
62
- import { ElysiaWSContext, ElysiaWSOptions, WSTypedSchema } from './ws'
62
+ import { ElysiaWSContext, ElysiaWSOptions, ws, WSTypedSchema } from './ws'
63
63
 
64
64
  /**
65
65
  * ### Elysia Server
@@ -133,7 +133,7 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
133
133
  method,
134
134
  path,
135
135
  handler,
136
- hooks: mergeHook(clone(this.event), hook as RegisteredHook)
136
+ hooks: mergeHook({ ...this.event }, hook as RegisteredHook)
137
137
  })
138
138
 
139
139
  const defs = this.meta[DEFS]
@@ -489,6 +489,8 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
489
489
  const instance = new Elysia<any>()
490
490
  instance.store = this.store
491
491
 
492
+ if (this.wsRouter) instance.use(ws())
493
+
492
494
  const sandbox = run(instance)
493
495
 
494
496
  if (sandbox.event.request.length)
@@ -500,28 +502,37 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
500
502
  this.setModel(sandbox.meta[DEFS])
501
503
 
502
504
  Object.values(instance.routes).forEach(
503
- ({ method, path, handler, hooks }) => {
504
- if (path === '/')
505
- this._addHandler(
506
- method,
507
- prefix,
508
- handler,
509
- mergeHook(hooks, {
510
- error: sandbox.event.error
511
- })
512
- )
513
- else
514
- this._addHandler(
515
- method,
516
- `${prefix}${path}`,
517
- handler,
518
- mergeHook(hooks, {
519
- error: sandbox.event.error
520
- })
505
+ ({ method, path: originalPath, handler, hooks }) => {
506
+ const path =
507
+ originalPath === '/' ? prefix : `${prefix}${originalPath}`
508
+
509
+ const hasWsRoute = instance.wsRouter?.match('subscribe', path)
510
+ if (hasWsRoute) {
511
+ const wsRoute = instance.wsRouter!.history.find(
512
+ ([_, wsPath]) => originalPath === wsPath
521
513
  )
514
+ if (!wsRoute) return
515
+
516
+ return this.ws(path as any, wsRoute[2] as any)
517
+ }
518
+
519
+ this._addHandler(
520
+ method,
521
+ path,
522
+ handler,
523
+ mergeHook(hooks, {
524
+ error: sandbox.event.error
525
+ })
526
+ )
522
527
  }
523
528
  )
524
529
 
530
+ if (instance.wsRouter && this.wsRouter)
531
+ instance.wsRouter.history.forEach(([method, path, handler]) => {
532
+ if (path === '/') this.wsRouter?.add(method, prefix, handler)
533
+ else this.wsRouter?.add(method, `${prefix}${path}`, handler)
534
+ })
535
+
525
536
  return this as any
526
537
  }
527
538
 
@@ -567,8 +578,8 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
567
578
  ? Elysia<NewInstance & Instance>
568
579
  : this {
569
580
  const instance = new Elysia<any>()
570
-
571
581
  instance.store = this.store
582
+ if (this.wsRouter) instance.use(ws())
572
583
 
573
584
  const sandbox = run(instance)
574
585
 
@@ -582,6 +593,16 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
582
593
 
583
594
  Object.values(instance.routes).forEach(
584
595
  ({ method, path, handler, hooks: localHook }) => {
596
+ const hasWsRoute = instance.wsRouter?.match('subscribe', path)
597
+ if (hasWsRoute) {
598
+ const wsRoute = instance.wsRouter!.history.find(
599
+ ([_, wsPath]) => path === wsPath
600
+ )
601
+ if (!wsRoute) return
602
+
603
+ return this.ws(path as any, wsRoute[2] as any)
604
+ }
605
+
585
606
  this._addHandler(
586
607
  method,
587
608
  path,
@@ -591,6 +612,11 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
591
612
  }
592
613
  )
593
614
 
615
+ if (instance.wsRouter && this.wsRouter)
616
+ instance.wsRouter.history.forEach(([method, path, handler]) => {
617
+ this.wsRouter?.add(method, path, handler)
618
+ })
619
+
594
620
  return this as any
595
621
  }
596
622
 
@@ -1309,9 +1335,11 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
1309
1335
  path,
1310
1336
  // @ts-ignore
1311
1337
  (context) => {
1338
+ console.log('Got', context.request.url)
1339
+
1312
1340
  if (
1313
1341
  // @ts-ignore
1314
- this.server!.upgrade(context.request, {
1342
+ this.server?.upgrade(context.request, {
1315
1343
  headers:
1316
1344
  typeof options.headers === 'function'
1317
1345
  ? options.headers(context as any)
package/src/schema.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Kind, type TSchema } from '@sinclair/typebox'
2
2
  import type { OpenAPIV3 } from 'openapi-types'
3
+ import deepClone from 'lodash.clonedeep'
3
4
 
4
5
  import type { HTTPMethod, LocalHook } from './types'
5
6
 
@@ -72,6 +73,8 @@ export const registerSchemaPath = ({
72
73
  hook?: LocalHook
73
74
  models: Record<string, TSchema>
74
75
  }) => {
76
+ if (hook) hook = deepClone(hook)
77
+
75
78
  path = toOpenAPIPath(path)
76
79
 
77
80
  const contentTypes =
package/src/utils.ts CHANGED
@@ -157,7 +157,8 @@ export const getResponseSchemaValidator = (
157
157
 
158
158
  return maybeNameOrSchema
159
159
  })
160
- .filter((a) => a) as TSchema[]
160
+ .filter((a) => a)
161
+ .reduce((a, b) => a.concat(b!), [] as TSchema[])
161
162
  )
162
163
 
163
164
  // @ts-ignore
@@ -167,7 +168,8 @@ export const getResponseSchemaValidator = (
167
168
  try {
168
169
  return TypeCompiler.Compile(schema)
169
170
  } catch (error) {
171
+ console.log(error)
170
172
  // Likely is already compile
171
- return maybeSchemaOrRecord
173
+ return s
172
174
  }
173
175
  }