@mikestraczek/cms-auth
Version:
Authentication and user management for the CMS template
1 lines • 748 kB
Source Map (JSON)
{"version":3,"sources":["../../../node_modules/nodemailer/lib/fetch/cookies.js","../../../node_modules/nodemailer/package.json","../../../node_modules/nodemailer/lib/fetch/index.js","../../../node_modules/nodemailer/lib/shared/index.js","../../../node_modules/nodemailer/lib/mime-funcs/mime-types.js","../../../node_modules/nodemailer/lib/punycode/index.js","../../../node_modules/nodemailer/lib/base64/index.js","../../../node_modules/nodemailer/lib/qp/index.js","../../../node_modules/nodemailer/lib/mime-funcs/index.js","../../../node_modules/nodemailer/lib/addressparser/index.js","../../../node_modules/nodemailer/lib/mime-node/last-newline.js","../../../node_modules/nodemailer/lib/mime-node/le-windows.js","../../../node_modules/nodemailer/lib/mime-node/le-unix.js","../../../node_modules/nodemailer/lib/mime-node/index.js","../../../node_modules/nodemailer/lib/mail-composer/index.js","../../../node_modules/nodemailer/lib/dkim/message-parser.js","../../../node_modules/nodemailer/lib/dkim/relaxed-body.js","../../../node_modules/nodemailer/lib/dkim/sign.js","../../../node_modules/nodemailer/lib/dkim/index.js","../../../node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js","../../../node_modules/nodemailer/lib/mailer/mail-message.js","../../../node_modules/nodemailer/lib/mailer/index.js","../../../node_modules/nodemailer/lib/smtp-connection/data-stream.js","../../../node_modules/nodemailer/lib/smtp-connection/index.js","../../../node_modules/nodemailer/lib/xoauth2/index.js","../../../node_modules/nodemailer/lib/smtp-pool/pool-resource.js","../../../node_modules/nodemailer/lib/well-known/services.json","../../../node_modules/nodemailer/lib/well-known/index.js","../../../node_modules/nodemailer/lib/smtp-pool/index.js","../../../node_modules/nodemailer/lib/smtp-transport/index.js","../../../node_modules/nodemailer/lib/sendmail-transport/index.js","../../../node_modules/nodemailer/lib/stream-transport/index.js","../../../node_modules/nodemailer/lib/json-transport/index.js","../../../node_modules/nodemailer/lib/ses-transport/index.js","../../../node_modules/nodemailer/lib/nodemailer.js","../src/config/auth-config.ts","../src/types/user.ts","../src/constants/roles.ts","../src/lib/email.ts","../src/lib/password.ts","../src/schemas/user-schema.ts"],"sourcesContent":["'use strict';\n\n// module to handle cookies\n\nconst urllib = require('url');\n\nconst SESSION_TIMEOUT = 1800; // 30 min\n\n/**\n * Creates a biskviit cookie jar for managing cookie values in memory\n *\n * @constructor\n * @param {Object} [options] Optional options object\n */\nclass Cookies {\n constructor(options) {\n this.options = options || {};\n this.cookies = [];\n }\n\n /**\n * Stores a cookie string to the cookie storage\n *\n * @param {String} cookieStr Value from the 'Set-Cookie:' header\n * @param {String} url Current URL\n */\n set(cookieStr, url) {\n let urlparts = urllib.parse(url || '');\n let cookie = this.parse(cookieStr);\n let domain;\n\n if (cookie.domain) {\n domain = cookie.domain.replace(/^\\./, '');\n\n // do not allow cross origin cookies\n if (\n // can't be valid if the requested domain is shorter than current hostname\n urlparts.hostname.length < domain.length ||\n // prefix domains with dot to be sure that partial matches are not used\n ('.' + urlparts.hostname).substr(-domain.length + 1) !== '.' + domain\n ) {\n cookie.domain = urlparts.hostname;\n }\n } else {\n cookie.domain = urlparts.hostname;\n }\n\n if (!cookie.path) {\n cookie.path = this.getPath(urlparts.pathname);\n }\n\n // if no expire date, then use sessionTimeout value\n if (!cookie.expires) {\n cookie.expires = new Date(Date.now() + (Number(this.options.sessionTimeout || SESSION_TIMEOUT) || SESSION_TIMEOUT) * 1000);\n }\n\n return this.add(cookie);\n }\n\n /**\n * Returns cookie string for the 'Cookie:' header.\n *\n * @param {String} url URL to check for\n * @returns {String} Cookie header or empty string if no matches were found\n */\n get(url) {\n return this.list(url)\n .map(cookie => cookie.name + '=' + cookie.value)\n .join('; ');\n }\n\n /**\n * Lists all valied cookie objects for the specified URL\n *\n * @param {String} url URL to check for\n * @returns {Array} An array of cookie objects\n */\n list(url) {\n let result = [];\n let i;\n let cookie;\n\n for (i = this.cookies.length - 1; i >= 0; i--) {\n cookie = this.cookies[i];\n\n if (this.isExpired(cookie)) {\n this.cookies.splice(i, i);\n continue;\n }\n\n if (this.match(cookie, url)) {\n result.unshift(cookie);\n }\n }\n\n return result;\n }\n\n /**\n * Parses cookie string from the 'Set-Cookie:' header\n *\n * @param {String} cookieStr String from the 'Set-Cookie:' header\n * @returns {Object} Cookie object\n */\n parse(cookieStr) {\n let cookie = {};\n\n (cookieStr || '')\n .toString()\n .split(';')\n .forEach(cookiePart => {\n let valueParts = cookiePart.split('=');\n let key = valueParts.shift().trim().toLowerCase();\n let value = valueParts.join('=').trim();\n let domain;\n\n if (!key) {\n // skip empty parts\n return;\n }\n\n switch (key) {\n case 'expires':\n value = new Date(value);\n // ignore date if can not parse it\n if (value.toString() !== 'Invalid Date') {\n cookie.expires = value;\n }\n break;\n\n case 'path':\n cookie.path = value;\n break;\n\n case 'domain':\n domain = value.toLowerCase();\n if (domain.length && domain.charAt(0) !== '.') {\n domain = '.' + domain; // ensure preceeding dot for user set domains\n }\n cookie.domain = domain;\n break;\n\n case 'max-age':\n cookie.expires = new Date(Date.now() + (Number(value) || 0) * 1000);\n break;\n\n case 'secure':\n cookie.secure = true;\n break;\n\n case 'httponly':\n cookie.httponly = true;\n break;\n\n default:\n if (!cookie.name) {\n cookie.name = key;\n cookie.value = value;\n }\n }\n });\n\n return cookie;\n }\n\n /**\n * Checks if a cookie object is valid for a specified URL\n *\n * @param {Object} cookie Cookie object\n * @param {String} url URL to check for\n * @returns {Boolean} true if cookie is valid for specifiec URL\n */\n match(cookie, url) {\n let urlparts = urllib.parse(url || '');\n\n // check if hostname matches\n // .foo.com also matches subdomains, foo.com does not\n if (\n urlparts.hostname !== cookie.domain &&\n (cookie.domain.charAt(0) !== '.' || ('.' + urlparts.hostname).substr(-cookie.domain.length) !== cookie.domain)\n ) {\n return false;\n }\n\n // check if path matches\n let path = this.getPath(urlparts.pathname);\n if (path.substr(0, cookie.path.length) !== cookie.path) {\n return false;\n }\n\n // check secure argument\n if (cookie.secure && urlparts.protocol !== 'https:') {\n return false;\n }\n\n return true;\n }\n\n /**\n * Adds (or updates/removes if needed) a cookie object to the cookie storage\n *\n * @param {Object} cookie Cookie value to be stored\n */\n add(cookie) {\n let i;\n let len;\n\n // nothing to do here\n if (!cookie || !cookie.name) {\n return false;\n }\n\n // overwrite if has same params\n for (i = 0, len = this.cookies.length; i < len; i++) {\n if (this.compare(this.cookies[i], cookie)) {\n // check if the cookie needs to be removed instead\n if (this.isExpired(cookie)) {\n this.cookies.splice(i, 1); // remove expired/unset cookie\n return false;\n }\n\n this.cookies[i] = cookie;\n return true;\n }\n }\n\n // add as new if not already expired\n if (!this.isExpired(cookie)) {\n this.cookies.push(cookie);\n }\n\n return true;\n }\n\n /**\n * Checks if two cookie objects are the same\n *\n * @param {Object} a Cookie to check against\n * @param {Object} b Cookie to check against\n * @returns {Boolean} True, if the cookies are the same\n */\n compare(a, b) {\n return a.name === b.name && a.path === b.path && a.domain === b.domain && a.secure === b.secure && a.httponly === a.httponly;\n }\n\n /**\n * Checks if a cookie is expired\n *\n * @param {Object} cookie Cookie object to check against\n * @returns {Boolean} True, if the cookie is expired\n */\n isExpired(cookie) {\n return (cookie.expires && cookie.expires < new Date()) || !cookie.value;\n }\n\n /**\n * Returns normalized cookie path for an URL path argument\n *\n * @param {String} pathname\n * @returns {String} Normalized path\n */\n getPath(pathname) {\n let path = (pathname || '/').split('/');\n path.pop(); // remove filename part\n path = path.join('/').trim();\n\n // ensure path prefix /\n if (path.charAt(0) !== '/') {\n path = '/' + path;\n }\n\n // ensure path suffix /\n if (path.substr(-1) !== '/') {\n path += '/';\n }\n\n return path;\n }\n}\n\nmodule.exports = Cookies;\n","{\n \"name\": \"nodemailer\",\n \"version\": \"7.0.5\",\n \"description\": \"Easy as cake e-mail sending from your Node.js applications\",\n \"main\": \"lib/nodemailer.js\",\n \"scripts\": {\n \"test\": \"node --test --test-concurrency=1 test/**/*.test.js test/**/*-test.js\",\n \"test:coverage\": \"c8 node --test --test-concurrency=1 test/**/*.test.js test/**/*-test.js\",\n \"lint\": \"eslint .\",\n \"update\": \"rm -rf node_modules/ package-lock.json && ncu -u && npm install\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/nodemailer/nodemailer.git\"\n },\n \"keywords\": [\n \"Nodemailer\"\n ],\n \"author\": \"Andris Reinman\",\n \"license\": \"MIT-0\",\n \"bugs\": {\n \"url\": \"https://github.com/nodemailer/nodemailer/issues\"\n },\n \"homepage\": \"https://nodemailer.com/\",\n \"devDependencies\": {\n \"@aws-sdk/client-sesv2\": \"3.839.0\",\n \"bunyan\": \"1.8.15\",\n \"c8\": \"10.1.3\",\n \"eslint\": \"8.57.0\",\n \"eslint-config-nodemailer\": \"1.2.0\",\n \"eslint-config-prettier\": \"9.1.0\",\n \"libbase64\": \"1.3.0\",\n \"libmime\": \"5.3.7\",\n \"libqp\": \"2.1.1\",\n \"nodemailer-ntlm-auth\": \"1.0.4\",\n \"proxy\": \"1.0.2\",\n \"proxy-test-server\": \"1.0.0\",\n \"smtp-server\": \"3.14.0\"\n },\n \"engines\": {\n \"node\": \">=6.0.0\"\n }\n}\n","'use strict';\n\nconst http = require('http');\nconst https = require('https');\nconst urllib = require('url');\nconst zlib = require('zlib');\nconst PassThrough = require('stream').PassThrough;\nconst Cookies = require('./cookies');\nconst packageData = require('../../package.json');\nconst net = require('net');\n\nconst MAX_REDIRECTS = 5;\n\nmodule.exports = function (url, options) {\n return nmfetch(url, options);\n};\n\nmodule.exports.Cookies = Cookies;\n\nfunction nmfetch(url, options) {\n options = options || {};\n\n options.fetchRes = options.fetchRes || new PassThrough();\n options.cookies = options.cookies || new Cookies();\n options.redirects = options.redirects || 0;\n options.maxRedirects = isNaN(options.maxRedirects) ? MAX_REDIRECTS : options.maxRedirects;\n\n if (options.cookie) {\n [].concat(options.cookie || []).forEach(cookie => {\n options.cookies.set(cookie, url);\n });\n options.cookie = false;\n }\n\n let fetchRes = options.fetchRes;\n let parsed = urllib.parse(url);\n let method = (options.method || '').toString().trim().toUpperCase() || 'GET';\n let finished = false;\n let cookies;\n let body;\n\n let handler = parsed.protocol === 'https:' ? https : http;\n\n let headers = {\n 'accept-encoding': 'gzip,deflate',\n 'user-agent': 'nodemailer/' + packageData.version\n };\n\n Object.keys(options.headers || {}).forEach(key => {\n headers[key.toLowerCase().trim()] = options.headers[key];\n });\n\n if (options.userAgent) {\n headers['user-agent'] = options.userAgent;\n }\n\n if (parsed.auth) {\n headers.Authorization = 'Basic ' + Buffer.from(parsed.auth).toString('base64');\n }\n\n if ((cookies = options.cookies.get(url))) {\n headers.cookie = cookies;\n }\n\n if (options.body) {\n if (options.contentType !== false) {\n headers['Content-Type'] = options.contentType || 'application/x-www-form-urlencoded';\n }\n\n if (typeof options.body.pipe === 'function') {\n // it's a stream\n headers['Transfer-Encoding'] = 'chunked';\n body = options.body;\n body.on('error', err => {\n if (finished) {\n return;\n }\n finished = true;\n err.type = 'FETCH';\n err.sourceUrl = url;\n fetchRes.emit('error', err);\n });\n } else {\n if (options.body instanceof Buffer) {\n body = options.body;\n } else if (typeof options.body === 'object') {\n try {\n // encodeURIComponent can fail on invalid input (partial emoji etc.)\n body = Buffer.from(\n Object.keys(options.body)\n .map(key => {\n let value = options.body[key].toString().trim();\n return encodeURIComponent(key) + '=' + encodeURIComponent(value);\n })\n .join('&')\n );\n } catch (E) {\n if (finished) {\n return;\n }\n finished = true;\n E.type = 'FETCH';\n E.sourceUrl = url;\n fetchRes.emit('error', E);\n return;\n }\n } else {\n body = Buffer.from(options.body.toString().trim());\n }\n\n headers['Content-Type'] = options.contentType || 'application/x-www-form-urlencoded';\n headers['Content-Length'] = body.length;\n }\n // if method is not provided, use POST instead of GET\n method = (options.method || '').toString().trim().toUpperCase() || 'POST';\n }\n\n let req;\n let reqOptions = {\n method,\n host: parsed.hostname,\n path: parsed.path,\n port: parsed.port ? parsed.port : parsed.protocol === 'https:' ? 443 : 80,\n headers,\n rejectUnauthorized: false,\n agent: false\n };\n\n if (options.tls) {\n Object.keys(options.tls).forEach(key => {\n reqOptions[key] = options.tls[key];\n });\n }\n\n if (parsed.protocol === 'https:' && parsed.hostname && parsed.hostname !== reqOptions.host && !net.isIP(parsed.hostname) && !reqOptions.servername) {\n reqOptions.servername = parsed.hostname;\n }\n\n try {\n req = handler.request(reqOptions);\n } catch (E) {\n finished = true;\n setImmediate(() => {\n E.type = 'FETCH';\n E.sourceUrl = url;\n fetchRes.emit('error', E);\n });\n return fetchRes;\n }\n\n if (options.timeout) {\n req.setTimeout(options.timeout, () => {\n if (finished) {\n return;\n }\n finished = true;\n req.abort();\n let err = new Error('Request Timeout');\n err.type = 'FETCH';\n err.sourceUrl = url;\n fetchRes.emit('error', err);\n });\n }\n\n req.on('error', err => {\n if (finished) {\n return;\n }\n finished = true;\n err.type = 'FETCH';\n err.sourceUrl = url;\n fetchRes.emit('error', err);\n });\n\n req.on('response', res => {\n let inflate;\n\n if (finished) {\n return;\n }\n\n switch (res.headers['content-encoding']) {\n case 'gzip':\n case 'deflate':\n inflate = zlib.createUnzip();\n break;\n }\n\n if (res.headers['set-cookie']) {\n [].concat(res.headers['set-cookie'] || []).forEach(cookie => {\n options.cookies.set(cookie, url);\n });\n }\n\n if ([301, 302, 303, 307, 308].includes(res.statusCode) && res.headers.location) {\n // redirect\n options.redirects++;\n if (options.redirects > options.maxRedirects) {\n finished = true;\n let err = new Error('Maximum redirect count exceeded');\n err.type = 'FETCH';\n err.sourceUrl = url;\n fetchRes.emit('error', err);\n req.abort();\n return;\n }\n // redirect does not include POST body\n options.method = 'GET';\n options.body = false;\n return nmfetch(urllib.resolve(url, res.headers.location), options);\n }\n\n fetchRes.statusCode = res.statusCode;\n fetchRes.headers = res.headers;\n\n if (res.statusCode >= 300 && !options.allowErrorResponse) {\n finished = true;\n let err = new Error('Invalid status code ' + res.statusCode);\n err.type = 'FETCH';\n err.sourceUrl = url;\n fetchRes.emit('error', err);\n req.abort();\n return;\n }\n\n res.on('error', err => {\n if (finished) {\n return;\n }\n finished = true;\n err.type = 'FETCH';\n err.sourceUrl = url;\n fetchRes.emit('error', err);\n req.abort();\n });\n\n if (inflate) {\n res.pipe(inflate).pipe(fetchRes);\n inflate.on('error', err => {\n if (finished) {\n return;\n }\n finished = true;\n err.type = 'FETCH';\n err.sourceUrl = url;\n fetchRes.emit('error', err);\n req.abort();\n });\n } else {\n res.pipe(fetchRes);\n }\n });\n\n setImmediate(() => {\n if (body) {\n try {\n if (typeof body.pipe === 'function') {\n return body.pipe(req);\n } else {\n req.write(body);\n }\n } catch (err) {\n finished = true;\n err.type = 'FETCH';\n err.sourceUrl = url;\n fetchRes.emit('error', err);\n return;\n }\n }\n req.end();\n });\n\n return fetchRes;\n}\n","/* eslint no-console: 0 */\n\n'use strict';\n\nconst urllib = require('url');\nconst util = require('util');\nconst fs = require('fs');\nconst nmfetch = require('../fetch');\nconst dns = require('dns');\nconst net = require('net');\nconst os = require('os');\n\nconst DNS_TTL = 5 * 60 * 1000;\n\nlet networkInterfaces;\ntry {\n networkInterfaces = os.networkInterfaces();\n} catch (err) {\n // fails on some systems\n}\n\nmodule.exports.networkInterfaces = networkInterfaces;\n\nconst isFamilySupported = (family, allowInternal) => {\n let networkInterfaces = module.exports.networkInterfaces;\n if (!networkInterfaces) {\n // hope for the best\n return true;\n }\n\n const familySupported =\n // crux that replaces Object.values(networkInterfaces) as Object.values is not supported in nodejs v6\n Object.keys(networkInterfaces)\n .map(key => networkInterfaces[key])\n // crux that replaces .flat() as it is not supported in older Node versions (v10 and older)\n .reduce((acc, val) => acc.concat(val), [])\n .filter(i => !i.internal || allowInternal)\n .filter(i => i.family === 'IPv' + family || i.family === family).length > 0;\n\n return familySupported;\n};\n\nconst resolver = (family, hostname, options, callback) => {\n options = options || {};\n const familySupported = isFamilySupported(family, options.allowInternalNetworkInterfaces);\n\n if (!familySupported) {\n return callback(null, []);\n }\n\n const resolver = dns.Resolver ? new dns.Resolver(options) : dns;\n resolver['resolve' + family](hostname, (err, addresses) => {\n if (err) {\n switch (err.code) {\n case dns.NODATA:\n case dns.NOTFOUND:\n case dns.NOTIMP:\n case dns.SERVFAIL:\n case dns.CONNREFUSED:\n case dns.REFUSED:\n case 'EAI_AGAIN':\n return callback(null, []);\n }\n return callback(err);\n }\n return callback(null, Array.isArray(addresses) ? addresses : [].concat(addresses || []));\n });\n};\n\nconst dnsCache = (module.exports.dnsCache = new Map());\n\nconst formatDNSValue = (value, extra) => {\n if (!value) {\n return Object.assign({}, extra || {});\n }\n\n return Object.assign(\n {\n servername: value.servername,\n host:\n !value.addresses || !value.addresses.length\n ? null\n : value.addresses.length === 1\n ? value.addresses[0]\n : value.addresses[Math.floor(Math.random() * value.addresses.length)]\n },\n extra || {}\n );\n};\n\nmodule.exports.resolveHostname = (options, callback) => {\n options = options || {};\n\n if (!options.host && options.servername) {\n options.host = options.servername;\n }\n\n if (!options.host || net.isIP(options.host)) {\n // nothing to do here\n let value = {\n addresses: [options.host],\n servername: options.servername || false\n };\n return callback(\n null,\n formatDNSValue(value, {\n cached: false\n })\n );\n }\n\n let cached;\n if (dnsCache.has(options.host)) {\n cached = dnsCache.get(options.host);\n\n if (!cached.expires || cached.expires >= Date.now()) {\n return callback(\n null,\n formatDNSValue(cached.value, {\n cached: true\n })\n );\n }\n }\n\n resolver(4, options.host, options, (err, addresses) => {\n if (err) {\n if (cached) {\n // ignore error, use expired value\n return callback(\n null,\n formatDNSValue(cached.value, {\n cached: true,\n error: err\n })\n );\n }\n return callback(err);\n }\n\n if (addresses && addresses.length) {\n let value = {\n addresses,\n servername: options.servername || options.host\n };\n\n dnsCache.set(options.host, {\n value,\n expires: Date.now() + (options.dnsTtl || DNS_TTL)\n });\n\n return callback(\n null,\n formatDNSValue(value, {\n cached: false\n })\n );\n }\n\n resolver(6, options.host, options, (err, addresses) => {\n if (err) {\n if (cached) {\n // ignore error, use expired value\n return callback(\n null,\n formatDNSValue(cached.value, {\n cached: true,\n error: err\n })\n );\n }\n return callback(err);\n }\n\n if (addresses && addresses.length) {\n let value = {\n addresses,\n servername: options.servername || options.host\n };\n\n dnsCache.set(options.host, {\n value,\n expires: Date.now() + (options.dnsTtl || DNS_TTL)\n });\n\n return callback(\n null,\n formatDNSValue(value, {\n cached: false\n })\n );\n }\n\n try {\n dns.lookup(options.host, { all: true }, (err, addresses) => {\n if (err) {\n if (cached) {\n // ignore error, use expired value\n return callback(\n null,\n formatDNSValue(cached.value, {\n cached: true,\n error: err\n })\n );\n }\n return callback(err);\n }\n\n let address = addresses\n ? addresses\n .filter(addr => isFamilySupported(addr.family))\n .map(addr => addr.address)\n .shift()\n : false;\n\n if (addresses && addresses.length && !address) {\n // there are addresses but none can be used\n console.warn(`Failed to resolve IPv${addresses[0].family} addresses with current network`);\n }\n\n if (!address && cached) {\n // nothing was found, fallback to cached value\n return callback(\n null,\n formatDNSValue(cached.value, {\n cached: true\n })\n );\n }\n\n let value = {\n addresses: address ? [address] : [options.host],\n servername: options.servername || options.host\n };\n\n dnsCache.set(options.host, {\n value,\n expires: Date.now() + (options.dnsTtl || DNS_TTL)\n });\n\n return callback(\n null,\n formatDNSValue(value, {\n cached: false\n })\n );\n });\n } catch (err) {\n if (cached) {\n // ignore error, use expired value\n return callback(\n null,\n formatDNSValue(cached.value, {\n cached: true,\n error: err\n })\n );\n }\n return callback(err);\n }\n });\n });\n};\n/**\n * Parses connection url to a structured configuration object\n *\n * @param {String} str Connection url\n * @return {Object} Configuration object\n */\nmodule.exports.parseConnectionUrl = str => {\n str = str || '';\n let options = {};\n\n [urllib.parse(str, true)].forEach(url => {\n let auth;\n\n switch (url.protocol) {\n case 'smtp:':\n options.secure = false;\n break;\n case 'smtps:':\n options.secure = true;\n break;\n case 'direct:':\n options.direct = true;\n break;\n }\n\n if (!isNaN(url.port) && Number(url.port)) {\n options.port = Number(url.port);\n }\n\n if (url.hostname) {\n options.host = url.hostname;\n }\n\n if (url.auth) {\n auth = url.auth.split(':');\n\n if (!options.auth) {\n options.auth = {};\n }\n\n options.auth.user = auth.shift();\n options.auth.pass = auth.join(':');\n }\n\n Object.keys(url.query || {}).forEach(key => {\n let obj = options;\n let lKey = key;\n let value = url.query[key];\n\n if (!isNaN(value)) {\n value = Number(value);\n }\n\n switch (value) {\n case 'true':\n value = true;\n break;\n case 'false':\n value = false;\n break;\n }\n\n // tls is nested object\n if (key.indexOf('tls.') === 0) {\n lKey = key.substr(4);\n if (!options.tls) {\n options.tls = {};\n }\n obj = options.tls;\n } else if (key.indexOf('.') >= 0) {\n // ignore nested properties besides tls\n return;\n }\n\n if (!(lKey in obj)) {\n obj[lKey] = value;\n }\n });\n });\n\n return options;\n};\n\nmodule.exports._logFunc = (logger, level, defaults, data, message, ...args) => {\n let entry = {};\n\n Object.keys(defaults || {}).forEach(key => {\n if (key !== 'level') {\n entry[key] = defaults[key];\n }\n });\n\n Object.keys(data || {}).forEach(key => {\n if (key !== 'level') {\n entry[key] = data[key];\n }\n });\n\n logger[level](entry, message, ...args);\n};\n\n/**\n * Returns a bunyan-compatible logger interface. Uses either provided logger or\n * creates a default console logger\n *\n * @param {Object} [options] Options object that might include 'logger' value\n * @return {Object} bunyan compatible logger\n */\nmodule.exports.getLogger = (options, defaults) => {\n options = options || {};\n\n let response = {};\n let levels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\n\n if (!options.logger) {\n // use vanity logger\n levels.forEach(level => {\n response[level] = () => false;\n });\n return response;\n }\n\n let logger = options.logger;\n\n if (options.logger === true) {\n // create console logger\n logger = createDefaultLogger(levels);\n }\n\n levels.forEach(level => {\n response[level] = (data, message, ...args) => {\n module.exports._logFunc(logger, level, defaults, data, message, ...args);\n };\n });\n\n return response;\n};\n\n/**\n * Wrapper for creating a callback that either resolves or rejects a promise\n * based on input\n *\n * @param {Function} resolve Function to run if callback is called\n * @param {Function} reject Function to run if callback ends with an error\n */\nmodule.exports.callbackPromise = (resolve, reject) =>\n function () {\n let args = Array.from(arguments);\n let err = args.shift();\n if (err) {\n reject(err);\n } else {\n resolve(...args);\n }\n };\n\nmodule.exports.parseDataURI = uri => {\n let input = uri;\n let commaPos = input.indexOf(',');\n if (!commaPos) {\n return uri;\n }\n\n let data = input.substring(commaPos + 1);\n let metaStr = input.substring('data:'.length, commaPos);\n\n let encoding;\n\n let metaEntries = metaStr.split(';');\n let lastMetaEntry = metaEntries.length > 1 ? metaEntries[metaEntries.length - 1] : false;\n if (lastMetaEntry && lastMetaEntry.indexOf('=') < 0) {\n encoding = lastMetaEntry.toLowerCase();\n metaEntries.pop();\n }\n\n let contentType = metaEntries.shift() || 'application/octet-stream';\n let params = {};\n for (let entry of metaEntries) {\n let sep = entry.indexOf('=');\n if (sep >= 0) {\n let key = entry.substring(0, sep);\n let value = entry.substring(sep + 1);\n params[key] = value;\n }\n }\n\n switch (encoding) {\n case 'base64':\n data = Buffer.from(data, 'base64');\n break;\n case 'utf8':\n data = Buffer.from(data);\n break;\n default:\n try {\n data = Buffer.from(decodeURIComponent(data));\n } catch (err) {\n data = Buffer.from(data);\n }\n data = Buffer.from(data);\n }\n\n return { data, encoding, contentType, params };\n};\n\n/**\n * Resolves a String or a Buffer value for content value. Useful if the value\n * is a Stream or a file or an URL. If the value is a Stream, overwrites\n * the stream object with the resolved value (you can't stream a value twice).\n *\n * This is useful when you want to create a plugin that needs a content value,\n * for example the `html` or `text` value as a String or a Buffer but not as\n * a file path or an URL.\n *\n * @param {Object} data An object or an Array you want to resolve an element for\n * @param {String|Number} key Property name or an Array index\n * @param {Function} callback Callback function with (err, value)\n */\nmodule.exports.resolveContent = (data, key, callback) => {\n let promise;\n\n if (!callback) {\n promise = new Promise((resolve, reject) => {\n callback = module.exports.callbackPromise(resolve, reject);\n });\n }\n\n let content = (data && data[key] && data[key].content) || data[key];\n let contentStream;\n let encoding = ((typeof data[key] === 'object' && data[key].encoding) || 'utf8')\n .toString()\n .toLowerCase()\n .replace(/[-_\\s]/g, '');\n\n if (!content) {\n return callback(null, content);\n }\n\n if (typeof content === 'object') {\n if (typeof content.pipe === 'function') {\n return resolveStream(content, (err, value) => {\n if (err) {\n return callback(err);\n }\n // we can't stream twice the same content, so we need\n // to replace the stream object with the streaming result\n if (data[key].content) {\n data[key].content = value;\n } else {\n data[key] = value;\n }\n callback(null, value);\n });\n } else if (/^https?:\\/\\//i.test(content.path || content.href)) {\n contentStream = nmfetch(content.path || content.href);\n return resolveStream(contentStream, callback);\n } else if (/^data:/i.test(content.path || content.href)) {\n let parsedDataUri = module.exports.parseDataURI(content.path || content.href);\n\n if (!parsedDataUri || !parsedDataUri.data) {\n return callback(null, Buffer.from(0));\n }\n return callback(null, parsedDataUri.data);\n } else if (content.path) {\n return resolveStream(fs.createReadStream(content.path), callback);\n }\n }\n\n if (typeof data[key].content === 'string' && !['utf8', 'usascii', 'ascii'].includes(encoding)) {\n content = Buffer.from(data[key].content, encoding);\n }\n\n // default action, return as is\n setImmediate(() => callback(null, content));\n\n return promise;\n};\n\n/**\n * Copies properties from source objects to target objects\n */\nmodule.exports.assign = function (/* target, ... sources */) {\n let args = Array.from(arguments);\n let target = args.shift() || {};\n\n args.forEach(source => {\n Object.keys(source || {}).forEach(key => {\n if (['tls', 'auth'].includes(key) && source[key] && typeof source[key] === 'object') {\n // tls and auth are special keys that need to be enumerated separately\n // other objects are passed as is\n if (!target[key]) {\n // ensure that target has this key\n target[key] = {};\n }\n Object.keys(source[key]).forEach(subKey => {\n target[key][subKey] = source[key][subKey];\n });\n } else {\n target[key] = source[key];\n }\n });\n });\n return target;\n};\n\nmodule.exports.encodeXText = str => {\n // ! 0x21\n // + 0x2B\n // = 0x3D\n // ~ 0x7E\n if (!/[^\\x21-\\x2A\\x2C-\\x3C\\x3E-\\x7E]/.test(str)) {\n return str;\n }\n let buf = Buffer.from(str);\n let result = '';\n for (let i = 0, len = buf.length; i < len; i++) {\n let c = buf[i];\n if (c < 0x21 || c > 0x7e || c === 0x2b || c === 0x3d) {\n result += '+' + (c < 0x10 ? '0' : '') + c.toString(16).toUpperCase();\n } else {\n result += String.fromCharCode(c);\n }\n }\n return result;\n};\n\n/**\n * Streams a stream value into a Buffer\n *\n * @param {Object} stream Readable stream\n * @param {Function} callback Callback function with (err, value)\n */\nfunction resolveStream(stream, callback) {\n let responded = false;\n let chunks = [];\n let chunklen = 0;\n\n stream.on('error', err => {\n if (responded) {\n return;\n }\n\n responded = true;\n callback(err);\n });\n\n stream.on('readable', () => {\n let chunk;\n while ((chunk = stream.read()) !== null) {\n chunks.push(chunk);\n chunklen += chunk.length;\n }\n });\n\n stream.on('end', () => {\n if (responded) {\n return;\n }\n responded = true;\n\n let value;\n\n try {\n value = Buffer.concat(chunks, chunklen);\n } catch (E) {\n return callback(E);\n }\n callback(null, value);\n });\n}\n\n/**\n * Generates a bunyan-like logger that prints to console\n *\n * @returns {Object} Bunyan logger instance\n */\nfunction createDefaultLogger(levels) {\n let levelMaxLen = 0;\n let levelNames = new Map();\n levels.forEach(level => {\n if (level.length > levelMaxLen) {\n levelMaxLen = level.length;\n }\n });\n\n levels.forEach(level => {\n let levelName = level.toUpperCase();\n if (levelName.length < levelMaxLen) {\n levelName += ' '.repeat(levelMaxLen - levelName.length);\n }\n levelNames.set(level, levelName);\n });\n\n let print = (level, entry, message, ...args) => {\n let prefix = '';\n if (entry) {\n if (entry.tnx === 'server') {\n prefix = 'S: ';\n } else if (entry.tnx === 'client') {\n prefix = 'C: ';\n }\n\n if (entry.sid) {\n prefix = '[' + entry.sid + '] ' + prefix;\n }\n\n if (entry.cid) {\n prefix = '[#' + entry.cid + '] ' + prefix;\n }\n }\n\n message = util.format(message, ...args);\n message.split(/\\r?\\n/).forEach(line => {\n console.log('[%s] %s %s', new Date().toISOString().substr(0, 19).replace(/T/, ' '), levelNames.get(level), prefix + line);\n });\n };\n\n let logger = {};\n levels.forEach(level => {\n logger[level] = print.bind(null, level);\n });\n\n return logger;\n}\n","/* eslint quote-props: 0 */\n\n'use strict';\n\nconst path = require('path');\n\nconst defaultMimeType = 'application/octet-stream';\nconst defaultExtension = 'bin';\n\nconst mimeTypes = new Map([\n ['application/acad', 'dwg'],\n ['application/applixware', 'aw'],\n ['application/arj', 'arj'],\n ['application/atom+xml', 'xml'],\n ['application/atomcat+xml', 'atomcat'],\n ['application/atomsvc+xml', 'atomsvc'],\n ['application/base64', ['mm', 'mme']],\n ['application/binhex', 'hqx'],\n ['application/binhex4', 'hqx'],\n ['application/book', ['book', 'boo']],\n ['application/ccxml+xml,', 'ccxml'],\n ['application/cdf', 'cdf'],\n ['application/cdmi-capability', 'cdmia'],\n ['application/cdmi-container', 'cdmic'],\n ['application/cdmi-domain', 'cdmid'],\n ['application/cdmi-object', 'cdmio'],\n ['application/cdmi-queue', 'cdmiq'],\n ['application/clariscad', 'ccad'],\n ['application/commonground', 'dp'],\n ['application/cu-seeme', 'cu'],\n ['application/davmount+xml', 'davmount'],\n ['application/drafting', 'drw'],\n ['application/dsptype', 'tsp'],\n ['application/dssc+der', 'dssc'],\n ['application/dssc+xml', 'xdssc'],\n ['application/dxf', 'dxf'],\n ['application/ecmascript', ['js', 'es']],\n ['application/emma+xml', 'emma'],\n ['application/envoy', 'evy'],\n ['application/epub+zip', 'epub'],\n ['application/excel', ['xls', 'xl', 'xla', 'xlb', 'xlc', 'xld', 'xlk', 'xll', 'xlm', 'xlt', 'xlv', 'xlw']],\n ['application/exi', 'exi'],\n ['application/font-tdpfr', 'pfr'],\n ['application/fractals', 'fif'],\n ['application/freeloader', 'frl'],\n ['application/futuresplash', 'spl'],\n ['application/geo+json', 'geojson'],\n ['application/gnutar', 'tgz'],\n ['application/groupwise', 'vew'],\n ['application/hlp', 'hlp'],\n ['application/hta', 'hta'],\n ['application/hyperstudio', 'stk'],\n ['application/i-deas', 'unv'],\n ['application/iges', ['iges', 'igs']],\n ['application/inf', 'inf'],\n ['application/internet-property-stream', 'acx'],\n ['application/ipfix', 'ipfix'],\n ['application/java', 'class'],\n ['application/java-archive', 'jar'],\n ['application/java-byte-code', 'class'],\n ['application/java-serialized-object', 'ser'],\n ['application/java-vm', 'class'],\n ['application/javascript', 'js'],\n ['application/json', 'json'],\n ['application/lha', 'lha'],\n ['application/lzx', 'lzx'],\n ['application/mac-binary', 'bin'],\n ['application/mac-binhex', 'hqx'],\n ['application/mac-binhex40', 'hqx'],\n ['application/mac-compactpro', 'cpt'],\n ['application/macbinary', 'bin'],\n ['application/mads+xml', 'mads'],\n ['application/marc', 'mrc'],\n ['application/marcxml+xml', 'mrcx'],\n ['application/mathematica', 'ma'],\n ['application/mathml+xml', 'mathml'],\n ['application/mbedlet', 'mbd'],\n ['application/mbox', 'mbox'],\n ['application/mcad', 'mcd'],\n ['application/mediaservercontrol+xml', 'mscml'],\n ['application/metalink4+xml', 'meta4'],\n ['application/mets+xml', 'mets'],\n ['application/mime', 'aps'],\n ['application/mods+xml', 'mods'],\n ['application/mp21', 'm21'],\n ['application/mp4', 'mp4'],\n ['application/mspowerpoint', ['ppt', 'pot', 'pps', 'ppz']],\n ['application/msword', ['doc', 'dot', 'w6w', 'wiz', 'word']],\n ['application/mswrite', 'wri'],\n ['application/mxf', 'mxf'],\n ['application/netmc', 'mcp'],\n ['application/octet-stream', ['*']],\n ['application/oda', 'oda'],\n ['application/oebps-package+xml', 'opf'],\n ['application/ogg', 'ogx'],\n ['application/olescript', 'axs'],\n ['application/onenote', 'onetoc'],\n ['application/patch-ops-error+xml', 'xer'],\n ['application/pdf', 'pdf'],\n ['application/pgp-encrypted', 'asc'],\n ['application/pgp-signature', 'pgp'],\n ['application/pics-rules', 'prf'],\n ['application/pkcs-12', 'p12'],\n ['application/pkcs-crl', 'crl'],\n ['application/pkcs10', 'p10'],\n ['application/pkcs7-mime', ['p7c', 'p7m']],\n ['application/pkcs7-signature', 'p7s'],\n ['application/pkcs8', 'p8'],\n ['application/pkix-attr-cert', 'ac'],\n ['application/pkix-cert', ['cer', 'crt']],\n ['application/pkix-crl', 'crl'],\n ['application/pkix-pkipath', 'pkipath'],\n ['application/pkixcmp', 'pki'],\n ['application/plain', 'text'],\n ['application/pls+xml', 'pls'],\n ['application/postscript', ['ps', 'ai', 'eps']],\n ['application/powerpoint', 'ppt'],\n ['application/pro_eng', ['part', 'prt']],\n ['application/prs.cww', 'cww'],\n ['application/pskc+xml', 'pskcxml'],\n ['application/rdf+xml', 'rdf'],\n ['application/reginfo+xml', 'rif'],\n ['application/relax-ng-compact-syntax', 'rnc'],\n ['application/resource-lists+xml', 'rl'],\n ['application/resource-lists-diff+xml', 'rld'],\n ['application/ringing-tones', 'rng'],\n ['application/rls-services+xml', 'rs'],\n ['application/rsd+xml', 'rsd'],\n ['application/rss+xml', 'xml'],\n ['application/rtf', ['rtf', 'rtx']],\n ['application/sbml+xml', 'sbml'],\n ['application/scvp-cv-request', 'scq'],\n ['application/scvp-cv-response', 'scs'],\n ['application/scvp-vp-request', 'spq'],\n ['application/scvp-vp-response', 'spp'],\n ['application/sdp', 'sdp'],\n ['application/sea', 'sea'],\n ['application/set', 'set'],\n ['application/set-payment-initiation', 'setpay'],\n ['application/set-registration-initiation', 'setreg'],\n ['application/shf+xml', 'shf'],\n ['application/sla', 'stl'],\n ['application/smil', ['smi', 'smil']],\n ['application/smil+xml', 'smi'],\n ['application/solids', 'sol'],\n ['application/sounder', 'sdr'],\n ['application/sparql-query', 'rq'],\n ['application/sparql-results+xml', 'srx'],\n ['application/srgs', 'gram'],\n ['application/srgs+xml', 'grxml'],\n ['application/sru+xml', 'sru'],\n ['application/ssml+xml', 'ssml'],\n ['application/step', ['step', 'stp']],\n ['application/streamingmedia', 'ssm'],\n ['application/tei+xml', 'tei'],\n ['application/thraud+xml', 'tfi'],\n ['application/timestamped-data', 'tsd'],\n ['application/toolbook', 'tbk'],\n ['application/vda', 'vda'],\n ['application/vnd.3gpp.pic-bw-large', 'plb'],\n ['application/vnd.3gpp.pic-bw-small', 'psb'],\n ['application/vnd.3gpp.pic-bw-var', 'pvb'],\n ['application/vnd.3gpp2.tcap', 'tcap'],\n ['application/vnd.3m.post-it-notes', 'pwn'],\n ['application/vnd.accpac.simply.aso', 'aso'],\n ['application/vnd.accpac.simply.imp', 'imp'],\n ['application/vnd.acucobol', 'acu'],\n ['application/vnd.acucorp', 'atc'],\n ['application/vnd.adobe.air-application-installer-package+zip', 'air'],\n ['application/vnd.adobe.fxp', 'fxp'],\n ['application/vnd.adobe.xdp+xml', 'xdp'],\n ['application/vnd.adobe.xfdf', 'xfdf'],\n ['application/vnd.ahead.space', 'ahead'],\n ['application/vnd.airzip.filesecure.azf', 'azf'],\n ['application/vnd.airzip.filesecure.azs', 'azs'],\n ['application/vnd.amazon.ebook', 'azw'],\n ['application/vnd.americandynamics.acc', 'acc'],\n ['application/vnd.amiga.ami', 'ami'],\n ['application/vnd.android.package-archive', 'apk'],\n ['application/vnd.anser-web-certificate-issue-initiation', 'cii'],\n ['application/vnd.anser-web-funds-transfer-initiation', 'fti'],\n ['application/vnd.antix.game-component', 'atx'],\n ['application/vnd.apple.installer+xml', 'mpkg'],\n ['application/vnd.apple.mpegurl', 'm3u8'],\n ['application/vnd.aristanetworks.swi', 'swi'],\n ['application/vnd.audiograph', 'aep'],\n ['application/vnd.blueice.multipass', 'mpm'],\n ['application/vnd.bmi', 'bmi'],\n ['application/vnd.businessobjects', 'rep'],\n ['application/vnd.chemdraw+xml', 'cdxml'],\n ['application/vnd.chipnuts.karaoke-mmd', 'mmd'],\n ['application/vnd.cinderella', 'cdy'],\n ['application/vnd.claymore', 'cla'],\n ['application/vnd.cloanto.rp9', 'rp9'],\n ['application/vnd.clonk.c4group', 'c4g'],\n ['application/vnd.cluetrust.cartomobile-config', 'c11amc'],\n ['application/vnd.cluetrust.cartomobile-config-pkg', 'c11amz'],\n ['application/vnd.commonspace', 'csp'],\n ['application/vnd.contact.cmsg', 'cdbcmsg'],\n ['application/vnd.cosmocaller', 'cmc'],\n ['application/vnd.crick.clicker', 'clkx'],\n ['application/vnd.crick.clicker.keyboard', 'clkk'],\n ['application/vnd.cri