elysia 0.3.0 → 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.0",
4
+ "version": "0.3.2",
5
5
  "author": {
6
6
  "name": "saltyAom",
7
7
  "url": "https://github.com/SaltyAom",
@@ -93,14 +93,16 @@
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
- "raikiri": "^0.1.0-beta.0",
99
+ "raikiri": "0.1.0-beta.1",
99
100
  "superjson": "^1.12.2"
100
101
  },
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,9 +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'
63
-
64
- const ASYNC_FN = 'AsyncFunction'
62
+ import { ElysiaWSContext, ElysiaWSOptions, ws, WSTypedSchema } from './ws'
65
63
 
66
64
  /**
67
65
  * ### Elysia Server
@@ -135,7 +133,7 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
135
133
  method,
136
134
  path,
137
135
  handler,
138
- hooks: mergeHook(clone(this.event), hook as RegisteredHook)
136
+ hooks: mergeHook({ ...this.event }, hook as RegisteredHook)
139
137
  })
140
138
 
141
139
  const defs = this.meta[DEFS]
@@ -491,6 +489,8 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
491
489
  const instance = new Elysia<any>()
492
490
  instance.store = this.store
493
491
 
492
+ if (this.wsRouter) instance.use(ws())
493
+
494
494
  const sandbox = run(instance)
495
495
 
496
496
  if (sandbox.event.request.length)
@@ -502,28 +502,37 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
502
502
  this.setModel(sandbox.meta[DEFS])
503
503
 
504
504
  Object.values(instance.routes).forEach(
505
- ({ method, path, handler, hooks }) => {
506
- if (path === '/')
507
- this._addHandler(
508
- method,
509
- prefix,
510
- handler,
511
- mergeHook(hooks, {
512
- error: sandbox.event.error
513
- })
514
- )
515
- else
516
- this._addHandler(
517
- method,
518
- `${prefix}${path}`,
519
- handler,
520
- mergeHook(hooks, {
521
- error: sandbox.event.error
522
- })
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
523
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
+ )
524
527
  }
525
528
  )
526
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
+
527
536
  return this as any
528
537
  }
529
538
 
@@ -569,8 +578,8 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
569
578
  ? Elysia<NewInstance & Instance>
570
579
  : this {
571
580
  const instance = new Elysia<any>()
572
-
573
581
  instance.store = this.store
582
+ if (this.wsRouter) instance.use(ws())
574
583
 
575
584
  const sandbox = run(instance)
576
585
 
@@ -584,6 +593,16 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
584
593
 
585
594
  Object.values(instance.routes).forEach(
586
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
+
587
606
  this._addHandler(
588
607
  method,
589
608
  path,
@@ -593,6 +612,11 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
593
612
  }
594
613
  )
595
614
 
615
+ if (instance.wsRouter && this.wsRouter)
616
+ instance.wsRouter.history.forEach(([method, path, handler]) => {
617
+ this.wsRouter?.add(method, path, handler)
618
+ })
619
+
596
620
  return this as any
597
621
  }
598
622
 
@@ -1311,9 +1335,11 @@ export default class Elysia<Instance extends ElysiaInstance = ElysiaInstance> {
1311
1335
  path,
1312
1336
  // @ts-ignore
1313
1337
  (context) => {
1338
+ console.log('Got', context.request.url)
1339
+
1314
1340
  if (
1315
1341
  // @ts-ignore
1316
- this.server!.upgrade(context.request, {
1342
+ this.server?.upgrade(context.request, {
1317
1343
  headers:
1318
1344
  typeof options.headers === 'function'
1319
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
  }