UNPKG

sw2express

Version:

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

604 lines (578 loc) 15.9 kB
(async function () { const sw2express = await Promise.resolve().then(function () { return index; }); const packageJson = await Promise.resolve().then(function () { return _package$1; }); if (!(self["sw2express-Version"] = packageJson.version)) { self["sw2express-Version"] = packageJson.version; self.sw2express = sw2express.default; self.checkPlatform = sw2express.checkPlatform; self.sw2expressPlugins = sw2express.plugins; } else { console.warn("sw2express is loaded"); } })(); 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 }; var index = /*#__PURE__*/Object.freeze({ __proto__: null, checkPlatform: checkPlatform, 'default': sw2express, plugins: plugins }); var type = "module"; var main = "index.js"; var exports = { require: "./build/index.cjs", "import": "./index.js" }; var name = "sw2express"; var version = "0.0.10-3"; var description = "A lite & simple cross-platform Express-like web application framework"; var directories = { test: "test" }; var scripts = { "build:normal": "rollup index.js -p @rollup/plugin-json -o build/index.js && minify build/index.js > build/index.min.js", "build:sw": "rollup sw.js -p @rollup/plugin-json --inlineDynamicImports -o build/index.sw.js && minify build/index.sw.js > build/index.sw.min.js", "build:cjs": "rollup index.js -p @rollup/plugin-json --format cjs -o build/index.cjs", prettier: "prettier -w \\*\\*.js \\*\\*.ts", dist: "yarn build:normal&yarn build:sw& yarn build:cjs", clean: "rm -rf ./build", prepublishOnly: "yarn prettier && yarn clean && yarn dist" }; var repository = { type: "git", url: "https://git.186526.xyz/186526/Sw2Express.git" }; var keywords = [ "Express-like", "Web", "Framework", "API", "express" ]; var author = "real186 <i@186526.xyz>"; var license = "WTFPL"; var mode = "module"; var devDependencies = { "@rollup/plugin-json": "^4.1.0", minify: "^7.0.1", prettier: "^2.2.1", rollup: "^2.44.0", yarn: "^1.22.10" }; var types = "index.d.ts"; var dependencies = { }; var _package = { type: type, main: main, exports: exports, name: name, version: version, description: description, directories: directories, scripts: scripts, repository: repository, keywords: keywords, author: author, license: license, mode: mode, devDependencies: devDependencies, types: types, dependencies: dependencies }; var _package$1 = /*#__PURE__*/Object.freeze({ __proto__: null, type: type, main: main, exports: exports, name: name, version: version, description: description, directories: directories, scripts: scripts, repository: repository, keywords: keywords, author: author, license: license, mode: mode, devDependencies: devDependencies, types: types, dependencies: dependencies, 'default': _package });