UNPKG

@coatl/tokio

Version:

<img src='logo.jpeg' width='200px' heigth='200px'/>

4 lines (3 loc) 11.6 kB
var F=Object.defineProperty;var U=(o,e)=>{for(var t in e)F(o,t,{get:e[t],enumerable:!0})};import{Server as se}from"http";var l=class{static#e={0:"\x1B[30m",1:"\x1B[31m",2:"\x1B[32m",3:"\x1B[33m",4:"\x1B[34m",5:"\x1B[35m",6:"\x1B[36m",7:"\x1B[37m",8:"\x1B[90m",9:"\x1B[91m",reset:"\x1B[0m"};static#t={0:"\x1B[40m",1:"\x1B[41m",2:"\x1B[42m",3:"\x1B[43m",4:"\x1B[44m",5:"\x1B[45m",6:"\x1B[46m",7:"\x1B[47m",8:"\x1B[100m",9:"\x1B[101m",reset:"\x1B[0m"};static parse(e){let t=/([0-9]+)~(.*?)~/g;return e.replace(t,(s,r,i)=>r.length===2?`${this.#e[r[0]]}${this.#t[r[1]]}${i}${this.#e.reset}`:`${this.#e[r]}${i}${this.#e.reset}`)}static print(e){console.log(this.parse(e))}};var f=class extends Error{constructor(e){super(e),this.stack=void 0,this.name="TokioError",this.message=l.parse(`9~${this.name}~: ${e}`)}};var y=class extends f{constructor(){super("You must specify an api and/or public directory")}},w=class extends f{constructor(e){super(`The directory ${e} was not found`)}};import{readdir as _}from"fs/promises";import{existsSync as L}from"fs";import{resolve as D,dirname as K}from"path";import{fileURLToPath as V,pathToFileURL as W}from"url";var h=class{static absolutePath(e){return D(process.cwd(),e)}static join(...e){return D(...e)}static directory(e){return K(V(e))}static exists(e){return L(e)}static toUrl(e){return W(e).href}static async readDir(e){return await _(e,{withFileTypes:!0})}};var x=class{port;host;public;api;logs;cors;fileMaxSize=1024;#e=process.env;#t=8e3;#s="http://localhost";constructor(e){let{port:t,host:s,api:r,public:i}=e,{HOST:n=s??this.#s,PORT:a=t??this.#t}=this.#e;if(!r&&!i)throw new y;this.port=Number(a),this.host=n,this.public=i?this.#r(i):void 0,this.api=r?this.#r(r):void 0,this.logs=e.logs??!0,this.cors=e.cors??[]}#r(e){if(!e)return;let t=h.absolutePath(e);if(!h.exists(t))throw new w(e);return t}};import z from"querystring";var R=class{constructor(e,t){this.body=e;this.req=t}async parse(){let e=this.req.headers["content-encoding"];return z.parse(this.body.toString(e||"utf8"))}};var b=class{constructor(e,t){this.body=e;this.req=t}async parse(){let e=this.req.headers["content-encoding"];return JSON.parse(this.body.toString(e||"utf8"))}};import{writeFile as X}from"fs/promises";import{tmpdir as Y}from"os";import{join as G}from"path";var v=class{constructor(e,t){this.body=e;this.req=t}boundaryRegex=/boundary=(?:"([^"]+)"|([^;]+))/i;nameRegex=/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t/]+))/i;filenameRegex=/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t/]+))($|;\s)/i;EOF=`\r \r `;async parse(){let e=this.getBoundary();return e?await this.parseFormData(e):{}}getBoundary(){let e=this.req.headers["content-type"];if(!e)return;let t=e.match(this.boundaryRegex);if(t)return t[1]??t[2]??void 0}async parseFormData(e){let t={},s=this.body.toString().split(`--${e}`);return await Promise.all(s.map(async r=>{let[i,...n]=r.split(this.EOF),[a]=i?.match(this.nameRegex)??[];if(!a)return;let d=a[2]??a[3]??"",p=i?.match(this.filenameRegex);if(p){let c=await this.parseFile(p,n.join(this.EOF));t[d]=c}else{let c=n.join(this.EOF).trim();t[d]=c}})),t}async parseFile(e,t){let s=Y(),r=e[2]??e[3]??"",i=G(s,r),n=this.req.headers["content-type"],a=Buffer.from(t,"binary");return await X(i,a),new P(r,i,n,a.length)}},P=class{constructor(e,t,s,r){this.name=e;this.path=t;this.mimetype=s;this.size=r}};var T=class{constructor(e,t){this.body=e;this.req=t}async parse(){let e=this.req.headers["content-encoding"];return this.body.toString(e||"utf8")}};var J=new Map([["application/x-www-form-urlencoded",R],["application/json",b],["multipart/form-data",v],["text/plain",T]]),C=class{constructor(e){this.req=e;this.#e=this.req.headers["content-type"]}#e;#t=1024*1024;async#s(){return await new Promise(e=>{let t=[];this.req.on("data",s=>{if(t.length+s.length>this.#t)throw new Error;t.push(s)}),this.req.on("end",()=>e(Buffer.concat(t)))})}#r(){let e=this.#e.split(";").shift(),t=J.get(e);if(!t)throw new Error(this.#e);return t}async parse(){let e=this.#r(),t=await this.#s();return await new e(t,this.req).parse()}};var E=class{constructor(e,t){this.req=e;this.config=t}regex=/./;#e=new j;get kv(){return this.#e}get#t(){return new URL(this.req.url,this.config.host)}get url(){return this.#t.pathname}get method(){return this.req.method}get headers(){return this.req.headers}get ip(){return this.req.socket.remoteAddress}get host(){return this.req.headers.host}get origin(){return this.req.headers.origin}get referer(){return this.req.headers.referer}get cookie(){return this.req.headers.cookie}get userAgent(){return this.req.headers["user-agent"]}get contentType(){return this.req.headers["content-type"]}params(){let e=new RegExp(this.regex,"i").exec(this.url)?.groups;return e?{...e}:{}}segments(){let e=new RegExp(this.regex,"i").exec(this.url)??[];if(!e)return[];let[t]=e;return t?.split("/").slice(1).filter(Boolean)??[]}queries(){let{searchParams:e}=this.#t;return Object.fromEntries(e)}query(e){let{searchParams:t}=this.#t;return t.getAll(e)}async body(){return await new C(this.req).parse()}},j=class{#e={};get(e){return this.#e[e]}set(e,t){this.#e[e]=t}};import{join as Q}from"path";import{readFile as N}from"fs/promises";var q=class{constructor(e){this.res=e}get headers(){return new I(this.res)}get cookies(){return new S(this.res)}get code(){return this.res.statusCode}status(e){return this.res.statusCode=e,this}send(e){this.res.end(e)}sendResponse(e,t){this.status(e).send(t)}json(e){this.headers.set("Content-Type","application/json; charset=utf-8"),this.send(JSON.stringify(e))}redirect(e){this.status(302),this.headers.set("Location",e),this.send("Redirecting to "+e)}html(e){this.headers.set("Content-Type","text/html; charset=utf-8"),this.send(e)}text(e){this.headers.set("Content-Type","text/plain; charset=utf-8"),this.send(e)}raw(e){this.send(e)}async file(e){let t=await N(e);this.raw(t)}async view(e){let t=await N(e,"utf-8");this.html(t)}async download(e,t){let s=await N(Q(process.cwd(),e));this.headers.set("Content-Disposition",`attachment; filename=${t}`),this.raw(s)}ok(e="OK"){this.sendResponse(200,e)}created(e="Created"){this.sendResponse(201,e)}accepted(e="Accepted"){this.sendResponse(202,e)}noContent(e="No Content"){this.sendResponse(204,e)}badRequest(e="Bad Request"){this.sendResponse(400,e)}unauthorized(e="Unauthorized"){this.sendResponse(401,e)}forbidden(e="Forbidden"){this.sendResponse(403,e)}notFound(e="Not Found"){this.sendResponse(404,e)}methodNotAllowed(e="Method Not Allowed"){this.sendResponse(405,e)}conflict(e="Conflict"){this.sendResponse(409,e)}internalServerError(e="Internal Server Error"){this.sendResponse(500,e)}notImplemented(e="Not Implemented"){this.sendResponse(501,e)}badGateway(e="Bad Gateway"){this.sendResponse(502,e)}serviceUnavailable(e="Service Unavailable"){this.sendResponse(503,e)}gatewayTimeout(e="Gateway Timeout"){this.sendResponse(504,e)}httpVersionNotSupported(e="HTTP Version Not Supported"){this.sendResponse(505,e)}networkAuthenticationRequired(e="Network Authentication Required"){this.sendResponse(511,e)}},I=class{constructor(e){this.res=e}set(e,t){this.res.setHeader(e,t)}get(e){return this.res.getHeader(e)}},S=class{constructor(e){this.res=e}set(e,t,s={}){let r=`${e}=${t}; ${this.serialize(s)}`;this.res.setHeader("Set-Cookie",r)}get(e){let t=this.res.getHeader("Set-Cookie");if(typeof t=="string")return t;if(Array.isArray(t))return t.find(s=>s.startsWith(e))}serialize(e){return Object.entries(e).map(([r,i])=>r==="expires"?`${r}=${i.toUTCString()}`:r==="maxAge"?`${r}=${i}`:`${r}=${encodeURIComponent(i)}`).join("; ")}};var B={};U(B,{AFTER:()=>ee,BEFORE:()=>Z});async function Z(o,e){o.kv.set("tokio-start-time",Date.now())}async function ee(o,e){let t=o.kv.get("tokio-start-time"),r=Date.now()-t;l.print(`11~${o.method}~ 4~${o.url}~ 2~${e.code}~ 3~${r}ms~`)}var $=[B];var M=class{config;router;publicPath;constructor(e){this.config=new x(e),this.config.public&&(this.publicPath=h.join(process.cwd(),this.config.public))}async exec(e,t){let s=new E(e,this.config),r=new q(t);try{if(s.url==="/favicon.ico")return;let i=this.router?.match(s.url),n=h.absolutePath(this.publicPath+s.url),a=h.exists(n);if(!i&&!a)return r.notFound();if(!i&&a)return await r.file(n);if(i&&!a){let{route:d,regex:p}=i;if(!d?.module?.[s.method])return r.methodNotAllowed();if(await Promise.all($.map(async m=>await m.BEFORE(s,r))),d.middlewares)for(let m in d?.middlewares){let{BEFORE:u}=d.middlewares[m];await u?.(s,r)}s.regex=new RegExp(p,"i");let c=d.module[s.method];if(await c(s,r),d.middlewares)for(let m in d?.middlewares){let{AFTER:u}=d.middlewares[m];await u?.(s,r)}await Promise.all($.map(async m=>await m.AFTER(s,r)))}}catch(i){console.error(i),r.internalServerError()}}};import{readdir as te}from"fs/promises";import{resolve as re}from"path";import{pathToFileURL as H}from"url";var A=class{constructor(e,t){this.apiDir=e;this.publicDir=t}MIDDLEWARE_MATCH=[/\(.*\)/gi,""];EXTENSION_MATCH=[/\.[^/.]+$/i,""];INDEX_MATCH=[/\/index(?=\/|$)/i,""];SLASHES_MATCH=[/\\/g,"/"];DYNAMIC_MATCH=[/\/\[(\w+)\]/g,"/(?<$1>[^/]+)"];MULTIPLE_MATCH=[/\[\.{3}(\w+)\]/g,"(?<$1>.*)"];routes={};async readRecursiveDir(e){return(await te(e,{withFileTypes:!0,recursive:!0})).filter(r=>r.isFile()).map(r=>re(r.path,r.name))}buildRegexKey(e){let t=e.replace(this.apiDir,"").replace(this.EXTENSION_MATCH[0],this.EXTENSION_MATCH[1]).replace(this.SLASHES_MATCH[0],this.SLASHES_MATCH[1]).replace(this.INDEX_MATCH[0],this.INDEX_MATCH[1]).replace(this.DYNAMIC_MATCH[0],this.DYNAMIC_MATCH[1]).replace(this.MULTIPLE_MATCH[0],this.MULTIPLE_MATCH[1]);return t=t.endsWith("/")?t.slice(0,-1):t,`^${t}/?$`}async extractMiddlewares(e){let t={},s=e.split("\\").filter(r=>r!=="");return await Promise.all(s.map(async r=>{let i=r.match(this.MIDDLEWARE_MATCH[0]);i&&(t[i[0]]={...await import(H(e).href)})})),t}async init(){let e=await this.readRecursiveDir(this.apiDir);for(let t of e){let s=t.match(this.MIDDLEWARE_MATCH[0]);if(s&&s.length>0){let i=t.replace(this.MIDDLEWARE_MATCH[0],""),n=this.buildRegexKey(i),a=t.replace(this.MIDDLEWARE_MATCH[0],"index");this.routes[n]||(this.routes[n]={module:{...await import(H(a).href)},middlewares:{}});let d=await this.extractMiddlewares(t),p=this.routes[n]?.middlewares;this.routes[n].middlewares={...p,...d};let c=i.split("/").slice(0,-1).join("/"),m=this.buildRegexKey(c);if(this.routes[m]?.middlewares){let u=this.routes[m]?.middlewares;this.routes[n].middlewares={...u,...this.routes[n]?.middlewares}}}else{let i=this.buildRegexKey(t);this.routes[i]||(this.routes[i]={module:{...await import(H(t).href)},middlewares:{}});let n=await this.extractMiddlewares(t),a=this.routes[i]?.middlewares;this.routes[i].middlewares={...a,...n};let d=e.filter(p=>p!==t&&p.startsWith(t.split("\\")[0]));for(let p of d){let c=this.buildRegexKey(p);this.routes[c]?.middlewares&&(this.routes[i].middlewares={...this.routes[i]?.middlewares,...this.routes[c]?.middlewares})}}}}match(e){for(let[t,s]of Object.entries(this.routes))if(new RegExp(t,"i").test(e))return{regex:t,route:s};return null}};var g=class extends M{server;constructor(e){super(e),this.server=new se(this.exec.bind(this))}async run(){l.print(`Server running at 4~${this.config.host}:${this.config.port}~ \u{1F680}`),this.router=new A(this.config.api,this.config.public),await this.router.init(),this.server.listen(this.config.port)}};import O from"cluster";import{cpus as ie}from"os";var k=class extends g{workers=ie().length;constructor(e){super(e)}async run(){if(O.isPrimary){for(let e=0;e<this.workers;e++)O.fork();O.on("exit",e=>l.print(`1~worker ${e.process.pid} died~`))}else await this.run()}};export{g as Tokio,k as TokioWithClusters};