UNPKG

sw2express

Version:

A lite & simple cross-platform Express-like web application framework

502 lines (479 loc) 13.3 kB
class nodeRequest { constructor(req, body) { this.req = req; this.headers = req.headers; this.method = req.method; this.host = this.headers["x-forward-for"] || req.host; this.protocol = this.headers["x-forward-proto"] || req.protocol; this.path = req.url; if (this.method === "POST") { this.body = body; } } } class swRequest { constructor(req, body) { this.req = req; this.headers = Object.fromEntries(req.headers.entries()); this.method = req.method; this.url = new URL(req.url); this.host = this.headers["x-forward-for"] || this.url.host; this.path = this.url.pathname; this.protocol = this.headers["x-forward-proto"] || this.url.protocol; if (this.method === "POST") { this.body = body; } } } const Headers = { "Content-Type": "text/plain; charset=utf-8", server: "Sw=>Express", "X-Powered-By": (() => { switch (checkPlatform()) { case "NODE": if (process.env.NODE_ENV !== "production") { return `Sw2Express on Node.js/${process.version} on ${process.platform}`; } else { return `Sw2Express`; } case "SW": return `Sw2Express on Service Worker`; default: return `Sw2Express on JavaScript`; } })(), "X-Served-By": "Sw=>Express", }; class nodeReply { constructor(response, request, options) { this.request = request; this.response = response; this.statusCode = 200; this.headers = Object.assign({}, Headers); this.isSendHeader = false; this.isEnd = false; this.sendMsg = []; this.options = options; } setHeader(name, value) { this.headers[name] = value; return this; } getHeader(name) { return this.headers[name]; } Nativesend(text) { this.response.statusCode = this.statusCode; for (let i in this.headers) { this.response.setHeader(i, this.headers[i]); } this.isSendHeader = true; this.response.write(text); return this; } send(text) { this.sendMsg.push(text); this.MSG = this.sendMsg.join(""); } async end(text) { this.sendMsg.push(text); this.MSG = this.sendMsg.join(""); if (this.options.ETag) { const ETag = `W/"${await globalThis.md5(this.MSG)}"`; this.setHeader("ETag", ETag); if (ETag === this.request.headers["if-none-match"]) { this.statusCode = 304; this.response.statusCode = this.statusCode; this.response.end(""); return true; } else { this.Nativeend(this.MSG); } return true; } this.Nativeend(this.MSG); return true; } json(text) { if (this.getHeader("Content-Type") === "text/plain; charset=utf-8") { this.setHeader("Content-Type", "application/json; charset=utf-8"); } this.end(JSON.stringify(text)); return this; } Nativeend(text) { if (!this.isSendHeader && text) this.Nativesend(text).Nativeend(); else this.response.end(text); this.isEnd = true; return true; } set(a, b) { this[a] = b; } } class swReply { constructor(request, options) { //this.response = response; this.request = request; this.statusCode = 200; this.headers = Object.assign({}, Headers); this.isSendHeader = false; this.isEnd = false; this.sendMsg = []; this.options = options; } setHeader(name, value) { this.headers[name] = value; return this; } getHeader(name) { return this.headers[name]; } async GenResponse() { if (this.options.ETag) { const ETag = `W/"${await globalThis.md5(this.MSG)}"`; this.headers.ETag = ETag; this.requestHeaders = Object.fromEntries(this.request.headers.entries()); if (ETag === this.requestHeaders["if-none-match"]) { return new Response(null, { headers: this.headers, status: 304, }); } } this.isSendHeader = true; return new Response(this.MSG, { headers: this.headers, status: this.statusCode, }); } send(text) { this.sendMsg.push(text); this.MSG = this.sendMsg.join(""); return this; } json(text) { if (this.getHeader("Content-Type") === "text/plain; charset=utf-8") { this.setHeader("Content-Type", "application/json; charset=utf-8"); } this.end(JSON.stringify(text)); return this; } end(text) { this.sendMsg.push(text); this.MSG = this.sendMsg.join(""); this.isEnd = true; return this; } set(a, b) { this[a] = b; } } async function startHTTPServer(PORT) { const http = await import('http').then((e) => e.default); const app = http.createServer(); if (PORT) { app.listen(PORT); } return app; } async function doRequest$1(func, NodeRequest, NodeReply) { const rep = await func(NodeRequest, NodeReply).catch(async (e) => { await doRequest$1(page.Code(500).GenAsync, NodeRequest, NodeReply); console.log(e.stack); }); if (!NodeReply.isEnd) { if (typeof rep !== "object") NodeReply.end(rep); else NodeReply.json(rep); } } const newHTTPServer = async (port) => { return async (that) => { const options = that.options; if (options.cluster) { const cluster = await import('cluster').then((e) => e.default); if (cluster.isMaster) { console.log("Master Process is running..."); for (let i = 0; i < options.cluster; i++) { cluster.fork(); } cluster.on("exit", (worker, code, signal) => { console.log(`Worker ${worker.id} is exited with code ${code}`); cluster.fork(); }); } if (cluster.isWorker) { console.log(`Worker Process ${cluster.worker.id} is running...`); const server = await startHTTPServer(port); server.on("request", await HTTPHandler(that)); } } else { const server = await startHTTPServer(port); server.on("request", await HTTPHandler(that)); return server; } }; }; const HTTPHandler = async (that) => { const page = that.DefaultPage; return async (request, response) => { let body = await new Promise((resolve) => { if (request.method === "POST" && !request.body) { let body = []; request.on("data", (chunk) => body.push(chunk)); request.on("end", () => { body = Buffer.concat(body).toString(); resolve(body); }); } else if (request.body) { resolve(request.body); } else { resolve(); } }); let NodeRequest = new nodeRequest(request, body), NodeReply = new nodeReply(response, request, that.options), path = request.url; new Promise((resolve) => { if (that.options.logger) { console.log( `[${new Date().toLocaleString()}] ${request.socket.remoteAddress} - ${ request.method } ${path}` ); resolve(); } }); await Promise.all( that._middleWare.map((middleware) => middleware(NodeRequest, NodeReply)) ); if (!NodeReply.isEnd) { if (that._route[path]) { if (that._route[path][request.method]) { await doRequest$1( that._route[path][request.method], NodeRequest, NodeReply ); } else if (that._route[path].all) { await doRequest$1(that._route[path].all, NodeRequest, NodeReply); } else { await doRequest$1(page.Code(500).GenAsync, NodeRequest, NodeReply); } } else { await doRequest$1(page.Code(404).GenAsync, NodeRequest, NodeReply); } } }; }; const makeHandler = (that) => { const page = that.DefaultPage; return async (request) => { let body = ""; if (request.method === "POST") { body = await request.text(); } let req = new swRequest(request, body), rep = new swReply(request, that.options), answer; const path = req.path; await Promise.all( that._middleWare.map((middleware) => middleware(req, rep)) ); if (!rep.isEnd) { if (that._route[path]) { if (that._route[path][request.method]) { answer = await doRequest(that._route[path][request.method], req, rep); } else if (that._route[path].all) { answer = await doRequest(that._route[path].all, req, rep); } else { answer = await doRequest(page.Code(500).GenAsync, req, rep); } } else { answer = await doRequest(page.Code(404).GenAsync, req, rep); } } else { answer = await doRequest( async (req, rep) => { "NotThingToDoHere."; }, req, rep ); } return answer; }; }; async function doRequest(func, req, rep) { const funcAnswer = await func(req, rep).catch(async (e) => { await doRequest(page.Code(500).GenAsync, req, rep); console.log(e.stack); }); if (!rep.isEnd) { if (typeof funcAnswer !== "object") rep.end(funcAnswer); rep.json(funcAnswer); } return await rep.GenResponse(); } var toMD5 = () => { if (globalThis.platform === "NODE") { const crypto = import('crypto').then((e) => e.default); return async (text) => { const md5 = (await crypto).createHash("md5"); const answer = md5.update(text).digest("hex"); return answer; }; } else if (globalThis.platform === "SW") { return async (text) => { const textUint8 = new TextEncoder().encode(text); const hashBuffer = await crypto.subtle.digest("MD5", textUint8); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray .map((b) => b.toString(16).padStart(2, "0")) .join(""); return hashHex; }; } else { return async (text) => { throw new Error("Can't Found Platform!"); }; } }; const getReallyPrefix = (relativePrefix, globalPrefix) => { const prefix = new URL("http://sw2express.localhost"); prefix.pathname = globalPrefix; const reallyPrefix = new URL(relativePrefix, prefix.href); return reallyPrefix.pathname; }; var registerPlugins = { name: "register", func: (that) => (func, globalPrefix = "/") => { const app = { route: (prefix) => { const reallyRoute = that.route(getReallyPrefix(prefix, globalPrefix)); return reallyRoute; }, use: (Handler) => { that._registerMiddlewares.push({ prefix: globalPrefix, Handler: Handler, }); }, }; func(app); return that; }, bootstrap: (app) => { app._register = true; app._registerMiddlewares = []; app.use(async (req, rep) => { app._registerMiddlewares.forEach(async (handler) => { if (req.path.indexOf(handler.prefix) === 0) { await handler.Handler(req, rep); } }); }); }, }; function Code(statusCode) { return { GenAsync: async function (res, rep) { rep.statusCode = statusCode; rep.setHeader("Content-Type", "text/plain"); rep.send(`Error: ${statusCode}`); }, }; } var page$1 = /*#__PURE__*/Object.freeze({ __proto__: null, Code: Code }); function checkPlatform() { if (!(typeof process === "undefined")) { return "NODE"; } else if (!(typeof self === "undefined")) { return "SW"; } else { return "UNKNOWN"; } } class sw2express { constructor(options = { logger: false, ETag: true, cluster: false }) { this.options = { logger: options.logger, ETag: options.ETag, cluster: options.cluster, }; this._route = {}; this._middleWare = []; this.DefaultPage = page$1; globalThis.platform = checkPlatform(); globalThis.md5 = toMD5(); } use(MiddleWare) { this._middleWare.push(MiddleWare); return this; } route(url) { this._route[url] = {}; const that = { POST: (func) => { this._route[url].POST = func; return that; }, GET: (func) => { this._route[url].GET = func; return that; }, all: (func) => { this._route[url].all = func; return that; }, }; return that; } makeNewHandler(platform) { switch (platform) { case "NODE": { return { listen: (port) => newHTTPServer(port).then((e) => e(this)), Server: () => newHTTPServer().then((e) => e(this)), Handler: HTTPHandler(this), }; } case "SW": { return makeHandler(this); } case "VERCEL": { return HTTPHandler(this); } default: return new Error("Invalid Platform"); } } listen(port) { switch (checkPlatform()) { case "NODE": { let answer = "NODE", Handler = this.makeNewHandler(answer); Handler.listen(port); return Handler; } case "SW": { let answer = "SW", Handler = this.makeNewHandler(answer); self.addEventListener("fetch", (event) => event.respondWith(Handler(event.request)) ); return Handler; } } } extend({ name, func, bootstrap }) { this[name] = func(this); return bootstrap(this); } } const plugins = { register: registerPlugins }; export default sw2express; export { checkPlatform, plugins };