{"version":3,"sources":["jsdelivr-header.js","/npm/webn@0.2.14/webn.js"],"names":[],"mappings":"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","file":"/npm/webn@0.2.14/webn.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/webn@0.2.14/webn.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/* Copyright © 2021 Magnus Steen\r\n * This software is free. It comes without any warranty, to\r\n * the extent permitted by applicable law. You can redistribute, use\r\n * and/or modify it under the terms of the Do What The Fuck You Want\r\n * To Public License, Version 2, as published by Sam Hocevar. See\r\n * the LICENSE.txt file for more details.\r\n */\r\n\r\n'use strict'\r\n\r\nimport crypto, {constants} from 'node:crypto'\r\nimport {constants as mode, readFileSync} from 'node:fs'\r\nimport fs from 'node:fs/promises'\r\nimport http from 'node:http'\r\nimport https from 'node:https'\r\nimport path from 'node:path'\r\nimport stream from 'node:stream'\r\nimport util from 'node:util'\r\nimport zlib from 'node:zlib'\r\n\r\nconst config = {\t// default configurations that can still be changed by users\r\n\tlog: {error: true},\r\n\tpath: 'web',\r\n\tport: 80,\r\n\tscript: 'script.js',\r\n\ttitle: '',\r\n\ttimeout: 60 * 60 * 24,\r\n\tstreamChunkSize: 2 ** 20,\r\n\tsmallStreamChunkSize: 2 ** 15,\r\n}\r\n\r\nconst internalConfig = {\t// default configurations that will be auto changed if needed, not by users\r\n\tprotocol: http,\r\n\tserverConfig: {},\r\n}\r\n\r\nconst statusCode = {\r\n\tok: 200,\r\n\tempty: 204,\r\n\tpartialContent: 206,\r\n\tbadRequest: 400,\r\n\tunauthorized: 401,\r\n\tillegal: 403,\r\n\tnotFound: 404,\r\n\tnotAllowed: 405,\r\n\terror: 500,\r\n}\r\n\r\nconst pipe = util.promisify(stream.pipeline)\r\n\r\nconst sessionStore = {}\r\n\r\nfunction illegal(request) {\r\n\treturn request.url.includes('..')\r\n}\r\n\r\nfunction verbose(...args) {\r\n\tconfig.log.info(...args)\r\n}\r\n\r\nfunction verror(...args) {\r\n\tconfig.log.error(...args)\r\n}\r\n\r\nfunction responseEncoding(request, data) {\r\n\tif(data === undefined || (typeof data == 'string' || Buffer.isBuffer(data)) && data.length < 500) return []\r\n\tconst acceptEncoding = request.headers['accept-encoding']\r\n\tif(!acceptEncoding) return []\r\n\tif(/gzip/.test(acceptEncoding)) return ['gzip', zlib.createGzip()]\r\n\tif(/deflate/.test(acceptEncoding)) return ['deflate', zlib.createDeflate()]\r\n\treturn []\r\n}\r\n\r\nfunction responseHeaders(encoding, method, url, code, cookies, contentType) {\r\n\tconst headers = {}\r\n\tif(encoding) headers['content-encoding'] = encoding\r\n\tif(config.develop) headers['cache-control'] = 'no-store'\r\n\tif(!config.path) headers['access-control-allow-origin'] = '*'\r\n\tif(code != statusCode.ok) return headers\r\n\tconst sc = []\r\n\tif(cookies) Object.entries(cookies).filter(([, value]) => value !== undefined).forEach(([key, value]) => sc.push(`${key}=${value}`))\r\n\tif(sc.length) headers['set-cookie'] = sc\r\n\theaders['content-type'] = contentType || (method == 'POST'\r\n\t\t? 'application/json; charset=utf-8'\r\n\t\t: [\r\n\t\t\t[/\\/webn$/i, 'application/javascript; charset=utf-8'],\r\n\t\t\t[/\\.js$/i, 'application/javascript; charset=utf-8'],\r\n\t\t\t[/\\.css$/i, 'text/css; charset=utf-8'],\r\n\t\t\t[/\\.ico$/i, 'image/vnd.microsoft.icon'],\r\n\t\t\t[/\\.gif$/i, 'image/gif'],\r\n\t\t\t[/\\.jpg$/i, 'image/jpeg'],\r\n\t\t\t[/\\.jpeg$/i, 'image/jpeg'],\r\n\t\t\t[/\\.jfif$/i, 'image/jpeg'],\r\n\t\t\t[/\\.pjpeg$/i, 'image/jpeg'],\r\n\t\t\t[/\\.pjp$/i, 'image/jpeg'],\r\n\t\t\t[/\\.png$/i, 'image/png'],\r\n\t\t\t[/\\.svg$/i, 'image/svg+xml'],\r\n\t\t\t[/\\.webp$/i, 'image/webp'],\r\n\t\t\t[/\\.avi$/i, 'video/x-msvideo'],\r\n\t\t\t[/\\.bmp$/i, 'image/bmp'],\r\n\t\t\t[/\\.bz$/i, 'application/x-bzip'],\r\n\t\t\t[/\\.bz2$/i, 'application/x-bzip2'],\r\n\t\t\t[/\\.gz$/i, 'application/gzip'],\r\n\t\t\t[/\\.htm$/i, 'text/html; charset=utf-8'],\r\n\t\t\t[/\\.html$/i, 'text/html; charset=utf-8'],\r\n\t\t\t[/\\.mp3$/i, 'audio/mpeg'],\r\n\t\t\t[/\\.mp4$/i, 'video/mp4'],\r\n\t\t\t[/\\.mpeg$/i, 'video/mpeg'],\r\n\t\t\t[/\\.otf$/i, 'font/otf'],\r\n\t\t\t[/\\.ttf$/i, 'font/ttf'],\r\n\t\t\t[/\\.txt$/i, 'text/plain'],\r\n\t\t\t[/./, 'application/octet-stream'],\r\n\t\t].find(type => type[0].test(url))[1])\r\n\treturn headers\r\n}\r\n\r\nfunction streamHeaders(headers, {start, end, length}) {\r\n\theaders['content-range'] = `bytes ${start}-${end}/${length}`\r\n\theaders['accept-ranges'] = 'bytes'\r\n\theaders['content-length'] = end - start + 1\r\n\treturn headers\r\n}\r\n\r\nfunction responseStream(data) {\r\n\tif(data === undefined) return stream.Readable.from(Buffer.from([]))\r\n\tif(typeof data == 'string' || Buffer.isBuffer(data)) return stream.Readable.from(data)\r\n\tif(typeof data.pipe != 'function') return stream.Readable.from(JSON.stringify(data))\r\n\treturn data\r\n}\r\n\r\nasync function respond(request, response, code, data, options, cookies, contentType) {\r\n\tif(!config.nolog || !config.nolog.test(request.url)) verbose(`${`${request.connection.remoteAddress}:${request.connection.remotePort}`} << ${request.url} : ${code}${request.headers.range ? `${options.start}-${options.end}` : ''}`)\r\n\ttry {\r\n\t\tconst responseData = responseStream(data)\r\n\t\tconst [encoding, encodingStream] = responseEncoding(request, data)\r\n\t\tlet headers = responseHeaders(encoding, request.method, request.url, code, cookies, contentType, options)\r\n\t\tif(request.headers.range) {\r\n\t\t\theaders = streamHeaders(headers, options)\r\n\t\t\tif(code == statusCode.ok) {\r\n\t\t\t\tcode = statusCode.partialContent\r\n\t\t\t}\r\n\t\t}\r\n\t\tresponse.writeHead(code == statusCode.ok && data === undefined ? statusCode.empty : code, headers)\r\n\t\tif(encodingStream) {\r\n\t\t\treturn await pipe(responseData, encodingStream, response)\r\n\t\t}\r\n\t\tresponseData.pipe(response)\r\n\t}\r\n\tcatch(error) {\r\n\t\tverror(error.stack)\r\n\t}\r\n}\r\n\r\nfunction setupRootPage() {\r\n\tconfig.page = `<html>\r\n\t\t<head>\r\n\t\t\t${config.title ? `<title>${config.title}</title>` : ''}\r\n\t\t\t<script src=\"${config.script}\" type=\"module\"></script>\r\n\t\t</head>\r\n\t\t<body>\r\n\t\t</body>\r\n\t</html>`\r\n}\r\n\r\nfunction setupClient() {\r\n\tconfig.client = `let url = ''\r\n\tlet base = () => {}\r\n\tconst proxy = path => new Proxy(base, {\r\n\t\tasync apply(_, thisArg, args) {\r\n\t\t\t${config.develop ? 'const t0 = Date.now()' : ''}\r\n\t\t\tconst response = await fetch(\\`\\${url}/\\${path.join('/')}\\`, {\r\n\t\t\t\tmethod: 'POST',\r\n\t\t\t\tbody: JSON.stringify(args),\r\n\t\t\t})\r\n\t\t\tif(response.status != 204) {\r\n\t\t\t\tvar body = await response.json()\r\n\t\t\t}\r\n\t\t\t${config.develop ? 'const t1 = Date.now()' : ''}\r\n\t\t\t${config.develop ? 'console.debug(`${path.join(\\'.\\')}(): ${response.status} ${t1 - t0}ms`)' : ''}\r\n\t\t\tif(response.status >= 400) throw body || ''\r\n\t\t\treturn body\r\n\t\t},\r\n\t\tget(_, prop) {\r\n\t\t\treturn proxy(path.concat([prop]))\r\n\t\t},\r\n\t\tset(_, prop, value) {\r\n\t\t\tif(prop == 'url') {\r\n\t\t\t\turl = value\r\n\t\t\t\treturn true\r\n\t\t\t}\r\n\t\t}\r\n\t})\r\n\texport default ${config.global ? 'window.webn = ' : ''}proxy([])`\r\n}\r\n\r\nfunction extractRequestCookies(request) {\r\n\treturn request.headers.cookie && request.headers.cookie.split(';').reduce((cookies, cookie) => {\r\n\t\tconst [, name, value] = cookie.match(/(.*?)=(.*)$/)\r\n\t\tcookies[name.trim()] = (value || '').trim()\r\n\t\treturn cookies\r\n\t}, {}) || []\r\n}\r\n\r\nfunction createSession() {\r\n\twhile(true) {\r\n\t\tconst token = crypto.randomBytes(150).toString('base64')\r\n\t\tif(!sessionStore[token]) return {token, userSession: {}, access: []}\r\n\t}\r\n}\r\n\r\nfunction refreshSession(session) {\r\n\tsession.expiry = Date.now() + 1000 * config.timeout\r\n}\r\n\r\nfunction getSession(requestCookies) {\r\n\tconst sessionCookie = requestCookies['webn-session']\r\n\tconst session = sessionStore[sessionCookie]\r\n\tif(session) refreshSession(session)\r\n\treturn session\r\n}\r\n\r\nfunction preparePostUtils(request, requestCookies, session) {\r\n\tconst responseCookies = {}\r\n\tconst postUtils = {\r\n\t\tcookie: new Proxy({}, {\r\n\t\t\tget(_, name) {return requestCookies[name]},\r\n\t\t\tset(_, name, value) {responseCookies[name] = value},\r\n\t\t}),\r\n\t\tgrant(access) {\r\n\t\t\tif(!session) {\r\n\t\t\t\tsession = createSession()\r\n\t\t\t\tsessionStore[session.token] = session\r\n\t\t\t\tresponseCookies['webn-session'] = `${session.token}; httponly; samesite=strict`\r\n\t\t\t}\r\n\t\t\tif(!session.access) session.access = []\r\n\t\t\tif(!session.access.includes(access)) session.access.push(access)\r\n\t\t},\r\n\t\trevoke(access) {\r\n\t\t\tif(!session) return\r\n\t\t\tif(!access) delete sessionStore[session.token]\r\n\t\t\tif(access instanceof RegExp) session.access = session.access.filter(path => !access.test(path))\r\n\t\t\telse session.access = session.access.filter(path => path != access)\r\n\t\t},\r\n\t\tget grants() {\r\n\t\t\treturn session.access || []\r\n\t\t},\r\n\t\tget request() {\r\n\t\t\treturn request\r\n\t\t},\r\n\t\tinfo: (...args) => verbose(...args),\r\n\t\terror: (...args) => verror(...args),\r\n\t\tsession: session?.userSession,\r\n\t}\r\n\treturn [postUtils, responseCookies]\r\n}\r\n\r\nfunction findPostMethod(functionPath, handlers) {\r\n\tif(!handlers) return undefined\r\n\tif(functionPath[0].startsWith('$')) return undefined\r\n\tif(functionPath.length > 1) return findPostMethod(functionPath.slice(1), handlers[functionPath[0]])\r\n\treturn handlers[functionPath[0]]\r\n}\r\n\r\nfunction verifyAccess(functionPath, session) {\r\n\tif(functionPath.length == 1) return true\r\n\tif(!session) return false\r\n\tconst requested = functionPath.slice(0, -1).join('.')\r\n\treturn session.access.some(path => path == requested)\r\n}\r\n\r\nfunction post(request, response, requestCookies, session) {\r\n\tconst functionName = request.url.substring(1)\r\n\tconst functionPath = functionName.split('/').filter(part => part.length)\r\n\tconst handler = findPostMethod(functionPath, config.handlers)\r\n\tif(!verifyAccess(functionPath, session)) return respond(request, response, statusCode.unauthorized, JSON.stringify(new Error(`Not authorized for \"${functionName}\"`)))\r\n\tif(!handler) return respond(request, response, statusCode.badRequest, JSON.stringify(new Error(`No resource named \"${functionName}\"`)))\r\n\tconst body = []\r\n\trequest.on('data', chunk => body.push(chunk))\r\n\trequest.on('end', async () => {\r\n\t\ttry {\r\n\t\t\tconst data = Buffer.concat(body)\r\n\t\t\tconst args = data.length\r\n\t\t\t\t? JSON.parse(data)\r\n\t\t\t\t: []\r\n\t\t\tconst [postUtils, responseCookies] = preparePostUtils(request, requestCookies, session)\r\n\t\t\tconst result = await handler.call(postUtils, ...args)\r\n\t\t\treturn await respond(request, response, statusCode.ok, JSON.stringify(result), responseCookies)\r\n\t\t}\r\n\t\tcatch(error) {\r\n\t\t\tif(error.stack) verror(error.stack)\r\n\t\t\treturn respond(request, response, statusCode.error, JSON.stringify(config.develop || typeof error == 'string' ? error.message || error : 'Internal Server Error'))\r\n\t\t}\r\n\t})\r\n}\r\n\r\nfunction prepareRangeOptions(request, file, stat) {\r\n\t// if(!request.headers.range) return undefined\r\n\tconst start = Number(request.headers.range.replace(/\\D/g, ''))\r\n\tconst end = Math.min(start + (start == 0 ? config.smallStreamChunkSize : config.streamChunkSize), stat.size - 1)\r\n\treturn {start, end, length: stat.size}\r\n}\r\n\r\nasync function get(request, response) {\r\n\ttry {\r\n\t\trequest.url = request.url.replace(/\\?.*/, '')\r\n\t\tif(request.url == '/') request.url = '/index.html'\r\n\t\tif(request.url == '/webn') return await respond(request, response, statusCode.ok, config.client)\r\n\t\tif(request.url.startsWith('/.well-known') && !config.wellknown) return await respond(request, response, statusCode.empty, '')\r\n\t\tconst handler = config.handlers?.$get?.[request.url]\r\n\t\tif(handler) {\r\n\t\t\tconst {statusCode, body, contentType, cookies} = await handler()\r\n\t\t\treturn await respond(request, response, statusCode, body, undefined, cookies, contentType)\r\n\t\t}\r\n\t\tlet fileName = path.join(config.path, request.url.substring(1))\r\n\t\tawait fs.access(fileName, mode.R_OK)\r\n\t\tconst stat = await fs.lstat(fileName)\r\n\t\tif(stat.isDirectory()) {\r\n\t\t\tfileName = path.join(fileName, 'index.html')\r\n\t\t\trequest.url = `${request.url}/index.html`.replace(/\\/\\//, '/')\r\n\t\t}\r\n\t\tverbose(`FILE ACCESS ${fileName}`)\r\n\t\tconst file = await fs.open(fileName)\r\n\t\tif(request.headers.range)  {\r\n\t\t\tconst {start, end, length} = await prepareRangeOptions(request, fileName, stat)\r\n\t\t\tconst data = file.createReadStream({start, end})\r\n\t\t\tawait respond(request, response, statusCode.partialContent, data, {start, end, length})\r\n\t\t}\r\n\t\telse {\r\n\t\t\tconst data = file.createReadStream({})\r\n\t\t\tawait respond(request, response, statusCode.ok, data)\r\n\t\t}\r\n\t}\r\n\tcatch(error) {\r\n\t\tconsole.error(error)\r\n\t\tif(request.url == '/index.html') return respond(request, response, statusCode.ok, config.page)\r\n\t\tverror(error.stack)\r\n\t\tif(error.code == 'ENOENT') return respond(request, response, statusCode.notFound, config.develop && JSON.stringify(error.message) || undefined)\r\n\t\tawait respond(request, response, statusCode.error, config.develop && config.JSON.stringify(error.message) || undefined)\r\n\t}\r\n}\r\n\r\nasync function serve(request, response) {\r\n\ttry {\r\n\t\tif(!config.nolog || !config.nolog.test(request.url)) verbose(`${`${request.connection.remoteAddress}:${request.connection.remotePort}`} >> ${request.method} ${request.url}`)\r\n\t\tif(illegal(request)) return respond(request, response, statusCode.illegal)\r\n\t\tconst requestCookies = extractRequestCookies(request)\r\n\t\tconst session = getSession(requestCookies)\r\n\t\tif(request.method == 'POST') return await post(request, response, requestCookies, session)\r\n\t\tif(request.method == 'GET') return await get(request, response)\r\n\t\tawait respond(request, response, statusCode.notAllowed)\r\n\t}\r\n\tcatch(error) {\r\n\t\tverror(error.stack)\r\n\t}\r\n}\r\n\r\nfunction ready(resolve, reject, server) {\r\n\treturn error => {\r\n\t\tif(error) {\r\n\t\t\tverror(error)\r\n\t\t\treject(error)\r\n\t\t}\r\n\t\tverbose(`${config.title || 'Listening'} @ port ${config.port}.`)\r\n\t\tresolve(server)\r\n\t}\r\n}\r\n\r\nfunction configure(applicationConfig, handlers) {\r\n\tObject.assign(config, applicationConfig)\r\n\tObject.assign(config, internalConfig)\r\n\tif(config.log === true) config.log = {info: true, error: true}\r\n\tif(config.log === false) config.log = {info: false, error: false}\r\n\tif(typeof config.log == 'function') config.log = {info: config.log, error: config.log}\r\n\tif(config.log.info === true) config.log.info = (...args) => console.info(`[${new Date().toLocaleString()}]`, ...args)\r\n\tif(!config.log.info) config.log.info = () => {}\r\n\tif(applicationConfig?.log && !applicationConfig.log.hasOwnProperty('error')) config.log.error = true\r\n\tif(config.log.error === true) config.log.error = (...args) => console.error(`[${new Date().toLocaleString()}]`, ...args)\r\n\tif(config.log.error === false) config.log.error = () => {}\r\n\tconfig.handlers = handlers\r\n\tif(config.key && config.cert || config.pfx && config.passphrase) {\r\n\t\tif(!applicationConfig.port) config.port = 443\r\n\t\thttpsConfig()\r\n\t}\r\n\r\n}\r\n\r\nfunction httpsConfig() {\r\n\tObject.assign(config.serverConfig, {\r\n\t\tsecureOptions: constants.SSL_OP_NO_SSLv2\r\n\t\t\t| constants.SSL_OP_NO_SSLv3\r\n\t\t\t| constants.SSL_OP_NO_TLSv1\r\n\t\t\t| constants.SSL_OP_NO_TLSv1_1,\r\n\t})\r\n\tconfig.protocol = https\r\n\tif(config.key && config.cert) {\r\n\t\ttry {config.key = readFileSync(config.key)}\r\n\t\tcatch{}\r\n\t\ttry {config.cert = readFileSync(config.cert)}\r\n\t\tcatch{}\r\n\t\tObject.assign(config.serverConfig, {key: config.key, cert: config.cert})\r\n\t}\r\n\telse if(config.pfx && config.passphrase) {\r\n\t\ttry {config.pfx = readFileSync(config.pfx)}\r\n\t\tcatch{}\r\n\t\tObject.assign(config.serverConfig, {pfx: config.pfx, passphrase: config.passphrase})\r\n\t}\r\n}\r\n\r\nfunction sessionCleaner() {\r\n\tconst sessions = Object.entries(sessionStore)\r\n\tconst now = Date.now()\r\n\tfor(let i = sessions.length; i --> 0;) {\r\n\t\tif(now > sessionStore[sessions[i]].expiry) {\r\n\t\t\tdelete sessionStore[sessions[i]]\r\n\t\t}\r\n\t}\r\n\tsetTimeout(sessionCleaner, 60 * 60 * 1000)\r\n}\r\n\r\nfunction setupSessionCleaner() {\r\n\tsessionCleaner()\r\n}\r\n\r\n/**\r\n *\r\n * @param {object} [applicationConfig]\r\n * @param {string} applicationConfig.title - Name of application. Default is empty string\r\n * @param {function} applicationConfig.log.info - Function to call for logging. Set to true to log to console.info\r\n * @param {function} applicationConfig.log.error - Function to call for logging. Defaults to console.error. Set to false to turn off error logging\r\n * @param {boolean} applicationConfig.develop - Set this to true to remove caching and add some extra logging\r\n * @param {string} applicationConfig.path - Path to frontend files. Default is \"web\"\r\n * @param {number} applicationConfig.port - Http/s port used by application. Default is 80 for http and 443 for https\r\n * @param {string} applicationConfig.script - Inject this script file into a dummy index file if there is no index.html. Default is script.js\r\n * @param {string} applicationConfig.version - Version number. No default\r\n * @param {string} applicationConfig.key - Define this together with cert for https. Filename or key content\r\n * @param {string} applicationConfig.cert - Define this together with key for https. Filename or certificate content\r\n * @param {string} applicationConfig.pfx - Define this together with passphrase for https. Filename or pfx content\r\n * @param {string} applicationConfig.passphrase - Define this together with pfx for https.\r\n * @param {string} applicationConfig.wellknown - Allow well-known urls (they are otherwise ignored).\r\n * @param {RegExp} applicationConfig.nolog - Do not log POST and response on urls matching this regexp.\r\n * @param {object} handlers - Object holding the backend handlers that can be called from the frontend\r\n */\r\nexport default (applicationConfig, handlers) => new Promise((resolve, reject) => {\r\n\tif(!handlers && applicationConfig && (Object.values(applicationConfig).some(value => typeof value == 'function') || applicationConfig.$get)) {\r\n\t\thandlers = applicationConfig\r\n\t\tapplicationConfig = undefined\r\n\t}\r\n\tconfigure(applicationConfig, handlers)\r\n\tsetupRootPage()\r\n\tsetupClient()\r\n\tsetupSessionCleaner()\r\n\tconst server = config.protocol.createServer(config.serverConfig, serve)\r\n\tserver.listen(config.port, ready(resolve, reject, server))\r\n})\r\n"]}