From 3ca522ca1d95553a10156c97dad62c5ebfa460bf Mon Sep 17 00:00:00 2001 From: jndaniels Date: Mon, 28 Oct 2024 19:16:06 -0500 Subject: [PATCH] INIT --- .dockerignore | 4 + .gitignore | 4 + .npmrc | 1 + .wappler/project.json | 52 + .../targets/Development/docker-compose.yml | 22 + .wappler/targets/Development/web/Dockerfile | 16 + .wappler/thumb.png | Bin 0 -> 4521 bytes app/config/routes.json | 10 + index.js | 3 + lib/auth/database.js | 72 + lib/auth/passport.js | 25 + lib/auth/provider.js | 159 + lib/auth/provider_experimental.js | 181 + lib/auth/single.js | 26 + lib/auth/static.js | 31 + lib/core/app.js | 840 ++ lib/core/async.js | 97 + lib/core/base32.js | 84 + lib/core/basicauth.js | 43 + lib/core/db.js | 217 + lib/core/diacritics.js | 96 + lib/core/memoryStore.js | 92 + lib/core/middleware.js | 163 + lib/core/parser.js | 752 ++ lib/core/path.js | 97 + lib/core/scope.js | 63 + lib/core/util.js | 186 + lib/core/webhook.js | 24 + lib/formatters/collections.js | 169 + lib/formatters/conditional.js | 30 + lib/formatters/core.js | 27 + lib/formatters/crypto.js | 99 + lib/formatters/date.js | 83 + lib/formatters/index.js | 17 + lib/formatters/number.js | 73 + lib/formatters/string.js | 210 + lib/locale/en-US.js | 59 + lib/modules/api.js | 122 + lib/modules/arraylist.js | 173 + lib/modules/auth.js | 72 + lib/modules/collections.js | 185 + lib/modules/core.js | 273 + lib/modules/crypto.js | 31 + lib/modules/csrf.js | 7 + lib/modules/dataset.js | 24 + lib/modules/dbconnector.js | 765 ++ lib/modules/dbupdater.js | 453 + lib/modules/export.js | 109 + lib/modules/fs.js | 258 + lib/modules/image.js | 505 + lib/modules/import.js | 129 + lib/modules/jwt.js | 22 + lib/modules/mail.js | 92 + lib/modules/metadata.js | 484 + lib/modules/oauth.js | 24 + lib/modules/otp.js | 174 + lib/modules/passkeys.js | 86 + lib/modules/ratelimit.js | 84 + lib/modules/recaptcha.js | 47 + lib/modules/redis.js | 237 + lib/modules/s3.js | 174 + lib/modules/sockets.js | 172 + lib/modules/stripe.js | 6400 ++++++++++ lib/modules/upload.js | 118 + lib/modules/validator.js | 18 + lib/modules/zip.js | 114 + lib/oauth/index.js | 170 + lib/oauth/services.js | 98 + lib/server.js | 110 + lib/setup/config.js | 105 + lib/setup/cron.js | 60 + lib/setup/database.js | 36 + lib/setup/redis.js | 9 + lib/setup/routes.js | 298 + lib/setup/secure.js | 112 + lib/setup/session.js | 94 + lib/setup/sockets.js | 195 + lib/setup/upload.js | 63 + lib/setup/util.js | 12 + lib/setup/webhooks.js | 22 + lib/validator/core.js | 243 + lib/validator/db.js | 21 + lib/validator/index.js | 120 + lib/validator/unicode.js | 466 + lib/validator/upload.js | 82 + lib/webhooks/stripe.js | 22 + package.json | 52 + public/PDF/ERT_Template_1.pdf | Bin 0 -> 633426 bytes public/PDF/PDF-Template.pdf | Bin 0 -> 78446 bytes public/PDF/PDFDemo-2.pdf | Bin 0 -> 78446 bytes public/PDF/PDFdemo.pdf | Bin 0 -> 75530 bytes public/PDF/blahtest.pdf | Bin 0 -> 173015 bytes public/PDF/test.pdf | 10293 ++++++++++++++++ public/PDF/testpdf.pdf | Bin 0 -> 12408 bytes public/bootstrap/5/css/bootstrap.min.css | 6 + public/bootstrap/5/js/bootstrap.bundle.min.js | 7 + public/css/style.css | 0 public/dmxAppConnect/dmxAppConnect.js | 10 + public/dmxAppConnect/dmxAppConnect.js.map | 1 + public/dmxAppConnect/dmxRouting/dmxRouting.js | 8 + .../dmxRouting/dmxRouting.js.map | 1 + views/index.ejs | 2 + views/layouts/main.ejs | 26 + 103 files changed, 28223 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 .wappler/project.json create mode 100644 .wappler/targets/Development/docker-compose.yml create mode 100644 .wappler/targets/Development/web/Dockerfile create mode 100644 .wappler/thumb.png create mode 100644 app/config/routes.json create mode 100644 index.js create mode 100644 lib/auth/database.js create mode 100644 lib/auth/passport.js create mode 100644 lib/auth/provider.js create mode 100644 lib/auth/provider_experimental.js create mode 100644 lib/auth/single.js create mode 100644 lib/auth/static.js create mode 100644 lib/core/app.js create mode 100644 lib/core/async.js create mode 100644 lib/core/base32.js create mode 100644 lib/core/basicauth.js create mode 100644 lib/core/db.js create mode 100644 lib/core/diacritics.js create mode 100644 lib/core/memoryStore.js create mode 100644 lib/core/middleware.js create mode 100644 lib/core/parser.js create mode 100644 lib/core/path.js create mode 100644 lib/core/scope.js create mode 100644 lib/core/util.js create mode 100644 lib/core/webhook.js create mode 100644 lib/formatters/collections.js create mode 100644 lib/formatters/conditional.js create mode 100644 lib/formatters/core.js create mode 100644 lib/formatters/crypto.js create mode 100644 lib/formatters/date.js create mode 100644 lib/formatters/index.js create mode 100644 lib/formatters/number.js create mode 100644 lib/formatters/string.js create mode 100644 lib/locale/en-US.js create mode 100644 lib/modules/api.js create mode 100644 lib/modules/arraylist.js create mode 100644 lib/modules/auth.js create mode 100644 lib/modules/collections.js create mode 100644 lib/modules/core.js create mode 100644 lib/modules/crypto.js create mode 100644 lib/modules/csrf.js create mode 100644 lib/modules/dataset.js create mode 100644 lib/modules/dbconnector.js create mode 100644 lib/modules/dbupdater.js create mode 100644 lib/modules/export.js create mode 100644 lib/modules/fs.js create mode 100644 lib/modules/image.js create mode 100644 lib/modules/import.js create mode 100644 lib/modules/jwt.js create mode 100644 lib/modules/mail.js create mode 100644 lib/modules/metadata.js create mode 100644 lib/modules/oauth.js create mode 100644 lib/modules/otp.js create mode 100644 lib/modules/passkeys.js create mode 100644 lib/modules/ratelimit.js create mode 100644 lib/modules/recaptcha.js create mode 100644 lib/modules/redis.js create mode 100644 lib/modules/s3.js create mode 100644 lib/modules/sockets.js create mode 100644 lib/modules/stripe.js create mode 100644 lib/modules/upload.js create mode 100644 lib/modules/validator.js create mode 100644 lib/modules/zip.js create mode 100644 lib/oauth/index.js create mode 100644 lib/oauth/services.js create mode 100644 lib/server.js create mode 100644 lib/setup/config.js create mode 100644 lib/setup/cron.js create mode 100644 lib/setup/database.js create mode 100644 lib/setup/redis.js create mode 100644 lib/setup/routes.js create mode 100644 lib/setup/secure.js create mode 100644 lib/setup/session.js create mode 100644 lib/setup/sockets.js create mode 100644 lib/setup/upload.js create mode 100644 lib/setup/util.js create mode 100644 lib/setup/webhooks.js create mode 100644 lib/validator/core.js create mode 100644 lib/validator/db.js create mode 100644 lib/validator/index.js create mode 100644 lib/validator/unicode.js create mode 100644 lib/validator/upload.js create mode 100644 lib/webhooks/stripe.js create mode 100644 package.json create mode 100644 public/PDF/ERT_Template_1.pdf create mode 100644 public/PDF/PDF-Template.pdf create mode 100644 public/PDF/PDFDemo-2.pdf create mode 100644 public/PDF/PDFdemo.pdf create mode 100644 public/PDF/blahtest.pdf create mode 100644 public/PDF/test.pdf create mode 100644 public/PDF/testpdf.pdf create mode 100644 public/bootstrap/5/css/bootstrap.min.css create mode 100644 public/bootstrap/5/js/bootstrap.bundle.min.js create mode 100644 public/css/style.css create mode 100644 public/dmxAppConnect/dmxAppConnect.js create mode 100644 public/dmxAppConnect/dmxAppConnect.js.map create mode 100644 public/dmxAppConnect/dmxRouting/dmxRouting.js create mode 100644 public/dmxAppConnect/dmxRouting/dmxRouting.js.map create mode 100644 views/index.ejs create mode 100644 views/layouts/main.ejs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6a3cec3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.wappler +**/.git +**/.svn +node_modules diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..277b791 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +.svn +.env +**/.DS_Store \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..a21347f --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +scripts-prepend-node-path=true \ No newline at end of file diff --git a/.wappler/project.json b/.wappler/project.json new file mode 100644 index 0000000..e8729f5 --- /dev/null +++ b/.wappler/project.json @@ -0,0 +1,52 @@ +{ + "projectName": "ERTFastFiller", + "styleFile": "/css/style.css", + "assetsFolder": "/assets", + "designFramework": "bootstrap5", + "frameworks": [ + { + "name": "fontawesome_5", + "type": "cdn" + }, + { + "name": "bootstrap5", + "type": "local" + }, + { + "name": "appConnect", + "type": "local" + } + ], + "hostingType": "docker", + "projectServerModel": "node", + "runtime": "capacitor", + "webRootFolder": "/public", + "useRouting": true, + "addBase": true, + "routingHandler": "node", + "projectLinksType": "site", + "targets": [ + { + "name": "Development", + "remoteURL": "http://localhost:8100", + "webServerPort": 8100, + "webServerLang": "node", + "targetType": "docker", + "webServer": "node", + "NodeVersion": "lts", + "NodeOS": "alpine", + "NodeImageType": "slim", + "webLoggingMaxFiles": "5", + "webLoggingMaxFileSize": "10m", + "databaseLoggingMaxFiles": "5", + "databaseLoggingMaxFileSize": "10m" + } + ], + "activeTarget": "Development", + "projectType": "web", + "extensions": [ + { + "name": "pdf-lib" + } + ] +} \ No newline at end of file diff --git a/.wappler/targets/Development/docker-compose.yml b/.wappler/targets/Development/docker-compose.yml new file mode 100644 index 0000000..feac4bb --- /dev/null +++ b/.wappler/targets/Development/docker-compose.yml @@ -0,0 +1,22 @@ +services: + web: + volumes: + - '../../../app:/opt/node_app/app' + - '../../../lib:/opt/node_app/lib' + - '../../../views:/opt/node_app/views' + - '../../../public:/opt/node_app/public' + - '../../../extensions:/opt/node_app/extensions' + - '../../../db:/opt/node_app/db' + - '../../../certs:/opt/node_app/certs' + ports: + - '8100:3000' + restart: 'always' + stdin_open: true + tty: true + build: + context: '../../../' + dockerfile: '.wappler/targets/Development/web/Dockerfile' + logging: + options: + max-file: '5' + max-size: '10m' diff --git a/.wappler/targets/Development/web/Dockerfile b/.wappler/targets/Development/web/Dockerfile new file mode 100644 index 0000000..c80c936 --- /dev/null +++ b/.wappler/targets/Development/web/Dockerfile @@ -0,0 +1,16 @@ +FROM node:lts-alpine + +ARG NODE_ENV=production +ENV NODE_ENV $NODE_ENV + +ARG PORT=3000 +ENV PORT $PORT +EXPOSE $PORT + +ENV PATH /opt/node_app/node_modules/.bin:$PATH +ENV NODE_ENV development +WORKDIR /opt/node_app +COPY index.js . +COPY package.json . +RUN npm install --no-optional --no-package-lock +CMD [ "nodemon", "--polling-interval", "5000", "--legacy-watch", "./index.js" ] diff --git a/.wappler/thumb.png b/.wappler/thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..287b22d17e183178f05bbde887719c6a018e36bc GIT binary patch literal 4521 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 literal 0 HcmV?d00001 diff --git a/app/config/routes.json b/app/config/routes.json new file mode 100644 index 0000000..215bf5f --- /dev/null +++ b/app/config/routes.json @@ -0,0 +1,10 @@ +{ + "routes": [ + { + "path": "/", + "page": "index", + "routeType": "page", + "layout": "main" + } + ] +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..e3c0683 --- /dev/null +++ b/index.js @@ -0,0 +1,3 @@ +const server = require('./lib/server'); + +server.start(); \ No newline at end of file diff --git a/lib/auth/database.js b/lib/auth/database.js new file mode 100644 index 0000000..5492455 --- /dev/null +++ b/lib/auth/database.js @@ -0,0 +1,72 @@ +const AuthProvider = require('./provider'); + +class DatabaseProvider extends AuthProvider { + + constructor(app, opts, name) { + super(app, opts, name); + this.users = opts.users; + this.perms = opts.permissions; + this.db = app.getDbConnection(opts.connection); + } + + async validate(username, password, passwordVerify) { + if (!username) return false; + if (!password) password = ''; + + let results = await this.db + .select(this.users.identity, this.users.username, this.users.password) + .from(this.users.table) + .where(this.users.username, username); + + for (let result of results) { + if (result[this.users.username] == username) { + if (passwordVerify && result[this.users.password].startsWith('$')) { + const argon2 = require('argon2'); + const valid = await argon2.verify(result[this.users.password], password); + return valid ? result[this.users.identity] : false; + } else if (result[this.users.password] == password) { + return result[this.users.identity]; + } + } + } + + return false; + } + + async permissions(identity, permissions) { + for (let permission of permissions) { + if (!this.perms[permission]) return false; + + let perm = this.perms[permission]; + let table = perm.table || this.users.table; + let ident = perm.identity || this.users.identity; + + let results = await this.db + .select(ident) + .from(table) + .where(ident, identity) + .where(function() { + for (let condition of perm.conditions) { + if (condition.operator == 'in') { + this.whereIn(condition.column, condition.value); + } else if (condition.operator == 'not in') { + this.whereNotIn(condition.column, condition.value); + } else if (condition.operator == 'is null') { + this.whereNull(condition.column); + } else if (condition.operator == 'is not null') { + this.whereNotNull(condition.column); + } else { + this.where(condition.column, condition.operator, condition.value); + } + } + }); + + if (!results.length) return false; + } + + return true; + } + +} + +module.exports = DatabaseProvider; \ No newline at end of file diff --git a/lib/auth/passport.js b/lib/auth/passport.js new file mode 100644 index 0000000..7c6ef95 --- /dev/null +++ b/lib/auth/passport.js @@ -0,0 +1,25 @@ +const PassportStrategy = require('passport-strategy'); + +class ServerConnectStrategy extends PassportStrategy { + constructor (options) { + const { provider } = options; + + if (!provider) { throw new TypeError('ServerConnectStrategy requires a provider'); } + + super(); + + this.name = 'server-connect'; + this._key = provider + 'Id'; + } + + authenticate (req, options = {}) { + if (req.session && req.session[this._key]) { + const property = req._userProperty || 'user'; + req[property] = { id: req.session[this._key] }; + } + + this.pass(); + } +} + +module.exports = ServerConnectStrategy; \ No newline at end of file diff --git a/lib/auth/provider.js b/lib/auth/provider.js new file mode 100644 index 0000000..da5b97d --- /dev/null +++ b/lib/auth/provider.js @@ -0,0 +1,159 @@ +const config = require('../setup/config'); +const crypto = require('crypto'); +const debug = require('debug')('server-connect:auth'); + +class AuthProvider { + + constructor(app, opts, name) { + this.app = app; + this.name = name; + this.identity = this.app.getSession(this.name + 'Id') || false; + this.secret = opts.secret || config.secret; + this.basicAuth = opts.basicAuth; + this.basicRealm = opts.basicRealm; + this.passwordVerify = opts.passwordVerify || false; + + this.cookieOpts = { + domain: opts.domain || undefined, + httpOnly: true, + maxAge: (opts.expires || 30) * 24 * 60 * 60 * 1000, // from days to ms + path: opts.path || '/', + secure: !!opts.secure, + sameSite: opts.sameSite || 'Strict', + signed: true + }; + } + + async autoLogin() { + if (this.basicAuth) { + const auth = require('../core/basicauth')(this.app.req); + debug(`Basic auth credentials received: %o`, auth); + if (auth) await this.login(auth.username, auth.password, false, true); + } + + const cookie = this.app.getCookie(this.name + '.auth', true); + if (cookie) { + const auth = this.decrypt(cookie); + debug(`Login with cookie: %o`, auth); + if (auth) await this.login(auth.username, auth.password, true, true); + } else { + debug(`No login cookie found`); + } + } + + async impersonate(identity) { + this.app.setSession(this.name + 'Id', identity); + this.app.set('identity', identity); + this.identity = identity; + await this.app.regenerateSessionId(); + } + + async login(username, password, remember, autoLogin) { + const identity = await this.validate(username, password, this.passwordVerify); + + if (!identity) { + await this.logout(); + + if (!autoLogin) { + this.unauthorized(); + return false; + } + } else { + this.app.setSession(this.name + 'Id', identity); + this.app.set('identity', identity); + + if (remember) { + debug('setCookie', identity, username, password); + this.app.setCookie(this.name + '.auth', this.encrypt({ username, password }), this.cookieOpts); + } + + this.identity = identity; + } + + await this.app.regenerateSessionId(); + + return identity; + } + + async logout() { + this.app.removeSession(this.name + 'Id'); + this.app.removeCookie(this.name + '.auth', this.cookieOpts); + this.app.remove('identity'); + this.identity = false; + await this.app.regenerateSessionId(); + } + + async restrict(opts) { + if (this.identity === false) { + if (opts.loginUrl) { + if (this.app.req.fragment) { + this.app.res.status(222).send(opts.loginUrl); + } else { + this.app.res.redirect(opts.loginUrl); + } + } else { + this.unauthorized(); + } + + return; + } + + if (opts.permissions) { + const allowed = await this.permissions(this.identity, opts.permissions); + if (!allowed) { + if (opts.forbiddenUrl) { + if (this.app.req.fragment) { + this.app.res.status(222).send(opts.forbiddenUrl); + } else { + this.app.res.redirect(opts.forbiddenUrl); + } + } else { + this.forbidden(); + } + } + } + } + + encrypt(data) { + const iv = crypto.randomBytes(16); + const key = crypto.scryptSync(this.secret, iv, 32); + const cipher = crypto.createCipheriv('aes-256-cbc', key, iv); + const encrypted = cipher.update(JSON.stringify(data), 'utf8', 'base64'); + return iv.toString('base64') + '.' + encrypted + cipher.final('base64'); + } + + decrypt(data) { + // try/catch to prevent errors on currupt cookies + try { + const iv = Buffer.from(data.split('.')[0], 'base64'); + const key = crypto.scryptSync(this.secret, iv, 32); + const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); + const decrypted = decipher.update(data.split('.')[1], 'base64', 'utf8'); + return JSON.parse(decrypted + decipher.final('utf8')); + } catch (err) { + return undefined; + } + } + + unauthorized() { + if (this.basicAuth) { + this.app.res.set('WWW-Authenticate', `Basic Realm="${this.basicRealm}"`); + } + this.app.res.sendStatus(401); + } + + forbidden() { + this.app.res.sendStatus(403); + } + + async validate(username, password) { + throw new Error('auth.validate needs to be extended.'); + } + + async permissions(identity, permissions) { + throw new Error('auth.permissions needs to be extended.'); + } + +} + +module.exports = AuthProvider; \ No newline at end of file diff --git a/lib/auth/provider_experimental.js b/lib/auth/provider_experimental.js new file mode 100644 index 0000000..a852ce6 --- /dev/null +++ b/lib/auth/provider_experimental.js @@ -0,0 +1,181 @@ +const config = require('../setup/config'); +const crypto = require('crypto'); +const debug = require('debug')('server-connect:auth'); + +class AuthProvider { + + constructor(app, opts, name) { + this.app = app; + this.name = name; + this.identity = options.jwt ? false : this.app.getSession(this.name + 'Id') || false; + this.jwt = options.jwt || false; + this.jwtExpires = options.jwtExpires || '1h'; + this.jwtIssuer = options.jwtIssuer || 'serverconnect'; + this.secret = opts.secret || config.secret; + this.basicAuth = opts.basicAuth; + this.basicRealm = opts.basicRealm; + this.passwordVerify = opts.passwordVerify || false; + + this.cookieOpts = { + domain: opts.domain || undefined, + httpOnly: true, + maxAge: (opts.expires || 30) * 24 * 60 * 60 * 1000, // from days to ms + path: opts.path || '/', + secure: !!opts.secure, + sameSite: opts.sameSite || 'Strict', + signed: true + }; + } + + async autoLogin() { + if (this.basicAuth) { + const auth = require('../core/basicauth')(this.app.req); + debug(`Basic auth credentials received: %o`, auth); + if (auth) await this.login(auth.username, auth.password, false, true); + } + + if (this.jwt && this.app.req.headers['authorization']) { + const matches = /(\S+)\s+(\S+)/; + + if (matches && matches[1].toLowerCase == 'bearer') { + const jwt = require('jsonwebtoken'); + const { identity } = jwt.verify(matches[2], this.secret, { + algorithm: 'HS256', + issuer: this.jwtIssuer, + }); + + this.app.set('identity', identity); + this.identity = identity; + } + } + + const cookie = this.app.getCookie(this.name + '.auth', true); + if (cookie) { + const auth = this.decrypt(cookie); + debug(`Login with cookie: %o`, auth); + if (auth) await this.login(auth.username, auth.password, true, true); + } else { + debug(`No login cookie found`); + } + } + + async login(username, password, remember, autoLogin) { + const identity = await this.validate(username, password, this.passwordVerify); + + if (!identity) { + await this.logout(); + + if (!autoLogin) { + this.unauthorized(); + return false; + } + } else { + this.app.set('identity', identity); + + if (!this.jwt) { + this.app.setSession(this.name + 'Id', identity); + + if (remember) { + debug('setCookie', identity, username, password); + this.app.setCookie(this.name + '.auth', this.encrypt({ username, password }), this.cookieOpts); + } + } + + this.identity = identity; + } + + if (this.jwt) { + const jwt = require('jsonwebtoken'); + return jwt.sign({ identity }, this.secret, { + algorithm: 'HS256', + expiresIn: this.jwtExpires, + issuer: this.jwtIssuer, + }); + } + + return identity; + } + + async logout() { + if (!this.jwt) { + this.app.removeSession(this.name + 'Id'); + this.app.removeCookie(this.name + '.auth', this.cookieOpts); + } + this.app.remove('identity'); + this.identity = false; + } + + async restrict(opts) { + if (this.identity === false) { + if (opts.loginUrl) { + if (this.app.req.fragment) { + this.app.res.status(222).send(opts.loginUrl); + } else { + this.app.res.redirect(opts.loginUrl); + } + } else { + this.unauthorized(); + } + + return; + } + + if (opts.permissions) { + const allowed = await this.permissions(this.identity, opts.permissions); + if (!allowed) { + if (opts.forbiddenUrl) { + if (this.app.req.fragment) { + this.app.res.status(222).send(opts.forbiddenUrl); + } else { + this.app.res.redirect(opts.forbiddenUrl); + } + } else { + this.forbidden(); + } + } + } + } + + encrypt(data) { + const iv = crypto.randomBytes(16); + const key = crypto.scryptSync(this.secret, iv, 32); + const cipher = crypto.createCipheriv('aes-256-cbc', key, iv); + const encrypted = cipher.update(JSON.stringify(data), 'utf8', 'base64'); + return iv.toString('base64') + '.' + encrypted + cipher.final('base64'); + } + + decrypt(data) { + // try/catch to prevent errors on currupt cookies + try { + const iv = Buffer.from(data.split('.')[0], 'base64'); + const key = crypto.scryptSync(this.secret, iv, 32); + const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); + const decrypted = decipher.update(data.split('.')[1], 'base64', 'utf8'); + return JSON.parse(decrypted + decipher.final('utf8')); + } catch (err) { + return undefined; + } + } + + unauthorized() { + if (this.basicAuth) { + this.app.res.set('WWW-Authenticate', `Basic Realm="${this.basicRealm}"`); + } + this.app.res.sendStatus(401); + } + + forbidden() { + this.app.res.sendStatus(403); + } + + async validate(username, password) { + throw new Error('auth.validate needs to be extended.'); + } + + async permissions(identity, permissions) { + throw new Error('auth.permissions needs to be extended.'); + } + +} + +module.exports = AuthProvider; \ No newline at end of file diff --git a/lib/auth/single.js b/lib/auth/single.js new file mode 100644 index 0000000..5b38e64 --- /dev/null +++ b/lib/auth/single.js @@ -0,0 +1,26 @@ +const AuthProvider = require('./provider'); +const debug = require('debug')('server-connect:auth'); + +class SingleProvider extends AuthProvider { + + constructor(app, opts, name) { + super(app, opts, name); + this.username = opts.username; + this.password = opts.password; + } + + validate(username, password) { + if (username == this.username && password == this.password) { + return username; + } + + return false; + } + + permissions() { + return true; + } + +} + +module.exports = SingleProvider; \ No newline at end of file diff --git a/lib/auth/static.js b/lib/auth/static.js new file mode 100644 index 0000000..ce1113f --- /dev/null +++ b/lib/auth/static.js @@ -0,0 +1,31 @@ +const AuthProvider = require('./provider'); + +class StaticProvider extends AuthProvider { + + constructor(app, opts, name) { + super(app, opts, name); + this.users = opts.users; + this.perms = opts.perms; + } + + validate(username, password) { + if (this.users[username] == password) { + return username; + } + + return false; + } + + permissions(username, permissions) { + for (let permission of permissions) { + if (!this.perms[permission] || !this.perms[permission].includes(username)) { + return false; + } + } + + return true; + } + +} + +module.exports = StaticProvider; \ No newline at end of file diff --git a/lib/core/app.js b/lib/core/app.js new file mode 100644 index 0000000..27d08f1 --- /dev/null +++ b/lib/core/app.js @@ -0,0 +1,840 @@ +const fs = require('fs-extra'); +const Scope = require('./scope'); +const Parser = require('./parser'); +const db = require('./db'); +const validator = require('../validator'); +const config = require('../setup/config'); +const debug = require('debug')('server-connect:app'); +const debug2 = require('debug')('server-connect:output'); +const { clone, formatDate, parseDate } = require('../core/util'); +const { toSystemPath } = require('../core/path'); +const os = require('os'); + +if (!global.fileCache) { + global.fileCache = new Map(); +} + +if (!global.db) { + global.db = {}; +} + +if (!global.rateLimiter) { + global.rateLimiter = {}; +} + +function App(req = {}, res = {}) { + this.error = false; + this.data = {}; + this.meta = {}; + this.settings = {}; + this.modules = {}; + + this.io = global.io; + this.req = req; + this.res = res; + this.global = new Scope(); + this.scope = this.global; + + this.rateLimiter = {}; + this.mail = {}; + this.auth = {}; + this.oauth = {}; + this.db = {}; + this.s3 = {}; + this.jwt = {}; + this.trx = {}; + + if (config.globals.data) { + this.set(config.globals.data); + } + + this.set({ + $_ERROR: null, + //$_SERVER: process.env, + $_ENV: process.env, + $_GET: req.query, + $_POST: req.body, + $_PARAM: req.params, + $_HEADER: req.headers, + $_COOKIE: req.cookies, + $_SESSION: req.session, + }); + + let urlParts = req.originalUrl ? req.originalUrl.split('?') : ['', '']; + let $server = { + CONTENT_TYPE: req.headers && req.headers['content-type'], + HTTPS: req.protocol == 'https', + PATH_INFO: req.path, + QUERY_STRING: urlParts[1], + REMOTE_ADDR: req.ip, + REQUEST_PROTOCOL: req.protocol, + REQUEST_METHOD: req.method, + SERVER_NAME: req.hostname, + BASE_URL: req.headers && req.protocol + '://' + req.headers['host'], + URL: urlParts[0], + HOSTNAME: os.hostname(), + }; + + if (req.headers) { + for (let header in req.headers) { + $server['HTTP_' + header.toUpperCase().replace(/-/, '_')] = req.headers[header]; + } + } + + // Try to keep same as ASP/PHP + this.set('$_SERVER', $server); +} + +App.prototype = { + set: function (key, value) { + this.global.set(key, value); + }, + + get: function (key, def) { + let value = this.global.get(key); + return value !== undefined ? value : def; + }, + + remove: function (key) { + this.global.remove(key); + }, + + setSession: function (key, value) { + this.req.session[key] = value; + }, + + getSession: function (key) { + return this.req.session[key]; + }, + + removeSession: function (key) { + delete this.req.session[key]; + }, + + regenerateSessionId: function () { + return new Promise((resolve, reject) => { + const oldSession = this.req.session; + this.req.session.regenerate((err) => { + if (err) return reject(err); + + for (let key in oldSession) { + this.req.session[key] = oldSession[key]; + } + + resolve(); + }); + }); + }, + + setCookie: function (name, value, opts) { + if (!this.res.headersSent) { + if (this.res.cookie) { + this.res.cookie(name, value, opts); + } + } else { + debug(`Trying to set ${name} cookie while headers were already sent.`); + } + }, + + getCookie: function (name, signed) { + return signed ? this.req.signedCookies[name] : this.req.cookies[name]; + }, + + removeCookie: function (name, opts) { + if (this.res.clearCookie) { + // copy all options except expires and maxAge + const clearOpts = Object.assign({}, opts); + delete clearOpts.expires; + delete clearOpts.maxAge; + this.res.clearCookie(name, clearOpts); + } + }, + + setRateLimiter: function (name, options) { + const { RateLimiterMemory, RateLimiterRedis } = require('rate-limiter-flexible'); + + if (global.redisClient) { + global.rateLimiter[name] = new RateLimiterRedis({ + ...options, + storeClient: global.redisClient, + keyPrefix: 'ac:' + name + ':', + }); + } else { + global.rateLimiter[name] = new RateLimiterMemory({ + ...options, + keyPrefix: 'ac:' + name + ':', + }); + } + + return global.rateLimiter[name]; + }, + + getRateLimiter: function (name) { + if (global.rateLimiter[name]) { + return global.rateLimiter[name]; + } + + if (config.rateLimiter[name]) { + return this.setRateLimiter(name, config.rateLimiter[name]); + } + + if (fs.existsSync(`app/modules/RateLimiter/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/RateLimiter/${name}.json`); + return this.setRateLimiter(name, action.options); + } + + throw new Error(`Couldn't find rate limiter "${name}".`); + }, + + setMailer: function (name, options) { + let setup = {}; + + options = this.parse(options); + + switch (options.server) { + case 'mail': + setup.sendmail = true; + break; + + case 'ses': + const aws = require('@aws-sdk/client-ses'); + const ses = new AWS.SES({ + endpoint: options.endpoint, + credentials: { + accessKeyId: options.accessKeyId, + secretAccessKey: options.secretAccessKey, + }, + }); + setup.SES = { ses, aws }; + break; + + default: + // https://nodemailer.com/smtp/ + setup.host = options.host || 'localhost'; + setup.port = options.port || 25; + setup.secure = options.useSSL || false; + setup.auth = { + user: options.username, + pass: options.password, + }; + setup.tls = { + rejectUnauthorized: false, + }; + } + + return (this.mail[name] = setup); + }, + + getMailer: function (name) { + if (this.mail[name]) { + return this.mail[name]; + } + + if (config.mail[name]) { + return this.setMailer(name, config.mail[name]); + } + + if (fs.existsSync(`app/modules/Mailer/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/Mailer/${name}.json`); + return this.setMailer(name, action.options); + } + + throw new Error(`Couldn't find mailer "${name}".`); + }, + + setAuthProvider: async function (name, options) { + const Provider = require('../auth/' + options.provider.toLowerCase()); + const provider = new Provider(this, options, name); + if (!provider.identity) await provider.autoLogin(); + this.set('identity', provider.identity); + return (this.auth[name] = provider); + }, + + getAuthProvider: async function (name) { + if (this.auth[name]) { + return this.auth[name]; + } + + if (config.auth[name]) { + return await this.setAuthProvider(name, config.auth[name]); + } + + if (fs.existsSync(`app/modules/SecurityProviders/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/SecurityProviders/${name}.json`); + return await this.setAuthProvider(name, action.options); + } + + throw new Error(`Couldn't find security provider "${name}".`); + }, + + setOAuthProvider: async function (name, options) { + const OAuth2 = require('../oauth'); + const services = require('../oauth/services'); + + options = this.parse(options); + + let service = this.parseOptional(options.service, 'string', null); + let opts = service ? services[service] : {}; + opts.client_id = this.parseOptional(options.client_id, 'string', null); + opts.client_secret = this.parseOptional(options.client_secret, 'string', null); + opts.token_endpoint = + opts.token_endpoint || + this.parseRequired(options.token_endpoint, 'string', 'oauth.provider: token_endpoint is required.'); + opts.auth_endpoint = opts.auth_endpoint || this.parseOptional(options.auth_endpoint, 'string', ''); + opts.scope_separator = opts.scope_separator || this.parseOptional(options.scope_separator, 'string', ' '); + opts.access_token = this.parseOptional(options.access_token, 'string', null); + opts.refresh_token = this.parseOptional(options.refresh_token, 'string', null); + opts.jwt_bearer = this.parseOptional(options.jwt_bearer, 'string', false); + opts.client_credentials = this.parseOptional(options.client_credentials, 'boolean', false); + opts.params = Object.assign({}, opts.params, this.parseOptional(options.params, 'object', {})); + + this.oauth[name] = new OAuth2(this, this.parse(options), name); + await this.oauth[name].init(); + + return this.oauth[name]; + }, + + getOAuthProvider: async function (name) { + if (this.oauth[name]) { + return this.oauth[name]; + } + + if (config.oauth[name]) { + return await this.setOAuthProvider(name, config.oauth[name]); + } + + if (fs.existsSync(`app/modules/oauth/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/oauth/${name}.json`); + return await this.setOAuthProvider(name, action.options); + } + + throw new Error(`Couldn't find oauth provider "${name}".`); + }, + + setDbConnection: function (name, options) { + if (global.db[name]) { + return global.db[name]; + } + + options = this.parse(options); + + switch (options.client) { + case 'couchdb': + const { host, port, user, password, database } = options.connection; + const nano = require('nano'); + global.db[name] = nano( + `http://${user ? user + (password ? ':' + password : '') + '@' : ''}${host}${ + port ? ':' + port : '' + }/${database}` + ); + global.db[name].client = options.client; + return global.db[name]; + + case 'sqlite3': + if (options.connection.filename) { + options.connection.filename = toSystemPath(options.connection.filename); + } + break; + + case 'mysql': + case 'mysql2': + options.connection = { + supportBigNumbers: true, + dateStrings: !!options.tz, + decimalNumbers: true, + ...options.connection, + }; + /* + options.connection.typeCast = function(field, next) { + if (field.type == 'NEWDECIMAL') { + return field.string() + 'm'; + } + + return next(); + }; + */ + break; + + case 'mssql': + if (options.tz) { + // use local timezone to have same behavior as the other drivers + // to prevent problems node should have same timezone as database + options.connection.options = { useUTC: false, ...options.connection.options }; + } + break; + + case 'postgres': + if (options.tz) { + const types = require('pg').types; + const parseFn = (val) => val; + types.setTypeParser(types.builtins.TIME, parseFn); + types.setTypeParser(types.builtins.TIMETZ, parseFn); + types.setTypeParser(types.builtins.TIMESTAMP, parseFn); + types.setTypeParser(types.builtins.TIMESTAMPTZ, parseFn); + } + break; + } + + if (options.connection && options.connection.ssl) { + if (options.connection.ssl.key) { + options.connection.ssl.key = fs.readFileSync(toSystemPath(options.connection.ssl.key)); + } + + if (options.connection.ssl.ca) { + options.connection.ssl.ca = fs.readFileSync(toSystemPath(options.connection.ssl.ca)); + } + + if (options.connection.ssl.cert) { + options.connection.ssl.cert = fs.readFileSync(toSystemPath(options.connection.ssl.cert)); + } + } + + options.useNullAsDefault = true; + + const formatRecord = (record, meta) => { + for (column in record) { + if (record[column] != null) { + if (meta.has(column)) { + const info = meta.get(column); + + if (['json', 'object', 'array'].includes(info.type)) { + if (typeof record[column] == 'string') { + try { + // column of type json returned as string, need parse + record[column] = JSON.parse(record[column]); + } catch (err) { + console.warn(err); + } + } + } + + if (info.type == 'date') { + if (typeof record[column] == 'string' && record[column].startsWith('0000-00-00')) { + record[column] = undefined; + } else { + record[column] = formatDate(record[column], 'yyyy-MM-dd'); + } + } + + if (info.type == 'time') { + if (options.tz == 'local') { + record[column] = formatDate(record[column], 'HH:mm:ss.v'); + } else if (options.tz == 'utc') { + record[column] = parseDate( + parseDate(formatDate('now', 'yyyy-MM-dd ') + formatDate(record[column], 'HH:mm:ss.v')) + ) + .toISOString() + .slice(11); + } + } + + if ( + ['datetime', 'timestamp'].includes(info.type) || + Object.prototype.toString.call(record[column]) == '[object Date]' + ) { + if (typeof record[column] == 'string' && record[column].startsWith('0000-00-00')) { + record[column] = undefined; + } else if (options.tz == 'local') { + record[column] = formatDate(record[column], 'yyyy-MM-dd HH:mm:ss.v'); + } else if (options.tz == 'utc') { + record[column] = parseDate(record[column]).toISOString(); + } + } + } else { + // try to detect datetime + if (Object.prototype.toString.call(record[column]) == '[object Date]') { + if (options.tz == 'local') { + record[column] = formatDate(record[column], 'yyyy-MM-dd HH:mm:ss.v'); + } else if (options.tz == 'utc') { + record[column] = parseDate(record[column]).toISOString(); + } + } else if ( + typeof record[column] == 'string' && + /^\d{4}-\d{2}-\d{2}([T\s]\d{2}:\d{2}:\d{2}(\.\d+)?([Zz]|[+-]\d{2}(:\d{2})?)?)?$/.test(record[column]) + ) { + if (record[column].startsWith('0000-00-00')) { + record[column] = undefined; + } else if (options.tz == 'local') { + record[column] = formatDate(record[column], 'yyyy-MM-dd HH:mm:ss.v'); + } else if (options.tz == 'utc') { + record[column] = parseDate(record[column]).toISOString(); + } + } + } + + if (record[column] == undefined) { + record[column] = null; + } else if (record[column].toJSON) { + record[column] = record[column].toJSON(); + } + } + } + + return record; + }; + + options.postProcessResponse = function (result, queryContext) { + const meta = new Map(); + + if (Array.isArray(queryContext)) { + for (let item of queryContext) { + if (item.name && item.type) { + meta.set(item.name, item); + } + } + } + + if (Array.isArray(result)) { + return result.map((record) => formatRecord(record, meta)); + } else { + return formatRecord(result, meta); + } + }; + + global.db[name] = db(options); + + return global.db[name]; + }, + + getDbConnection: function (name) { + if (config.db[name]) { + return this.setDbConnection(name, config.db[name]); + } + + const path = `app/modules/connections/${name}.json`; + const action = global.fileCache.get(path) || fs.readJSONSync(`app/modules/connections/${name}.json`); + const isDynamic = this.isDynamic(action); + const options = clone(action.options); // need to clone because this.parse will update object by ref + + if (!global.fileCache.has(path)) { + // if action was not already cached do it now + global.fileCache.set(path, action); + } + + if (isDynamic) { + // since it is only used internal json stringify is good enough for creating a unique name + name = JSON.stringify(this.parse(options)); + } + + if (this.trx[name]) return this.trx[name]; + + return this.setDbConnection(name, options); + }, + + setS3Provider: function (name, options) { + options = this.parse(options); + + const { S3 } = require('@aws-sdk/client-s3'); + const endpoint = this.parseRequired(options.endpoint, 'string', 's3.provider: endpoint is required.'); + const accessKeyId = this.parseRequired(options.accessKeyId, 'string', 's3.provider: accessKeyId is required.'); + const secretAccessKey = this.parseRequired( + options.secretAccessKey, + 'string', + 's3.provider: secretAccessKey is required.' + ); + + let region = options.region || 'us-east-1'; + let pos = endpoint.indexOf('.amazonaws'); + if (pos > 3) region = endpoint.substr(3, pos - 3); + let forcePathStyle = options.forcePathStyle || false; + + this.s3[name] = new S3({ + endpoint: 'https://' + endpoint, + credentials: { accessKeyId, secretAccessKey }, + region, + signatureVersion: 'v4', + forcePathStyle, + }); + + return this.s3[name]; + }, + + getS3Provider: function (name) { + if (this.s3[name]) { + return this.s3[name]; + } + + if (config.s3[name]) { + return this.setS3Provider(name, config.s3[name]); + } + + if (fs.existsSync(`app/modules/s3/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/s3/${name}.json`); + return this.setS3Provider(name, action.options); + } + + throw new Error(`Couldn't find S3 provider "${name}".`); + }, + + setJSONWebToken: function (name, options) { + const jwt = require('jsonwebtoken'); + + options = this.parse(options); + + let opts = {}; + + if (options.alg) opts.algorithm = options.alg; + if (options.iss) opts.issuer = options.iss; + if (options.sub) opts.subject = options.sub; + if (options.aud) opts.audience = options.aud; + if (options.jti) opts.jwtid = options.jti; + if (options.expiresIn) opts.expiresIn = options.expiresIn; + + return (this.jwt[name] = jwt.sign( + { + ...options.claims, + }, + options.key, + { + expiresIn: 3600, // use as default (required by google) + ...opts, + } + )); + }, + + getJSONWebToken: function (name) { + if (config.jwt[name]) { + return this.setJSONWebToken(name, config.jwt[name]); + } + + if (fs.existsSync(`app/modules/jwt/${name}.json`)) { + let action = fs.readJSONSync(`app/modules/jwt/${name}.json`); + return this.setJSONWebToken(name, action.options); + } + + throw new Error(`Couldn't find JSON Web Token "${name}".`); + }, + + define: async function (cfg, internal) { + if (cfg.settings) { + this.settings = clone(cfg.settings); + } + + if (cfg.vars) { + this.set(clone(cfg.vars)); + } + + if (cfg.meta) { + this.meta = clone(cfg.meta); + await validator.init(this, this.meta); + } + + if (fs.existsSync('app/modules/global.json')) { + await this.exec(await fs.readJSON('app/modules/global.json'), true); + } + + /* + debug('body: %o', this.req.body); + debug('query: %o', this.req.query); + debug('params: %o', this.req.params); + debug('headers: %o', this.req.headers); + debug('cookies: %o', this.req.cookies); + debug('session: %o', this.req.session); + */ + + await this.exec(cfg.exec || cfg, internal); + }, + + sub: async function (actions, scope) { + const subApp = new App(this.req, this.res); + subApp.global = this.global; + subApp.scope = this.scope.create(scope); + await subApp.exec(actions, true); + return subApp.data; + }, + + exec: async function (actions, internal) { + if (actions.exec) { + return this.exec(actions.exec, internal); + } + + actions = clone(actions); + + await this._exec(actions.steps || actions); + + if (this.error !== false) { + if (actions.catch) { + this.scope.set('$_ERROR', this.error.message); + this.error = false; + await this._exec(actions.catch, true); + } else { + throw this.error; + } + } + + if (!internal && !this.res.headersSent && !this.noOutput) { + this.res.json(this.data); + } + }, + + _exec: async function (steps, ignoreAbort) { + if (this.res.headersSent) return; + + if (typeof steps == 'string') { + return this.exec(await fs.readJSON(`app/modules/${steps}.json`), true); + } + + if (this.res.headersSent) { + // do not execute other steps after headers has been sent + return; + } + + if (Array.isArray(steps)) { + for (let step of steps) { + await this._exec(step); + if (this.error) return; + } + return; + } + + if (steps.disabled) { + return; + } + + if (steps.action) { + try { + let module; + + if (fs.existsSync(`extensions/server_connect/modules/${steps.module}.js`)) { + module = require(`../../extensions/server_connect/modules/${steps.module}`); + } else if (fs.existsSync(`lib/modules/${steps.module}.js`)) { + module = require(`../modules/${steps.module}`); + } else { + throw new Error(`Module ${steps.module} doesn't exist`); + } + + if (typeof module[steps.action] != 'function') { + throw new Error(`Action ${steps.action} doesn't exist in ${steps.module || 'core'}`); + } + + debug(`Executing action step ${steps.action}`); + debug(`options: %O`, steps.options); + + if (!ignoreAbort && config.abortOnDisconnect && this.req.isDisconnected) { + throw new Error('Aborted'); + } + + const data = await module[steps.action].call(this, clone(steps.options), steps.name, steps.meta); + + if (data instanceof Error) { + throw data; + } + + debug2(`${steps.name || steps.action}: %O`, data); + + if (steps.name) { + this.scope.set(steps.name, data); + + if (steps.output) { + this.data[steps.name] = data; + } + } + } catch (e) { + debug2(`Error at ${steps.name || steps.action}: %O`, e); + + this.error = e; + return; + } + } + }, + + parse: function (value, scope) { + return Parser.parseValue(value, scope || this.scope); + }, + + parseRequired: function (value, type, err) { + if (value === undefined) { + throw new Error(err); + } + + let val = Parser.parseValue(value, this.scope); + + if (type == '*') { + if (val === undefined) { + throw new Error(err); + } + } else if (type == 'boolean') { + val = !!val; + } else if (typeof val != type) { + throw new Error(err); + } + + return val; + }, + + parseOptional: function (value, type, def) { + if (value === undefined) return def; + + let val = Parser.parseValue(value, this.scope); + + if (type == '*') { + if (val === undefined) val = def; + } else if (type == 'boolean') { + if (val === undefined) { + val = def; + } else { + val = !!val; + } + } else if (typeof val != type) { + val = def; + } + + return val; + }, + + parseSQL: function (sql) { + if (!sql) return null; + + ['values', 'orders'].forEach((prop) => { + if (Array.isArray(sql[prop])) { + sql[prop] = sql[prop].filter((value) => { + if (!value.condition) return true; + return !!Parser.parseValue(value.condition, this.scope); + }); + } + }); + + if (sql.wheres && sql.wheres.rules) { + if (sql.wheres.conditional && !Parser.parseValue(sql.wheres.conditional, this.scope)) { + delete sql.wheres; + } else { + sql.wheres.rules = sql.wheres.rules.filter(function filterConditional(rule) { + if (!rule.rules) return true; + if (rule.conditional && !Parser.parseValue(rule.conditional, this.scope)) return false; + rule.rules = rule.rules.filter(filterConditional, this); + return rule.rules.length; + }, this); + + if (!sql.wheres.rules.length) { + delete sql.wheres; + } + } + } + + if (sql.sub) { + for (const name in sql.sub) { + sql.sub[name] = this.parseSQL(sql.sub[name]); + } + } + + return Parser.parseValue(sql, this.scope); + }, + + isDynamic: function (value) { + if (typeof value == 'string') { + return value.includes('{{'); + } + + if (typeof value == 'object') { + for (const prop in value) { + if (this.isDynamic(value[prop])) { + return true; + } + } + } + + return false; + }, +}; + +module.exports = App; diff --git a/lib/core/async.js b/lib/core/async.js new file mode 100644 index 0000000..d25e20d --- /dev/null +++ b/lib/core/async.js @@ -0,0 +1,97 @@ +function defered() { + let resolve, reject; + let promise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + + return { + promise, + resolve, + reject + }; +} + +function limit(concurrency) { + let running = 0; + let queue = []; + + function init() { + if (running < concurrency) { + running++; + return Promise.resolve(); + } + + let defer = defered(); + queue.push(defer); + return defer.promise; + } + + function complete(a) { + let next = queue.shift(); + + if (next) { + next.resolve(); + } else { + running--; + } + + return a; + } + + return function(fn) { + return function() { + let args = Array.prototype.slice.apply(arguments); + + return init().then(() => { + return fn.apply(null, args); + }).finally(complete); + } + } +} + +module.exports = { + + map: function(arr, fn, concurrency) { + arr = Array.isArray(arr) ? arr : [arr]; + + if (concurrency) { + const limiter = limit(concurrency) + return Promise.all(arr.map(limiter(fn))); + } + + return Promise.all(arr.map(fn)); + }, + + mapSeries: function(arr, fn) { + arr = Array.isArray(arr) ? arr : [arr]; + + return arr.reduce((promise, curr, index, arr) => { + return promise.then(prev => { + return fn(curr, index, arr).then(val => { + prev.push(val); + return prev; + }); + }) + }, Promise.resolve([])); + }, + + reduce: function(arr, fn, start) { + arr = Array.isArray(arr) ? arr : [arr]; + + if (!arr.length) { + return Promise.resolve(start); + } + + return arr.reduce((promise, curr, index, arr) => { + return promise.then(prev => { + if (prev === undefined && arr.length === 1) { + return curr; + } + + return fn(prev, curr, index, arr); + }); + }, Promise.resolve(start)); + }, + +}; \ No newline at end of file diff --git a/lib/core/base32.js b/lib/core/base32.js new file mode 100644 index 0000000..81bfde0 --- /dev/null +++ b/lib/core/base32.js @@ -0,0 +1,84 @@ +const Variants = { + 'RFC4648': { + alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', + padding: true, + }, + 'RFC4648-HEX': { + alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUV', + padding: true, + }, + 'CROCKFORD': { + alphabet: '0123456789ABCDEFGHJKMNPQRSTVWXYZ', + padding: false, + }, +}; + +exports.encode = (input, opts = {}) => { + const Variant = Variants[opts.variant] || Variants['RFC4648']; + const alphabet = Variant.alphabet; + const padding = opts.padding != null ? opts.padding : Variant.padding; + const data = Buffer.from(input); + + let bits = 0; + let value = 0; + let output = ''; + + for (let i = 0; i < data.length; i++) { + value = (value << 8) | data.readUInt8(i); + bits += 8; + + while (bits >= 5) { + output += alphabet[(value >>> (bits - 5)) & 31]; + bits -= 5; + } + } + + if (bits > 0) { + output += alphabet[(value << (5 - bits)) & 31]; + } + + if (padding) { + while ((output.length % 8) !== 0) { + output += '='; + } + } + + return output; +}; + +exports.decode = (input, opts = {}) => { + const Variant = Variants[opts.variant] || Variants['RFC4648']; + const alphabet = Variant.alphabet; + + let bits = 0; + let value = 0; + + if (opts.variant === 'CROCKFORD') { + input = input.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1'); + } else { + input = input.replace(/=/g, ''); + } + + let index = 0; + let length = input.length; + let output = Buffer.alloc(Math.ceil(length * 5 / 8)); + + for (let i = 0; i < length; i++) { + let char = input[i]; + let char_index = alphabet.indexOf(char); + + if (char_index === -1) { + throw new Error('Invalid character: ' + char); + } + + value = (value << 5) | char_index; + bits += 5; + + if (bits >= 8) { + output.writeUInt8((value >>> (bits - 8)) & 255, index++); + bits -= 8; + } + } + + return output; +}; \ No newline at end of file diff --git a/lib/core/basicauth.js b/lib/core/basicauth.js new file mode 100644 index 0000000..e712232 --- /dev/null +++ b/lib/core/basicauth.js @@ -0,0 +1,43 @@ +// Code taken from https://github.com/jshttp/basic-auth/blob/master/index.js +// Copyright(c) 2013 TJ Holowaychuk +// Copyright(c) 2014 Jonathan Ong +// Copyright(c) 2015-2016 Douglas Christopher Wilson + +module.exports = auth; +module.exports.parse = parse; + +const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/; +const USER_PASS_REGEXP = /^([^:]*):(.*)$/; + +function auth(req) { + if (!req) throw new Error('argument req is required.'); + if (typeof req != 'object') throw new Error('argument req needs to be an object.'); + + return parse(getAuthorization(req)); +} + +function decodeBase64(str) { + return Buffer.from(str, 'base64').toString(); +} + +function getAuthorization(req) { + if (!req.headers || typeof req.headers != 'object') { + throw new Error('argument req needs to have headers.'); + } + + return req.headers.authorization; +} + +function parse(str) { + if (typeof str != 'string') { + return undefined; + } + + const match = CREDENTIALS_REGEXP.exec(str); + if (!match) return undefined; + + const userPass = USER_PASS_REGEXP.exec(decodeBase64(match[1])); + if (!userPass) return undefined; + + return { username: userPass[1], password: userPass[2] }; +} \ No newline at end of file diff --git a/lib/core/db.js b/lib/core/db.js new file mode 100644 index 0000000..a16eb0d --- /dev/null +++ b/lib/core/db.js @@ -0,0 +1,217 @@ +const knex = require('knex'); + +knex.QueryBuilder.extend('whereGroup', function(condition, rules) { + condition = condition.toLowerCase(); + + for (let rule of rules) { + if (!rule.condition) { + let where = condition == 'or' ? 'orWhere' : 'where'; + let column = rule.data ? rule.data.column : rule.column; + + if (rule.data && rule.data.table) { + if (rule.data.schema) { + column = rule.data.schema + '.' + rule.data.table + '.' + column; + } else { + column = rule.data.table + '.' + column; + } + } else if (rule.table) { + if (rule.schema) { + column = rule.schema + '.' + rule.table + '.' + column; + } else { + column = rule.table + '.' + column; + } + } + + if (typeof rule.value == 'undefined') { + rule.value = null; + } + + if (rule.operator == 'between') { + this[where + 'Between'](column, rule.value); + } else if (rule.operator == 'not_between') { + this[where + 'NotBetween'](column, rule.value); + } else if (rule.operator == 'is_null') { + this[where + 'Null'](column); + } else if (rule.operator == 'is_not_null') { + this[where + 'NotNull'](column); + } else if (rule.operator == 'in') { + this[where + 'In'](column, rule.value); + } else if (rule.operator == 'not_in') { + this[where + 'NotIn'](column, rule.value); + } else if (rule.operator == 'begins_with') { + // simple escaping for likes, knex doesn't do this (\ is default in most databases) + // perhaps improve by using raw query like + // this[where](knex.raw("?? like ? escape '\'"), [column, escapedValue]); + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where](column, 'like', escapedValue + '%'); + } else if (rule.operator == 'not_begins_with') { + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where + 'Not'](column, 'like', escapedValue + '%'); + } else if (rule.operator == 'ends_with') { + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where](column, 'like', '%' + escapedValue); + } else if (rule.operator == 'not_ends_with') { + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where + 'Not'](column, 'like', '%' + escapedValue); + } else if (rule.operator == 'contains') { + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where](column, 'like', '%' + escapedValue + '%'); + } else if (rule.operator == 'not_contains') { + let escapedValue = rule.value.replace(/[\\%_^]/g, '\\$&'); + this[where + 'Not'](column, 'like', '%' + escapedValue + '%'); + } else { + this[where](column, rule.operation, rule.value); + } + } else { + this[condition + 'Where'](function() { + this.whereGroup(rule.condition, rule.rules); + }); + } + } + + return this; +}); + +knex.QueryBuilder.extend('fromJSON', function(ast, meta) { + if (ast.type == 'count') { + return this.count('* as Total').from(function() { + this.fromJSON(Object.assign({}, ast, { + type: 'select', + offset: null, + limit: null, + orders: null + })).as('t1'); + }).first(); + } else if (ast.type == 'insert' || ast.type == 'update') { + let values = {}; + + for (let val of ast.values) { + if (val.type == 'json') { + // json support + values[val.column] = JSON.stringify(val.value); + } else { + values[val.column] = val.value; + } + } + + this[ast.type](values); + } else { + this[ast.type](); + } + + if (ast.returning && !(this.client?.config?.client?.includes('mysql'))) { + // Adding the option includeTriggerModifications allows you + // to run statements on tables that contain triggers. Only affects MSSQL. + this.returning(ast.returning, { includeTriggerModifications: true }); + } + + if (ast.table) { + let table = ast.table.name || ast.table; + + if (ast.table.schema) { + table = ast.table.schema + '.' + table; + } + + if (ast.table.alias) { + table += ' as ' + ast.table.alias; + } + + this.from(table); + } + + if ((ast.type == 'select' || ast.type == 'first') && ast.joins && ast.joins.length) { + for (let join of ast.joins) { + let table = join.table; + + if (join.schema) { + table = join.schema + '.' + table; + } + + if (join.alias) { + table += ' as ' + join.alias; + } + + this[(join.type || 'inner').toLowerCase() + 'Join'](table, function() { + for (let clause of join.clauses.rules) { + this.on( + (clause.schema ? clause.schema + '.' : '') + clause.table + '.' + clause.column, + clause.operation, + (clause.value.schema ? clause.value.schema + '.' : '') + clause.value.table + '.' + clause.value.column + ); + } + }); + } + } + + if ((ast.type == 'select' || ast.type == 'first') && ast.columns) { + for (let col of ast.columns) { + let column = col.column || col; + + if (ast.joins && ast.joins.length && col.table) { + column = col.table + '.' + column; + + if (col.schema) { + column = col.schema + '.' + column; + } + } + + if (col.alias) { + column += ' as ' + col.alias; + } + + this[col.aggregate ? col.aggregate.toLowerCase() : 'column'](column); + } + } + + if ((ast.type == 'select' || ast.type == 'first') && ast.groupBy && ast.groupBy.length) { + for (let col of ast.groupBy) { + if (ast.joins && ast.joins.length && col.table) { + if (col.schema) { + this.groupBy(col.schema + '.' + col.table + '.' + col.column); + } else { + this.groupBy(col.table + '.' + col.column); + } + } else { + this.groupBy(col.column || col); + } + } + } + + if (ast.wheres && ast.wheres.condition) { + this.whereGroup(ast.wheres.condition, ast.wheres.rules); + } + + if ((ast.type == 'select' || ast.type == 'first') && ast.orders && ast.orders.length) { + for (let order of ast.orders) { + if (ast.joins && ast.joins.length && order.table) { + if (order.schema) { + this.orderBy(order.schema + '.' + order.table + '.' + order.column, order.direction); + } else { + this.orderBy(order.table + '.' + order.column, order.direction); + } + } else { + this.orderBy(order.column, order.direction); + } + } + } + + if (ast.distinct) { + this.distinct(); + } + + if (ast.type == 'select' && ast.limit) { + this.limit(ast.limit); + } + + if (ast.type == 'select' && ast.offset) { + this.offset(ast.offset); + } + + if (meta) { + this.queryContext(meta); + } + + return this; +}); + +module.exports = knex; \ No newline at end of file diff --git a/lib/core/diacritics.js b/lib/core/diacritics.js new file mode 100644 index 0000000..8f4caa4 --- /dev/null +++ b/lib/core/diacritics.js @@ -0,0 +1,96 @@ +const diacriticsMap = [ + {'base':'A', 'letters':/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g}, + {'base':'AA','letters':/[\uA732]/g}, + {'base':'AE','letters':/[\u00C6\u01FC\u01E2]/g}, + {'base':'AO','letters':/[\uA734]/g}, + {'base':'AU','letters':/[\uA736]/g}, + {'base':'AV','letters':/[\uA738\uA73A]/g}, + {'base':'AY','letters':/[\uA73C]/g}, + {'base':'B', 'letters':/[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g}, + {'base':'C', 'letters':/[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g}, + {'base':'D', 'letters':/[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g}, + {'base':'DZ','letters':/[\u01F1\u01C4]/g}, + {'base':'Dz','letters':/[\u01F2\u01C5]/g}, + {'base':'E', 'letters':/[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g}, + {'base':'F', 'letters':/[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g}, + {'base':'G', 'letters':/[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g}, + {'base':'H', 'letters':/[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g}, + {'base':'I', 'letters':/[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g}, + {'base':'J', 'letters':/[\u004A\u24BF\uFF2A\u0134\u0248]/g}, + {'base':'K', 'letters':/[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g}, + {'base':'L', 'letters':/[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g}, + {'base':'LJ','letters':/[\u01C7]/g}, + {'base':'Lj','letters':/[\u01C8]/g}, + {'base':'M', 'letters':/[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g}, + {'base':'N', 'letters':/[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g}, + {'base':'NJ','letters':/[\u01CA]/g}, + {'base':'Nj','letters':/[\u01CB]/g}, + {'base':'O', 'letters':/[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g}, + {'base':'OI','letters':/[\u01A2]/g}, + {'base':'OO','letters':/[\uA74E]/g}, + {'base':'OU','letters':/[\u0222]/g}, + {'base':'P', 'letters':/[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g}, + {'base':'Q', 'letters':/[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g}, + {'base':'R', 'letters':/[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g}, + {'base':'S', 'letters':/[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g}, + {'base':'T', 'letters':/[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g}, + {'base':'TZ','letters':/[\uA728]/g}, + {'base':'U', 'letters':/[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g}, + {'base':'V', 'letters':/[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g}, + {'base':'VY','letters':/[\uA760]/g}, + {'base':'W', 'letters':/[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g}, + {'base':'X', 'letters':/[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g}, + {'base':'Y', 'letters':/[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g}, + {'base':'Z', 'letters':/[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g}, + {'base':'a', 'letters':/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g}, + {'base':'aa','letters':/[\uA733]/g}, + {'base':'ae','letters':/[\u00E6\u01FD\u01E3]/g}, + {'base':'ao','letters':/[\uA735]/g}, + {'base':'au','letters':/[\uA737]/g}, + {'base':'av','letters':/[\uA739\uA73B]/g}, + {'base':'ay','letters':/[\uA73D]/g}, + {'base':'b', 'letters':/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g}, + {'base':'c', 'letters':/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g}, + {'base':'d', 'letters':/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g}, + {'base':'dz','letters':/[\u01F3\u01C6]/g}, + {'base':'e', 'letters':/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g}, + {'base':'f', 'letters':/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g}, + {'base':'g', 'letters':/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g}, + {'base':'h', 'letters':/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g}, + {'base':'hv','letters':/[\u0195]/g}, + {'base':'i', 'letters':/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g}, + {'base':'j', 'letters':/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g}, + {'base':'k', 'letters':/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g}, + {'base':'l', 'letters':/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g}, + {'base':'lj','letters':/[\u01C9]/g}, + {'base':'m', 'letters':/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g}, + {'base':'n', 'letters':/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g}, + {'base':'nj','letters':/[\u01CC]/g}, + {'base':'o', 'letters':/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g}, + {'base':'oi','letters':/[\u01A3]/g}, + {'base':'ou','letters':/[\u0223]/g}, + {'base':'oo','letters':/[\uA74F]/g}, + {'base':'p','letters':/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g}, + {'base':'q','letters':/[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g}, + {'base':'r','letters':/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g}, + {'base':'s','letters':/[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g}, + {'base':'t','letters':/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g}, + {'base':'tz','letters':/[\uA729]/g}, + {'base':'u','letters':/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g}, + {'base':'v','letters':/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g}, + {'base':'vy','letters':/[\uA761]/g}, + {'base':'w','letters':/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g}, + {'base':'x','letters':/[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g}, + {'base':'y','letters':/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g}, + {'base':'z','letters':/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g} +]; + +module.exports = { + replace: function(str) { + for (let change of diacriticsMap) { + str = str.replace(change.letters, change.base); + } + + return str; + } +}; \ No newline at end of file diff --git a/lib/core/memoryStore.js b/lib/core/memoryStore.js new file mode 100644 index 0000000..767fc4e --- /dev/null +++ b/lib/core/memoryStore.js @@ -0,0 +1,92 @@ +module.exports = function(session) { + const Store = session.Store + + const defer = (cb, ...args) => { + if (typeof cb != 'function') return + setImmediate(cb, ...args) + } + + class ExtendedMap extends Map { + set(key, value, maxAge) { + const cached = super.get(key) + if (cached && cached.timer) clearTimeout(cached.timer) + const timer = maxAge ? setTimeout(super.delete.bind(this, key), maxAge) : null + return super.set(key, {timer, value}) + } + + get(key) { + const cached = super.get(key) + return cached && cached.value + } + + delete(key) { + const cached = super.get(key) + if (cached && cached.timer) clearTimeout(cached.timer) + return super.delete(key) + } + } + + class MemoryStore extends Store { + constructor(options = {}) { + super(options) + this.sessions = new ExtendedMap() + this.ttl = options.ttl + } + + get(sid, callback) { + let session = this.sessions.get(sid) + defer(callback, null, session) + } + + set(sid, session, callback) { + let ttl = this._getTTL(session) + if (ttl > 0) { + this.sessions.set(sid, session, ttl) + } else { + this.sessions.delete(sid) + } + defer(callback, null) + } + + touch(sid, session, callback) { + let ttl = this._getTTL(session) + let stored = this.sessions.get(sid) + stored.cookie = session.cookie + this.sessions.set(sid, stored, ttl) + defer(callback, null) + } + + destroy(sid, callback) { + this.sessions.delete(sid) + defer(callback, null) + } + + clear(callback) { + this.sessions.clear() + defer(callback, null) + } + + length(callback) { + let len = this.sessions.size + defer(callback, null, len) + } + + ids(callback) { + let keys = Array.from(this.sessions.keys()) + defer(callback, null, keys) + } + + all(callback) { + let sessions = Array.from(this.sessions.values()) + defer(callback, null, sessions) + } + + _getTTL(session) { + if (typeof this.ttl == 'number') return this.ttl + let maxAge = (session && session.cookie) ? session.cookie.maxAge : null + return (typeof maxAge == 'number') ? maxAge : 86400000 + } + } + + return MemoryStore +} \ No newline at end of file diff --git a/lib/core/middleware.js b/lib/core/middleware.js new file mode 100644 index 0000000..3c662c2 --- /dev/null +++ b/lib/core/middleware.js @@ -0,0 +1,163 @@ +const config = require('../setup/config'); +const debug = require('debug')('server-connect:router'); +const { clone } = require('./util'); +const App = require('./app'); + +module.exports = { + cache: function(options) { + return async function(req, res, next) { + if (!options.ttl || !global.redisClient) { + // no caching + return next(); + } + + let key = 'erc:' + (req.originalUrl || req.url); + let nocache = false; + let nostore = false; + + if (req.fragment) { + key += '-fragment'; + } + + if (req.query.nocache) { + nocache = true; + } + + try { + const cacheControl = req.get('Cache-Control'); + if (cacheControl) { + if (cacheControl.includes('no-cache')) nocache = true; + if (cacheControl.includes('no-store')) nostore = true; + } + } catch (err) { + // Ignore but log errors + console.error(err); + } + + if (!nocache) { + try { + const cache = await global.redisClient.hgetall(key); + if (cache.body) { + res.type(cache.type || 'text/html'); + res.send(cache.body); + return; + } + } catch (err) { + // Ignore but log errors + console.error(err); + } + } + + if (!nostore) { + // wrap res.send + const send = res.send.bind(res); + res.send = function (body) { + const ret = send(body); + + if (this.statusCode !== 200 || typeof body !== 'string') { + // do not cache when not status 200 or body is not a string + return ret; + } + + global.redisClient.hset(key, { + body: body, + type: this.get('Content-Type') || 'text/html' + }).then(() => { + return global.redisClient.expire(key, +options.ttl); + }).catch(err => { + // Ignore but log errors + console.error(err); + }); + + return ret; + }; + } + + return next(); + }; + }, + + serverConnect: function(json) { + return async function(req, res, next) { + const app = new App(req, res); + + debug(`Serving serverConnect ${req.path}`); + + return Promise.resolve(app.define(json)).catch(next); + }; + }, + + templateView: function(layout, page, data, exec) { + return async function(req, res, next) { + const app = new App(req, res); + + debug(`Serving templateView ${req.path}`); + + const routeData = clone(data); + const methods = { + _: (expr, data = {}) => { + return app.parse(`{{${expr}}}`, app.scope.create(data)); + }, + + _exec: async (name, steps, data = {}) => { + let context = {}; + app.scope = app.scope.create(data, context); + await app.exec(steps, true); + app.scope = app.scope.parent; + app.set(name, context); + return context; + }, + + _repeat: (expr, cb) => { + let data = app.parse(`{{${expr}}}`); + if (Array.isArray(data)) return data.forEach(cb); + return ''; + }, + + _csrfToken: (overwrite) => { + return req.csrfToken(overwrite); + }, + + _csrfMeta: () => { + return ``; + }, + + _csrfInput: () => { + return ``; + }, + + _route: req.route.path + }; + + let template = page; + if (layout && !req.fragment) { + routeData.content = '/' + page; + template = 'layouts/' + layout; + } + + if (exec) { + return Promise.resolve(app.define(exec, true)).then(() => { + if (!res.headersSent) { + app.set(app.parse(routeData)); + debug(`Render template ${template}`); + debug(`Template data: %O`, Object.assign({}, app.global.data, app.data)); + res.render(template, Object.assign({}, app.global.data, app.data, methods), async (err, html) => { + // callback is needed when using ejs with aync, html is a promise + if (err) return next(err); + html.then(html => res.send(html)).catch(err => next(err)); + }); + } + }).catch(next) + } else { + app.set(app.parse(routeData)); + debug(`Render template ${template}`); + debug(`Template data: %O`, app.global.data); + res.render(template, Object.assign({}, app.global.data, methods), async (err, html) => { + // callback is needed when using ejs with aync, html is a promise + if (err) return next(err); + html.then(html => res.send(html)).catch(err => next(err)); + }); + } + }; + } +}; \ No newline at end of file diff --git a/lib/core/parser.js b/lib/core/parser.js new file mode 100644 index 0000000..73091dc --- /dev/null +++ b/lib/core/parser.js @@ -0,0 +1,752 @@ +const fs = require('fs-extra'); +const path = require('path'); +const { randomUUID } = require('crypto'); +const { v4: uuidv4 } = require('uuid'); + +const NOOP = function() {}; + +const OPERATORS = { + '{' : 'L_CURLY', + '}' : 'R_CURLY', + '(' : 'L_PAREN', + ')' : 'R_PAREN', + '[' : 'L_BRACKET', + ']' : 'R_BRACKET', + '.' : 'PERIOD', + ',' : 'COMMA', + ':' : 'COLON', + '?' : 'QUESTION', + // Arithmetic operators + '+' : 'ADDICTIVE', + '-' : 'ADDICTIVE', + '*' : 'MULTIPLICATIVE', + '/' : 'MULTIPLICATIVE', + '%' : 'MULTIPLICATIVE', + // Comparison operators + '===': 'EQUALITY', + '!==': 'EQUALITY', + '==' : 'EQUALITY', + '!=' : 'EQUALITY', + '<' : 'RELATIONAL', + '>' : 'RELATIONAL', + '<=' : 'RELATIONAL', + '>=' : 'RELATIONAL', + 'in' : 'RELATIONAL', + // Logical operators + '&&' : 'LOGICAL_AND', + '||' : 'LOGICAL_OR', + '!' : 'LOGICAL_NOT', + // Bitwise operators + '&' : 'BITWISE_AND', + '|' : 'BITWISE_OR', + '^' : 'BITWISE_XOR', + '~' : 'BITWISE_NOT', + '<<' : 'BITWISE_SHIFT', + '>>' : 'BITWISE_SHIFT', + '>>>': 'BITWISE_SHIFT' +}; + +const EXPRESSIONS = { + 'in' : function(a, b) { return a() in b(); }, + '?' : function(a, b, c) { return a() ? b() : c(); }, + '+' : function(a, b) { a = a(); b = b(); return a == null ? b : b == null ? a : a + b; }, + '-' : function(a, b) { return a() - b(); }, + '*' : function(a, b) { return a() * b(); }, + '/' : function(a, b) { return a() / b(); }, + '%' : function(a, b) { return a() % b(); }, + '===': function(a, b) { return a() === b(); }, + '!==': function(a, b) { return a() !== b(); }, + '==' : function(a, b) { return a() == b(); }, + '!=' : function(a, b) { return a() != b(); }, + '<' : function(a, b) { return a() < b(); }, + '>' : function(a, b) { return a() > b(); }, + '<=' : function(a, b) { return a() <= b(); }, + '>=' : function(a, b) { return a() >= b(); }, + '&&' : function(a, b) { return a() && b(); }, + '||' : function(a, b) { return a() || b(); }, + '&' : function(a, b) { return a() & b(); }, + '|' : function(a, b) { return a() | b(); }, + '^' : function(a, b) { return a() ^ b(); }, + '<<' : function(a, b) { return a() << b(); }, + '>>' : function(a, b) { return a() >> b(); }, + '>>>': function(a, b) { return a() >>> b(); }, + '~' : function(a) { return ~a(); }, + '!' : function(a) { return !a(); } +}; + +const ESCAPE = { + 'n': '\n', + 'f': '\f', + 'r': '\r', + 't': '\t', + 'v': '\v', + "'": "'", + '"': '"', + '`': '`' +}; + +const formatters = require('../formatters'); + +// User formatters +if (fs.existsSync('extensions/server_connect/formatters')) { + const files = fs.readdirSync('extensions/server_connect/formatters'); + for (let file of files) { + if (path.extname(file) == '.js') { + Object.assign(formatters, require(`../../extensions/server_connect/formatters/${file}`)); + } + } +} + +function lexer(expr) { + let tokens = [], + token, + name, + start, + index = 0, + op = true, + ch, + ch2, + ch3; + + while (index < expr.length) { + start = index; + + ch = read(); + + if (isQuote(ch) && op) { + name = 'STRING'; + token = readString(ch); + op = false; + } else if ((isDigid(ch) || (is('.') && peek() && isDigid(peek()))) && op) { + name = 'NUMBER'; + token = readNumber(); + op = false; + } else if (isAlpha(ch) && op) { + name = 'IDENT'; + token = readIdent(); + if (is('(')) { + name = 'METHOD'; + } + op = false; + } else if (is('/') && op && (token == '(' || token == ',' || token == '?' || token == ':') && testRegexp()) { + name = 'REGEXP'; + token = readRegexp(); + op = false; + } else if (isWhitespace(ch)) { + index++; + continue; + } else if ((ch3 = read(3)) && OPERATORS[ch3]) { + name = OPERATORS[ch3]; + token = ch3; + op = true; + index += 3; + } else if ((ch2 = read(2)) && OPERATORS[ch2]) { + name = OPERATORS[ch2]; + token = ch2; + op = true; + index += 2; + } else if (OPERATORS[ch]) { + name = OPERATORS[ch]; + token = ch; + op = true; + index++; + } else { + throw new Error('Lexer Error: Unexpected token "' + ch + '" at column ' + index + ' in expression {{' + expr + '}}'); + } + + tokens.push({ + name: name, + index: start, + value: token + }); + } + + return tokens; + + function read(n) { + if (!n) n = 1; + return expr.substr(index, n); + } + + function peek(n) { + n = n || 1; + return index + n < expr.length ? expr[index + n] : false; + } + + function is(chars) { + return chars.indexOf(ch) != -1; + } + + function isQuote(ch) { + return ch == '"' || ch == "'"; + } + + function isDigid(ch) { + return ch >= '0' && ch <= '9'; + } + + function isAlpha(ch) { + return (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + ch == '_' || ch == '$'; + } + + function isAlphaNum(ch) { + return isAlpha(ch) || isDigid(ch); + } + + function isWhitespace(ch) { + return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n' || ch == '\v' || ch == '\u00A0'; + } + + function isExpOperator(ch) { + return ch == '-' || ch == '+' || isDigid(ch); + } + + function readString(quote) { + let str = '', esc = false; + + index++; + + while (index < expr.length) { + ch = read(); + + if (esc) { + if (ch == 'u') { + // unicode escape + index++; + let hex = read(4); + if (!hex.match(/[\da-f]{4}/i)) { + throw new Error('Lexer Error: Invalid unicode escape at column ' + index + ' in expression {{' + expr + '}}'); + } + str += String.fromCharCode(parseInt(hex, 16)); + index += 3; + } else { + str += ESCAPE[ch] ? ESCAPE[ch] : ch; + } + + esc = false; + } else if (ch == '\\') { + // escape character + esc = true; + } else if (ch == quote) { + // end of string + index ++; + return str; + } else { + str += ch; + } + + index++; + } + + throw new Error('Lexer Error: Unterminated string at column ' + index + ' in expression {{' + expr + '}}'); + } + + function readNumber() { + let num = '', exp = false; + + while (index < expr.length) { + ch = read(); + + if (isDigid(ch) || (is('.') && peek() && isDigid(peek()))) { + num += ch; + } else { + let next = peek(); + + if (is('eE') && isExpOperator(next)) { + num += 'e'; + exp = true; + } else if (isExpOperator(ch) && next && isDigid(next) && exp) { + num += ch; + exp = false; + } else if (isExpOperator(ch) && (!next || !isDigid(next)) && exp) { + throw new Error('Lexer Error: Invalid exponent at column ' + index + ' in expression {{' + expr + '}}'); + } else { + break; + } + } + + index++; + } + + return +num; + } + + function readIdent() { + let ident = ''; + + while (index < expr.length) { + ch = read(); + + if (isAlphaNum(ch)) { + ident += ch; + } else { + break; + } + + index++; + } + + return ident; + } + + function readRegexp() { + let re = '', mod = '', esc = false; + + index ++; + + while (index < expr.length) { + ch = read(); + + if (esc) { + esc = false; + } else if (ch == '\\') { + esc = true; + } else if (ch == '/') { + index++; + + while ('ign'.indexOf(ch = read()) != -1) { + mod += ch; + index++; + } + + return re + '%%%' + mod; + } + + re += ch; + index++; + } + + throw new Error('Lexer Error: Unterminated regexp at column ' + index + ' in expression {{' + expr + '}}'); + } + + function testRegexp() { + var idx = index, ok = true; + + try { + readRegexp(); + } catch (e) { + ok = false; + } + + // reset our index and ch + index = idx; + ch = '/'; + + return ok; + } +} + +function parser(expr, scope) { + let tokens = lexer(expr), + context = undefined, + RESERVED = { + 'PI' : function() { return Math.PI; }, + 'UUID' : function() { return randomUUID ? randomUUID() : uuidv4(); }, + 'NOW' : function() { return date(); }, + 'NOW_UTC' : function() { return utc_date(); }, + 'TIMESTAMP': function() { return timestamp(); }, + '$this' : function() { return scope.data; }, + '$global' : function() { return globalScope.data; }, + '$parent' : function() { return scope.parent && scope.parent.data; }, + 'null' : function() { return null; }, + 'true' : function() { return true; }, + 'false' : function() { return false; }, + '_' : function() { return { __dmxScope__: true } } + }; + + return start()(); + + function pad(s, n) { + return ('000' + s).substr(-n); + } + + function date(dt) { + dt = dt || new Date(); + return pad(dt.getFullYear(), 4) + '-' + pad(dt.getMonth() + 1, 2) + '-' + pad(dt.getDate(), 2) + ' ' + + pad(dt.getHours(), 2) + ':' + pad(dt.getMinutes(), 2) + ':' + pad(dt.getSeconds(), 2); + } + + function utc_date(dt) { + dt = dt || new Date(); + return pad(dt.getUTCFullYear(), 4) + '-' + pad(dt.getUTCMonth() + 1, 2) + '-' + pad(dt.getUTCDate(), 2) + 'T' + + pad(dt.getUTCHours(), 2) + ':' + pad(dt.getUTCMinutes(), 2) + ':' + pad(dt.getUTCSeconds(), 2) + 'Z'; + } + + function timestamp(dt) { + dt = dt || new Date(); + return ~~(dt / 1000); + } + + function read() { + if (tokens.length === 0) { + throw new Error('Parser Error: Unexpected end of expression {{' + expr + '}}'); + } + + return tokens[0]; + } + + function peek(e) { + if (tokens.length > 0) { + let token = tokens[0]; + + if (!e || token.name == e) { + return token; + } + } + + return false; + } + + function expect(e) { + let token = peek(e); + + if (token) { + tokens.shift(); + return token; + } + + return false; + } + + function consume(e) { + if (!expect(e)) { + throw new Error('Parser Error: Unexpected token, expecting ' + e + ' in expression {{' + expr + '}}'); + } + } + + function fn(expr) { + let args = [].slice.call(arguments, 1); + + return function() { + if (EXPRESSIONS[expr]) { + return EXPRESSIONS[expr].apply(context, args); + } else { + return expr; + } + } + } + + function start() { + return conditional(); + } + + function conditional() { + let left = logicalOr(), middle, token; + + if ((token = expect('QUESTION'))) { + middle = conditional(); + + if ((token = expect('COLON'))) { + return fn('?', left, middle, conditional()); + } else { + throw new Error('Parse Error: Expecting : in expression {{' + expr + '}}'); + } + } else { + return left; + } + } + + function logicalOr() { + let left = logicalAnd(), token; + + while (true) { + if ((token = expect('LOGICAL_OR'))) { + left = fn(token.value, left, logicalAnd()); + } else { + return left; + } + } + } + + function logicalAnd() { + let left = bitwiseOr(), token; + + if ((token = expect('LOGICAL_AND'))) { + left = fn(token.value, left, logicalAnd()); + } + + return left; + } + + function bitwiseOr() { + let left = bitwiseXor(), token; + + if ((token = expect('BITWISE_OR'))) { + left = fn(token.value, left, bitwiseXor()); + } + + return left; + } + + function bitwiseXor() { + let left = bitwiseAnd(), token; + + if ((token = expect('BITWISE_XOR'))) { + left = fn(token.value, left, bitwiseAnd()); + } + + return left; + } + + function bitwiseAnd() { + let left = equality(), token; + + if ((token = expect('BITWISE_AND'))) { + left = fn(token.value, left, bitwiseAnd()); + } + + return left; + } + + function equality() { + let left = relational(), token; + + if ((token = expect('EQUALITY'))) { + left = fn(token.value, left, equality()); + } + + return left; + } + + function relational() { + let left = bitwiseShift(), token; + + if ((token = expect('RELATIONAL'))) { + left = fn(token.value, left, relational()); + } + + return left; + } + + function bitwiseShift() { + let left = addictive(), token; + + if ((token = expect('BITWISE_SHIFT'))) { + left = fn(token.value, left, addictive()); + } + + return left; + } + + function addictive() { + let left = multiplicative(), token; + + while ((token = expect('ADDICTIVE'))) { + left = fn(token.value, left, multiplicative()); + } + + return left; + } + + function multiplicative() { + let left = unary(), token; + + while ((token = expect('MULTIPLICATIVE'))) { + left = fn(token.value, left, unary()); + } + + return left; + } + + function unary() { + let token; + + if ((token = expect('ADDICTIVE'))) { + if (token.value == '+') { + return primary(); + } else { + return fn(token.value, function() { return 0; }, unary()); + } + } else if ((token = expect('LOGICAL_NOT'))) { + return fn(token.value, unary()); + } + + return primary(); + } + + function primary() { + let value, next; + + if (expect('L_PAREN')) { + value = start(); + consume('R_PAREN'); + } else if (expect('L_CURLY')) { + let obj = {}; + + if (read().name != 'R_CURLY') { + do { + let key = expect().value; + consume('COLON'); + obj[key] = start()(); + } while (expect('COMMA')); + } + + value = fn(obj); + + consume('R_CURLY'); + } else if (expect('L_BRACKET')) { + let arr = []; + + if (read().name != 'R_BRACKET') { + do { + arr.push(start()()); + } while (expect('COMMA')); + } + + value = fn(arr); + + consume('R_BRACKET'); + } else if (expect('PERIOD')) { + value = peek() ? objectMember(fn(scope.data)) : fn(scope.data); + } else { + let token = expect(); + + if (token === false) { + throw new Error('Parser Error: Not a primary expression {{' + expr + '}}'); + } + + if (token.name == 'IDENT') { + value = RESERVED.hasOwnProperty(token.value) + ? RESERVED[token.value] + : function() { return scope.get(token.value) }; + } else if (token.name == 'METHOD') { + if (!formatters[token.value]) { + throw new Error('Parser Error: Formatter "' + token.value + '" does not exist, expression {{' + expression + '}}'); + } + + value = fn(formatters[token.value]); + } else if (token.name == 'REGEXP') { + value = function() { + let re = token.value.split('%%%'); + return new RegExp(re[0], re[1]); + }; + } else { + value = function() { return token.value }; + } + } + + while ((next = expect('L_PAREN') || expect('L_BRACKET') || expect('PERIOD'))) { + if (next.value == '(') { + value = functionCall(value, context); + } else if (next.value == '[') { + value = objectIndex(value); + } else if (next.value == '.') { + context = value; + value = objectMember(value); + } else { + throw new Error('Parser Error: Parse error in expression {{' + expr + '}}'); + } + } + + context = undefined; + + return value; + } + + function functionCall(func, ctx) { + let argsFn = []; + + if (read().name != 'R_PAREN') { + do { + argsFn.push(start()); + } while (expect('COMMA')); + } + + consume('R_PAREN'); + + return function() { + let args = []; + + if (ctx) args.push(ctx()); + + for (let argFn of argsFn) { + args.push(argFn()); + } + + let fnPtr = func() || NOOP; + + return fnPtr.apply(null, args); + } + } + + function objectIndex(obj) { + let indexFn = start(); + + consume('R_BRACKET'); + + return function() { + let o = obj(), + i = indexFn(); + + if (typeof o != 'object') return undefined; + + if (o.__dmxScope__) { + return scope.get(i); + } + + return o[i]; + } + } + + function objectMember(obj) { + let token = expect(); + + return function() { + let o = obj(); + + if (token.name == 'METHOD') { + if (!formatters[token.value]) { + throw new Error('Parser Error: Formatter "' + token.value + '" does not exist, expression {{' + expr + '}}'); + } + + return formatters[token.value]; + } + + if (o && o.__dmxScope) { + return scope.get(token.value); + } + + return o && o[token.value]; + } + } +} + +function parseValue(value, scope) { + if (value == null) return value; + + value = value.valueOf(); + + if (typeof value == 'object') { + for (let key in value) { + value[key] = parseValue(value[key], scope); + } + } + + if (typeof value == 'string') { + if (value.substr(0, 2) == '{{' && value.substr(-2) == '}}') { + let expr = value.replace(/^\{\{|\}\}$/g, ''); + + if (expr.indexOf('{{') == -1) { + return parser(expr, scope); + } + } + + return parseTemplate(value, scope); + } + + return value; +} + +function parseTemplate(template, scope) { + return template.replace(/\{\{(.*?)\}\}/g, function(a, m) { + var value = parser(m, scope); + return value != null ? String(value) : ''; + }); +} + +exports.lexer = lexer; +exports.parse = parser; +exports.parseValue = parseValue; +exports.parseTemplate = parseTemplate; \ No newline at end of file diff --git a/lib/core/path.js b/lib/core/path.js new file mode 100644 index 0000000..2166a34 --- /dev/null +++ b/lib/core/path.js @@ -0,0 +1,97 @@ +const { existsSync: exists } = require('fs'); +const { dirname, basename, extname, join, resolve, relative, posix } = require('path'); +const { v4: uuidv4 } = require('uuid'); +const debug = require('debug')('server-connect:path'); + +module.exports = { + + getFilesArray: function(paths) { + let files = []; + + if (!Array.isArray(paths)) { + paths = [paths]; + } + + for (let path of paths) { + if (Array.isArray(path)) { + files = files.concat(module.exports.getFilesArray(path)); + } else if (path && path.path) { + files.push(module.exports.toSystemPath(path.path)); + } else if (path) { + files.push(module.exports.toSystemPath(path)); + } + } + + return files; + }, + + toSystemPath: function(path) { + if (path[0] != '/' || path.includes('../')) { + throw new Error(`path.toSystemPath: Invalid path "${path}".`); + } + + return resolve('.' + path); + }, + + toAppPath: function(path) { + let root = resolve('.'); + let rel = relative(root, path).replace(/\\/g, '/'); + + debug('toAppPath: %O', { root, path, rel }); + + if (rel.includes('../')) { + throw new Error(`path.toAppPath: Invalid path "${rel}".`); + } + + return '/' + rel; + }, + + toSiteUrl: function(path) { + let root = resolve('public'); + let rel = relative(root, path).replace(/\\/g, '/'); + + debug('toSiteUrl: %O', { root, path, rel }); + + if (rel.includes('../')) { + return ''; + } + + return '/' + rel; + }, + + getUniqFile: function(path) { + let n = 1; + + while (exists(path)) { + path = path.replace(/(_(\d+))?(\.\w+)$/, (a, b, c, d) => '_' + (n++) + (d || a)); + if (n > 999) throw new Error(`path.getUniqFile: Couldn't create a unique filename for ${path}`); + } + + return path; + }, + + parseTemplate: function(path, template) { + let n = 1, dir = dirname(path), file = template.replace(/\{([^\}]+)\}/g, (a, b) => { + switch (b) { + case 'name': return basename(path, extname(path)); + case 'ext' : return extname(path); + case 'guid': return uuidv4(); + } + + return a; + }); + + if (file.includes('{_n}')) { + template = file; + file = template.replace('{_n}', ''); + + while (exists(join(dir, file))) { + file = template.replace('{_n}', n++); + if (n > 999) throw new Error(`path.parseTemplate: Couldn't create a unique filename for ${path}`); + } + } + + return join(dir, file); + } + +}; \ No newline at end of file diff --git a/lib/core/scope.js b/lib/core/scope.js new file mode 100644 index 0000000..dc8d92e --- /dev/null +++ b/lib/core/scope.js @@ -0,0 +1,63 @@ +function Scope(data, parent, context) { + if (typeof data != 'object') { + data = { $value: data }; + } + + this.data = data; + this.parent = parent; + this.context = context; +}; + +Scope.prototype = { + create: function(data, context) { + return new Scope(data, this, context); + }, + + get: function(name) { + if (this.data[name] !== undefined) { + return this.data[name]; + } + + if (this.parent) { + return this.parent.get(name); + } + + return undefined; + }, + + set: function(name, value) { + if (typeof name == 'object') { + for (let prop in name) { + this.set(prop, name[prop]); + } + } else { + this.data[name] = value; + + if (this.context) { + this.context[name] = value; + } + } + }, + + has: function(name) { + if (this.data[name] !== undefined) { + return true; + } + + if (this.parent) { + return this.parent.has(name); + } + + return false; + }, + + remove: function(name) { + delete this.data[name]; + + if (this.context) { + delete this.context[name]; + } + }, +} + +module.exports = Scope; \ No newline at end of file diff --git a/lib/core/util.js b/lib/core/util.js new file mode 100644 index 0000000..5c7ebd4 --- /dev/null +++ b/lib/core/util.js @@ -0,0 +1,186 @@ +const reGrouping = /(\d+)(\d{3})/; + +function clone(o) { + if (o == null) { + return o; + } + + if (Array.isArray(o)) { + return o.map(clone); + } + + if (o instanceof Date) { + // we do not clone date objects but convert them to an iso string + return o.toISOString(); + } + + if (typeof o == 'object') { + let oo = {}; + for (let key in o) { + oo[key] = clone(o[key]); + } + return oo; + } + + return o; +} + +function mergeDeep(target, source) { + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (isObject(source[key])) { + if (!target[key]) { + Object.assign(target, { [key]: {} }); + } + + mergeDeep(target[key], source[key]); + } else { + Object.assign(target, { [key]: source[key] }); + } + } + } + + return target; +} + +function isObject(item) { + return (item && typeof item === 'object' && !Array.isArray(item) && item !== null); + } + +module.exports = { + + clone, + + mergeDeep, + + escapeRegExp: function(val) { + // https://github.com/benjamingr/RegExp.escape + return val.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&'); + }, + + keysToLowerCase: function(o) { + return Object.keys(o).reduce((c, k) => (c[k.toLowerCase()] = o[k], c), {}); + }, + + padNumber: function(num, digids, trim) { + let sign = ''; + + if (num < 0) { + sign = '-'; + num = -num; + } + + num = String(num); + + while (num.length < digids) { + num = '0' + num; + } + + if (trim) { + num = num.substr(num.length - digids); + } + + return sign + num; + }, + + formatNumber: function(num, decimals, decimalSeparator, groupingSeparator) { + num = Number(num); + + if (isNaN(num) || !isFinite(num)) return ''; + + decimalSeparator = typeof decimalSeparator == 'string' ? decimalSeparator : '.'; + groupingSeparator = typeof groupingSeparator == 'string' ? groupingSeparator : ''; + precision = typeof precision == 'number' ? Math.abs(precision) : null; + + let minus = num < 0; + let parts = (decimals == null ? String(Math.abs(num)) : Math.abs(num).toFixed(decimals)).split('.'); + let wholePart = parts[0]; + let decimalPart = parts.length > 1 ? decimalSeparator + parts[1] : ''; + + if (groupingSeparator) { + while (reGrouping.test(wholePart)) { + wholePart = wholePart.replace(reGrouping, '$1' + groupingSeparator + '$2'); + } + } + + return (minus ? '-' : '') + wholePart + decimalPart; + }, + + parseDate: function(obj) { + if (Object.prototype.toString.call(obj) == '[object Date]') return new Date(obj); + + let date = new Date(obj); + + if (typeof obj == 'number') { + date = new Date(obj * 1000); + } + + if (typeof obj == 'string') { + if (obj == 'now') { + date = new Date(); + } else if (/^\d{4}-\d{2}-\d{2}$/.test(obj)) { + let parts = obj.split('-'); + date = new Date(parts[0], parts[1] - 1, parts[2]); + } else if (/^\d{2}:\d{2}:\d{2}$/.test(obj)) { + let parts = obj.split(':'); + date = new Date(); + date.setHours(parts[0]); + date.setMinutes(parts[1]); + date.setSeconds(parts[2]); + date.setMilliseconds(0); + } + } + + if (date.toString() == 'Invalid Date') { + return undefined; + } + + return date; + }, + + formatDate: function(date, format, utc, locale) { + date = module.exports.parseDate(date); + if (date == null) return date; + locale = typeof locale == 'string' ? locale : 'en-US'; + const lang = require('../locale/' + locale); + const pad = (v, n) => `0000${v}`.substr(-n); + + let y = utc ? date.getUTCFullYear() : date.getFullYear(), + n = utc ? date.getUTCMonth() : date.getMonth(), + d = utc ? date.getUTCDate() : date.getDate(), + w = utc ? date.getUTCDay() : date.getDay(), + h = utc ? date.getUTCHours() : date.getHours(), + m = utc ? date.getUTCMinutes() : date.getMinutes(), + s = utc ? date.getUTCSeconds() : date.getSeconds(), + v = utc ? date.getUTCMilliseconds() : date.getMilliseconds(); + + return format.replace(/([yMdHhmsaAvw])(\1+)?/g, part => { + switch (part) { + case 'yyyy': return pad(y, 4); + case 'yy': return pad(y, 2); + case 'y': return y; + case 'MMMM': return lang.months[n]; + case 'MMM': return lang.monthsShort[n]; + case 'MM': return pad(n + 1, 2); + case 'M': return n + 1; + case 'dddd': return lang.days[w]; + case 'ddd': return lang.daysShort[w]; + case 'dd': return pad(d, 2); + case 'd': return d; + case 'HH': return pad(h, 2); + case 'H': return h; + case 'hh': return pad((h % 12) || 12, 2); + case 'h': return (h % 12) || 12; + case 'mm': return pad(m, 2); + case 'm': return m; + case 'ss': return pad(s, 2); + case 's': return s; + case 'a': return h < 12 ? 'am' : 'pm'; + case 'A': return h < 12 ? 'AM' : 'PM'; + case 'v': return pad(v, 3); + case 'w': return w; + } + }); + } + +}; \ No newline at end of file diff --git a/lib/core/webhook.js b/lib/core/webhook.js new file mode 100644 index 0000000..f6ac8f1 --- /dev/null +++ b/lib/core/webhook.js @@ -0,0 +1,24 @@ +// Helper methods for webhooks + +const fs = require('fs-extra'); +const App = require('./app'); + +exports.createHandler = function(name, fn = () => 'handler') { + return async (req, res, next) => { + const action = await fn(req, res, next); + + if (typeof action == 'string') { + const path = `app/webhooks/${name}/${action}.json`; + + if (fs.existsSync(path)) { + const app = new App(req, res); + let json = await fs.readJSON(path); + return Promise.resolve(app.define(json)).catch(next); + } else { + res.json({error: `No action found for ${action}.`}); + // do not return 404 else stripe will retry + //next(); + } + } + } +}; \ No newline at end of file diff --git a/lib/formatters/collections.js b/lib/formatters/collections.js new file mode 100644 index 0000000..f7dd2a0 --- /dev/null +++ b/lib/formatters/collections.js @@ -0,0 +1,169 @@ +module.exports = { + + join: function(arr, separator, prop) { + if (!Array.isArray(arr)) return arr; + return arr.map(item => prop ? item[prop] : item).join(separator); + }, + + first: function(arr) { + if (!Array.isArray(arr)) return arr; + return arr.length ? arr[0] : null; + }, + + top: function(arr, count) { + if (!Array.isArray(arr)) return arr; + return arr.slice(0, count); + }, + + last: function(arr, count) { + if (!Array.isArray(arr)) return arr; + if (count) return arr.slice(-count); + return arr[arr.length - 1]; + }, + + where: function(arr, prop, operator, value) { + if (!Array.isArray(arr)) return arr; + return arr.filter(item => { + let val = item[prop]; + + switch (operator) { + case 'startsWith': return String(val).startsWith(value); + case 'endsWith': return String(val).endsWith(value); + case 'contains': return String(val).includes(value); + case '===': return val === value; + case '!==': return val !== value; + case '==': return val == value; + case '!=': return val != value; + case '<=': return val <= value; + case '>=': return val >= value; + case '<': return val < value; + case '>': return val > value; + }; + + return true; + }); + }, + + unique: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + if (prop) arr = arr.map(item => item[prop]); + + let lookup = []; + return arr.filter(item => { + if (lookup.includes(item)) return false; + lookup.push(item); + return true; + }); + }, + + groupBy: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.reduce((groups, item) => { + let group = String(item[prop]); + if (!groups[group]) groups[group] = []; + groups[group].push(item); + return groups; + }, {}); + }, + + sort: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.slice(0).sort((a, b) => { + a = prop ? a && a[prop] : a; + b = prop ? b && b[prop] : b; + + if (typeof a == 'string' && typeof b == 'string') { + return a.localeCompare(b); + } + + return a < b ? -1 : a > b ? 1 : 0; + }); + }, + + randomize: function(arr) { + if (!Array.isArray(arr)) return arr; + arr = arr.slice(0); + let len = arr.length; + if (len) { + while (--len) { + let rnd = Math.floor(Math.random() * (len + 1)); + let val = arr[len]; + arr[len] = arr[rnd]; + arr[rnd] = val; + } + } + return arr; + }, + + reverse: function(arr) { + if (!Array.isArray(arr)) return arr; + return arr.slice(0).reverse(); + }, + + count: function(arr) { + if (!Array.isArray(arr)) return arr; + return arr.length; + }, + + min: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.reduce((min, num) => { + num = prop ? num[prop] : num; + if (num == null) return min; + num = Number(num); + if (isNaN(num) || !isFinite(num)) return min; + return min == null || num < min ? num : min; + }, null); + }, + + max: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.reduce((max, num) => { + num = prop ? num[prop] : num; + if (num == null) return max; + num = Number(num); + if (isNaN(num) || !isFinite(num)) return max; + return max == null || num > max ? num : max; + }, null); + }, + + sum: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.reduce((sum, num) => { + num = prop ? num[prop] : num; + if (num == null) return sum; + num = Number(num); + if (isNaN(num) || !isFinite(num)) return sum; + return sum + num; + }, 0); + }, + + avg: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + let cnt = 0; + return arr.reduce((avg, num) => { + num = prop ? num[prop] : num; + if (num == null) return avg; + num = Number(num); + if (isNaN(num) || !isFinite(num)) return avg; + cnt++; + return avg + num; + }, 0) / cnt; + }, + + flatten: function(arr, prop) { + if (!Array.isArray(arr)) return arr; + return arr.map(item => item[prop]); + }, + + keys: function(obj) { + if (typeof obj != 'object') return obj; + return Object.keys(obj); + }, + + values: function(obj) { + if (typeof obj != 'object') return obj; + return Object.values(obj); + }, + +}; \ No newline at end of file diff --git a/lib/formatters/conditional.js b/lib/formatters/conditional.js new file mode 100644 index 0000000..9ef50d0 --- /dev/null +++ b/lib/formatters/conditional.js @@ -0,0 +1,30 @@ +module.exports = { + + startsWith: function(val, str) { + if (val == null) return false; + return String(val).startsWith(str); + }, + + endsWith: function(val, str) { + if (val == null) return false; + return String(val).endsWith(str); + }, + + contains: function(val, str) { + if (val == null) return false; + return String(val).includes(str); + }, + + between: function(val, min, max) { + return val >= min && val <= max; + }, + + inRange: function(val, min, max) { + val = Number(val); + min = Number(min); + max = Number(max); + + return val >= min && val <= max; + }, + +}; \ No newline at end of file diff --git a/lib/formatters/core.js b/lib/formatters/core.js new file mode 100644 index 0000000..23e1755 --- /dev/null +++ b/lib/formatters/core.js @@ -0,0 +1,27 @@ +module.exports = { + + default: function(val, def) { + return val ? val : def; + }, + + then: function(val, trueVal, falseVal) { + return val ? trueVal : falseVal; + }, + + toNumber: function(val) { + return Number(val); + }, + + toString: function(val) { + return String(val); + }, + + toJSON: function(val) { + return JSON.stringify(val); + }, + + parseJSON: function(val) { + return JSON.parse(val); + }, + +}; \ No newline at end of file diff --git a/lib/formatters/crypto.js b/lib/formatters/crypto.js new file mode 100644 index 0000000..b6673da --- /dev/null +++ b/lib/formatters/crypto.js @@ -0,0 +1,99 @@ +const { createHash, createHmac, createCipheriv, createDecipheriv, randomBytes, scryptSync, randomUUID } = require('crypto'); +const { v4: uuidv4 } = require('uuid'); + +module.exports = { + + randomUUID: function() { + return randomUUID ? randomUUID() : uuidv4(); + }, + + md5: function(data, salt, enc) { + return hash('md5', data, salt, enc) + }, + + sha1: function(data, salt, enc) { + return hash('sha1', data, salt, enc) + }, + + sha256: function(data, salt, enc) { + return hash('sha256', data, salt, enc) + }, + + sha512: function(data, salt, enc) { + return hash('sha512', data, salt, enc) + }, + + hash: function(data, alg, enc) { + return hash(alg, data, '', enc); + }, + + hmac: function(data, alg, secret, enc) { + return hmac(alg, data, secret, enc); + }, + + transform: function(data, from, to) { + return transform(data, from, to); + }, + + encodeBase64: function(data, enc) { + enc = typeof enc == 'string' ? enc : 'utf8'; + return transform(data, enc, 'base64'); + }, + + decodeBase64: function(data, enc) { + enc = typeof enc == 'string' ? enc : 'utf8'; + return transform(data, 'base64', enc); + }, + + encodeBase64Url: function(data, enc) { + enc = typeof enc == 'string' ? enc : 'utf8'; + return transform(data, enc, 'base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); + }, + + decodeBase64Url: function(data, enc) { + enc = typeof enc == 'string' ? enc : 'utf8'; + return transform(data.replace(/-/g, '+').replace(/_/g, '/'), 'base64', enc); + }, + + encrypt: function(data, password, enc) { + const config = require('../setup/config'); + password = typeof password == 'string' ? password : config.secret; + enc = typeof enc == 'string' ? enc : 'base64'; + const iv = randomBytes(16); + const key = scryptSync(password, iv, 32); + const cipher = createCipheriv('aes-256-cbc', key, iv); + const encrypted = cipher.update(data, 'utf8', enc); + return iv.toString(enc) + '.' + encrypted + cipher.final(enc); + }, + + decrypt: function(data, password, enc) { + const config = require('../setup/config'); + password = typeof password == 'string' ? password : config.secret; + enc = typeof enc == 'string' ? enc : 'base64'; + const iv = Buffer.from(data.split('.')[0], enc); + const key = scryptSync(password, iv, 32); + const decipher = createDecipheriv('aes-256-cbc', key, iv); + const decrypted = decipher.update(data.split('.')[1], enc, 'utf8'); + return decrypted + decipher.final('utf8'); + }, + +}; + +function hash(alg, data, salt, enc) { + if (data == null) return data; + salt = typeof salt == 'string' ? salt : ''; + enc = typeof enc == 'string' ? enc : 'hex'; + return createHash(alg).update(data + salt).digest(enc); +} + +function hmac(alg, data, secret, enc) { + if (data == null) return data; + secret = typeof secret == 'string' ? secret : ''; + enc = typeof enc == 'string' ? enc : 'hex'; + return createHmac(alg, secret).update(data).digest(enc); +} + +function transform(data, from, to) { + if (data == null) return data; + return Buffer.from(String(data), from).toString(to); +} \ No newline at end of file diff --git a/lib/formatters/date.js b/lib/formatters/date.js new file mode 100644 index 0000000..f7c8c97 --- /dev/null +++ b/lib/formatters/date.js @@ -0,0 +1,83 @@ +const { padNumber, parseDate, formatDate } = require('../core/util'); + +module.exports = { + + formatDate: function(date, format, utc) { + date = parseDate(date); + if (date == null) return date; + + return formatDate(date, format, utc); + }, + + dateAdd: function(date, interval, num) { + date = parseDate(date); + if (date == null) return date; + + switch (interval) { + case 'years': date.setFullYear(date.getFullYear() + num); break; + case 'months': date.setMonth(date.getMonth() + num); break; + case 'weeks': date.setDate(date.getDate() + (num * 7)); break; + case 'days': date.setDate(date.getDate() + num); break; + case 'hours': date.setHours(date.getHours() + num); break; + case 'minutes': date.setMinutes(date.getMinutes() + num); break; + case 'seconds': date.setSeconds(date.getSeconds() + num); break; + } + + return date.toISOString(); + }, + + dateDiff: function(date, interval, date2) { + date = parseDate(date); + date2 = parseDate(date2); + + let diff = Math.abs(date2 - date); + + if (isNaN(diff) || date == null || date2 == null) return undefined; + + let s = 1000, m = s * 60, h = m * 60, d = h * 24, w = d * 7; + + switch (interval) { + case 'years': return Math.abs(date2.getFullYear() - date.getFullYear()); + case 'months': return Math.abs((date2.getFullYear() * 12 + date2.getMonth()) - (date.getFullYear() * 12 + date.getMonth())); + case 'weeks': return Math.floor(diff / w); + case 'days': return Math.floor(diff / d); + case 'hours': return Math.floor(diff/ h); + case 'minutes': return Math.floor(diff / m); + case 'seconds': return Math.floor(diff / s); + case 'hours:minutes': + h = Math.floor(diff / h); + m = Math.floor(diff / m); + return padNumber(h, 2) + ':' + padNumber(m - (h * 60), 2); + case 'minutes:seconds': + m = Math.floor(diff / m); + s = Math.floor(diff / s); + return padNumber(m, 2) + ':' + padNumber(s - (m * 60), 2); + case 'hours:minutes:seconds': + h = Math.floor(diff / h); + m = Math.floor(diff / m); + s = Math.floor(diff / s); + return padNumber(h, 2) + ':' + padNumber(m - (h * 60), 2) + ':' + padNumber(s - m * 60, 2); + } + + return undefined; + }, + + toTimestamp: function(date) { + date = parseDate(date); + if (date == null) return date; + return Math.floor(date.getTime() / 1000); + }, + + toLocalTime: function(date) { + date = parseDate(date); + if (date == null) return date; + return formatDate(date, 'yyyy-MM-dd HH:mm:ss.v'); + }, + + toUTCTime: function(date) { + date = parseDate(date); + if (date == null) return date; + return date.toISOString(); + }, + +}; \ No newline at end of file diff --git a/lib/formatters/index.js b/lib/formatters/index.js new file mode 100644 index 0000000..316458c --- /dev/null +++ b/lib/formatters/index.js @@ -0,0 +1,17 @@ +const collections = require('./collections'); +const conditional = require('./conditional'); +const core = require('./core'); +const crypto = require('./crypto'); +const date = require('./date'); +const number = require('./number'); +const string = require('./string'); + +module.exports = { + ...collections, + ...conditional, + ...core, + ...crypto, + ...date, + ...number, + ...string +}; \ No newline at end of file diff --git a/lib/formatters/number.js b/lib/formatters/number.js new file mode 100644 index 0000000..4ae2d4b --- /dev/null +++ b/lib/formatters/number.js @@ -0,0 +1,73 @@ +const { padNumber, formatNumber } = require('../core/util'); + +module.exports = { + + floor: function(num) { + return Math.floor(Number(num)); + }, + + ceil: function(num) { + return Math.ceil(Number(num)); + }, + + round: function(num) { + return Math.round(Number(num)); + }, + + abs: function(num) { + return Math.abs(Number(num)); + }, + + pow: function(num, exp) { + return Math.pow(num, exp); + }, + + padNumber: function(num, digids) { + num = Number(num); + + if (isNaN(num) || !isFinite(num)) return 'NaN'; + + return padNumber(num, digids); + }, + + formatNumber: function(num, decimals, decimalSeparator, groupingSeparator) { + return formatNumber(num, decimals, decimalSeparator, groupingSeparator); + }, + + hex: function(num) { + return parseInt(String(num), 16) || NaN; + }, + + currency: function(num, unit, decimalSeparator, groupingSeparator, decimals) { + unit = typeof unit == 'string' ? unit : '$'; + decimalSeparator = typeof decimalSeparator == 'string' ? decimalSeparator : '.'; + groupingSeparator = typeof groupingSeparator == 'string' ? groupingSeparator : ','; + decimals = typeof decimals == 'number' ? Math.abs(decimals) : 2; + + let formatted = formatNumber(num, decimals, decimalSeparator, groupingSeparator); + let minus = formatted[0] == '-'; + + return (minus ? '-' : '') + unit + formatted.replace(/^\-/, ''); + }, + + formatSize: function(num, decimals, binary) { + num = Number(num); + + if (isNaN(num) || !isFinite(num)) return 'NaN'; + + decimals = typeof decimals == 'number' ? Math.abs(decimals) : 2; + + let base = binary ? 1024 : 1000; + let suffix = binary ? ['KiB', 'MiB', 'GiB', 'TiB'] : ['kB', 'MB', 'GB', 'TB']; + + for (let i = 3; i >= 0; i--) { + let n = Math.pow(base, i + 1); + if (num >= n) { + return formatNumber(num / n, decimals) + suffix[i]; + } + } + + return num + 'B'; + }, + +}; \ No newline at end of file diff --git a/lib/formatters/string.js b/lib/formatters/string.js new file mode 100644 index 0000000..18499ea --- /dev/null +++ b/lib/formatters/string.js @@ -0,0 +1,210 @@ +const { escapeRegExp } = require('../core/util'); + +module.exports = { + + lowercase: function(str) { + if (str == null) return str; + + return String(str).toLowerCase(); + }, + + uppercase: function(str) { + if (str == null) return str; + + return String(str).toUpperCase(); + }, + + camelize: function(str) { + if (str == null) return str; + + return String(str) + .trim() + .replace(/(\-|_|\s)+(.)?/g, (a, b, c) => c ? c.toUpperCase() : ''); + }, + + capitalize: function(str) { + if (str == null) return str; + + str = String(str); + + return str.charAt(0).toUpperCase() + str.substr(1); + }, + + dasherize: function(str) { + if (str == null) return str; + + return String(str) + .replace(/[_\s]+/g, '-') + .replace(/([A-Z])/g, '-$1') + .replace(/-+/g, '-') + .toLowerCase(); + }, + + humanize: function(str) { + if (str == null) return str; + + str = String(str) + .trim() + .replace(/([a-z\D])([A-Z]+)/g, '$1_$2') + .replace(/[-\s]+/g, '_') + .toLowerCase() + .replace(/_id$/, '') + .replace(/_/g, ' ') + .trim(); + + return str.charAt(0).toUpperCase() + str.substr(1); + }, + + slugify: function(str) { + if (str == null) return str; + + const map = {"2d":"-","20":"-","24":"s","26":"and","30":"0","31":"1","32":"2","33":"3","34":"4","35":"5","36":"6","37":"7","38":"8","39":"9","41":"A","42":"B","43":"C","44":"D","45":"E","46":"F","47":"G","48":"H","49":"I","50":"P","51":"Q","52":"R","53":"S","54":"T","55":"U","56":"V","57":"W","58":"X","59":"Y","61":"a","62":"b","63":"c","64":"d","65":"e","66":"f","67":"g","68":"h","69":"i","70":"p","71":"q","72":"r","73":"s","74":"t","75":"u","76":"v","77":"w","78":"x","79":"y","100":"A","101":"a","102":"A","103":"a","104":"A","105":"a","106":"C","107":"c","108":"C","109":"c","110":"D","111":"d","112":"E","113":"e","114":"E","115":"e","116":"E","117":"e","118":"E","119":"e","120":"G","121":"g","122":"G","123":"g","124":"H","125":"h","126":"H","127":"h","128":"I","129":"i","130":"I","131":"i","132":"IJ","133":"ij","134":"J","135":"j","136":"K","137":"k","138":"k","139":"L","140":"l","141":"L","142":"l","143":"N","144":"n","145":"N","146":"n","147":"N","148":"n","149":"n","150":"O","151":"o","152":"OE","153":"oe","154":"R","155":"r","156":"R","157":"r","158":"R","159":"r","160":"S","161":"s","162":"T","163":"t","164":"T","165":"t","166":"T","167":"t","168":"U","169":"u","170":"U","171":"u","172":"U","173":"u","174":"W","175":"w","176":"Y","177":"y","178":"Y","179":"Z","180":"b","181":"B","182":"b","183":"b","184":"b","185":"b","186":"C","187":"C","188":"c","189":"D","190":"E","191":"F","192":"f","193":"G","194":"Y","195":"h","196":"i","197":"I","198":"K","199":"k","200":"A","201":"a","202":"A","203":"a","204":"E","205":"e","206":"E","207":"e","208":"I","209":"i","210":"R","211":"r","212":"R","213":"r","214":"U","215":"u","216":"U","217":"u","218":"S","219":"s","220":"n","221":"d","222":"8","223":"8","224":"Z","225":"z","226":"A","227":"a","228":"E","229":"e","230":"O","231":"o","232":"Y","233":"y","234":"l","235":"n","236":"t","237":"j","238":"db","239":"qp","240":"<","241":"?","242":"?","243":"B","244":"U","245":"A","246":"E","247":"e","248":"J","249":"j","250":"a","251":"a","252":"a","253":"b","254":"c","255":"e","256":"d","257":"d","258":"e","259":"e","260":"g","261":"g","262":"g","263":"Y","264":"x","265":"u","266":"h","267":"h","268":"i","269":"i","270":"w","271":"m","272":"n","273":"n","274":"N","275":"o","276":"oe","277":"m","278":"o","279":"r","280":"R","281":"R","282":"S","283":"f","284":"f","285":"f","286":"f","287":"t","288":"t","289":"u","290":"Z","291":"Z","292":"3","293":"3","294":"?","295":"?","296":"5","297":"C","298":"O","299":"B","363":"a","364":"e","365":"i","366":"o","367":"u","368":"c","369":"d","386":"A","388":"E","389":"H","390":"i","391":"A","392":"B","393":"r","394":"A","395":"E","396":"Z","397":"H","398":"O","399":"I","400":"E","401":"E","402":"T","403":"r","404":"E","405":"S","406":"I","407":"I","408":"J","409":"jb","410":"A","411":"B","412":"V","413":"G","414":"D","415":"E","416":"ZH","417":"Z","418":"I","419":"Y","420":"R","421":"S","422":"T","423":"U","424":"F","425":"H","426":"TS","427":"CH","428":"SH","429":"SCH","430":"a","431":"b","432":"v","433":"g","434":"d","435":"e","436":"zh","437":"z","438":"i","439":"y","440":"r","441":"s","442":"t","443":"u","444":"f","445":"h","446":"ts","447":"ch","448":"sh","449":"sch","450":"e","451":"e","452":"h","453":"r","454":"e","455":"s","456":"i","457":"i","458":"j","459":"jb","460":"W","461":"w","462":"Tb","463":"tb","464":"IC","465":"ic","466":"A","467":"a","468":"IA","469":"ia","470":"Y","471":"y","472":"O","473":"o","474":"V","475":"v","476":"V","477":"v","478":"Oy","479":"oy","480":"C","481":"c","490":"R","491":"r","492":"F","493":"f","494":"H","495":"h","496":"X","497":"x","498":"3","499":"3","500":"d","501":"d","502":"d","503":"d","504":"R","505":"R","506":"R","507":"R","508":"JT","509":"JT","510":"E","511":"e","512":"JT","513":"jt","514":"JX","515":"JX","531":"U","532":"D","533":"Q","534":"N","535":"T","536":"2","537":"F","538":"r","539":"p","540":"z","541":"2","542":"n","543":"x","544":"U","545":"B","546":"j","547":"t","548":"n","549":"C","550":"R","551":"8","552":"R","553":"O","554":"P","555":"O","556":"S","561":"w","562":"f","563":"q","564":"n","565":"t","566":"q","567":"t","568":"n","569":"p","570":"h","571":"a","572":"n","573":"a","574":"u","575":"j","576":"u","577":"2","578":"n","579":"2","580":"n","581":"g","582":"l","583":"uh","584":"p","585":"o","586":"S","587":"u","4a":"J","4b":"K","4c":"L","4d":"M","4e":"N","4f":"O","5a":"Z","6a":"j","6b":"k","6c":"l","6d":"m","6e":"n","6f":"o","7a":"z","a2":"c","a3":"f","a5":"Y","a7":"s","a9":"c","aa":"a","ae":"r","b2":"2","b3":"3","b5":"u","b6":"p","b9":"1","c0":"A","c1":"A","c2":"A","c3":"A","c4":"A","c5":"A","c6":"AE","c7":"C","c8":"E","c9":"E","ca":"E","cb":"E","cc":"I","cd":"I","ce":"I","cf":"I","d0":"D","d1":"N","d2":"O","d3":"O","d4":"O","d5":"O","d6":"O","d7":"X","d8":"O","d9":"U","da":"U","db":"U","dc":"U","dd":"Y","de":"p","df":"b","e0":"a","e1":"a","e2":"a","e3":"a","e4":"a","e5":"a","e6":"ae","e7":"c","e8":"e","e9":"e","ea":"e","eb":"e","ec":"i","ed":"i","ee":"i","ef":"i","f0":"o","f1":"n","f2":"o","f3":"o","f4":"o","f5":"o","f6":"o","f8":"o","f9":"u","fa":"u","fb":"u","fc":"u","fd":"y","ff":"y","10a":"C","10b":"c","10c":"C","10d":"c","10e":"D","10f":"d","11a":"E","11b":"e","11c":"G","11d":"g","11e":"G","11f":"g","12a":"I","12b":"i","12c":"I","12d":"i","12e":"I","12f":"i","13a":"l","13b":"L","13c":"l","13d":"L","13e":"l","13f":"L","14a":"n","14b":"n","14c":"O","14d":"o","14e":"O","14f":"o","15a":"S","15b":"s","15c":"S","15d":"s","15e":"S","15f":"s","16a":"U","16b":"u","16c":"U","16d":"u","16e":"U","16f":"u","17a":"z","17b":"Z","17c":"z","17d":"Z","17e":"z","17f":"f","18a":"D","18b":"d","18c":"d","18d":"q","18e":"E","18f":"e","19a":"l","19b":"h","19c":"w","19d":"N","19e":"n","19f":"O","1a0":"O","1a1":"o","1a2":"P","1a3":"P","1a4":"P","1a5":"p","1a6":"R","1a7":"S","1a8":"s","1a9":"E","1aa":"l","1ab":"t","1ac":"T","1ad":"t","1ae":"T","1af":"U","1b0":"u","1b1":"U","1b2":"U","1b3":"Y","1b4":"y","1b5":"Z","1b6":"z","1b7":"3","1b8":"3","1b9":"3","1ba":"3","1bb":"2","1bc":"5","1bd":"5","1be":"5","1bf":"p","1c4":"DZ","1c5":"Dz","1c6":"dz","1c7":"Lj","1c8":"Lj","1c9":"lj","1ca":"NJ","1cb":"Nj","1cc":"nj","1cd":"A","1ce":"a","1cf":"I","1d0":"i","1d1":"O","1d2":"o","1d3":"U","1d4":"u","1d5":"U","1d6":"u","1d7":"U","1d8":"u","1d9":"U","1da":"u","1db":"U","1dc":"u","1dd":"e","1de":"A","1df":"a","1e0":"A","1e1":"a","1e2":"AE","1e3":"ae","1e4":"G","1e5":"g","1e6":"G","1e7":"g","1e8":"K","1e9":"k","1ea":"Q","1eb":"q","1ec":"Q","1ed":"q","1ee":"3","1ef":"3","1f0":"J","1f1":"dz","1f2":"dZ","1f3":"DZ","1f4":"g","1f5":"G","1f6":"h","1f7":"p","1f8":"N","1f9":"n","1fa":"A","1fb":"a","1fc":"AE","1fd":"ae","1fe":"O","1ff":"o","20a":"I","20b":"i","20c":"O","20d":"o","20e":"O","20f":"o","21a":"T","21b":"t","21c":"3","21d":"3","21e":"H","21f":"h","22a":"O","22b":"o","22c":"O","22d":"o","22e":"O","22f":"o","23a":"A","23b":"C","23c":"c","23d":"L","23e":"T","23f":"s","24a":"Q","24b":"q","24c":"R","24d":"r","24e":"Y","24f":"y","25a":"e","25b":"3","25c":"3","25d":"3","25e":"3","25f":"j","26a":"i","26b":"I","26c":"I","26d":"I","26e":"h","26f":"w","27a":"R","27b":"r","27c":"R","27d":"R","27e":"r","27f":"r","28a":"u","28b":"v","28c":"A","28d":"M","28e":"Y","28f":"Y","29a":"B","29b":"G","29c":"H","29d":"j","29e":"K","29f":"L","2a0":"q","2a1":"?","2a2":"c","2a3":"dz","2a4":"d3","2a5":"dz","2a6":"ts","2a7":"tf","2a8":"tc","2a9":"fn","2aa":"ls","2ab":"lz","2ac":"ww","2ae":"u","2af":"u","2b0":"h","2b1":"h","2b2":"j","2b3":"r","2b4":"r","2b5":"r","2b6":"R","2b7":"W","2b8":"Y","2df":"x","2e0":"Y","2e1":"1","2e2":"s","2e3":"x","2e4":"c","36a":"h","36b":"m","36c":"r","36d":"t","36e":"v","36f":"x","37b":"c","37c":"c","37d":"c","38a":"I","38c":"O","38e":"Y","38f":"O","39a":"K","39b":"A","39c":"M","39d":"N","39e":"E","39f":"O","3a0":"TT","3a1":"P","3a3":"E","3a4":"T","3a5":"Y","3a6":"O","3a7":"X","3a8":"Y","3a9":"O","3aa":"I","3ab":"Y","3ac":"a","3ad":"e","3ae":"n","3af":"i","3b0":"v","3b1":"a","3b2":"b","3b3":"y","3b4":"d","3b5":"e","3b6":"c","3b7":"n","3b8":"0","3b9":"1","3ba":"k","3bb":"j","3bc":"u","3bd":"v","3be":"c","3bf":"o","3c0":"tt","3c1":"p","3c2":"s","3c3":"o","3c4":"t","3c5":"u","3c6":"q","3c7":"X","3c8":"Y","3c9":"w","3ca":"i","3cb":"u","3cc":"o","3cd":"u","3ce":"w","3d0":"b","3d1":"e","3d2":"Y","3d3":"Y","3d4":"Y","3d5":"O","3d6":"w","3d7":"x","3d8":"Q","3d9":"q","3da":"C","3db":"c","3dc":"F","3dd":"f","3de":"N","3df":"N","3e2":"W","3e3":"w","3e4":"q","3e5":"q","3e6":"h","3e7":"e","3e8":"S","3e9":"s","3ea":"X","3eb":"x","3ec":"6","3ed":"6","3ee":"t","3ef":"t","3f0":"x","3f1":"e","3f2":"c","3f3":"j","3f4":"O","3f5":"E","3f6":"E","3f7":"p","3f8":"p","3f9":"C","3fa":"M","3fb":"M","3fc":"p","3fd":"C","3fe":"C","3ff":"C","40a":"Hb","40b":"Th","40c":"K","40d":"N","40e":"Y","40f":"U","41a":"K","41b":"L","41c":"M","41d":"N","41e":"O","41f":"P","42a":"","42b":"Y","42c":"","42d":"E","42e":"U","42f":"YA","43a":"k","43b":"l","43c":"m","43d":"n","43e":"o","43f":"p","44a":"","44b":"y","44c":"","44d":"e","44e":"u","44f":"ya","45a":"Hb","45b":"h","45c":"k","45d":"n","45e":"y","45f":"u","46a":"mY","46b":"my","46c":"Im","46d":"Im","46e":"3","46f":"3","47a":"O","47b":"o","47c":"W","47d":"w","47e":"W","47f":"W","48a":"H","48b":"H","48c":"B","48d":"b","48e":"P","48f":"p","49a":"K","49b":"k","49c":"K","49d":"k","49e":"K","49f":"k","4a0":"K","4a1":"k","4a2":"H","4a3":"h","4a4":"H","4a5":"h","4a6":"Ih","4a7":"ih","4a8":"O","4a9":"o","4aa":"C","4ab":"c","4ac":"T","4ad":"t","4ae":"Y","4af":"y","4b0":"Y","4b1":"y","4b2":"X","4b3":"x","4b4":"TI","4b5":"ti","4b6":"H","4b7":"h","4b8":"H","4b9":"h","4ba":"H","4bb":"h","4bc":"E","4bd":"e","4be":"E","4bf":"e","4c0":"I","4c1":"X","4c2":"x","4c3":"K","4c4":"k","4c5":"jt","4c6":"jt","4c7":"H","4c8":"h","4c9":"H","4ca":"h","4cb":"H","4cc":"h","4cd":"M","4ce":"m","4cf":"l","4d0":"A","4d1":"a","4d2":"A","4d3":"a","4d4":"AE","4d5":"ae","4d6":"E","4d7":"e","4d8":"e","4d9":"e","4da":"E","4db":"e","4dc":"X","4dd":"X","4de":"3","4df":"3","4e0":"3","4e1":"3","4e2":"N","4e3":"n","4e4":"N","4e5":"n","4e6":"O","4e7":"o","4e8":"O","4e9":"o","4ea":"O","4eb":"o","4ec":"E","4ed":"e","4ee":"Y","4ef":"y","4f0":"Y","4f1":"y","4f2":"Y","4f3":"y","4f4":"H","4f5":"h","4f6":"R","4f7":"r","4f8":"bI","4f9":"bi","4fa":"F","4fb":"f","4fc":"X","4fd":"x","4fe":"X","4ff":"x","50a":"H","50b":"h","50c":"G","50d":"g","50e":"T","50f":"t","51a":"Q","51b":"q","51c":"W","51d":"w","53a":"d","53b":"r","53c":"L","53d":"Iu","53e":"O","53f":"y","54a":"m","54b":"o","54c":"N","54d":"U","54e":"Y","54f":"S","56a":"d","56b":"h","56c":"l","56d":"lu","56e":"d","56f":"y","57a":"w","57b":"2","57c":"n","57d":"u","57e":"y","57f":"un"}; + + let clean = ''; + for (let i = 0; i < str.length; i++) { + clean += map[str.charCodeAt(i).toString(16)] || ''; + } + + return clean.toLowerCase().replace(/-+/g, '-').replace(/^-|-$/, ''); + }, + + underscore: function(str) { + if (str == null) return str; + + return String(str) + .trim() + .replace(/([a-z\d])([A-Z]+)/g, '$1_$2') + .replace(/[-\s]+/g, '_') + .toLowerCase(); + }, + + titlecase: function(str) { + if (str == null) return str; + + return String(str) + .replace(/(?:^|\s)\S/g, a => a.toUpperCase()); + }, + + camelcase: function(str) { + if (str == null) return str; + + return String(str) + .toLowerCase() + .replace(/\s+(\S)/g, (a, b) => b.toUpperCase()); + }, + + replace: function(str, search, value) { + if (str == null) return str; + + if (typeof search == 'string') { + search = new RegExp(escapeRegExp(search), 'g'); + } + + return String(str).replace(search, value); + }, + + trim: function(str) { + if (str == null) return str; + + return String(str).trim(); + }, + + split: function(str, separator) { + if (str == null) return []; + + return String(str).split(separator); + }, + + pad: function(str, length, chr, pos) { + if (str == null) return str; + + str = String(str); + chr = chr && typeof chr == 'string' ? chr[0] : ' '; + + if (str.length < length) { + let n = length - str.length; + + switch (pos) { + case 'right': + str += chr.repeat(n); + break; + + case 'center': + str = chr.repeat(Math.floor(n / 2)) + str + chr.repeat(Math.ceil(n / 2)); + break; + + default: + str = chr.repeat(n) + str; + } + } + + return str; + }, + + repeat: function(str, num) { + if (str == null) return str; + + return String(str).repeat(num); + }, + + substr: function(str, start, length) { + if (str == null) return str; + + return String(str).substr(start, length); + }, + + trunc: function(str, length, useWordBoundary, fragment) { + if (str == null) return str; + + str = String(str); + length = Number(length); + fragment = fragment && typeof fragment == 'string' ? fragment : '\u2026'; + + if (str.length > length) { + str = str.substr(0, length - fragment.length); + + if (useWordBoundary && str.includes(' ')) { + str = str.substr(0, str.lastIndexOf(' ')); + } + + str += fragment; + } + + return str; + }, + + stripTags: function(str) { + if (str == null) return str; + + return String(str).replace(/<[^>]+>/g, ''); + }, + + wordCount: function(str) { + if (str == null) return 0; + + return String(str) + .replace(/\.\?!,/g, ' ') + .trim() + .split(/\s+/) + .length; + }, + + length: function(str) { + if (str == null) return 0; + + return String(str).length; + }, + + urlencode: function(str) { + if (str == null) return str; + + return encodeURI(String(str)); + }, + + urldecode: function(str) { + if (str == null) return str; + + return decodeURI(String(str)); + }, + +}; \ No newline at end of file diff --git a/lib/locale/en-US.js b/lib/locale/en-US.js new file mode 100644 index 0000000..792513c --- /dev/null +++ b/lib/locale/en-US.js @@ -0,0 +1,59 @@ +module.exports = { + months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + + validator: { + core: { + required: 'This field is required.', + email: 'Please enter a valid email address.', + url: 'Please enter a valid URL.', + date: 'Please enter a valid date.', + time: 'Please enter a valid time.', + month: 'Please enter a valid month.', + week: 'Please enter a valid week.', + color: 'Please enter a color in the format #xxxxxx.', + pattern: 'Invalid format.', + number: 'Please enter a valid number.', + digits: 'Please enter only digits.', + alphanumeric: 'Letters, numbers and underscores only please.', + creditcard: 'Please enter a valid credit card number.', + bic: 'Please specify a valid BIC code.', + iban: 'Please specify a valid IBAN.', + vat: 'Please specify a valid VAT number.', + integer: 'A positive or negative non-decimal number please.', + ipv4: 'Please enter a valid IP v4 address.', + ipv6: 'Please enter a valid IP v6 address.', + lettersonly: 'Letters only please.', + unicodelettersonly: 'Letters only please.', + letterswithbasicpunc: 'Letters or punctuation only please.', + nowhitespace: 'No whitespace please.', + minlength: 'Please enter at least {0} characters.', + maxlength: 'Please enter no more than {0} characters.', + minitems: 'Please select at least {0} items.', + maxitems: 'Please select no more than {0} items.', + min: 'Please enter a value greater than or equal to {0}.', + max: 'Please enter a value less than or equal to {0}.', + equalTo: 'Please enter the same value again.', + notEqualTo: 'Please enter a different value, values must not be the same.', + }, + db: { + exists: 'Value does not exist in database.', + notexists: 'Value already exists in database.', + }, + unicode: { + unicodelettersonly: 'Entered characters are not allowed.', + unicodescripts: 'Entered characters are not allowed.', + }, + upload: { + accept: 'Please select a file with a valid file type.', + minsize: 'Please select a file of at least {0} bytes.', + maxsize: 'Please select a file of no more than {0} bytes.', + mintotalsize: 'Total size of selected files should be at least {0} bytes.', + maxtotalsize: 'Total size of selected files should be no more than {0} bytes.', + minfiles: 'Please select at least {0} files.', + maxfiles: 'Please select no more than {0} files.', + }, + }, +}; \ No newline at end of file diff --git a/lib/modules/api.js b/lib/modules/api.js new file mode 100644 index 0000000..8d24541 --- /dev/null +++ b/lib/modules/api.js @@ -0,0 +1,122 @@ +const { http, https } = require('follow-redirects'); +const querystring = require('querystring'); +const zlib = require('zlib'); +const pkg = require('../../package.json'); + +module.exports = { + + send: async function(options) { + let url = this.parseRequired(options.url, 'string', 'api.send: url is required.'); + let method = this.parseOptional(options.method, 'string', 'GET'); + let data = this.parseOptional(options.data, '*', ''); + let dataType = this.parseOptional(options.dataType, 'string', 'auto'); + let verifySSL = this.parseOptional(options.verifySSL, 'boolean', false); + let params = this.parseOptional(options.params, 'object', null); + let headers = this.parseOptional(options.headers, 'object', {}); + let username = this.parseOptional(options.username, 'string', ''); + let password = this.parseOptional(options.password, 'string', ''); + let oauth = this.parseOptional(options.oauth, 'string', ''); + let throwErrors = this.parseOptional(options.throwErrors, 'boolean', false); + let passErrors = this.parseOptional(options.passErrors, 'boolean', true); + let timeout = this.parseOptional(options.timeout, 'number', 0); + + if (params) { + url += '?' + querystring.stringify(params); + } + + if (dataType == 'auto' && method == 'POST') { + dataType = 'x-www-form-urlencoded'; + } + + if (dataType != 'auto' && !headers['Content-Type']) { + headers['Content-Type'] = `application/${dataType}`; + } + + if (dataType == 'x-www-form-urlencoded') { + data = querystring.stringify(data); + } else if (typeof data != 'string') { + data = JSON.stringify(data); + } + + if (data) { + headers['Content-Length'] = Buffer.byteLength(data); + } + + const Url = new URL(url); + const opts = { method, headers, rejectUnauthorized: !!verifySSL, maxBodyLength: 1_000_000_000 }; + + if (timeout > 0) { + opts.timeout = timeout; + } + + if (username || password) { + opts.auth = `${username}:${password}`; + } + + if (oauth) { + //const provider = this.oauth[oauth]; + const provider = await this.getOAuthProvider(oauth); + if (provider && provider.access_token) { + headers['Authorization'] = 'Bearer ' + provider.access_token; + } + } + + if (!headers['User-Agent']) headers['User-Agent'] = `${pkg.name}/${pkg.version}`; + if (!headers['Accept']) headers['Accept'] = 'application/json'; + + return new Promise((resolve, reject) => { + const req = (Url.protocol == 'https:' ? https : http).request(Url, opts, res => { + let body = ''; + + let output = res; + if (res.headers['content-encoding'] == 'br') { + output = res.pipe(zlib.createBrotliDecompress()); + } + if (res.headers['content-encoding'] == 'gzip') { + output = res.pipe(zlib.createGunzip()); + } + if (res.headers['content-encoding'] == 'deflate') { + output = res.pipe(zlib.createInflate()); + } + + output.setEncoding('utf8'); + output.on('data', chunk => body += chunk); + output.on('end', () => { + if (res.statusCode >= 400) { + if (throwErrors) { + return reject(res.statusCode + ' ' + body); + } + + if (passErrors) { + this.res.status(res.statusCode).send(body); + return resolve(); + } + } + + if (body.charCodeAt(0) === 0xFEFF) { + body = body.slice(1); + } + + if (res.headers['content-type'] && res.headers['content-type'].includes('json')) { + try { + body = JSON.parse(body); + } catch(e) { + console.error(e); + } + } + + resolve({ + status: res.statusCode, + headers: res.headers, + data: body + }); + }); + }); + + req.on('error', reject); + req.write(data); + req.end(); + }); + }, + +}; \ No newline at end of file diff --git a/lib/modules/arraylist.js b/lib/modules/arraylist.js new file mode 100644 index 0000000..79e2fa5 --- /dev/null +++ b/lib/modules/arraylist.js @@ -0,0 +1,173 @@ +// ArrayList + +const { clone } = require('../core/util'); + +// Create a new ArrayList +exports.create = function(options, name) { + const value = this.parseOptional(options.value, 'object', []); + + if (!name) throw Error('arraylist.create: name is required.'); + + if (!this.req.arrays) { + this.req.arrays = {}; + } + + this.req.arrays[name] = Array.isArray(value) ? clone(value) : []; +}; + +// Return the ArrayList as array value +exports.value = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.value: ref is required.'); + + if (!this.req.arrays) throw Error('arraylist.value: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.value: ArrayList ${ref} not found.`); + + return clone(this.req.arrays[ref]); +}; + +// Return the size of the ArrayList +exports.size = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.size: ref is required.'); + + if (!this.req.arrays) throw Error('arraylist.size: No arraylists are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.size: ArrayList ${ref} not found.`); + + return this.req.arrays[ref].length; +}; + +// Return the value at a specific index +exports.get = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.get: ref is required.'); + const index = this.parseRequired(options.index, 'number', 'arraylist.get: index is required.'); + + if (!this.req.arrays) throw Error('arraylist.get: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.get: ArrayList ${ref} not found.`); + + return clone(this.req.arrays[ref][index]); +}; + +// Add a value to the ArrayList +exports.add = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.add: ref is required.'); + const value = this.parseRequired(options.value, '*', 'arraylist.add: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.add: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.add: ArrayList ${ref} not found.`); + + this.req.arrays[ref].push(clone(value)); +}; + +// Add an array of values to the ArrayList +exports.addAll = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.addAll: ref is required.'); + const value = this.parseRequired(options.value, 'object', 'arraylist.addAll: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.addAll: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.addAll: ArrayList ${ref} not found.`); + if (!Array.isArray(value)) throw Error('arraylist.addAll: Value must be an array.') + + for (let val of value) { + this.req.arrays[ref].push(clone(val)); + } +}; + +// Update a value at a specific index +exports.set = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.set: ref is required.'); + const index = this.parseRequired(options.index, 'number', 'arraylist.set: index is required.'); + const value = this.parseRequired(options.value, '*', 'arraylist.set: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.set: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.set: ArrayList ${ref} not found.`); + + this.req.arrays[ref][index] = clone(value); +}; + +// Remove the first occurrence of a specific value +exports.remove = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.remove: ref is required.'); + const value = this.parseRequired(options.value, '*', 'arraylist.remove: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.remove: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.remove: ArrayList ${ref} not found.`); + + const index = this.req.arrays[ref].indexOf(value); + + if (index == -1) throw Error('arraylist.remove: Value does not exist in the ArrayList.'); + + this.req.arrays[ref].splice(index, 1); +}; + +// Remove a value from the ArrayList at a specific index +exports.removeAt = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.removeAt: ref is required.'); + const index = this.parseRequired(options.index, 'number', 'arraylist.removeAt: index is required.'); + + if (!this.req.arrays) throw Error('arraylist.removeAt: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.removeAt: ArrayList ${ref} not found.`); + + this.req.arrays[ref].splice(index, 1); +}; + +// Clear all values from the ArrayList +exports.clear = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.clear: ref is required.'); + + if (!this.req.arrays) throw Error('arraylist.clear: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.clear: ArrayList ${ref} not found.`); + + this.req.arrays[ref] = []; +}; + +// Sort the ArrayList +exports.sort = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.sort: ref is required.'); + const prop = this.parseOptional(options.prop, 'string', null); + const desc = this.parseOptional(options.desc, 'boolean', false); + + if (!this.req.arrays) throw Error('arraylist.sort: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.sort: ArrayList ${ref} not found.`); + + this.req.arrays[ref].sort((a, b) => { + if (prop) { + a = a[prop]; + b = b[prop]; + } + + if (a == b) return 0; + if (a < b) return desc ? 1 : -1; + return desc ? -1 : 1; + }); +}; + +// Return the index of the first occurrence of a specific value +exports.indexOf = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.indexOf: ref is required.'); + const value = this.parseRequired(options.value, '*', 'arraylist.indexOf: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.indexOf: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.indexOf: ArrayList ${ref} not found.`); + + return this.req.arrays[ref].indexOf(value); +}; + +// Return true if the ArrayList contains a specific value +exports.contains = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.contains: ref is required.'); + const value = this.parseRequired(options.value, '*', 'arraylist.contains: value is required.'); + + if (!this.req.arrays) throw Error('arraylist.contains: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.contains: ArrayList ${ref} not found.`); + + return this.req.arrays[ref].includes(value); +}; + +// Return true when ArrayList is empty +exports.isEmpty = function(options) { + const ref = this.parseRequired(options.ref, 'string', 'arraylist.isEmpty: ref is required.'); + + if (!this.req.arrays) throw Error('arraylist.isEmpty: No ArrayList are created.'); + if (!this.req.arrays[ref]) throw Error(`arraylist.isEmpty: ArrayList ${ref} not found.`); + + return !this.req.arrays[ref].length; +}; \ No newline at end of file diff --git a/lib/modules/auth.js b/lib/modules/auth.js new file mode 100644 index 0000000..b572d6f --- /dev/null +++ b/lib/modules/auth.js @@ -0,0 +1,72 @@ +module.exports = { + + provider: async function(options, name) { + const provider = await this.setAuthProvider(name, options); + + return { identity: provider.identity }; + }, + + identify: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.validate: provider is required.')); + + return provider.identity; + }, + + validate: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.validate: provider is required.')); + const { action, username, password, remember } = this.req.body; + + if (action == 'validate') { + return provider.validate(username, password); + } + + if (action == 'login') { + return provider.login(username, password, remember); + } + + if (action == 'logout') { + return provider.logout(); + } + + if (!provider.identity) { + return provider.unauthorized(); + } + }, + + login: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.login: provider is required.')); + const username = this.parseOptional(options.username, 'string', this.parse('{{$_POST.username}}')); + const password = this.parseOptional(options.password, 'string', this.parse('{{$_POST.password}}')); + const remember = this.parseOptional(options.remember, '*', this.parse('{{$_POST.remember}}')); + + return provider.login(username, password, remember); + }, + + logout: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.logout: provider is required.')); + + return provider.logout(); + }, + + restrict: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.restrict: provider is required.')); + + return provider.restrict(this.parse(options)); + }, + + impersonate: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.impersonate: provider is required.')); + const identity = this.parseRequired(options.identity, 'string', 'auth.impersonate: identity is required.'); + + return provider.impersonate(identity); + }, + + verify: async function(options) { + const provider = await this.getAuthProvider(this.parseRequired(options.provider, 'string', 'auth.verify: provider is required.')); + const username = this.parseRequired(options.username, 'string', 'auth.verify: username is required.'); + const password = this.parseRequired(options.password, 'string', 'auth.verify: password is required.'); + + return provider.validate(username, password); + }, + +}; \ No newline at end of file diff --git a/lib/modules/collections.js b/lib/modules/collections.js new file mode 100644 index 0000000..7cec883 --- /dev/null +++ b/lib/modules/collections.js @@ -0,0 +1,185 @@ +const { clone } = require('../core/util'); + +module.exports = { + + addColumns: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.addColumns: collection is required.'); + let add = this.parseRequired(options.add, 'object', 'collections.addColumns: add is required.'); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let output = []; + + for (let row of collection) { + let newRow = clone(row); + + for (let column in add) { + if (overwrite || newRow[column] == null) { + newRow[column] = add[column]; + } + } + + output.push(newRow); + } + + return output; + }, + + filterColumns: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.filterColumns: collection is required.'); + let columns = this.parseRequired(options.columns, 'object'/*array[string]*/, 'collections.filterColumns: columns is required.'); + let keep = this.parseOptional(options.keep, 'boolean', false); + let output = []; + + for (let row of collection) { + let newRow = {}; + + for (let column in row) { + if (columns.includes(column)) { + if (keep) { + newRow[column] = clone(row[column]); + } + } else if (!keep) { + newRow[column] = clone(row[column]); + } + } + + output.push(newRow); + } + + return output; + }, + + renameColumns: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.renamecolumns: collection is required.'); + let rename = this.parseRequired(options.rename, 'object', 'collections.renamecolumns: rename is required.'); + let output = []; + + for (let row of collection) { + let newRow = {}; + + for (let column in row) { + newRow[rename[column] || column] = clone(row[column]); + } + + output.push(newRow); + } + + return output; + }, + + fillDown: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.fillDown: collection is required.'); + let columns = this.parseRequired(options.columns, 'object'/*array[string]*/, 'collections.fillDown: columns is required.'); + let output = []; + let values = {}; + + for (let column of columns) { + values[column] = null; + } + + for (let row of collection) { + let newRow = clone(row); + + for (let column in values) { + if (newRow[column] == null) { + newRow[column] = values[column]; + } else { + values[column] = newRow[column]; + } + } + + output.push(newRow); + } + + return output; + }, + + addRows: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.addRows: collection is required.'); + let rows = this.parseRequired(options.rows, 'object'/*array[object]*/, 'collections.addRows: rows is required.'); + + return clone(collection).concat(clone(rows)); + }, + + addRownumbers: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.addRownumbers: collection is required.'); + let column = this.parseOptional(options.column, 'string', 'nr'); + let startAt = this.parseOptional(options.startAt, 'number', 1); + let desc = this.parseOptional(options.desc, 'boolean', false); + let output = []; + let nr = desc ? collection.length + startAt - 1 : startAt; + + for (let row of collection) { + let newRow = clone(row); + + newRow[column] = desc ? nr-- : nr++; + + output.push(newRow); + } + + return output; + }, + + join: function(options) { + let collection1 = this.parseRequired(options.collection1, 'object'/*array[object]*/, 'collections.join: collection1 is required.'); + let collection2 = this.parseRequired(options.collection2, 'object'/*array[object]*/, 'collections.join: collection2 is required.'); + let matches = this.parseRequired(options.matches, 'object', 'collections.join: matches is required.'); + let matchAll = this.parseOptional(options.matchAll, 'boolean', false); + let output = []; + + for (let row1 of collection1) { + let newRow = clone(row1); + + for (let row2 of collection2) { + let join = false; + + for (let match in matches) { + if (row1[match] == row2[matches[match]]) { + join = true; + if (!matchAll) break; + } else if (matchAll) { + join = false; + break; + } + } + + if (join) { + for (let column in row2) { + newRow[column] = clone(row2[column]); + } + break; + } + } + + output.push(newRow); + } + + return output; + }, + + normalize: function(options) { + let collection = this.parseRequired(options.collection, 'object'/*array[object]*/, 'collections.normalize: collection is required.'); + let columns = []; + let output = []; + + for (let row of collection) { + for (let column in row) { + if (!columns.includes(column)) { + columns.push(column); + } + } + } + + for (let row of collection) { + let newRow = {}; + + for (let column of columns) { + newRow[column] = row[column] == null ? null : clone(row[column]); + } + + output.push(newRow); + } + + return output; + }, + +}; \ No newline at end of file diff --git a/lib/modules/core.js b/lib/modules/core.js new file mode 100644 index 0000000..7f509b3 --- /dev/null +++ b/lib/modules/core.js @@ -0,0 +1,273 @@ +const fs = require('fs-extra'); +const { clone } = require('../core/util'); + +module.exports = { + + wait: function(options) { + let delay = this.parseOptional(options.delay, 'number', 1000); + return new Promise(resolve => setTimeout(resolve, delay)) + }, + + log: function(options) { + let message = this.parse(options.message); + console.log(message); + return message; + }, + + repeat: async function(options) { + let repeater = this.parseRequired(options.repeat, '*', 'core.repeater: repeat is required.'); + let outputFilter = this.parseOptional(options.outputFilter, 'string', 'include'); // include/exclude + let outputFields = this.parseOptional(options.outputFields, 'object'/*array[string]*/, []); + let index = 0, data = [], parentData = this.data; + + switch (typeof repeater) { + case 'boolean': + repeater = repeater ? [0] : []; + break; + + case 'string': + repeater = repeater.split(','); + break; + + case 'number': + repeater = (n => { + let a = [], i = 0; + while (i < n) a.push(i++); + return a; + })(repeater); + break; + } + + if (!(Array.isArray(repeater) || repeater instanceof Object)) { + throw new Error('Repeater data is not an array or object'); + } + + for (let key in repeater) { + if (Object.hasOwn(repeater, key)) { + let scope = {}; + this.data = {}; + + if (repeater[key] instanceof Object) { + for (var prop in repeater[key]) { + if (Object.hasOwn(repeater[key], prop)) { + scope[prop] = repeater[key][prop]; + + if (outputFilter == 'exclude') { + if (!outputFields.includes(prop)) { + this.data[prop] = repeater[key][prop]; + } + } else { + if (outputFields.includes(prop)) { + this.data[prop] = repeater[key][prop]; + } + } + } + } + } + + scope.$key = key; + scope.$name = key; + scope.$value = clone(repeater[key]); + scope.$index = index; + scope.$number = index + 1; + scope.$oddeven = index % 2; + + if (repeater[key] == null) { + repeater[key] = {}; + } + + this.scope = this.scope.create(scope, clone(repeater[key])); + await this.exec(options.exec, true); + this.scope = this.scope.parent; + + data.push({ ...this.data }); + + index++; + } + } + + this.data = parentData; + + return data; + }, + + while: async function(options) { + let max = this.parseOptional(options.max, 'number', Number.MAX_SAFE_INTEGER); + let i = 0; + + while (this.parse(options.while)) { + await this.exec(options.exec, true); + if (++i == max) break; + } + }, + + condition: async function(options) { + let condition = this.parse(options.if); + + if (!!condition) { + if (options.then) { + await this.exec(options.then, true); + } + } else if (options.else) { + await this.exec(options.else, true); + } + }, + + conditions: async function(options) { + if (Array.isArray(options.conditions)) { + for (let condition of options.conditions) { + let when = this.parse(condition.when); + + if (!!when) { + return this.exec(condition.then, true); + } + } + } + }, + + select: async function(options) { + let expression = this.parse(options.expression); + + if (Array.isArray(options.cases)) { + for (let item of options.cases) { + let value = this.parse(item.value); + + if (expression === value) { + return this.exec(item.exec, true); + } + } + } + }, + + setvalue: function(options) { + let key = this.parseOptional(options.key, 'string', ''); + let value = this.parse(options.value); + if (key) this.set(key, value); + return value; + }, + + setsession: function(options, name) { + let value = this.parse(options.value); + this.req.session[name] = value; + return value; + }, + + removesession: function(options, name) { + delete this.req.session[name]; + }, + + setcookie: function(options, name) { + options = this.parse(options); + + let cookieOptions = { + domain: options.domain || undefined, + httpOnly: !!options.httpOnly, + maxAge: options.expires === 0 ? undefined : (options.expires || 30) * 24 * 60 * 60 * 1000, // from days to ms + path: options.path || '/', + secure: !!options.secure, + sameSite: options.sameSite || false + }; + + this.setCookie(name, options.value, cookieOptions); + }, + + removecookie: function(options, name) { + options = this.parse(options); + + let cookieOptions = { + domain: options.domain || undefined, + httpOnly: !!options.httpOnly, + maxAge: options.expires === 0 ? undefined : (options.expires || 30) * 24 * 60 * 60 * 1000, // from days to ms + path: options.path || '/', + secure: !!options.secure, + sameSite: !!options.sameSite + }; + + this.removeCookie(name, cookieOptions); + }, + + response: function(options) { + let data = this.parseOptional(options.data, '*', null); + let status = this.parseOptional(options.status, 'number', 200); + let contentType = this.parseOptional(options.contentType, 'string', 'application/json'); + if (contentType != 'application/json') { + this.res.set('Content-Type', contentType); + this.res.status(status).send(data); + } else { + this.res.status(status).json(data); + } + }, + + end: function(options) { + this.res.json(this.data); + }, + + error: function(options) { + let message = this.parseRequired(options.message, 'string', 'core.error: message is required.'); + throw new Error(message); + }, + + redirect: function(options) { + let url = this.parseRequired(options.url, 'string', 'core.redirect: url is required.'); + let status = this.parseOptional(options.status, 'number', 302); + + this.res.redirect(status, url); + }, + + trycatch: async function(options) { + try { + await this.exec(options.try, true); + } catch (error) { + this.scope.set('$_ERROR', error.message); + this.error = false; + if (options.catch) { + await this.exec(options.catch, true); + } + } + }, + + exec: async function(options) { + var data = {}; + + if (options.exec && fs.existsSync(`app/modules/lib/${options.exec}.json`)) { + let parentData = this.data; + this.data = {}; + this.scope = this.scope.create({ $_PARAM: this.parse(options.params) }); + await this.exec(await fs.readJSON(`app/modules/lib/${options.exec}.json`), true); + data = this.data; + this.scope = this.scope.parent; + this.data = parentData; + } else { + throw new Error(`There is no action called '${options.exec}' found in the library.`); + } + + return data; + }, + + group: async function(options, name) { + if (name) { + return this.sub(options.exec); + } else { + return this.exec(options.exec, true); + } + }, + + parallel: async function(options, name) { + let actions = options.exec.steps || options.exec; + if (!Array.isArray(actions)) actions = [actions]; + return await Promise.all(actions.map(exec => { + if (name) { + return this.sub(exec); + } else { + return this.exec(exec, true); + } + })); + }, + + randomUUID: function(options) { + const { randomUUID } = require('crypto'); + const { v4: uuidv4 } = require('uuid'); + return randomUUID ? randomUUID() : uuidv4(); + }, + +}; \ No newline at end of file diff --git a/lib/modules/crypto.js b/lib/modules/crypto.js new file mode 100644 index 0000000..11e274e --- /dev/null +++ b/lib/modules/crypto.js @@ -0,0 +1,31 @@ +// Perhaps use hashy as reference to implement bcrypt and needRehash action +// https://github.com/JsCommunity/hashy + +// supports argon2i, argon2d and argon2id +exports.passwordHash = async function(options) { + const password = this.parseRequired(options.password, 'string', 'crypto.passwordHash: password is required.') + const algo = this.parseOptional(options.algo, 'string', 'argon2i') + const argon2 = require('argon2') + const type = argon2[algo] + return argon2.hash(password, { type }) +} + +exports.passwordVerify = async function(options) { + const password = this.parseRequired(options.password, 'string', 'crypto.passwordVerify: password is required.') + const hash = this.parseRequired(options.hash, 'string', 'crypto.passwordVerify: hash is required.') + const argon2 = require('argon2') + return argon2.verify(hash, password) +} + +exports.passwordNeedsRehash = async function(options) { + const hash = this.parseRequired(options.hash, 'string', 'crypto.passwordNeedsRehash: hash is required.') + const algo = this.parseOptional(options.algo, 'string', 'argon2i') + const argon2 = require('argon2') + return argon2.needsRehash(hash, {}) +} + +exports.uuid = async function(options) { + const { randomUUID } = require('crypto') + const { v4: uuidv4 } = require('uuid') + return randomUUID ? randomUUID() : uuidv4() +} \ No newline at end of file diff --git a/lib/modules/csrf.js b/lib/modules/csrf.js new file mode 100644 index 0000000..d135ae2 --- /dev/null +++ b/lib/modules/csrf.js @@ -0,0 +1,7 @@ +module.exports = { + + generateToken: async function (options) { + return this.req.csrfToken(options.overwrite); + }, + +}; diff --git a/lib/modules/dataset.js b/lib/modules/dataset.js new file mode 100644 index 0000000..b28dca3 --- /dev/null +++ b/lib/modules/dataset.js @@ -0,0 +1,24 @@ +const { readFileSync } = require('fs'); +const { resolve } = require('path'); + +module.exports = { + + json: function(options) { + return JSON.parse(this.parse(options.data)); + }, + + local: function(options) { + let path = this.parse(options.path); + + if (typeof path !== 'string') throw new Error('dataset.local: path is required.'); + + let data = readFileSync(resolve('public', path)); + + return JSON.parse(data); + }, + + remote: function(options) { + throw new Error('dataset.remote: not implemented, use api instead.'); + }, + +}; \ No newline at end of file diff --git a/lib/modules/dbconnector.js b/lib/modules/dbconnector.js new file mode 100644 index 0000000..9acf8b9 --- /dev/null +++ b/lib/modules/dbconnector.js @@ -0,0 +1,765 @@ +const db = require('../core/db'); +const { where } = require('../formatters'); +const debug = require('debug')('server-connect:db'); + +module.exports = { + + connect: function(options, name) { + if (!name) throw new Error('dbconnector.connect has no name.'); + this.setDbConnection(name, options); + }, + + select: async function(options, name, meta) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.select: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.select: sql is required.'); + if (!sql.table) throw new Error('dbconnector.select: sql.table is required.'); + if (typeof sql.sort != 'string') sql.sort = this.parseOptional('{{ $_GET.sort }}', 'string', null); + if (typeof sql.dir != 'string') sql.dir = this.parseOptional('{{ $_GET.dir }}', 'string', 'asc'); + + if (sql.sort && sql.columns) { + if (!sql.orders) sql.orders = []; + + for (let column of sql.columns) { + if (column.column == sql.sort || column.alias == sql.sort) { + let order = { + column: column.alias || column.column, + direction: sql.dir.toLowerCase() == 'desc' ? 'desc' : 'asc' + }; + + if (column.table && !column.alias) order.table = column.table; + + sql.orders.unshift(order); + + break; + } + } + } + + sql.type = 'select'; + + if (db.client == 'couchdb') { + let table = sql.table.name || sql.table; + let { rows } = await db.list({ include_docs: true, startkey: table + '/', endkey: table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + if (sql.orders && sql.orders.length) { + rows.sort((a, b) => { + for (let order of sql.orders) { + if (a[order.column] == b[order.column]) continue; + let desc = order.direction && order.direction.toLowerCase() == 'desc'; + if (a[order.column] < b[order.column]) { + return desc ? 1 : -1; + } else { + return desc ? -1 : 1; + } + } + + return 0; + }); + } + + if (sql.columns && sql.columns.length) { + // we can also skip if user want just all columns + if (!(sql.columns.length == 1 && sql.columns[0].column == '*')) { + rows = rows.map(doc => { + const row = {}; + + for (let column of sql.columns) { + if (column.column == '*') { + Object.assign(row, doc); + } else { + // only support single level for now + row[column.alias || column.column || column] = doc[column.column || column]; + } + } + + return row; + }); + } + } + + if (sql.distinct) { + rows = [...new Set(rows)]; + } + + let offset = Number(sql.offset || 0); + let limit = Number(sql.limit || 0); + return rows.slice(offset, limit ? offset + limit : undefined); + } + + if (options.test) { + return { + options: options, + query: db.fromJSON(sql, meta).toSQL().toNative() + }; + } + + if (hasSubs(sql)) { + prepareColumns(sql); + + const results = await db.fromJSON(sql, meta); + + if (results.length) { + if (sql.sub) { + await _processSubQueries.call(this, db, results, sql.sub, meta); + } + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub) { + await _processSubQueries.call(this, db, results, join.sub, meta, '_' + (join.alias || join.table)); + } + } + } + + cleanupResults(results); + } + + return results; + } + + return db.fromJSON(sql, meta); + }, + + count: async function(options) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.count: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.count: sql is required.'); + if (!sql.table) throw new Error('dbconnector.count: sql.table is required.'); + + sql.type = 'count'; + + if (db.client == 'couchdb') { + let table = sql.table.name || sql.table; + let { rows } = await db.list({ include_docs: true, startkey: table + '/', endkey: table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + if (sql.distinct) { + rows = [...new Set(rows)]; + } + + return rows.length; + } + + if (options.test) { + return { + options: options, + query: db.fromJSON(sql, meta).toSQL().toNative() + }; + } + + return (await db.fromJSON(sql)).Total; + }, + + single: async function(options, name, meta) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.single: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.single: sql is required.'); + if (!sql.table) throw new Error('dbconnector.single: sql.table is required.'); + if (typeof sql.sort != 'string') sql.sort = this.parseOptional('{{ $_GET.sort }}', 'string', null); + if (typeof sql.dir != 'string') sql.dir = this.parseOptional('{{ $_GET.dir }}', 'string', 'asc'); + + sql.type = 'first'; + + if (db.client == 'couchdb') { + let table = sql.table.name || sql.table; + let { rows } = await db.list({ include_docs: true, startkey: table + '/', endkey: table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + if (sql.orders && sql.orders.length) { + rows.sort((a, b) => { + for (let order of sql.orders) { + if (a[order.column] == b[order.column]) continue; + let desc = order.direction && order.direction.toLowerCase() == 'desc'; + if (a[order.column] < b[order.column]) { + return desc ? 1 : -1; + } else { + return desc ? -1 : 1; + } + } + + return 0; + }); + } + + if (sql.columns && sql.columns.length) { + // we can also skip if user want just all columns + if (!(sql.columns.length == 1 && sql.columns[0].column == '*')) { + rows = rows.map(doc => { + const row = {}; + + for (let column of sql.columns) { + if (column.column == '*') { + Object.assign(row, doc); + } else { + // only support single level for now + row[column.alias || column.column || column] = doc[column.column || column]; + } + } + + return row; + }); + } + } + + if (sql.distinct) { + rows = [...new Set(rows)]; + } + + return rows.length ? rows[0] : null; + } + + if (options.test) { + return { + options: options, + query: db.fromJSON(sql, meta).toSQL().toNative() + }; + } + + if (hasSubs(sql)) { + prepareColumns(sql); + + const result = await db.fromJSON(sql, meta); + + if (!result) return null; + + if (sql.sub) { + await _processSubQueries.call(this, db, [result], sql.sub, meta); + } + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub) { + await _processSubQueries.call(this, db, [result], join.sub, meta, '_' + (join.alias || join.table)); + } + } + } + + cleanupResults([result]); + + return result; + } + + return db.fromJSON(sql, meta) || null; + }, + + paged: async function(options, name, meta) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.paged: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.paged: sql is required.'); + if (!sql.table) throw new Error('dbconnector.paged: sql.table is required.'); + if (typeof sql.offset != 'number') sql.offset = Number(this.parseOptional('{{ $_GET.offset }}', '*', 0)); + if (typeof sql.limit != 'number') sql.limit = Number(this.parseOptional('{{ $_GET.limit }}', '*', 25)); + if (typeof sql.sort != 'string') sql.sort = this.parseOptional('{{ $_GET.sort }}', 'string', null); + if (typeof sql.dir != 'string') sql.dir = this.parseOptional('{{ $_GET.dir }}', 'string', 'asc'); + + if (sql.sort && sql.columns) { + if (!sql.orders) sql.orders = []; + + for (let column of sql.columns) { + if (column.column == sql.sort || column.alias == sql.sort) { + let order = { + column: column.alias || column.column, + direction: sql.dir.toLowerCase() == 'desc' ? 'desc' : 'asc' + }; + + if (column.table && !column.alias) order.table = column.table; + + sql.orders.unshift(order); + + break; + } + } + } + + if (db.client == 'couchdb') { + let table = sql.table.name || sql.table; + let { rows } = await db.list({ include_docs: true, startkey: table + '/', endkey: table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + if (sql.orders && sql.orders.length) { + rows.sort((a, b) => { + for (let order of sql.orders) { + if (a[order.column] == b[order.column]) continue; + let desc = order.direction && order.direction.toLowerCase() == 'desc'; + if (a[order.column] < b[order.column]) { + return desc ? 1 : -1; + } else { + return desc ? -1 : 1; + } + } + + return 0; + }); + } + + if (sql.columns && sql.columns.length) { + // we can also skip if user want just all columns + if (!(sql.columns.length == 1 && sql.columns[0].column == '*')) { + rows = rows.map(doc => { + const row = {}; + + for (let column of sql.columns) { + if (column.column == '*') { + Object.assign(row, doc); + } else { + // only support single level for now + row[column.alias || column.column || column] = doc[column.column || column]; + } + } + + return row; + }); + } + } + + if (sql.distinct) { + rows = [...new Set(rows)]; + } + + let offset = Number(sql.offset || 0); + let limit = Number(sql.limit || 0); + let total = rows.length; + + return { + offset, + limit, + total, + page: { + offset: { + first: 0, + prev: offset - limit > 0 ? offset - limit : 0, + next: offset + limit < total ? offset + limit : offset, + last: (Math.ceil(total / limit) - 1) * limit + }, + current: Math.floor(offset / limit) + 1, + total: Math.ceil(total / limit) + }, + data: rows.slice(offset, limit ? offset + limit : undefined) + }; + } + + sql.type = 'count'; + let total = +(await db.fromJSON(sql, meta))['Total']; + + sql.type = 'select'; + let data = []; + + if (options.test) { + return { + options: options, + query: db.fromJSON(sql, meta).toSQL().toNative() + }; + } + + if (hasSubs(sql)) { + prepareColumns(sql); + + const results = await db.fromJSON(sql, meta); + + if (results.length) { + if (sql.sub) { + await _processSubQueries.call(this, db, results, sql.sub, meta); + } + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub) { + await _processSubQueries.call(this, db, results, join.sub, meta, '_' + (join.alias || join.table)); + } + } + } + + cleanupResults(results); + } + + data = results; + } else { + data = await db.fromJSON(sql, meta); + } + + return { + offset: sql.offset, + limit: sql.limit, + total, + page: { + offset: { + first: 0, + prev: sql.offset - sql.limit > 0 ? sql.offset - sql.limit : 0, + next: sql.offset + sql.limit < total ? sql.offset + sql.limit : sql.offset, + last: (Math.ceil(total / sql.limit) - 1) * sql.limit + }, + current: Math.floor(sql.offset / sql.limit) + 1, + total: Math.ceil(total / sql.limit) + }, + data + } + }, + +}; + +async function _processSubQueries(db, results, sub, meta, prefix = '') { + const lookup = new Map(); + const keys = new Set(); + + // get keys from results and create lookup table + // add initial sub field to results (empty array) + for (const result of results) { + const key = String(result['__dmxPrimary' + prefix]); + + if (lookup.has(key)) { + lookup.get(key).push(result); + } else { + lookup.set(key, [result]); + } + + keys.add(key); + + for (const field in sub) { + result[field] = []; + } + } + + for (const field in sub) { + const sql = this.parseSQL(sub[field]); + + sql.type = 'select'; + + prepareColumns(sql); + + let submeta = meta && meta.find(data => data.name == field); + if (submeta && submeta.sub) submeta = submeta.sub; + + // get all subresults with a single query + const subResults = await db.fromJSON(sql, submeta).whereIn(sql.key, Array.from(keys)); + + if (subResults.length) { + if (sql.sub) { + await _processSubQueries.call(this, db, subResults, sql.sub, submeta); + } + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub) { + await _processSubQueries.call(this, db, subResults, join.sub, submeta, '_' + (join.alias || join.table)); + } + } + } + + // map the sub results to the parent recordset + for (const subResult of subResults) { + const results = lookup.get(String(subResult['__dmxForeign'])); + + if (results) { + for (const result of results) { + result[field].push(subResult); + } + } + } + } + } + + // we don't need to return anything since all is updated by reference +} + +function hasSubs(sql) { + if (sql.sub) return true; + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub) return true; + } + } + + return false; +} + +function prepareColumns(sql) { + const table = sql.table.alias || sql.table.name || sql.table; + + if (!Array.isArray(sql.columns) || !sql.columns.length) { + sql.columns = [{ + table: table, + column: '*' + }]; + + if (Array.isArray(sql.joins) && sql.joins.length) { + for (join of sql.joins) { + sql.columns.push({ + table: join.alias || join.table, + column: '*' + }); + } + } + } + + if (sql.sub && sql.primary) { + sql.columns.push({ + table: table, + column: sql.primary, + alias: '__dmxPrimary' + }); + + if (sql.groupBy && sql.groupBy.length) { + sql.groupBy.push({ + table: table, + column: sql.primary + }); + } + } + + if (sql.key) { + sql.columns.push({ + table: table, + column: sql.key, + alias: '__dmxForeign' + }); + + if (sql.groupBy && sql.groupBy.length) { + sql.groupBy.push({ + table: table, + column: sql.key + }); + } + } + + if (sql.joins && sql.joins.length) { + for (const join of sql.joins) { + if (join.sub && join.primary) { + sql.columns.push({ + table: join.alias || join.table, + column: join.primary, + alias: '__dmxPrimary_' + (join.alias || join.table) + }); + + if (sql.groupBy && sql.groupBy.length) { + sql.groupBy.push({ + table: join.alias || join.table, + column: join.primary + }); + } + } + } + } +} + +function cleanupResults(results) { + for (const result of results) { + for (const field of Object.keys(result)) { + if (field.startsWith('__dmx')) { + delete result[field]; + } else if (Array.isArray(result[field])) { + cleanupResults(result[field]); + } + } + } +} \ No newline at end of file diff --git a/lib/modules/dbupdater.js b/lib/modules/dbupdater.js new file mode 100644 index 0000000..285cec0 --- /dev/null +++ b/lib/modules/dbupdater.js @@ -0,0 +1,453 @@ +module.exports = { + + insert: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.insert: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.insert: sql is required.'); + if (!sql.table) throw new Error('dbconnector.insert: sql.table is required.'); + + sql.type = 'insert'; + + if (db.client == 'couchdb') { + const doc = {}; + + for (const value of sql.values) { + doc[value.column] = value.value; + } + + const result = await db.insert(doc, sql.table + '/' + Date.now()); + + if (result.ok) { + return { affected: 1, identity: result.id }; + } else { + //throw new Error('dbconnector.insert: error inserting document into couchdb.'); + return { affected: 0 }; + } + } + + if (options.test) { + return { + options: options, + query: sql.toString() + }; + } + + if (sql.sub) { + return db.transaction(async trx => { + // TODO: test how identity is returned for each database + // main insert, returns inserted id + const [identity] = (await trx.fromJSON(sql)).map(value => value[sql.returning] || value); + + // loop sub (relation table) + for (let { table, key, value, values } of Object.values(sql.sub)) { + if (!Array.isArray(value)) break; + + for (const current of value) { + if (typeof current == 'object') { + current[key] = identity; + await trx(table).insert(current); + } else { + if (values.length != 1) throw new Error('Invalid value mapping'); + await trx(table).insert({ + [key]: identity, + [values[0].column]: current + }); + } + } + } + + return { affected: 1, identity }; + }); + } + + let identity = await db.fromJSON(sql); + + if (identity) { + if (Array.isArray(identity)) { + identity = identity[0]; + } + + if (typeof identity == 'object') { + identity = identity[Object.keys(identity)[0]]; + } + } + + return { affected: 1, identity }; + }, + + update: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.update: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.update: sql is required.'); + if (!sql.table) throw new Error('dbconnector.update: sql.table is required.'); + + sql.type = 'update'; + + if (db.client == 'couchdb') { + let { rows } = await db.list({ include_docs: true, startkey: sql.table + '/', endkey: sql.table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + let result = [] + if (rows.length) { + result = await db.bulk({ + docs: rows.map(doc => { + for (const value of sql.values) { + doc[value.column] = value.value; + } + return doc; + }) + }); + } + + return { affected: Array.isArray(result) ? result.filter(result => result.ok).length : rows.length }; + } + + if (options.test) { + return { + options: options, + query: sql.toString() + }; + } + + if (sql.sub) { + return db.transaction(async trx => { + let updated = await trx.fromJSON(sql); + + if (!Array.isArray(updated)) { + // check if is single update + const single = ( + sql.wheres && + sql.wheres.rules && + sql.wheres.rules.length == 1 && + sql.wheres.rules[0].field == sql.returning && + sql.wheres.rules[0].operation == '=' + ); + + if (single) { + // get id from where condition + updated = [sql.wheres.rules[0].value]; + } else { + // create a select with same where conditions + updated = await trx.fromJSON({ + ...sql, + type: 'select', + columns: [sql.returning] + }); + + updated = updated.map(value => value[sql.returning]); + } + } else { + updated = updated.map(value => value[sql.returning]); + } + + // loop sub + for (let { table, key, value, values } of Object.values(sql.sub)) { + if (!Array.isArray(value)) continue; + + // delete old related data first + await trx(table).whereIn(key, updated).del(); + + // for each updated item + for (const identity of updated) { + // insert value + for (const current of value) { + if (typeof current == 'object') { + current[key] = identity; + await trx(table).insert(current); + } else { + if (values.length != 1) throw new Error('Invalid value mapping'); + await trx(table).insert({ + [key]: identity, + [values[0].column]: current + }); + } + } + } + } + + return { affected: updated.length }; + }); + } + + let affected = await db.fromJSON(sql); + + return { affected }; + }, + + delete: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbconnector.delete: connection is required.'); + const sql = this.parseSQL(options.sql); + + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.delete: sql is required.'); + if (!sql.table) throw new Error('dbconnector.delete: sql.table is required.'); + + sql.type = 'del'; + + if (db.client == 'couchdb') { + + let { rows } = await db.list({ include_docs: true, startkey: sql.table + '/', endkey: sql.table + '0' }); + + rows = rows.map(row => row.doc); + + if (sql.wheres) { + const validate = (row, rule) => { + if (rule.operator) { + let a = row[rule.data.column]; + let b = rule.value; + + switch (rule.operator) { + case 'equal': return a == b; + case 'not_equal': return a != b; + case 'in': return b.includes(a); + case 'not_in': return !b.includes(a); + case 'less': return a < b; + case 'less_or_equal': return a <= b; + case 'greater': return a > b; + case 'greater_or_equal': return a >= b; + case 'between': return b[0] <= a <= b[1]; + case 'not_between': return !(b[0] <= a <= b[1]); + case 'begins_with': return String(a).startsWith(String(b)); + case 'not_begins_with': return !String(a).startsWith(String(b)); + case 'contains': return String(a).includes(String(b)); + case 'not_contains': return !String(a).includes(String(b)); + case 'ends_with': return String(a).endsWith(String(b)); + case 'not_ends_with': return !String(a).endsWith(String(b)); + case 'is_empty': return a == null || a == ''; + case 'is_not_empty': return a != null && a != ''; + case 'is_null': return a == null; + case 'is_not_null': return a != null; + } + } + + if (rule.condition && rule.rules.length) { + for (const _rule of rule.rules) { + const valid = validate(row, _rule); + if (!valid && rule.condition == 'AND') return false; + if (valid && rule.condition == 'OR') return true; + } + + return rule.condition == 'OR' ? false : true; + } + + return true; + }; + + rows = rows.filter(row => { + return validate(row, sql.wheres); + }); + } + + let result = [] + if (rows.length) { + result = await db.bulk({ + docs: rows.map(doc => { + doc._deleted = true; + return doc; + }) + }); + } + + return { affected: Array.isArray(result) ? result.filter(result => result.ok).length : rows.length }; + } + + if (options.test) { + return { + options: options, + query: sql.toString() + }; + } + + if (sql.sub) { + return db.transaction(async trx => { + const deleted = (await trx.fromJSON(sql)).map(value => value[sql.returning] || value); + + // loop sub + for (let { table, key } of Object.values(sql.sub)) { + // delete related data + await trx(table).whereIn(key, deleted).del(); + } + + return { affected: deleted.length }; + }); + } + + let affected = await db.fromJSON(sql); + + return { affected }; + }, + + custom: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbupdater.custom: connection is required.'); + const sql = this.parseSQL(options.sql); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + if (!sql) throw new Error('dbconnector.custom: sql is required.'); + if (typeof sql.query != 'string') throw new Error('dbupdater.custom: sql.query is required.'); + if (!Array.isArray(sql.params)) throw new Error('dbupdater.custom: sql.params is required.'); + + if (db.client == 'couchdb') { + throw new Error('dbupdater.custom: couchdb is not supported.'); + } + + const params = []; + const query = sql.query.replace(/([:@][a-zA-Z_]\w*|\?)/g, param => { + if (param == '?') { + params.push(sql.params[params.length].value); + return '?'; + } + + let p = sql.params.find(p => p.name == param); + if (p) { + params.push(p.value); + return '?'; + } + + return param; + }); + + let results = await db.raw(query, params); + + if (db.client.config.client == 'mysql' || db.client.config.client == 'mysql2') { + results = results[0]; + } else if (db.client.config.client == 'postgres' || db.client.config.client == 'redshift') { + results = results.rows; + } + + return results; + }, + + execute: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbupdater.execute: connection is required.'); + const query = this.parseRequired(options.query, 'string', 'dbupdater.execute: query is required.'); + const params = this.parseOptional(options.params, 'object', []); + const db = this.getDbConnection(connection); + + if (!db) throw new Error(`Connection "${connection}" doesn't exist.`); + + if (db.client == 'couchdb') { + throw new Error('dbupdater.execute: couchdb is not supported.'); + } + + let results = await db.raw(query, params); + + if (db.client.config.client == 'mysql' || db.client.config.client == 'mysql2') { + results = results[0]; + } else if (db.client.config.client == 'postgres' || db.client.config.client == 'redshift') { + results = results.rows; + } + + return results; + }, + + // bulk insert + bulkinsert: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbupdater.bulkinsert: connection is required.'); + const sql = this.parseSQL(options.sql); + const source = this.parseRequired(options.source, 'object', 'dbupdater.bulkinsert: source is required.'); + const batchsize = this.parseOptional(options.batchsize, 'number', 100); + const db = this.getDbConnection(connection); + + if (db.client == 'couchdb') { + throw new Error('dbupdater.bulkinsert: couchdb is not supported.'); + } + + return await db.transaction(async transaction => { + for (let i = 0; i < source.length; i += batchsize) { + let batch = source.slice(i, i + batchsize).map(data => { + let values = {}; + + for (let value of sql.values) { + if (value.type == 'json') { + values[value.column] = JSON.stringify(data[value.value]); + } else { + values[value.column] = data[value.value]; + } + } + + return values; + }); + + await transaction(sql.table.name || sql.table).insert(batch); + } + + return { affected: source.length }; + }); + }, + + transaction: async function (options) { + const connection = this.parseRequired(options.connection, 'string', 'dbupdater.transaction: connection is required.'); + //const exec = this.parseRequired(options.exec, 'object', 'dbupdater.transaction: exec is required.'); + const db = this.getDbConnection(connection); + + if (db.client == 'couchdb') { + throw new Error('dbupdater.transaction: couchdb is not supported.'); + } + + return await db.transaction(async trx => { + this.trx[connection] = trx; + return await this.exec(options.exec, true).finally(() => { + this.trx[connection] = null; + }); + }); + }, + +}; \ No newline at end of file diff --git a/lib/modules/export.js b/lib/modules/export.js new file mode 100644 index 0000000..4c09f65 --- /dev/null +++ b/lib/modules/export.js @@ -0,0 +1,109 @@ +const fs = require('fs-extra'); +const { toSystemPath } = require('../core/path'); + +module.exports = { + + csv: async function(options) { + let path = this.parse(options.path); + let data = this.parse(options.data); + let header = this.parse(options.header); + let delimiter = this.parse(options.delimiter); + let overwrite = this.parse(options.overwrite); + + if (typeof path != 'string') throw new Error('export.csv: path is required.'); + if (!Array.isArray(data) || !data.length) throw new Error ('export.csv: data is required.'); + + delimiter = typeof delimiter == 'string' ? delimiter : ','; + + if (delimiter == '\\t') delimiter = '\t'; + + const fd = await fs.open(toSystemPath(path), overwrite ? 'w' : 'wx'); + + if (header) { + await putcsv(fd, Object.keys(data[0]), delimiter); + } + + for (let row of data) { + await putcsv(fd, row, delimiter); + } + + await fs.close(fd); + + return path; + }, + + xml: async function(options) { + let path = this.parse(options.path); + let data = this.parse(options.data); + let root = this.parse(options.root); + let item = this.parse(options.item); + let overwrite = this.parse(options.overwrite); + + if (typeof path != 'string') throw new Error('export.xml: path is required.'); + if (!Array.isArray(data) || !data.length) throw new Error('export.xml: data is required.'); + + root = typeof root == 'string' ? root : 'export'; + item = typeof item == 'string' ? item : 'item'; + + const fd = await fs.open(toSystemPath(path), overwrite ? 'w' : 'wx'); + + await fs.write(fd, `<${root}>`); + for (let row of data) { + await fs.write(fd, `<${item}>`); + for (let prop in row) { + await fs.write(fd, `<${prop}>`); + } + await fs.write(fd, ``); + } + await fs.write(fd, ``); + + await fs.close(fd); + + return path; + }, + +}; + +async function putcsv(fd, data, delimiter) { + let str = ''; + + if (typeof data != 'object') { + throw new Error('putcsv: Invalid data.'); + } + + for (let prop in data) { + if (Object.hasOwn(data, prop)) { + let value = String(data[prop]); + + if (/["\n\r\t\s]/.test(value) || value.includes(delimiter)) { + let escaped = false; + + str += '"'; + + for (let i = 0; i < value.length; i++) { + if (value.charAt(i) == '\\') { + escaped = true; + } else if (!escaped && value.charAt(i) == '"') { + str += '"'; + } else { + escaped = false; + } + + str += value.charAt(i); + } + + str += '"'; + } else { + str += value; + } + + str += delimiter; + } + } + + if (!str) { + throw new Error('putcsv: No data.'); + } + + return fs.write(fd, str.substr(0, str.length - delimiter.length) + '\r\n'); +} \ No newline at end of file diff --git a/lib/modules/fs.js b/lib/modules/fs.js new file mode 100644 index 0000000..e3f0ebc --- /dev/null +++ b/lib/modules/fs.js @@ -0,0 +1,258 @@ +const fs = require('fs-extra'); +const { join, dirname, basename, extname } = require('path'); +const { toSystemPath, toAppPath, toSiteUrl, getUniqFile, parseTemplate } = require('../core/path'); +const { map } = require('../core/async'); + +module.exports = { + + download: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.download: path is required.')); + let filename = this.parseOptional(options.filename, 'string', basename(path)); + + if (fs.existsSync(path)) { + this.res.download(path, filename); + this.noOutput = true; + } else { + this.res.sendStatus(404); + } + }, + + exists: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.exists: path is required.')); + + if (await isFile(path)) { + if (options.then) { + await this.exec(options.then, true); + } + return true; + } else { + if (options.else) { + await this.exec(options.else, true); + } + return false; + } + }, + + direxists: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.direxists: path is required.')); + + if (await isDirectory(path)) { + if (options.then) { + await this.exec(options.then, true); + } + return true; + } else { + if (options.else) { + await this.exec(options.else, true); + } + return false; + } + }, + + createdir: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.createdir: path is required.')); + + await fs.ensureDir(path); + + return toAppPath(path); + }, + + removedir: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.removedir: path is required.')); + + await fs.remove(path); + + return toAppPath(path); + }, + + emptydir: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.emptydir: path is required.')); + + await fs.emptyDir(path) + + return toAppPath(path); + }, + + move: async function(options) { + let from = toSystemPath(this.parseRequired(options.from, 'string', 'fs.move: from is required.')); + let to = toSystemPath(this.parseRequired(options.to, 'string', 'fs.move: to is required.')); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let createdir = this.parseOptional(options.createdir, 'boolean', true); + + if (!fs.existsSync(to)) { + if (createdir) { + await fs.ensureDir(to); + } else { + throw new Error(`Destination path doesn't exists.`); + } + } + + to = join(to, basename(from)); + + if (!overwrite) { + to = getUniqFile(to); + } + + await fs.move(from, to, { overwrite: true }); + + return toAppPath(to); + }, + + rename: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.rename: path is required.')); + let template = this.parseRequired(options.template, 'string', 'fs.rename: template is required.'); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let to = parseTemplate(path, template); + + if (!overwrite && fs.existsSync(to)) { + throw new Error(`fs.rename: file "${to}" already exists.`); + } + + await fs.rename(path, to); + + return toAppPath(to); + }, + + copy: async function(options) { + let from = toSystemPath(this.parseRequired(options.from, 'string', 'fs.copy: from is required.')); + let to = toSystemPath(this.parseRequired(options.to, 'string', 'fs.copy: to is required.')); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let createdir = this.parseOptional(options.createdir, 'boolean', true); + + if (!fs.existsSync(to)) { + if (createdir) { + await fs.ensureDir(to); + } else { + throw new Error(`Destination path doesn't exist.`); + } + } + + to = join(to, basename(from)); + + if (!overwrite && fs.existsSync(to)) { + to = getUniqFile(to); + } + + await fs.copy(from, to); + + return toAppPath(to); + }, + + remove: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.remove: path is required.')); + + await fs.unlink(path); + + return true; + }, + + dir: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.dir: path is required.')); + let allowedExtensions = this.parseOptional(options.allowedExtensions, 'string', ''); + let showHidden = this.parseOptional(options.showHidden, 'boolean', false); + let includeFolders = this.parseOptional(options.includeFolders, 'boolean', false); + let folderSize = this.parseOptional(options.folderSize, 'string', 'none'); + let concurrency = this.parseOptional(options.concurrency, 'number', 4); + + folderSize = ['none', 'files', 'recursive'].includes(folderSize) ? folderSize : 'none'; + allowedExtensions = allowedExtensions ? allowedExtensions.split(/\s*,\s*/).map(ext => lowercase(ext[0] == '.' ? ext : '.' + ext)) : []; + + let files = await fs.readdir(path, { withFileTypes: true }); + + files = files.filter(entry => { + if (!includeFolders && entry.isDirectory()) return false; + if (!showHidden && entry.name[0] == '.') return false; + if (allowedExtensions.length && entry.isFile() && !allowedExtensions.includes(lowercase(extname(entry.name)))) return false; + return entry.isFile() || entry.isDirectory(); + }); + + // Fast parallel map + return map(files, async (entry) => { + let curr = join(path, entry.name); + let stat = await fs.stat(curr); + + if (folderSize != 'none' && entry.isDirectory()) { + stat.size = await calcSize(curr, folderSize == 'recursive', concurrency); + } + + return { + type: entry.isFile() ? 'file' : 'dir', + name: entry.name, + folder: toAppPath(dirname(curr)), + basename: basename(curr, extname(curr)), + extension: extname(curr), + path: toAppPath(curr), + url: toSiteUrl(curr), + size: stat.size, + created: stat.ctime, + accessed: stat.atime, + modified: stat.mtime + }; + }, concurrency); + }, + + stat: async function(options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'fs.stat: path is required.')); + let folderSize = this.parseOptional(options.folderSize, 'string', 'none'); + let concurrency = this.parseOptional(options.concurrency, 'number', 4); + + folderSize = ['none', 'files', 'recursive'].includes(folderSize) ? folderSize : 'none'; + + let stat = await fs.stat(path); + + if (folderSize != 'none' && stat.isDirectory()) { + stat.size = await calcSize(path, folderSize == 'recursive', concurrency); + } + + return { + type: stat.isFile() ? 'file' : 'dir', + name: basename(path), + folder: toAppPath(dirname(path)), + basename: basename(path, extname(path)), + extension: extname(path), + path: toAppPath(path), + url: toSiteUrl(path), + size: stat.size, + created: stat.ctime, + accessed: stat.atime, + modified: stat.mtime + }; + }, + +}; + +function lowercase(str) { + return str.toLowerCase(); +} + +async function calcSize(folder, recursive, concurrency) { + let entries = await fs.readdir(folder); + + return map(entries, async (entry) => { + let stat = await fs.stat(join(folder, entry)); + + if (stat.isDirectory() && recursive) { + return calcSize(join(folder, entry), recursive, concurrency); + } + + return stat.size; + }, concurrency).then(arr => arr.reduce((size, curr) => size + curr, 0)); +}; + +async function isFile(path) { + try { + let stats = await fs.stat(path); + return stats.isFile(); + } catch (err) { + return false; + } +} + +async function isDirectory(path) { + try { + let stats = await fs.stat(path); + return stats.isDirectory(); + } catch (err) { + return false; + } +} \ No newline at end of file diff --git a/lib/modules/image.js b/lib/modules/image.js new file mode 100644 index 0000000..a76db50 --- /dev/null +++ b/lib/modules/image.js @@ -0,0 +1,505 @@ +const fs = require('fs-extra'); +const Sharp = require('sharp'); +const debug = require('debug')('server-connect:image'); +const { basename, extname, join } = require('path'); +const { toAppPath, toSystemPath, parseTemplate, getUniqFile } = require('../core/path'); + +const positions = { + 'center': 0, + 'centre': 0, + 'top': 1, + 'north': 1, + 'right': 2, + 'east': 2, + 'bottom': 3, + 'south': 3, + 'left': 4, + 'west': 4, + 'top right': 5, + 'right top': 5, + 'northeast': 5, + 'bottom right': 6, + 'right bottom': 6, + 'southeast': 6, + 'bottom left': 7, + 'left bottom': 7, + 'southwest': 7, + 'top left': 8, + 'left top': 8, + 'northwest': 8, + 'entropy': 16, + 'attention': 17 +}; + +function cw(w, meta) { + if (typeof w == 'string') { + if (/%$/.test(w)) { + w = meta.width * parseFloat(w) / 100; + } + } + + if (w < 0) { + w = meta.width + w; + } + + return parseInt(w); +} + +function ch(h, meta) { + if (typeof h == 'string') { + if (/%$/.test(h)) { + h = meta.height * parseFloat(h) / 100; + } + } + + if (h < 0) { + h = meta.height + h; + } + + return parseInt(h); +} + +function cx(x, w, meta) { + if (typeof x == 'string') { + switch (x) { + case 'left': + x = 0; + break; + case 'center': + x = (meta.width - w) / 2; + break; + case 'right': + x = meta.width - w; + break; + default: + if (/%$/.test(x)) { + x = (meta.width - w) * parseFloat(x) / 100; + } + } + } + + if (x < 0) { + x = meta.width - w + x; + } + + return parseInt(x); +} + +function cy(y, h, meta) { + if (typeof y == 'string') { + switch (y) { + case 'top': + y = 0; + break; + case 'middle': + y = (meta.height - h) / 2; + break; + case 'bottom': + y = meta.height - h; + break; + default: + if (/%$/.test(y)) { + y = (meta.height - h) * parseFloat(y) / 100; + } + } + } + + if (y < 0) { + y = meta.height - h + y; + } + + return parseInt(y); +} + +async function updateImage(sharp) { + sharp.image = Sharp(await sharp.image.toBuffer()); + sharp.metadata = await sharp.image.metadata(); +} + +module.exports = { + + getImageSize: async function (options) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'image.getImageSize: path is required.')); + + const image = Sharp(path); + const metadata = await image.metadata(); + + return { + width: metadata.width, + height: metadata.height + }; + }, + + load: async function (options, name) { + let path = toSystemPath(this.parseRequired(options.path, 'string', 'image.load: path is required.')); + let orient = this.parseOptional(options.autoOrient, 'boolean', false); + + this.req.image = this.req.image || {}; + this.req.image[name] = { name: basename(path), image: Sharp(path), metadata: null }; + + const sharp = this.req.image[name]; + if (orient) sharp.image.rotate(); + + await updateImage(sharp); + + return { + name: basename(path), + width: sharp.metadata.width, + height: sharp.metadata.height + }; + }, + + save: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.save: instance "${options.instance} doesn't exist.`); + + let path = toSystemPath(this.parseRequired(options.path, 'string', 'image.save: path is required.')); + let format = this.parseOptional(options.format, 'string', 'jpeg').toLowerCase(); + let template = this.parseOptional(options.template, 'string', '{name}{ext}'); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let createPath = this.parseOptional(options.createPath, 'boolean', true); + let background = this.parseOptional(options.background, 'string', '#FFFFFF'); + let quality = this.parseOptional(options.quality, 'number', 75); + + if (!fs.existsSync(path)) { + if (createPath) { + await fs.ensureDir(path); + } else { + throw new Error(`image.save: path "${path}" doesn't exist.`); + } + } + + let file = join(path, sharp.name); + + if (template) { + file = parseTemplate(file, template); + } + + if (format == 'auto') { + switch (extname(file).toLowerCase()) { + case '.png': format = 'png'; break; + case '.gif': format = 'gif'; break; + case '.webp': format = 'webp'; break; + default: format = 'jpeg'; + } + } + + if (format == 'jpeg') { + sharp.image.flatten({ background }); + sharp.image.toFormat(format, { quality }); + } else if (format == 'webp') { + sharp.image.toFormat(format, { quality }); + } else { + sharp.image.toFormat(format); + } + + const data = await sharp.image.toBuffer(); + + file = file.replace(extname(file), '.' + format.replace('jpeg', 'jpg')); + + if (fs.existsSync(file)) { + if (overwrite) { + await fs.unlink(file); + } else { + file = getUniqFile(file); + } + } + + await fs.writeFile(file, data) //.toFile(file); + + return toAppPath(file); + }, + + resize: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.resize: instance "${options.instance} doesn't exist.`); + + let width = this.parseOptional(cw(this.parse(options.width), sharp.metadata), 'number', null); + let height = this.parseOptional(ch(this.parse(options.height), sharp.metadata), 'number', null); + let upscale = this.parseOptional(options.upscale, 'boolean', false); + + if (isNaN(width)) width = null; + if (isNaN(height)) height = null; + + sharp.image.resize(width, height, { fit: width && height ? 'fill' : 'cover', withoutEnlargement: !upscale }); + + await updateImage(sharp); + }, + + crop: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.crop: instance "${options.instance} doesn't exist.`); + + let width = this.parseRequired(cw(this.parse(options.width)), 'number', 'image.crop: width is required.'); + let height = this.parseRequired(ch(this.parse(options.height)), 'number', 'image.crop: height is required.'); + if (width > sharp.metadata.width) width = sharp.metadata.width; + if (height > sharp.metadata.height) height = sharp.metadata.height; + let left = this.parseRequired(cx(this.parse(options.x), width, sharp.metadata), 'number', 'image.crop: x is required.'); + let top = this.parseRequired(cy(this.parse(options.y), height, sharp.metadata), 'number', 'image.crop: y is required.'); + + sharp.image.extract({ left, top, width, height }); + + await updateImage(sharp); + }, + + cover: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.cover: instance "${options.instance}" doesn't exist.`); + + let width = this.parseRequired(options.width, 'number', 'image.cover: width is required.'); + let height = this.parseRequired(options.height, 'number', 'image.cover: height is required.'); + // position: see positions object for options + let position = this.parseOptional(options.position, 'string', 'center'); + // kernel: 'nearest', 'cubic', 'mitchell', 'lanczos2', 'lanczos3' + let kernel = this.parseOptional(options.kernel, 'string', 'lanczos3'); + + position = positions[position] || 0; + + sharp.image.resize({ width, height, position, kernel }); + + await updateImage(sharp); + }, + + watermark: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.watermark: instance "${options.instance} doesn't exist.`); + + let path = toSystemPath(this.parseRequired(options.path, 'string', 'image.watermark: path is required.')); + let image = Sharp(path); + let metadata = await image.metadata(); + let input = await image.toBuffer(); + let left = this.parseRequired(cx(this.parse(options.x), metadata.width, sharp.metadata), 'number', 'image.watermark: x is required.'); + let top = this.parseRequired(cy(this.parse(options.y), metadata.height, sharp.metadata), 'number', 'image.watermark: y is required.'); + + sharp.image.composite([{ input, left, top }]); + }, + + text: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.text: instance "${options.instance} doesn't exist.`); + + let x = this.parse(options.x); + let y = this.parse(options.y); + let text = this.parseRequired(options.text, 'string', 'image.text: text is required.'); + let font = this.parseOptional(options.font, 'string', 'Verdana'); + let size = this.parseOptional(options.size, 'number', 24); + let color = this.parseOptional(options.color, 'string', '#ffffff'); + + let width = sharp.metadata.width; + let height = sharp.metadata.height; + let anchor = 'start'; + + switch (x) { + case 'left': + x = '0%'; + anchor = 'start'; + break; + case 'center': + x = '50%'; + anchor = 'middle'; + break; + case 'right': + x = '100%'; + anchor = 'end'; + break; + default: + if (x < 0) { + x = width - x; + anchor = 'end'; + } + } + + switch (y) { + case 'top': + y = size; + break; + case 'middle': + y = (height / 2) - (size / 2); + break; + case 'bottom': + y = height; + break; + default: + if (y < 0) { + y = height - size - y; + } + } + + let svg = ` + + + ${text} + + `; + + const input = await Sharp(Buffer.from(svg)).toBuffer(); + + sharp.image.composite([{ input, left: 0, top: 0 }]); + }, + + tiled: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.tiled: instance "${options.instance} doesn't exist.`); + + let input = toSystemPath(this.parseRequired(options.path, 'string', 'image.tiled: path is required.')); + let padding = this.parseOptional(options.padding, 'number', 0); + + if (padding) { + input = await Sharp(input).extend({ + top: padding, left: padding, bottom: 0, right: 0, background: { r: 0, g: 0, b: 0, alpha: 0 } + }).toBuffer(); + } + + sharp.image.composite([{ input, left: 0, top: 0, tile: true }]); + }, + + flip: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.flip: instance "${options.instance} doesn't exist.`); + + let horizontal = this.parseOptional(options.horizontal, 'boolean', false); + let vertical = this.parseOptional(options.vertical, 'boolean', false); + + if (horizontal) sharp.image.flop(); + if (vertical) sharp.image.flip(); + }, + + rotateLeft: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.rotateLeft: instance "${options.instance} doesn't exist.`); + + sharp.image.rotate(-90); + + await updateImage(sharp); + }, + + rotateRight: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.rotateRight: instance "${options.instance} doesn't exist.`); + + sharp.image.rotate(90); + + await updateImage(sharp); + }, + + smooth: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.smooth: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + 1, 1, 1, + 1, 1, 1, + 1, 1, 1 + ] + }); + }, + + blur: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.blur: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + 1, 2, 1, + 2, 4, 2, + 1, 2, 1 + ] + }); + }, + + sharpen: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.sharpen: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + 0, -2, 0, + -2, 15, -2, + 0, -2, 0 + ] + }); + }, + + meanRemoval: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.meanRemoval: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + -1, -1, -1, + -1, 9, -1, + -1, -1, -1 + ] + }); + }, + + emboss: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.emboss: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + -1, 0, -1, + 0, 4, 0, + -1, 0, -1 + ], + offset: 127 + }); + }, + + edgeDetect: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.edgeDetect: instance "${options.instance} doesn't exist.`); + + sharp.image.convolve({ + width: 3, + height: 3, + kernel: [ + -1, -1, -1, + 0, 0, 0, + 1, 1, 1 + ], + offset: 127 + }); + }, + + grayscale: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.grayscale: instance "${options.instance} doesn't exist.`); + + sharp.image.grayscale(); + }, + + sepia: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.sepia: instance "${options.instance} doesn't exist.`); + + sharp.image.tint({ r: 112, g: 66, b: 20 }); + }, + + invert: async function (options) { + const sharp = this.req.image[options.instance]; + if (!sharp) throw new Error(`image.invert: instance "${options.instance} doesn't exist.`); + + sharp.image.negate(); + }, + +}; \ No newline at end of file diff --git a/lib/modules/import.js b/lib/modules/import.js new file mode 100644 index 0000000..053d8c4 --- /dev/null +++ b/lib/modules/import.js @@ -0,0 +1,129 @@ +const fs = require('fs-extra'); +const { toSystemPath } = require('../core/path'); +const { keysToLowerCase } = require('../core/util'); + +// simple inital implementation +// better implementation at: +// https://github.com/adaltas/node-csv-parse +// https://github.com/mafintosh/csv-parser +function parseCSV(csv, options) { + if (!csv) return []; + + if (csv.charCodeAt(0) === 0xFEFF) { + csv = csv.slice(1); + } + + let delimiter = options.delimiter.replace('\\t', '\t'); + let keys = options.fields; + let line = 1; + let data = []; + + if (options.header) { + keys = getcsv(); + + options.fields.forEach(field => { + if (keys.indexOf(field) == -1) { + throw new Error('parseCSV: ' + field + ' is missing in ' + options.path); + } + }); + + line++; + } + + let size = keys.length; + + while (csv.length) { + let values = getcsv(); + let o = {}; + + if (values.length != size) { + throw new Error('parseCSV: columns do not match. keys: ' + size + ', values: ' + values.length + ' at line ' + line); + } + + for (let i = 0; i < size; i++) { + o[keys[i]] = values[i]; + } + + data.push(o); + + line++; + } + + return data; + + function getcsv() { + let data = [''], l = csv.length, + esc = false, escesc = false, + n = 0, i = 0; + + while (i < l) { + let s = csv.charAt(i); + + if (s == '\n') { + if (esc) { + data[n] += s; + } else { + i++; + break; + } + } else if (s == '\r') { + if (esc) { + data[n] += s; + } + } else if (s == delimiter) { + if (esc) { + data[n] += s; + } else { + data[++n] = ''; + esc = false; + escesc = false; + } + } else if (s == '"') { + if (escesc) { + data[n] += s; + escesc = false; + } + + if (esc) { + esc = false; + escesc = true; + } else { + esc = true; + escesc = false; + } + } else { + if (escesc) { + data[n] += '"'; + escesc = false; + } + + data[n] += s; + } + + i++; + } + + csv = csv.substr(i); + + return data; + } +} + +module.exports = { + + csv: async function(options) { + let path = this.parseRequired(options.path, 'string', 'export.csv: path is required.'); + let fields = this.parseOptional(options.fields, 'object', []); + let header = this.parseOptional(options.header, 'boolean', false); + let delimiter = this.parseOptional(options.delimiter, 'string', ','); + let csv = await fs.readFile(toSystemPath(path), 'utf8'); + + return parseCSV(csv, { fields, header, delimiter }); + }, + + xml: async function(options) { + // TODO: import.xml + throw new Error('import.xml: not implemented.'); + }, + +}; \ No newline at end of file diff --git a/lib/modules/jwt.js b/lib/modules/jwt.js new file mode 100644 index 0000000..6299f05 --- /dev/null +++ b/lib/modules/jwt.js @@ -0,0 +1,22 @@ +exports.sign = function(options, name) { + return this.setJSONWebToken(name, options) +}; + +exports.decode = function(options) { + const jwt = require('jsonwebtoken'); + return jwt.decode(this.parse(options.token), { complete: true }); +}; + +exports.verify = function(options) { + const jwt = require('jsonwebtoken'); + options = this.parse(options); + + try { + return jwt.verify(options.token, options.key, options); + } catch (error) { + const debug = require('debug')('server-connect:jwt'); + debug('jwt verify failed: %o', error); + if (options.throw) throw error; + return { error }; + } +}; \ No newline at end of file diff --git a/lib/modules/mail.js b/lib/modules/mail.js new file mode 100644 index 0000000..34721fe --- /dev/null +++ b/lib/modules/mail.js @@ -0,0 +1,92 @@ +const fs = require('fs-extra'); +const { getFilesArray, toSystemPath } = require('../core/path'); +const { basename, posix } = require('path'); +const { v4: uuidv4 } = require('uuid'); +const IMPORTANCE = { 0: 'low', 1: 'normal', 2: 'high' }; + +module.exports = { + + setup: function(options, name) { + if (!name) throw new Error('mail.setup has no name.'); + this.setMailer(name, options); + }, + + send: async function(options) { + let setup = this.getMailer(this.parseOptional(options.instance, 'string', 'system')); + let subject = this.parseRequired(options.subject, 'string', 'mail.send: subject is required.'); + let fromEmail = this.parseRequired(options.fromEmail, 'string', 'mail.send: fromEmail is required.'); + let fromName = this.parseOptional(options.fromName, 'string', ''); + let toEmail = this.parseRequired(options.toEmail, 'string', 'mail.send: toEmail is required.'); + let toName = this.parseOptional(options.toName, 'string', ''); + let replyTo = this.parseOptional(options.replyTo, 'string', ''); + let cc = this.parseOptional(options.cc, 'string', ''); + let bcc = this.parseOptional(options.bcc, 'string', ''); + let source = this.parseOptional(options.source, 'string', 'static'); // static, file + let contentType = this.parseOptional(options.contentType, 'string', 'text'); // text / html + let body = this.parseOptional(options.body, 'string', ''); + let bodyFile = this.parseOptional(options.bodyFile, 'string', ''); + let embedImages = this.parseOptional(options.embedImages, 'boolean', false); + let priority = IMPORTANCE[this.parseOptional(options.importance, 'number', 1)]; + let attachments = this.parseOptional(options.attachments, '*', []); // "/file.ext" / ["/file.ext"] / {path:"/file.ext"} / [{path:"/file.ext"}] + + let from = fromName ? `"${fromName}" <${fromEmail}>` : fromEmail; + let to = toName ? `"${toName}" <${toEmail}>` : toEmail; + let text = body; + let html = null; + + if (source == 'file') { + body = this.parse(await fs.readFile(toSystemPath(bodyFile), 'utf8')); + } + + if (attachments) { + attachments = getFilesArray(attachments).map((path) => ({ filename: basename(path), path })); + } + + if (contentType == 'html') { + html = body; + + if (embedImages) { + let cid = {}; + + html = html.replace(/(?:"|')([^"']+\.(jpg|png|gif))(?:"|')/gi, (m, url) => { + let path = toSystemPath(url); + + if (fs.existsSync(path)) { + if (!cid[path]) { + cid[path] = uuidv4(); + attachments.push({ + filename: basename(path), + path: path, + cid: cid[path] + }); + } + + return `"cid:${cid[path]}"`; + } else { + console.warn(`${path} not found`); + } + + return `"${url}"`; + }); + } + + if (this.req.get) { // we can only do this if we have a request to get our hostname + const hasProxy = !!this.req.get('x-forwarded-host'); + const host = hasProxy ? `${this.req.protocol}://${this.req.hostname}` : this.req.get('host'); + + html = html.replace(/(href|src)(?:\s*=\s*)(?:"|')([^"']+)(?:"|')/gi, (m, attr, url) => { + if (!url.includes(':')) { + url = posix.join(host, url); + } + + return `${attr}="${url}"`; + }); + } + } + + const nodemailer = require('nodemailer'); + let transport = nodemailer.createTransport(setup); + return transport.sendMail({ from, to, cc, bcc, replyTo, subject, html, text, priority, attachments }); + }, + +}; \ No newline at end of file diff --git a/lib/modules/metadata.js b/lib/modules/metadata.js new file mode 100644 index 0000000..9ebe541 --- /dev/null +++ b/lib/modules/metadata.js @@ -0,0 +1,484 @@ +const fs = require('fs-extra'); + +const imageTypes = ['PNG', 'GIF', 'BMP', 'JPEG', 'TIFF']; +const videoTypes = ['AVI', 'MP4', 'MOV', 'MKV', 'WEBM', 'OGV']; +const soundTypes = ['OGG', 'WAV', 'MP3', 'FLAC']; + +const read = async (path, offset, length) => { + const fp = await fs.open(path); + const buff = Buffer.alloc(length); + + await fs.read(fd, buff, 0, length, offset); + await fs.close(); + + return buff; +}; + + +const parser = { + + PNG: async (path, result) => { + const buff = await read(path, 18, 6); + result.width = buff.readUInt16BE(0); + result.height = buff.readUInt16BE(4); + }, + + GIF: async (path, result) => { + const buff = await read(path, 6, 4); + result.width = buff.readUInt16LE(0); + result.height = buff.readUInt16LE(2); + }, + + BMP: async (path, result) => { + const buff = await read(path, 18, 8); + result.width = buff.readUInt32LE(0); + result.height = buff.readUInt32LE(4); + }, + + JPEG: async (path, result) => { + const sof = [0xc0, 0xc1, 0xc2, 0xc3, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xde]; + const buff = await read(path, 2, 64000); + + let pos = 0; + + while (buff[pos++] == 0xff) { + let marker = buff[pos++]; + let size = buff.readUInt16BE(pos); + + if (marker == 0xda) break; + + if (sof.includes(marker)) { + result.height = buff.readUInt16BE(pos + 3); + result.width = buff.readUInt16BE(pos + 5); + break; + } + + pos += size; + } + }, + + TIFF: async (path, result) => { + const buff = await read(path, 0, 64000); + const le = buff.toString('ascii', 0, 2) == 'II'; + let pos = 0; + const readUInt16 = () => { pos += 2; return buff[le ? 'readUInt16LE' : 'readUInt16BE'](pos - 2); } + const readUInt32 = () => { pos += 4; return buff[le ? 'readUInt32LE' : 'readUInt32BE'](pos - 4); } + + let offset = readUInt32(); + + while (pos < buff.length && offset > 0) { + let entries = readUInt16(offset); + let start = pos; + + for (let i = 0; i < entries; i++) { + let tag = readUInt16(); + let type = readUInt16(); + let length = readUInt32(); + let data = (type == 3) ? readUInt16() : readUInt32(); + if (type == 3) pos += 2; + + if (tag == 256) { + result.width = data; + } else if (tag == 257) { + result.height = data; + } + + if (result.width > 0 && result.height > 0) { + return; + } + } + + offset = readUInt32(); + pos += offset; + } + }, + + AVI: async (path, result) => { + const buff = await read(path, 0, 144); + result.width = buff.readUInt32LE(64); + result.height = buff.readUInt32LE(68); + result.duration = ~~(buff.readUInt32LE(128) / buff.readUInt32LE(132) * buff.readUInt32LE(140)); + }, + + MP4: async (path, result) => { + return parser.MOV(path, result); + }, + + MOV: async (path, result, pos = 0) => { + const buff = await read(path, 0, 64000); + + while (pos < buff.length) { + let size = buff.readUInt32BE(pos); + let name = buff.toString('ascii', pos + 4, 4); + + if (name == 'mvhd') { + let scale = buff.readUInt32BE(pos + 20); + let duration = buff.readUInt32BE(pos + 24); + result.duration = ~~(duration / scale); + } + + if (name == 'tkhd') { + let m0 = buff.readUInt32BE(pos + 48); + let m4 = buff.readUInt32BE(pos + 64); + let w = buff.readUInt32BE(pos + 84); + let h = buff.readUInt32BE(pos + 88); + if (w > 0 && h > 0) { + result.width = w / m0; + result.height = h / m4; + return; + } + } + + if (name == 'moov' || name == 'trak') { + await parser.MOV(path, pos + 8); + } + + pos += size; + } + }, + + WEBM: async (path, result) => { + return parser.EBML(path, result); + }, + + MKV: async (path, result) => { + return parser.EBML(path, result); + }, + + EBML: async (path, result) => { + const containers = ['\x1a\x45\xdf\xa3', '\x18\x53\x80\x67', '\x15\x49\xa9\x66', '\x16\x54\xae\x6b', '\xae', '\xe0']; + const buff = await read(path, 0, 64000); + + // TODO parse EBML + }, + + OGV: async (path, result) => { + return parser.OGG(path, result); + }, + + OGG: async (path, result) => { + const buff = await read(apth, 0, 64000); + let pos = 0, vorbis; + + while (buff.toString('ascii', pos, pos + 4) == 'OggS') { + let version = buff[pos + 4]; + let b = buff[pos + 5]; + let continuation = !!(b & 0x01); + let bos = !!(b & 0x02); + let eos = !!(b & 0x04); + let position = Number(buff.readBigUInt64LE(pos + 6)); + let serial = buff.readUInt32LE(pos + 14); + let pageNumber = buff.readUInt32LE(pos + 18); + let checksum = buff.readUInt32LE(pos + 22); + let pageSegments = buff[path + 26]; + let lacing = buff.slice(pos + 27, pos + 27 + pageSegments); + let pageSize = lacing.reduce((p, v) => p + v, 0); + let start = pos + 27 + pageSegments; + let pageHeader = buff.slice(start, start + 7); + + if (pageHeader.compare(Buffer.from([0x01, 'v', 'o', 'r', 'b', 'i', 's']))) { + vorbis = { serial, sampleRate: buff.readUInt32LE(start + 12) }; + } + + if (pageHeader.compare(Buffer.from([0x80, 't', 'h', 'e', 'o', 'r', 'a']))) { + let version = buff.slice(start + 7, start + 10); + result.width = buff.readUInt16BE(start + 10) << 4; + result.height = buff.readUInt16BE(start + 12) << 4; + + if (version >= 0x030200) { + let width = buff.slice(start + 14, start + 17); + let height = buff.slice(start + 17, start + 20); + + if (width <= result.width && width > result.width - 16 && height <= result.height && height > result.height - 16) { + result.width = width; + result.height = height; + } + } + } + + if (eos && vorbis && serail == vorbis.serial) { + result.duration = ~~(position / vorbis.sampleRate); + } + + pos = start + pageSize; + } + }, + + WAV: async (path, result) => { + const buff = await read(path, 0, 32); + let size = buff.readUInt32LE(4); + let rate = buff.readUInt32LE(28); + result.duration = ~~(size / rate); + }, + + MP3: async (path, result) => { + const versions = [2.5, 0, 2, 1]; + const layers = [0, 3, 2, 1]; + const bitrates = [ + [ // version 2.5 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160], // layer 3 + [0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160], // layer 2 + [0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256] // layer 1 + ], + [ // reserved + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // reserved + ], + [ // version 2 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160], // layer 3 + [0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160], // layer 2 + [0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256] // layer 1 + ], + [ // version 1 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // reserved + [0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320], // layer 3 + [0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384], // layer 2 + [0,32,64,96,128,160,192,224,256,288,320,352,384,416,448] // layer 1 + ] + ] + const srates = [ + [11025, 12000, 8000, 0], // mpeg 2.5 + [ 0, 0, 0, 0], // reserved + [22050, 24000, 16000, 0], // mpeg 2 + [44100, 48000, 32000, 0] // mpeg 1 + ] + const tsamples = [ + [0, 576, 1152, 384], // mpeg 2.5 + [0, 0, 0, 0], // reserved + [0, 576, 1152, 384], // mpeg 2 + [0, 1152, 1152, 384] // mpeg 1 + ]; + const slotSizes = [0, 1, 1, 4]; + const modes = ['stereo', 'joint_stereo', 'dual_channel', 'mono']; + const buff = await read(path, 0, 64000); + + let duration = 0; + let count = 0; + let skip = 0; + let pos = 0; + + while (pos < buff.length) { + let start = pos; + + if (buff.toString('ascii', pos, 4) == 'TAG+') { + skip += 227; + pos += 227; + } else if (buff.toString('ascii', pos, 3) == 'TAG') { + skip += 128; + pos += 128; + } else if (buff.toString('ascii', pos, 3) == 'ID3') { + let bytes = buff.readUInt32BE(pos + 6); + let size = 10 + (bytes[0] << 21 | bytes[1] << 14 | bytes[2] << 7 | bytes[3]); + skip += size; + pos += size; + } else { + let hdr = buff.slice(pos, pos + 4); + + while (pos < buff.length && !(hdr[0] == 0xff && (hdr[1] & 0xe0) == 0xe0)) { + pos++; + hdr = buff.slice(pos, pos + 4); + } + + let ver = (hdr[1] & 0x18) >> 3; + let lyr = (hdr[1] & 0x06) >> 1; + let pad = (hdr[2] & 0x02) >> 1; + let brx = (hdr[2] & 0xf0) >> 4; + let srx = (hdr[2] & 0x0c) >> 2; + let mdx = (hdr[3] & 0xc0) >> 6; + + let version = versions[ver]; + let layer = layers[lyr]; + let bitrate = bitrates[ver][lyr][brx] * 1000; + let samprate = srates[ver][srx]; + let samples = tsamples[ver][lyr]; + let slotSize = slotSizes[lyr]; + let mode = modes[mdx]; + let fsize = ~~(((samples / 8 * bitrate) / samprate) + (pad ? slotSize : 0)); + + count++; + + if (count == 1) { + if (layer != 3) { + pos += 2; + } else { + if (mode != 'mono') { + if (version == 1) { + pos += 32; + } else { + pos += 17; + } + } else { + if (version == 1) { + pos += 17; + } else { + pos += 9; + } + } + } + + if (buff.toString('ascii', pos, pos + 4) == 'Xing' && (buff.readUInt32BE(pos + 4) & 0x0001) == 0x0001) { + let totalFrames = buff.readUInt32BE(pos + 8); + duration = totalFrames * samples / samprate; + break; + } + } + + if (fsize < 1) break; + + pos = start + fsize; + + duration += (samples / samprate); + } + } + + result.duration = ~~duration; + }, + + FLAC: async (path, result) => { + const buff = await read(path, 18, 8); + let rate = (buff[0] << 12) | (buff[1] << 4) | ((buff[2] & 0xf0) >> 4); + let size = ((buff[3] & 0x0f) << 32) | (buff[4] << 24) | (buff[5] << 16) | (buff[6] << 8) | buff[7]; + result.duration = ~~(size / rate); + }, + +}; + +async function detect(path) { + const buff = await read(path, 0, 12); + + if (buff.slice(0, 8).compare(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) { + return 'PNG'; + } + + if (buff.toString('ascii', 0, 3) == 'GIF') { + return 'GIF'; + } + + if (buff.toString('ascii', 0, 2) == 'BM') { + return 'BMP'; + } + + if (buff.slice(0, 2).compare(Buffer.from([0xff, 0xd8]))) { + return 'JPEG'; + } + + if (buff.toString('ascii', 0, 2) == 'II' && buff.readUInt16LE(2) == 42) { + return 'TIFF'; + } + + if (buff.toString('ascii', 0, 2) == 'MM' && buff.readUInt16BE(2) == 42) { + return 'TIFF'; + } + + if (buff.toString('ascii', 0, 4) == 'RIFF' && buff.toString('ascii', 8, 4) == 'AVI ') { + return 'AVI'; + } + + if (buff.toString('ascii', 4, 4) == 'ftyp') { + return 'MP4'; + } + + if (buff.toString('ascii', 4, 4) == 'moov') { + return 'MOV'; + } + + if (buff.slice(0, 4).compare(Buffer.from([0x1a, 0x45, 0xdf, 0xa3]))) { + // TODO detect MKV + + return 'EBML'; + } + + if (buff.toString('ascii', 0, 4) == 'OggS') { + // TODO detect OGV + + return 'OGG' + } + + if (buff.toString('ascii', 0, 4) == 'RIFF', buff.toString('ascii', 8, 4) == 'WAVE') { + return 'WAV'; + } + + if (buff.toString('ascii', 0, 3) == 'ID3' || (buf[0] == 0xff && (buff[1] & 0xe0))) { + return 'MP3'; + } + + if (buff.toString('ascii', 0, 4) == 'fLaC') { + return 'FLAC'; + } + + return null +} + +module.exports = { + + detect: async function(options) { + let path = this.parseRequired(options.path, 'string', 'metadata.detect: path is required.'); + + return detect(path); + }, + + isImage: async function(options) { + let path = this.parseRequired(options.path, 'string', 'metadata.isImage: path is required.'); + let type = await detect(path); + let cond = imageTypes.includes(type); + + if (cond) { + if (options.then) { + await this.exec(options.then, true); + } + } else if (options.else) { + await this.exec(options.else, true); + } + + return cond; + }, + + isVideo: async function(options) { + let path = this.parseRequired(options.path, 'string', 'metadata.isVideo: path is required.'); + let type = await detect(path); + let cond = videoTypes.includes(type); + + if (cond) { + if (options.then) { + await this.exec(options.then, true); + } + } else if (options.else) { + await this.exec(options.else, true); + } + + return cond; + }, + + isSound: async function(options) { + let path = this.parseRequired(options.path, 'string', 'metadata.isSound: path is required.'); + let type = await detect(path); + let cond = soundTypes.includes(type); + + if (cond) { + if (options.then) { + await this.exec(options.then, true); + } + } else if (options.else) { + await this.exec(options.else, true); + } + + return cond; + }, + + fileinfo: async function(options) { + let path = this.parseRequired(options.path, 'string', 'metadata.fileinfo: path is required.'); + let type = await detect(path); + let result = { type, width: null, height: null, duration: null }; + + if (parser[type]) { + await parser[type](path, result); + } + + return result; + }, + +}; \ No newline at end of file diff --git a/lib/modules/oauth.js b/lib/modules/oauth.js new file mode 100644 index 0000000..e9e94ee --- /dev/null +++ b/lib/modules/oauth.js @@ -0,0 +1,24 @@ +module.exports = { + + provider: async function(options, name) { + const oauth = await this.setOAuthProvider(name, options) + + return { + access_token: oauth.access_token, + refresh_token: oauth.refresh_token + }; + }, + + authorize: async function(options) { + const oauth = await this.getOAuthProvider(options.provider); + + return oauth.authorize(this.parse(options.scopes), this.parse(options.params)); + }, + + refresh: async function(options) { + const oauth = await this.getOAuthProvider(options.provider); + + return oauth.refreshToken(this.parse(options.refresh_token)); + }, + +}; \ No newline at end of file diff --git a/lib/modules/otp.js b/lib/modules/otp.js new file mode 100644 index 0000000..dcb53f3 --- /dev/null +++ b/lib/modules/otp.js @@ -0,0 +1,174 @@ +const crypto = require('crypto'); +const base32 = require('../core/base32'); + +const secretLength = { + 'sha1': 20, + 'sha224': 28, + 'sha256': 32, + 'sha384': 48, + 'sha512': 64, + 'sha3-224': 28, + 'sha3-256': 32, + 'sha3-384': 48, + 'sha3-512': 64, +}; + +const hotp = {}; // HOTP: An HMAC-Based One-Time Password Algorithm +const totp = {}; // TOTP: Time-Based One-Time Password Algorithm + +hotp.counter = 0n; +hotp.defaults = { + algorithm: 'sha1', + digits: 6, +}; + +hotp.generate = function (secret, opts) { + opts = { ...hotp.defaults, ...opts }; + const counter = opts.counter == null ? ++hotp.counter : opts.counter; + const buffer = Buffer.alloc(8); + buffer.writeBigUInt64BE(BigInt(counter)); + const hash = crypto.createHmac(opts.algorithm, secret).update(buffer.slice(0, secretLength[opts.algorithm])).digest(); + return truncate(hash, opts.digits); +}; + +hotp.validate = function (code, secret, opts) { + opts = { ...hotp.defaults, ...opts }; + if (opts.counter == null) opts.counter = hotp.counter; + if (code === hotp.generate(secret, opts)) return true; + if (typeof opts.window == 'number' && opts.window > 0) { + for (let n = 1; n < opts.window + 1; n++) { + if (code === hotp.generate(secret, { ...opts, counter: opts.counter - n })) { + return -n; + } + } + } + return false; +}; + +totp.defaults = { + ...hotp.defaults, + time: null, + period: 30, +}; + +totp.generate = function(secret, opts) { + opts = { ...totp.defaults, ...opts }; + const counter = ~~((opts.time || Date.now()) / 1000 / opts.period); + return hotp.generate(secret, { ...opts, counter }); +}; + +totp.validate = function(code, secret, opts) { + opts = { ...totp.defaults, ...opts }; + const counter = ~~((opts.time || Date.now()) / 1000 / opts.period); + return code === hotp.generate(secret, { ...opts, counter }); +}; + +function truncate(hash, digits) { + const offset = hash[hash.length-1] & 0xf; + const binary = (hash[offset] & 0x7f) << 24 + | (hash[offset+1] & 0xff) << 16 + | (hash[offset+2] & 0xff) << 8 + | (hash[offset+3] & 0xff); + const otp = binary % Math.pow(10, digits); + + return String(otp).padStart(digits, '0'); +}; + +module.exports = { + + hotpGenerage (options) { + const secret = this.parseRequired(options.secret, 'string', 'otp.hotpGenerate: secret is required.'); + const counter = this.parseOptional(options.counter, 'number', null); + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + return hotp.generate(secret, { counter, digits, algorithm }); + }, + + hotpValidate (options) { + const code = this.parseRequired(options.code, 'string', 'otp.hotpValidate: code is required.'); + const secret = this.parseRequired(options.secret, 'string', 'otp.hotpValidate: secret is required.'); + const counter = this.parseOptional(options.counter, 'number', null); + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + return hotp.validate(code, secret, { counter, digits, algorithm }); + }, + + totpGenerate (options) { + const secret = this.parseRequired(options.secret, 'string', 'otp.totpGenerate: secret is required.'); + const time = this.parseOptional(options.time, 'string', null); + const period = this.parseOptional(options.period, 'number', 30); // 30 | 60 + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + if (typeof time == 'string') time = +(new Date(time)); + + return totp.generate(secret, { time, period, digits, algorithm }); + }, + + totpValidate (options) { + const code = this.parseRequired(options.code, 'string', 'otp.totpValidate: code is required.'); + const secret = this.parseRequired(options.secret, 'string', 'otp.totpValidate: secret is required.'); + const time = this.parseOptional(options.time, 'string', null); + const period = this.parseOptional(options.period, 'number', 30); // 30 | 60 + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + if (typeof time == 'string') time = +(new Date(time)); + + return totp.validate(code, secret, { time, period, digits, algorithm }); + }, + + // https://git.coolaj86.com/coolaj86/browser-authenticator.js + // 20 cryptographically random binary bytes (160-bit key) + generateSecret (options) { + const size = this.parseOptional(options.size, 'number', 20); + return base32.encode(crypto.randomBytes(size), { padding: false }).toString(); + }, + + // generates a 6-digit (20-bit) decimal time-based token + generateToken (options) { + const secret = this.parseRequired(options.secret, 'string', 'otp.generateToken: secret is required.'); + const period = this.parseOptional(options.period, 'number', 30); // 30 | 60 + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + return totp.generate(base32.decode(secret), { period, digits, algorithm }); + }, + + // validates a time-based token within a +/- 30 second (90 seconds) window + //returns null on failure or an object such as { delta: 0 } on success + verifyToken (options) { + const code = this.parseRequired(options.code, 'string', 'otp.verifyToken: code is required.') + const secret = this.parseRequired(options.secret, 'string', 'otp.verifyToken: secret is required.'); + const period = this.parseOptional(options.period, 'number', 30); // 30 | 60 + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + return code === totp.generate(base32.decode(secret), { period, digits, algorithm, window: 1 }); + }, + + // generates an OTPAUTH:// scheme URI for QR Code generation. + // https://github.com/google/google-authenticator/wiki/Key-Uri-Format + generateOtpUri (options) { + const secret = this.parseRequired(options.secret, 'string', 'otp.generateTotpUri: secret is required.'); + const issuer = this.parseRequired(options.issuer, 'string', 'otp.generateTotpUri: issuer is required.'); + const account = this.parseRequired(options.account, 'string', 'otp.generateTotpUri: account is required.'); + const digits = this.parseOptional(options.digits, 'number', 6); // 6 | 8 + const period = this.parseOptional(options.period, 'number', 30); // 30 | 60 + const algorithm = this.parseOptional(options.algorithm, 'string', 'sha1'); // 'sha1' | 'sha224' | 'sha256' | 'sha384' | 'sha512' | 'sha3-224' | 'sha3-256' | 'sha3-384' | 'sha3-512' + + return `otpauth://totp/${encodeURI(issuer)}:${encodeURI(account)}?secret=${secret}&issuer=${encodeURIComponent(issuer)}&algorithm=${encodeURIComponent(algorithm.toUpperCase())}&digits=${digits}&period=${period}`; + }, + + generateQrCodeUrl (options) { + const data = this.parseRequired(options.data, 'string', 'otp.generateQrCodeUrl: data is required.'); + const size = this.parseOptional(options.size, 'number', 200); + const ecl = this.parseOptional(options.ecl, 'string', 'L'); // error_correction_level: L | M | Q | H + const margin = this.parseOptional(options.margin, 'number', 4); + + return `https://www.google.com/chart?cht=qr&chs=${size}x${size}&chl=${encodeURIComponent(data)}&chld=${ecl}|${margin}`; + }, + +}; \ No newline at end of file diff --git a/lib/modules/passkeys.js b/lib/modules/passkeys.js new file mode 100644 index 0000000..5215b99 --- /dev/null +++ b/lib/modules/passkeys.js @@ -0,0 +1,86 @@ +module.exports = { + + // rpName: User-visible, "friendly" website/service name + // rpID: Valid domain name (after `https://`) + // userID: User's website-specific unique ID (must be a string, do not use integer, user GUID instead) + // userName: User's website-specific username (email, etc...) + // userDisplayName: User's actual name + // timeout: How long (in ms) the user can take to complete attestation + // attestationType: Specific attestation statement + // excludeCredentials: Authenticators registered by the user so the user can't register the same credential multiple times + // supportedAlgorithmIDs Array of numeric COSE algorithm identifiers supported for attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms + async generateRegistrationOptions (options) { + const { generateRegistrationOptions } = require('@simplewebauthn/server'); + const rpName = this.parseRequired(options.rpName, 'string', 'passkeys.generateRegistrationOptions: rpName is required.'); + const rpID = this.parseRequired(options.rpID, 'string', 'passkeys.generateRegistrationOptions: rpID is required.'); + const userID = this.parseRequired(options.userID, 'string', 'passkeys.generateRegistrationOptions: userID is required.'); + const userName = this.parseRequired(options.userName, 'string', 'passkeys.generateRegistrationOptions: userName is required.'); + const userDisplayName = this.parseOptional(options.userDisplayName, 'string', ''); + const timeout = this.parseOptional(options.timeout, 'number', 60000); + const attestationType = this.parseOptional(options.attestationType, 'string', 'none'); // "direct" | "enterprise" | "indirect" | "none" + const excludeCredentials = this.parseOptional(options.excludeCredentials, 'object', []); // array[{id, transports}] + const supportedAlgorithmIDs = this.parseOptional(options.supportedAlgorithmIDs, 'object', [-8, -7, -257]) // array[number] (-8 requires Node 18 LTS) + // authenticator selection criteria (https://simplewebauthn.dev/docs/packages/server#1-generate-registration-options) + const residentKey = 'preferred'; // "discouraged" | "preferred" | "required" + const userVerification = 'preferred'; // "discouraged" | "preferred" | "required" + + return await generateRegistrationOptions({ + rpName, rpID, userID, userName, userDisplayName, + timeout, attestationType, excludeCredentials, + supportedAlgorithmIDs, authenticatorSelection: { + residentKey, userVerification, + }, + }); + }, + + // response: Response returned by **@simplewebauthn/browser**'s `startAuthentication()` + // expectedChallenge: The base64url-encoded `options.challenge` returned by `generateRegistrationOptions()` + // expectedOrigin: Website URL (or array of URLs) that the registration should have occurred on + // expectedRPID: RP ID (or array of IDs) that was specified in the registration options + async verifyRegistrationResponse (options) { + const { verifyRegistrationResponse } = require('@simplewebauthn/server'); + const response = this.parseRequired(options.response, 'object', 'passkeys.verifyRegistrationResponse: response is required.'); + const expectedChallenge = this.parseRequired(options.expectedChallenge, 'string', 'passkeys.verifyRegistrationResponse: expectedChallenge is required.'); + const expectedOrigin = this.parseRequired(options.expectedOrigin, 'string', 'passkeys.verifyRegistrationResponse: expectedOrigin is required.'); + const expectedRPID = this.parseRequired(options.expectedRPID, 'string', 'passkeys.verifyRegistrationResponse: expectedRPID is required.'); + + return await verifyRegistrationResponse({ + response, expectedChallenge, expectedOrigin, expectedRPID, + }); + }, + + // allowCredentials: Authenticators previously registered by the user, if any. If undefined the client will ask the user which credential they want to use + // timeout: How long (in ms) the user can take to complete authentication + // userVerification: Set to `'discouraged'` when asserting as part of a 2FA flow, otherwise set to `'preferred'` or `'required'` as desired. + // rpID: Valid domain name (after `https://`) + async generateAuthenticationOptions (options) { + const { generateAuthenticationOptions } = require('@simplewebauthn/server'); + const allowCredentials = this.parseOptional(options.allowCredentials, 'object', undefined); + const timeout = this.parseOptional(options.timeout, 'number', 60000); + const userVerification = this.parseOptional(options.userVerification, 'string', 'preferred'); // "discouraged" | "preferred" | "required" + const rpID = this.parseOptional(options.rpID, 'string', undefined); + + return await generateAuthenticationOptions({ + allowCredentials, timeout, userVerification, rpID, + }); + }, + + // response: Response returned by **@simplewebauthn/browser**'s `startAssertion()` + // expectedChallenge: The base64url-encoded `options.challenge` returned by `generateAuthenticationOptions()` + // expectedOrigin: Website URL (or array of URLs) that the registration should have occurred on + // expectedRPID: RP ID (or array of IDs) that was specified in the registration options + // authenticator: An internal {@link AuthenticatorDevice} matching the credential's ID + async verifyAuthenticationResponse (options) { + const { verifyAuthenticationResponse } = require('@simplewebauthn/server'); + const response = this.parseRequired(options.response, 'object', 'passkeys.verifyAuthenticationResponse: response is required.'); + const expectedChallenge = this.parseRequired(options.expectedChallenge, 'string', 'passkeys.verifyAuthenticationResponse: expectedChallenge is required.'); + const expectedOrigin = this.parseRequired(options.expectedOrigin, 'string', 'passkeys.verifyAuthenticationResponse: expectedOrigin is required.'); + const expectedRPID = this.parseRequired(options.expectedRPID, 'string', 'passkeys.verifyAuthenticationResponse: expectedRPID is required.'); + const authenticator = this.parseRequired(options.authenticator, 'object', 'passkeys.verifyAuthenticationResponse: authenticator is required.') + + return await verifyAuthenticationResponse({ + response, expectedChallenge, expectedOrigin, expectedRPID, authenticator, + }); + }, + +}; \ No newline at end of file diff --git a/lib/modules/ratelimit.js b/lib/modules/ratelimit.js new file mode 100644 index 0000000..273b40a --- /dev/null +++ b/lib/modules/ratelimit.js @@ -0,0 +1,84 @@ +module.exports = { + /** + * Setup a rate limiter instance + * + * @param {Object} options + * @param {number} options.points - Maximum number of points + * @param {number} options.duration - Duration in seconds + * @param {number} options.blockDuration - Duration in seconds to block the user + * @param {string} name - The name of the rate limiter instance + */ + setup: function (options, name) { + if (!name) throw new Error('rateLimit.setup has no name.'); + this.setRateLimiter(name, options); + }, + + /** + * Consume points from a rate limiter instance + * + * @typedef {Object} options + * @param {string} options.instance - The rate limiter instance name + * @param {number} [options.points=1] - The number of points to consume + * @param {number} [options.status=429] - The status code to return + * @param {string} [options.message=Too Many Requests] - The message to return + * @param {boolean} [options.throw=false] - Throw an error instead of sending a response + * + * @return {Promise} - Resolves if the points were consumed, rejects if the rate limit was exceeded and options.throw is true + */ + consume: function (options) { + options = options || {}; + options.points = options.points || 1; + options.status = options.status || 429; + options.message = options.message || 'Too Many Requests'; + + if (options.instance) { + const rateLimiter = this.getRateLimiter(options.instance); + + return rateLimiter.consume(this.req.ip, options.points).catch(() => { + if (options.throw) { + throw new Error(options.message); + } else { + if (this.req.is('json')) { + this.res.status(options.status).json({ error: options.message }); + } else { + this.res.status(options.status).send(options.message); + } + } + }); + } else if (this.req.app.rateLimiter) { + const config = require('../setup/config'); + let isPrivate = false; + let key = this.req.ip; + + if (this.req.app.privateRateLimiter) { + if (this.req.session && this.req.session[config.rateLimit.private.provider + 'Id']) { + isPrivate = true; + key = this.req.session[config.rateLimit.private.provider + 'Id']; + } + } + + const limit = isPrivate ? config.rateLimit.private.points : config.rateLimit.points; + const duration = isPrivate ? config.rateLimit.private.duration : config.rateLimit.duration; + this.req.app[isPrivate ? 'privateRateLimiter' : 'rateLimiter'].consume(key, points).then((rateLimiterRes) => { + const reset = Math.ceil(rateLimiterRes.msBeforeNext / 1000); + this.res.set('RateLimit-Policy', `${limit};w=${duration}`); + this.res.set('RateLimit', `limit=${limit}, remaining=${rateLimiterRes.remainingPoints}, reset=${reset}`); + next(); + }).catch((rateLimiterRes) => { + if (options.throw) { + throw new Error(options.message); + } else { + const reset = Math.ceil(rateLimiterRes.msBeforeNext / 1000); + this.res.set('RateLimit-Policy', `${limit};w=${duration}`); + this.res.set('RateLimit', `limit=${limit}, remaining=${rateLimiterRes.remainingPoints}, reset=${reset}`); + this.res.set('Retry-After', reset); + if (this.req.is('json')) { + this.res.status(options.status).json({ error: options.message }); + } else { + this.res.status(options.status).send(options.message); + } + } + }); + } + }, +}; diff --git a/lib/modules/recaptcha.js b/lib/modules/recaptcha.js new file mode 100644 index 0000000..2f5266f --- /dev/null +++ b/lib/modules/recaptcha.js @@ -0,0 +1,47 @@ +const querystring = require('querystring'); +const https = require('https'); + +exports.validate = function(options) { + const secret = this.parseRequired(options.secret, 'string', 'recaptcha.validate: secret is required.'); + const msg = this.parseOptional(options.msg, 'string', 'Recaptcha check failed.'); + + const response = this.req.body['g-recaptcha-response']; + const remoteip = this.req.ip; + const data = querystring.stringify({ secret, response, remoteip }); + + return new Promise((resolve, reject) => { + const req = https.request('https://www.google.com/recaptcha/api/siteverify', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': data.length + } + }, (res) => { + let body = ''; + + res.setEncoding('utf8'); + res.on('data', (chunk) => body += chunk); + res.on('end', () => { + if (res.statusCode >= 400) return reject(body); + + if (body.charCodeAt(0) === 0xFEFF) { + body = body.slice(1); + } + + body = JSON.parse(body); + + if (!body.success) { + this.res.status(400).json({ + form: { 'g-recaptcha-response': msg } + }); + } + + resolve(body); + }); + }); + + req.on('error', reject); + req.write(data); + req.end(); + }); +}; \ No newline at end of file diff --git a/lib/modules/redis.js b/lib/modules/redis.js new file mode 100644 index 0000000..143f0b7 --- /dev/null +++ b/lib/modules/redis.js @@ -0,0 +1,237 @@ +const client = global.redisClient; + +if (client) { + const { promisify } = require('util'); + const commands = {}; + + [ + 'append', 'decr', 'decrby', 'del', 'exists', 'get', 'getset', 'incr', 'incrby', + 'mget', 'set', 'setex', 'setnx', 'strlen', 'lindex', 'linsert', 'llen', 'lpop', + 'lpos', 'lpush', 'lpushx', 'lrange', 'lrem', 'lset', 'ltrim', 'rpop', 'rpush', + 'rpushx', 'copy', 'del', 'expire', 'expireat', 'keys', 'persist', 'pexpire', + 'pexpireat', 'pttl', 'randomkey', 'rename', 'renamenx', 'touch', 'ttl', 'type', + 'unlink' + ].forEach(command => { + commands[command] = promisify(client[command]).bind(client); + }); + + exports.append = function(options) { + options = this.parse(options); + return commands.append(options.key, options.value); + }; + + exports.decr = function(options) { + options = this.parse(options); + return commands.decr(options.key); + }; + + exports.decrby = function(options) { + options = this.parse(options); + return commands.decrby(options.key, options.decrement); + }; + + exports.del = function(options) { + options = this.parse(options); + return commands.del(options.key); + }; + + exports.exists = function(options) { + options = this.parse(options); + return commands.exists(options.key); + }; + + exports.get = function(options) { + options = this.parse(options); + return commands.get(options.key); + }; + + exports.getset = function(options) { + options = this.parse(options); + return commands.getset(options.key, options.value); + }; + + exports.incr = function(options) { + options = this.parse(options); + return commands.incr(options.key); + }; + + exports.incrby = function(options) { + options = this.parse(options); + return commands.incrby(options.key, options.increment); + }; + + exports.mget = function(options) { + options = this.parse(options); + return commands.mget(options.keys); + }; + + exports.set = function(options) { + options = this.parse(options); + return commands.set(options.key, options.value); + }; + + exports.setex = function(options) { + options = this.parse(options); + return commands.setex(options.key, options.seconds, options.value); + }; + + exports.setnx = function(options) { + options = this.parse(options); + return commands.setnx(options.key, options.value); + }; + + exports.strlen = function(options) { + options = this.parse(options); + return commands.strlen(options.key); + }; + + exports.lindex = function(options) { + options = this.parse(options); + return commands.lindex(options.key, options.index); + }; + + exports.linsert = function(options) { + options = this.parse(options); // position: BEFORE|AFTER + return commands.linsert(options.key, options.position, options.pivot, options.element); + }; + + exports.llen = function(options) { + options = this.parse(options); + return commands.llen(options.key); + }; + + exports.lpop = function(options) { + options = this.parse(options); + return commands.lpop(options.key, options.count); + }; + + exports.lpos = function(options) { + options = this.parse(options); + return commands.lpos(options.key, options.element); + }; + + exports.lpush = function(options) { + options = this.parse(options); + return commands.lpush(options.key, options.element); + }; + + exports.lpushx = function(options) { + options = this.parse(options); + return commands.lpushx(options.key, options.element); + }; + + exports.lrange = function(options) { + options = this.parse(options); + return commands.lrange(options.key, options.start, options.stop); + }; + + exports.lrem = function(options) { + options = this.parse(options); + return commands.lrem(options.key, options.count, options.element); + }; + + exports.lset = function(options) { + options = this.parse(options); + return commands.lset(options.key, options.index, options.element); + }; + + exports.ltrim = function(options) { + options = this.parse(options); + return commands.ltrim(options.key, options.start, options.stop); + }; + + exports.rpop = function(options) { + options = this.parse(options); + return commands.rpop(options.key, options.count); + }; + + exports.rpush = function(options) { + options = this.parse(options); + return commands.rpush(options.key, options.element); + }; + + exports.rpushx = function(options) { + options = this.parse(options); + return commands.rpushx(options.key, options.element); + }; + + exports.copy = function(options) { + options = this.parse(options); + return commands.copy(options.source, options.destination); + }; + + exports.del = function(options) { + options = this.parse(options); + return commands.del(options.key); + }; + + exports.expire = function(options) { + options = this.parse(options); + return commands.expire(options.key, options.seconds); + }; + + exports.expireat = function(options) { + options = this.parse(options); + return commands.expireat(options.key, options.timestamp); + }; + + exports.keys = function(options) { + options = this.parse(options); + return commands.keys(options.pattern); + }; + + exports.persist = function(options) { + options = this.parse(options); + return commands.persist(options.key); + }; + + exports.pexpire = function(options) { + options = this.parse(options); + return commands.pexpire(options.key, options.milliseconds); + }; + + exports.pexpireat = function(options) { + options = this.parse(options); + return commands.pexpireat(options.key, options.mstimestamp) + }; + + exports.pttl = function(options) { + options = this.parse(options); + return commands.pttl(options.key); + }; + + exports.randomkey = function(options) { + options = this.parse(options); + return commands.randomkey(); + }; + + exports.rename = function(options) { + options = this.parse(options); + return commands.rename(options.key, options.newkey); + }; + + exports.renamenx = function(options) { + options = this.parse(options); + return commands.renamenx(options.key, options.newkey); + }; + + exports.touch = function(options) { + options = this.parse(options); + return commands.touch(options.key); + }; + + exports.ttl = function(options) { + options = this.parse(options); + return commands.ttl(options.key); + }; + + exports.type = function(options) { + options = this.parse(options); + return commands.type(options.key); + }; + + exports.unlink = function(options) { + options = this.parse(options); + return commands.unlink(options.key); + }; +} \ No newline at end of file diff --git a/lib/modules/s3.js b/lib/modules/s3.js new file mode 100644 index 0000000..bebda02 --- /dev/null +++ b/lib/modules/s3.js @@ -0,0 +1,174 @@ +const fs = require('fs-extra'); +const mime = require('mime-types'); +const { join, basename, dirname } = require('path'); +const { toSystemPath } = require('../core/path'); + +module.exports = { + + provider: function (options, name) { + this.setS3Provider(name, options); + }, + + createBucket: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.createBucket: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.createBucket: bucket is required.'); + const ACL = this.parseOptional(options.acl, 'string', undefined); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + return s3.createBucket({ Bucket, ACL }); + }, + + listBuckets: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.listBuckets: provider is required.'); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + return s3.listBuckets({}); + }, + + deleteBucket: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.deleteBucket: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.deleteBucket: bucket is required.'); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + return s3.deleteBucket({ Bucket }); + }, + + listFiles: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.listFiles: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.listFiles: bucket is required.'); + const MaxKeys = this.parseOptional(options.maxKeys, 'number', undefined); + const Prefix = this.parseOptional(options.prefix, 'string', undefined); + const ContinuationToken = this.parseOptional(options.continuationToken, 'string', undefined); + const StartAfter = this.parseOptional(options.startAfter, 'string', undefined); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + return s3.listObjectsV2({ Bucket, MaxKeys, Prefix, ContinuationToken, StartAfter }); + }, + + putFile: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.uploadFile: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.uploadFile: bucket is required.'); + const path = toSystemPath(this.parseRequired(options.path, 'string', 's3.uploadFile: path is required.')); + const Key = this.parseRequired(options.key, 'string', 's3.uploadFile: key is required.'); + const ContentType = this.parseOptional(options.contentType, 'string', mime.lookup(Key) || 'application/octet-stream'); + const ContentDisposition = this.parseOptional(options.contentDisposition, 'string', undefined); + const ACL = this.parseOptional(options.acl, 'string', undefined); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + let Body = fs.createReadStream(path); + + const result = await s3.putObject({ Bucket, ACL, Key, ContentType, ContentDisposition, Body }); + + try { + const endpoint = await s3.config.endpoint(); + result.Location = `https://${Bucket}.${endpoint.hostname}/${Key}`; + } catch (e) {} + + + return result; + }, + + getFile: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.getFile: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.getFile: bucket is required.'); + const Key = this.parseRequired(options.key, 'string', 's3.getFile: key is required.'); + const path = toSystemPath(this.parseRequired(options.path, 'string', 's3.getFile: path is required.')); + const stripKeyPath = this.parseOptional(options.stripKeyPath, 'boolean', false); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + const file = Key; + if (stripKeyPath) file = basename(file); + const destination = join(path, file); + + await fs.ensureDir(dirname(destination)); + + const writer = fs.createWriteStream(destination); + + const { Body } = await s3.getObject({ Bucket, Key }); + + Body.pipe(writer); + + return new Promise((resolve, reject) => { + writer.on('finish', resolve); + writer.on('error', reject); + }); + }, + + deleteFile: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.deleteFile: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.deleteFile: bucket is required.'); + const Key = this.parseRequired(options.key, 'string', 's3.deleteFile: key is required.'); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + return s3.deleteObject({ Bucket, Key }); + }, + + downloadFile: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.downloadFile: profider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.downloadFile: bucket is required.'); + const Key = this.parseRequired(options.key, 'string', 's3.downloadFile: key is required.'); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + const data = await s3.headObject({ Bucket, Key }); + + this.res.set('Content-Length', data.ContentLength); + this.res.attachment(basename(Key)); + + const { Body } = await s3.getObject({ Bucket, Key }); + + Body.pipe(this.res); + + this.noOutput = true; + }, + + signDownloadUrl: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.signDownloadUrl: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.signDownloadUrl: bucket is required.'); + const Key = this.parseRequired(options.key, 'string', 's3.signDownloadUrl: key is required.'); + const expiresIn = this.parseOptional(options.expires, 'number', 300); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); + const { GetObjectCommand } = require('@aws-sdk/client-s3'); + const command = new GetObjectCommand({ Bucket, Key }); + + return getSignedUrl(s3, command, { expiresIn }); + }, + + signUploadUrl: async function (options) { + const provider = this.parseRequired(options.provider, 'string', 's3.signUploadUrl: provider is required.'); + const Bucket = this.parseRequired(options.bucket, 'string', 's3.signUploadUrl: bucket is required.'); + const Key = this.parseRequired(options.key, 'string', 's3.signUploadUrl: key is required.'); + const ContentType = this.parseOptional(options.contentType, 'string', mime.lookup(Key) || 'application/octet-stream'); + const expiresIn = this.parseOptional(options.expires, 'number', 300); + const ACL = this.parseOptional(options.acl, 'string', undefined); + const s3 = this.getS3Provider(provider); + + if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`); + + const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); + const { PutObjectCommand } = require('@aws-sdk/client-s3'); + const command = new PutObjectCommand({ Bucket, Key, ContentType, ACL }); + + return getSignedUrl(s3, command, { expiresIn }); + } + +}; \ No newline at end of file diff --git a/lib/modules/sockets.js b/lib/modules/sockets.js new file mode 100644 index 0000000..b03291e --- /dev/null +++ b/lib/modules/sockets.js @@ -0,0 +1,172 @@ +/** + * emits an event to all clients filtered by the given options + * @param {string} namespace - only to the specified namespace + * @param {string} room - emit only to the specified room (can be combined with namespace) + * @param {string} eventName - the name of the event + * @param {object} params - any parameters/data to send with the event + */ +exports.emit = function(options) { + if (this.io) { + options = this.parse(options); + + if (options.namespace) { + if (options.room) { + this.io.of(options.namespace).to(options.room).emit(options.eventName, options.params); + } else { + this.io.of(options.namespace).emit(options.eventName, options.params); + } + } else { + if (options.room) { + this.io.in(options.room).emit(options.eventName, options.params); + } else { + this.io.emit(options.eventName, options.params); + } + } + } +}; + +/** + * emit an event to all clients except the sender + * @param {string} room - broadcast in a specific room + * @param {string} eventName - the name of the event + * @param {object} params - the parameters/data to send with the event + */ +exports.broadcast = function(options) { + if (this.socket) { + options = this.parse(options); + + if (options.room) { + this.socket.to(options.room).emit(options.eventName, options.params); + } else { + this.socket.broadcast.emit(options.eventName, options.params); + } + } +}; + +/** + * emit an event to client and wait for an answer + * @param {string} room - broadcast in a specific room + * @param {string} eventName - the name of the event + * @param {object} params - the parameters/data to send with the event + */ +exports.request = function(options) { + if (this.socket) { + options = this.parse(options); + + return new Promise((resolve) => { + this.socket.emit(options.eventName, options.params, resolve); + }); + } + + return null; +}; + +/** + * send a private meessage to a socket + * @param {string} socketId - the socket id to send the message to + * @param {string} eventName - the name of the event + * @param {object} params - the parameters/data to send with the event + */ +exports.message = function(options) { + if (this.io) { + options = this.parse(options); + + this.io.to(options.socketId).emit(options.eventName, options.params); + } +}; + +/** + * special serverconnect refresh broadcast + * @param {string} action - the action that require refresh + */ +exports.refresh = async function(options) { + if (this.io) { + options = this.parse(options); + + // Do we have a global redis client? + if (global.redisClient) { + try { // ignore any errors here + let wsKeys = await global.redisClient.keys('ws:' + options.action + ':*'); + if (wsKeys.length) await global.redisClient.del(wsKeys); + let scKeys = await global.redisClient.keys('erc:' + '/api/' + options.action + '*'); + if (scKeys.length) await global.redisClient.del(scKeys); + } catch (e) { + console.error(e); + } + } + + this.io.of('/api').emit(options.action, options.params); + } +}; + +/** + * let current client join a room + * @param {string} room - the room to join + */ +exports.join = function(options) { + if (this.socket) { + this.socket.join(this.parse(options.room)); + } +}; + +/** + * let current client leave a room + * @param {string} room - the room to leave + */ +exports.leave = function(options) { + if (this.socket) { + this.socket.leave(this.parse(options.room)); + } +}; + +/** + * get the socket id of the current client + */ +exports.identify = function(options) { + return this.socket ? this.socket.id : null; +}; + +/** + * get the rooms the current client has joined + */ +exports.rooms = function(options) { + return this.socket ? Array.from(this.socket.rooms) : []; +}; + +/** + * get all the rooms + * @param {string} namespace - the namespace + */ +exports.allRooms = async function(options) { + if (this.io) { + let adapter = io.of(options.namespace || '/').adapter; + + if (typeof adapter.allRooms == 'function') { + return Array.from(await adapter.allRooms()); + } else if (adapter.rooms) { + return Array.from(adapter.rooms.keys()); + } + } + + return []; +}; + +/** + * get all the connected sockets + * @param {string} namespace - the namespace + * @param {string} room - return only clients in a specific room + */ +exports.allSockets = async function(options) { + if (this.io) { + options = this.parse(options); + + if (options.room) { + return Array.from(await this.io.of(options.namespace || '/').in(options.room).allSockets()); + } else { + return Array.from(await this.io.of(options.namespace || '/').allSockets()); + } + } + + return []; +}; + diff --git a/lib/modules/stripe.js b/lib/modules/stripe.js new file mode 100644 index 0000000..6de3fa0 --- /dev/null +++ b/lib/modules/stripe.js @@ -0,0 +1,6400 @@ + +const config = require('../setup/config'); +const Stripe = require('stripe'); + +const stripe = Stripe(config.stripe.secretKey); + +function convertExtraOptions(__extra) { + var extraOptions = null; + if (__extra && typeof __extra == "object" && Object.keys(__extra).length) { + extraOptions = {}; + for (var option in __extra) { + extraOptions[toCamelCase(option)] = __extra[option]; + } + } + return extraOptions; +} + +function toCamelCase(str) { + if (str == null) return str; + return String(str) + .toLowerCase() + .replace(/[\s_]+(\S)/g, function(a, b) { + return b.toUpperCase() + }); +} + + +// /v1/account_links - post +exports.createAccountLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.refresh_url && !options.refresh_url.includes('://')) { + options.refresh_url = this.parse('{{$_SERVER.BASE_URL}}') + options.refresh_url; + } + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.accountLinks.create(options,__extra); +}; + +// /v1/account_sessions - post +exports.createAccountSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.accountSessions.create(options,__extra); +}; + +// /v1/accounts - get +exports.listAccounts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.accounts.list(options,__extra); +}; + +// /v1/accounts - post +exports.createAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.accounts.create(options,__extra); +}; + +// /v1/accounts/{account} - delete +exports.deleteAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.deleteAccount: account is required.'); + delete options.account; + return stripe.accounts.del(account, options,__extra); +}; + +// /v1/accounts/{account} - get +exports.retrieveAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.retrieveAccount: account is required.'); + delete options.account; + return stripe.accounts.retrieve(account, options,__extra); +}; + +// /v1/accounts/{account} - post +exports.updateAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.updateAccount: account is required.'); + delete options.account; + return stripe.accounts.update(account, options,__extra); +}; + +// /v1/accounts/{account}/capabilities - get +exports.listAccountCapabilities = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.listAccountCapabilities: account is required.'); + delete options.account; + return stripe.accounts.listCapabilities(account, options,__extra); +}; + +// /v1/accounts/{account}/capabilities/{capability} - get +exports.retrieveAccountCapabilitie = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.retrieveAccountCapabilitie: account is required.'); + delete options.account; + const capability = this.parseRequired(options.capability, 'string', 'stripe.retrieveAccountCapabilitie: capability is required.'); + delete options.capability; + return stripe.accounts.retrieveCapabilitie(account, capability, options,__extra); +}; + +// /v1/accounts/{account}/capabilities/{capability} - post +exports.updateAccountCapabilitie = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.updateAccountCapabilitie: account is required.'); + delete options.account; + const capability = this.parseRequired(options.capability, 'string', 'stripe.updateAccountCapabilitie: capability is required.'); + delete options.capability; + return stripe.accounts.updateCapabilitie(account, capability, options,__extra); +}; + +// /v1/accounts/{account}/external_accounts - get +exports.listAccountExternalAccounts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.listAccountExternalAccounts: account is required.'); + delete options.account; + return stripe.accounts.listExternalAccounts(account, options,__extra); +}; + +// /v1/accounts/{account}/external_accounts - post +exports.createAccountExternalAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.createAccountExternalAccount: account is required.'); + delete options.account; + return stripe.accounts.createExternalAccount(account, options,__extra); +}; + +// /v1/accounts/{account}/external_accounts/{id} - delete +exports.deleteAccountExternalAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.deleteAccountExternalAccount: account is required.'); + delete options.account; + const id = this.parseRequired(options.id, 'string', 'stripe.deleteAccountExternalAccount: id is required.'); + delete options.id; + return stripe.accounts.deleteExternalAccount(account, id, options,__extra); +}; + +// /v1/accounts/{account}/external_accounts/{id} - get +exports.retrieveAccountExternalAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.retrieveAccountExternalAccount: account is required.'); + delete options.account; + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveAccountExternalAccount: id is required.'); + delete options.id; + return stripe.accounts.retrieveExternalAccount(account, id, options,__extra); +}; + +// /v1/accounts/{account}/external_accounts/{id} - post +exports.updateAccountExternalAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.updateAccountExternalAccount: account is required.'); + delete options.account; + const id = this.parseRequired(options.id, 'string', 'stripe.updateAccountExternalAccount: id is required.'); + delete options.id; + return stripe.accounts.updateExternalAccount(account, id, options,__extra); +}; + +// /v1/accounts/{account}/login_links - post +exports.createAccountLoginLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.createAccountLoginLink: account is required.'); + delete options.account; + return stripe.accounts.createLoginLink(account, options,__extra); +}; + +// /v1/accounts/{account}/persons - get +exports.listAccountPersons = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.listAccountPersons: account is required.'); + delete options.account; + return stripe.accounts.listPersons(account, options,__extra); +}; + +// /v1/accounts/{account}/persons - post +exports.createAccountPerson = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.createAccountPerson: account is required.'); + delete options.account; + return stripe.accounts.createPerson(account, options,__extra); +}; + +// /v1/accounts/{account}/persons/{person} - delete +exports.deleteAccountPerson = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.deleteAccountPerson: account is required.'); + delete options.account; + const person = this.parseRequired(options.person, 'string', 'stripe.deleteAccountPerson: person is required.'); + delete options.person; + return stripe.accounts.deletePerson(account, person, options,__extra); +}; + +// /v1/accounts/{account}/persons/{person} - get +exports.retrieveAccountPerson = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.retrieveAccountPerson: account is required.'); + delete options.account; + const person = this.parseRequired(options.person, 'string', 'stripe.retrieveAccountPerson: person is required.'); + delete options.person; + return stripe.accounts.retrievePerson(account, person, options,__extra); +}; + +// /v1/accounts/{account}/persons/{person} - post +exports.updateAccountPerson = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.updateAccountPerson: account is required.'); + delete options.account; + const person = this.parseRequired(options.person, 'string', 'stripe.updateAccountPerson: person is required.'); + delete options.person; + return stripe.accounts.updatePerson(account, person, options,__extra); +}; + +// /v1/accounts/{account}/reject - post +exports.rejectAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.rejectAccount: account is required.'); + delete options.account; + return stripe.accounts.reject(account, options,__extra); +}; + +// /v1/apple_pay/domains - get +exports.listApplePayDomains = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.applePay.domains.list(options,__extra); +}; + +// /v1/apple_pay/domains - post +exports.createApplePayDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.applePay.domains.create(options,__extra); +}; + +// /v1/apple_pay/domains/{domain} - delete +exports.deleteApplePayDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const domain = this.parseRequired(options.domain, 'string', 'stripe.deleteApplePayDomain: domain is required.'); + delete options.domain; + return stripe.applePay.domains.del(domain, options,__extra); +}; + +// /v1/apple_pay/domains/{domain} - get +exports.retrieveApplePayDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const domain = this.parseRequired(options.domain, 'string', 'stripe.retrieveApplePayDomain: domain is required.'); + delete options.domain; + return stripe.applePay.domains.retrieve(domain, options,__extra); +}; + +// /v1/application_fees - get +exports.listApplicationFees = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.applicationFees.list(options,__extra); +}; + +// /v1/application_fees/{fee}/refunds/{id} - get +exports.retrieveApplicationFeeRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const fee = this.parseRequired(options.fee, 'string', 'stripe.retrieveApplicationFeeRefund: fee is required.'); + delete options.fee; + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveApplicationFeeRefund: id is required.'); + delete options.id; + return stripe.applicationFees.retrieveRefund(fee, id, options,__extra); +}; + +// /v1/application_fees/{fee}/refunds/{id} - post +exports.updateApplicationFeeRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const fee = this.parseRequired(options.fee, 'string', 'stripe.updateApplicationFeeRefund: fee is required.'); + delete options.fee; + const id = this.parseRequired(options.id, 'string', 'stripe.updateApplicationFeeRefund: id is required.'); + delete options.id; + return stripe.applicationFees.updateRefund(fee, id, options,__extra); +}; + +// /v1/application_fees/{id} - get +exports.retrieveApplicationFee = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveApplicationFee: id is required.'); + delete options.id; + return stripe.applicationFees.retrieve(id, options,__extra); +}; + +// /v1/application_fees/{id}/refunds - get +exports.listApplicationFeeRefunds = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.listApplicationFeeRefunds: id is required.'); + delete options.id; + return stripe.applicationFees.listRefunds(id, options,__extra); +}; + +// /v1/application_fees/{id}/refunds - post +exports.createApplicationFeeRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.createApplicationFeeRefund: id is required.'); + delete options.id; + return stripe.applicationFees.createRefund(id, options,__extra); +}; + +// /v1/apps/secrets - get +exports.listAppsSecrets = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const scope = this.parseRequired(options.scope, 'string', 'stripe.listAppsSecrets: scope is required.'); + return stripe.apps.secrets.list(options,__extra); +}; + +// /v1/apps/secrets - post +exports.createAppsSecret = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.apps.secrets.create(options,__extra); +}; + +// /v1/apps/secrets/delete - post +exports.createAppsSecretsDelete = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.apps.secrets.delete.create(options,__extra); +}; + +// /v1/apps/secrets/find - get +exports.retrieveAppsSecretsFind = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const name = this.parseRequired(options.name, 'string', 'stripe.retrieveAppsSecretsFind: name is required.'); + const scope = this.parseRequired(options.scope, 'string', 'stripe.retrieveAppsSecretsFind: scope is required.'); + return stripe.apps.secrets.find.retrieve(options,__extra); +}; + +// /v1/balance - get +exports.retrieveBalance = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.balance.retrieve(options,__extra); +}; + +// /v1/balance_transactions - get +exports.listBalanceTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.balanceTransactions.list(options,__extra); +}; + +// /v1/balance_transactions/{id} - get +exports.retrieveBalanceTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveBalanceTransaction: id is required.'); + delete options.id; + return stripe.balanceTransactions.retrieve(id, options,__extra); +}; + +// /v1/billing/meter_event_adjustments - post +exports.createBillingMeterEventAdjustment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.billing.meterEventAdjustments.create(options,__extra); +}; + +// /v1/billing/meter_events - post +exports.createBillingMeterEvent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.billing.meterEvents.create(options,__extra); +}; + +// /v1/billing/meters - get +exports.listBillingMeters = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.billing.meters.list(options,__extra); +}; + +// /v1/billing/meters - post +exports.createBillingMeter = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.billing.meters.create(options,__extra); +}; + +// /v1/billing/meters/{id} - get +exports.retrieveBillingMeter = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveBillingMeter: id is required.'); + delete options.id; + return stripe.billing.meters.retrieve(id, options,__extra); +}; + +// /v1/billing/meters/{id} - post +exports.updateBillingMeter = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.updateBillingMeter: id is required.'); + delete options.id; + return stripe.billing.meters.update(id, options,__extra); +}; + +// /v1/billing/meters/{id}/deactivate - post +exports.deactivateBillingMeter = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.deactivateBillingMeter: id is required.'); + delete options.id; + return stripe.billing.meters.deactivate(id, options,__extra); +}; + +// /v1/billing/meters/{id}/event_summaries - get +exports.listBillingMeterEventSummaries = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listBillingMeterEventSummaries: customer is required.'); + const end_time = this.parseRequired(options.end_time, 'string', 'stripe.listBillingMeterEventSummaries: end_time is required.'); + const id = this.parseRequired(options.id, 'string', 'stripe.listBillingMeterEventSummaries: id is required.'); + delete options.id; + const start_time = this.parseRequired(options.start_time, 'string', 'stripe.listBillingMeterEventSummaries: start_time is required.'); + return stripe.billing.meters.listEventSummaries(id, options,__extra); +}; + +// /v1/billing/meters/{id}/reactivate - post +exports.reactivateBillingMeter = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.reactivateBillingMeter: id is required.'); + delete options.id; + return stripe.billing.meters.reactivate(id, options,__extra); +}; + +// /v1/billing_portal/configurations - get +exports.listBillingPortalConfigurations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.billingPortal.configurations.list(options,__extra); +}; + +// /v1/billing_portal/configurations - post +exports.createBillingPortalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.default_return_url && !options.default_return_url.includes('://')) { + options.default_return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.default_return_url; + } + return stripe.billingPortal.configurations.create(options,__extra); +}; + +// /v1/billing_portal/configurations/{configuration} - get +exports.retrieveBillingPortalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.retrieveBillingPortalConfiguration: configuration is required.'); + delete options.configuration; + return stripe.billingPortal.configurations.retrieve(configuration, options,__extra); +}; + +// /v1/billing_portal/configurations/{configuration} - post +exports.updateBillingPortalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.updateBillingPortalConfiguration: configuration is required.'); + delete options.configuration; + if (options.default_return_url && !options.default_return_url.includes('://')) { + options.default_return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.default_return_url; + } + return stripe.billingPortal.configurations.update(configuration, options,__extra); +}; + +// /v1/billing_portal/sessions - post +exports.createBillingPortalSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.billingPortal.sessions.create(options,__extra); +}; + +// /v1/charges - get +exports.listCharges = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.charges.list(options,__extra); +}; + +// /v1/charges - post +exports.createCharge = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.charges.create(options,__extra); +}; + +// /v1/charges/search - get +exports.searchCharges = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchCharges: query is required.'); + return stripe.charges.search(options,__extra); +}; + +// /v1/charges/{charge} - get +exports.retrieveCharge = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const charge = this.parseRequired(options.charge, 'string', 'stripe.retrieveCharge: charge is required.'); + delete options.charge; + return stripe.charges.retrieve(charge, options,__extra); +}; + +// /v1/charges/{charge} - post +exports.updateCharge = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const charge = this.parseRequired(options.charge, 'string', 'stripe.updateCharge: charge is required.'); + delete options.charge; + return stripe.charges.update(charge, options,__extra); +}; + +// /v1/charges/{charge}/capture - post +exports.captureCharge = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const charge = this.parseRequired(options.charge, 'string', 'stripe.captureCharge: charge is required.'); + delete options.charge; + return stripe.charges.capture(charge, options,__extra); +}; + +// /v1/charges/{charge}/refunds - get +exports.listChargeRefunds = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const charge = this.parseRequired(options.charge, 'string', 'stripe.listChargeRefunds: charge is required.'); + delete options.charge; + return stripe.charges.listRefunds(charge, options,__extra); +}; + +// /v1/charges/{charge}/refunds/{refund} - get +exports.retrieveChargeRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const charge = this.parseRequired(options.charge, 'string', 'stripe.retrieveChargeRefund: charge is required.'); + delete options.charge; + const refund = this.parseRequired(options.refund, 'string', 'stripe.retrieveChargeRefund: refund is required.'); + delete options.refund; + return stripe.charges.retrieveRefund(charge, refund, options,__extra); +}; + +// /v1/checkout/sessions - get +exports.listCheckoutSessions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.checkout.sessions.list(options,__extra); +}; + +// /v1/checkout/sessions - post +exports.createCheckoutSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.cancel_url && !options.cancel_url.includes('://')) { + options.cancel_url = this.parse('{{$_SERVER.BASE_URL}}') + options.cancel_url; + } + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + if (options.success_url && !options.success_url.includes('://')) { + options.success_url = this.parse('{{$_SERVER.BASE_URL}}') + options.success_url; + } + if (options && options.lineItemsType) { + if (options.lineItemsType == 'customList' || options.lineItemsType == 'customRef') { + if (options.line_items) { + if (!Array.isArray(options.line_items)) { + if (typeof options.line_items == "object") { + options.line_items = [options.line_items]; + } else { + throw new Error('createCheckoutSession: line_items is not an array or object for references'); + } + } else if (!options.line_items || options.line_items.length == 0) { + throw new Error('createCheckoutSession: line_items is empty!'); + } + } else { + throw new Error('createCheckoutSession: line_items is not set!'); + } + options.line_items = options.line_items.map(item => { + return { + price_data: { + currency: item.currency ? item.currency : 'USD', + product_data: { + name: item.title, + }, + unit_amount_decimal: item.amount, + }, + quantity: item.quantity ? item.quantity : 1, + } + }) + } + delete options.lineItemsType; + } else { + if (options.line_items) { + if (Array.isArray(options.line_items)) { + if (options.line_items.length == 0) { + throw new Error('createCheckoutSession: line_items is empty!'); + } + options.line_items = options.line_items.map(item => { + return { + price: item.price, + quantity: item.quantity ? item.quantity : 1 + } + }) + } else if (typeof options.line_items == "object") { + options.line_items = [options.line_items] + } else if (typeof options.line_items == "string") { + options.line_items = [{price: options.line_items, quantity: 1}] + } + } + } + + return stripe.checkout.sessions.create(options,__extra); +}; + +// /v1/checkout/sessions/{session} - get +exports.retrieveCheckoutSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.retrieveCheckoutSession: session is required.'); + delete options.session; + return stripe.checkout.sessions.retrieve(session, options,__extra); +}; + +// /v1/checkout/sessions/{session}/expire - post +exports.expireCheckoutSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.expireCheckoutSession: session is required.'); + delete options.session; + return stripe.checkout.sessions.expire(session, options,__extra); +}; + +// /v1/checkout/sessions/{session}/line_items - get +exports.listCheckoutSessionLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.listCheckoutSessionLineItems: session is required.'); + delete options.session; + return stripe.checkout.sessions.listLineItems(session, options,__extra); +}; + +// /v1/climate/orders - get +exports.listClimateOrders = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.climate.orders.list(options,__extra); +}; + +// /v1/climate/orders - post +exports.createClimateOrder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.climate.orders.create(options,__extra); +}; + +// /v1/climate/orders/{order} - get +exports.retrieveClimateOrder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const order = this.parseRequired(options.order, 'string', 'stripe.retrieveClimateOrder: order is required.'); + delete options.order; + return stripe.climate.orders.retrieve(order, options,__extra); +}; + +// /v1/climate/orders/{order} - post +exports.updateClimateOrder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const order = this.parseRequired(options.order, 'string', 'stripe.updateClimateOrder: order is required.'); + delete options.order; + return stripe.climate.orders.update(order, options,__extra); +}; + +// /v1/climate/orders/{order}/cancel - post +exports.cancelClimateOrder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const order = this.parseRequired(options.order, 'string', 'stripe.cancelClimateOrder: order is required.'); + delete options.order; + return stripe.climate.orders.cancel(order, options,__extra); +}; + +// /v1/climate/products - get +exports.listClimateProducts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.climate.products.list(options,__extra); +}; + +// /v1/climate/products/{product} - get +exports.retrieveClimateProduct = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const product = this.parseRequired(options.product, 'string', 'stripe.retrieveClimateProduct: product is required.'); + delete options.product; + return stripe.climate.products.retrieve(product, options,__extra); +}; + +// /v1/climate/suppliers - get +exports.listClimateSuppliers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.climate.suppliers.list(options,__extra); +}; + +// /v1/climate/suppliers/{supplier} - get +exports.retrieveClimateSupplier = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const supplier = this.parseRequired(options.supplier, 'string', 'stripe.retrieveClimateSupplier: supplier is required.'); + delete options.supplier; + return stripe.climate.suppliers.retrieve(supplier, options,__extra); +}; + +// /v1/confirmation_tokens/{confirmation_token} - get +exports.retrieveConfirmationToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const confirmation_token = this.parseRequired(options.confirmation_token, 'string', 'stripe.retrieveConfirmationToken: confirmation_token is required.'); + delete options.confirmation_token; + return stripe.confirmationTokens.retrieve(confirmation_token, options,__extra); +}; + +// /v1/country_specs - get +exports.listCountrySpecs = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.countrySpecs.list(options,__extra); +}; + +// /v1/country_specs/{country} - get +exports.retrieveCountrySpec = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const country = this.parseRequired(options.country, 'string', 'stripe.retrieveCountrySpec: country is required.'); + delete options.country; + return stripe.countrySpecs.retrieve(country, options,__extra); +}; + +// /v1/coupons - get +exports.listCoupons = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.coupons.list(options,__extra); +}; + +// /v1/coupons - post +exports.createCoupon = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.coupons.create(options,__extra); +}; + +// /v1/coupons/{coupon} - delete +exports.deleteCoupon = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const coupon = this.parseRequired(options.coupon, 'string', 'stripe.deleteCoupon: coupon is required.'); + delete options.coupon; + return stripe.coupons.del(coupon, options,__extra); +}; + +// /v1/coupons/{coupon} - get +exports.retrieveCoupon = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const coupon = this.parseRequired(options.coupon, 'string', 'stripe.retrieveCoupon: coupon is required.'); + delete options.coupon; + return stripe.coupons.retrieve(coupon, options,__extra); +}; + +// /v1/coupons/{coupon} - post +exports.updateCoupon = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const coupon = this.parseRequired(options.coupon, 'string', 'stripe.updateCoupon: coupon is required.'); + delete options.coupon; + return stripe.coupons.update(coupon, options,__extra); +}; + +// /v1/credit_notes - get +exports.listCreditNotes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.creditNotes.list(options,__extra); +}; + +// /v1/credit_notes - post +exports.createCreditNote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.creditNotes.create(options,__extra); +}; + +// /v1/credit_notes/preview - get +exports.retrieveCreditNotesPreview = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.retrieveCreditNotesPreview: invoice is required.'); + return stripe.creditNotes.preview(options,__extra); +}; + +// /v1/credit_notes/preview/lines - get +exports.listCreditNotesPreviewLines = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.listCreditNotesPreviewLines: invoice is required.'); + return stripe.creditNotes.listPreviewLineItems(options,__extra); +}; + +// /v1/credit_notes/{credit_note}/lines - get +exports.listCreditNoteLines = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const credit_note = this.parseRequired(options.credit_note, 'string', 'stripe.listCreditNoteLines: credit_note is required.'); + delete options.credit_note; + return stripe.creditNotes.listLines(credit_note, options,__extra); +}; + +// /v1/credit_notes/{id} - get +exports.retrieveCreditNote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveCreditNote: id is required.'); + delete options.id; + return stripe.creditNotes.retrieve(id, options,__extra); +}; + +// /v1/credit_notes/{id} - post +exports.updateCreditNote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.updateCreditNote: id is required.'); + delete options.id; + return stripe.creditNotes.update(id, options,__extra); +}; + +// /v1/credit_notes/{id}/void - post +exports.voidCreditNote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.voidCreditNote: id is required.'); + delete options.id; + return stripe.creditNotes.void(id, options,__extra); +}; + +// /v1/customer_sessions - post +exports.createCustomerSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.customerSessions.create(options,__extra); +}; + +// /v1/customers - get +exports.listCustomers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.customers.list(options,__extra); +}; + +// /v1/customers - post +exports.createCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.customers.create(options,__extra); +}; + +// /v1/customers/search - get +exports.searchCustomers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchCustomers: query is required.'); + return stripe.customers.search(options,__extra); +}; + +// /v1/customers/{customer} - delete +exports.deleteCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.deleteCustomer: customer is required.'); + delete options.customer; + return stripe.customers.del(customer, options,__extra); +}; + +// /v1/customers/{customer} - get +exports.retrieveCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomer: customer is required.'); + delete options.customer; + return stripe.customers.retrieve(customer, options,__extra); +}; + +// /v1/customers/{customer} - post +exports.updateCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.updateCustomer: customer is required.'); + delete options.customer; + return stripe.customers.update(customer, options,__extra); +}; + +// /v1/customers/{customer}/balance_transactions - get +exports.listCustomerBalanceTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listCustomerBalanceTransactions: customer is required.'); + delete options.customer; + return stripe.customers.listBalanceTransactions(customer, options,__extra); +}; + +// /v1/customers/{customer}/balance_transactions - post +exports.createCustomerBalanceTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.createCustomerBalanceTransaction: customer is required.'); + delete options.customer; + return stripe.customers.createBalanceTransaction(customer, options,__extra); +}; + +// /v1/customers/{customer}/balance_transactions/{transaction} - get +exports.retrieveCustomerBalanceTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerBalanceTransaction: customer is required.'); + delete options.customer; + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.retrieveCustomerBalanceTransaction: transaction is required.'); + delete options.transaction; + return stripe.customers.retrieveBalanceTransaction(customer, transaction, options,__extra); +}; + +// /v1/customers/{customer}/balance_transactions/{transaction} - post +exports.updateCustomerBalanceTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.updateCustomerBalanceTransaction: customer is required.'); + delete options.customer; + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.updateCustomerBalanceTransaction: transaction is required.'); + delete options.transaction; + return stripe.customers.updateBalanceTransaction(customer, transaction, options,__extra); +}; + +// /v1/customers/{customer}/cash_balance - get +exports.retrieveCustomerCashBalance = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerCashBalance: customer is required.'); + delete options.customer; + return stripe.customers.retrieveCashBalance(customer, options,__extra); +}; + +// /v1/customers/{customer}/cash_balance - post +exports.cashBalanceCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.cashBalanceCustomer: customer is required.'); + delete options.customer; + return stripe.customers.cashBalance(customer, options,__extra); +}; + +// /v1/customers/{customer}/cash_balance_transactions - get +exports.listCustomerCashBalanceTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listCustomerCashBalanceTransactions: customer is required.'); + delete options.customer; + return stripe.customers.listCashBalanceTransactions(customer, options,__extra); +}; + +// /v1/customers/{customer}/cash_balance_transactions/{transaction} - get +exports.retrieveCustomerCashBalanceTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerCashBalanceTransaction: customer is required.'); + delete options.customer; + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.retrieveCustomerCashBalanceTransaction: transaction is required.'); + delete options.transaction; + return stripe.customers.retrieveCashBalanceTransaction(customer, transaction, options,__extra); +}; + +// /v1/customers/{customer}/discount - delete +exports.deleteCustomerDiscount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.deleteCustomerDiscount: customer is required.'); + delete options.customer; + return stripe.customers.deleteDiscount(customer, options,__extra); +}; + +// /v1/customers/{customer}/funding_instructions - post +exports.createCustomerFundingInstruction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.createCustomerFundingInstruction: customer is required.'); + delete options.customer; + return stripe.customers.createFundingInstruction(customer, options,__extra); +}; + +// /v1/customers/{customer}/payment_methods - get +exports.listCustomerPaymentMethods = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listCustomerPaymentMethods: customer is required.'); + delete options.customer; + return stripe.customers.listPaymentMethods(customer, options,__extra); +}; + +// /v1/customers/{customer}/payment_methods/{payment_method} - get +exports.retrieveCustomerPaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerPaymentMethod: customer is required.'); + delete options.customer; + const payment_method = this.parseRequired(options.payment_method, 'string', 'stripe.retrieveCustomerPaymentMethod: payment_method is required.'); + delete options.payment_method; + return stripe.customers.retrievePaymentMethod(customer, payment_method, options,__extra); +}; + +// /v1/customers/{customer}/sources - get +exports.listCustomerSources = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listCustomerSources: customer is required.'); + delete options.customer; + return stripe.customers.listSources(customer, options,__extra); +}; + +// /v1/customers/{customer}/sources - post +exports.createCustomerSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.createCustomerSource: customer is required.'); + delete options.customer; + return stripe.customers.createSource(customer, options,__extra); +}; + +// /v1/customers/{customer}/sources/{id} - delete +exports.deleteCustomerSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.deleteCustomerSource: customer is required.'); + delete options.customer; + const id = this.parseRequired(options.id, 'string', 'stripe.deleteCustomerSource: id is required.'); + delete options.id; + return stripe.customers.deleteSource(customer, id, options,__extra); +}; + +// /v1/customers/{customer}/sources/{id} - get +exports.retrieveCustomerSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerSource: customer is required.'); + delete options.customer; + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveCustomerSource: id is required.'); + delete options.id; + return stripe.customers.retrieveSource(customer, id, options,__extra); +}; + +// /v1/customers/{customer}/sources/{id} - post +exports.updateCustomerSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.updateCustomerSource: customer is required.'); + delete options.customer; + const id = this.parseRequired(options.id, 'string', 'stripe.updateCustomerSource: id is required.'); + delete options.id; + return stripe.customers.updateSource(customer, id, options,__extra); +}; + +// /v1/customers/{customer}/tax_ids - get +exports.listCustomerTaxIds = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.listCustomerTaxIds: customer is required.'); + delete options.customer; + return stripe.customers.listTaxIds(customer, options,__extra); +}; + +// /v1/customers/{customer}/tax_ids - post +exports.createCustomerTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.createCustomerTaxId: customer is required.'); + delete options.customer; + return stripe.customers.createTaxId(customer, options,__extra); +}; + +// /v1/customers/{customer}/tax_ids/{id} - delete +exports.deleteCustomerTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.deleteCustomerTaxId: customer is required.'); + delete options.customer; + const id = this.parseRequired(options.id, 'string', 'stripe.deleteCustomerTaxId: id is required.'); + delete options.id; + return stripe.customers.deleteTaxId(customer, id, options,__extra); +}; + +// /v1/customers/{customer}/tax_ids/{id} - get +exports.retrieveCustomerTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.retrieveCustomerTaxId: customer is required.'); + delete options.customer; + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveCustomerTaxId: id is required.'); + delete options.id; + return stripe.customers.retrieveTaxId(customer, id, options,__extra); +}; + +// /v1/disputes - get +exports.listDisputes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.disputes.list(options,__extra); +}; + +// /v1/disputes/{dispute} - get +exports.retrieveDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.retrieveDispute: dispute is required.'); + delete options.dispute; + return stripe.disputes.retrieve(dispute, options,__extra); +}; + +// /v1/disputes/{dispute} - post +exports.updateDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.updateDispute: dispute is required.'); + delete options.dispute; + return stripe.disputes.update(dispute, options,__extra); +}; + +// /v1/disputes/{dispute}/close - post +exports.closeDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.closeDispute: dispute is required.'); + delete options.dispute; + return stripe.disputes.close(dispute, options,__extra); +}; + +// /v1/ephemeral_keys - post +exports.createEphemeralKey = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.ephemeralKeys.create(options,__extra); +}; + +// /v1/ephemeral_keys/{key} - delete +exports.deleteEphemeralKey = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const key = this.parseRequired(options.key, 'string', 'stripe.deleteEphemeralKey: key is required.'); + delete options.key; + return stripe.ephemeralKeys.del(key, options,__extra); +}; + +// /v1/events - get +exports.listEvents = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.events.list(options,__extra); +}; + +// /v1/events/{id} - get +exports.retrieveEvent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveEvent: id is required.'); + delete options.id; + return stripe.events.retrieve(id, options,__extra); +}; + +// /v1/exchange_rates - get +exports.listExchangeRates = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.exchangeRates.list(options,__extra); +}; + +// /v1/exchange_rates/{rate_id} - get +exports.retrieveExchangeRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const rate_id = this.parseRequired(options.rate_id, 'string', 'stripe.retrieveExchangeRate: rate_id is required.'); + delete options.rate_id; + return stripe.exchangeRates.retrieve(rate_id, options,__extra); +}; + +// /v1/file_links - get +exports.listFileLinks = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.fileLinks.list(options,__extra); +}; + +// /v1/file_links - post +exports.createFileLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.fileLinks.create(options,__extra); +}; + +// /v1/file_links/{link} - get +exports.retrieveFileLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const link = this.parseRequired(options.link, 'string', 'stripe.retrieveFileLink: link is required.'); + delete options.link; + return stripe.fileLinks.retrieve(link, options,__extra); +}; + +// /v1/file_links/{link} - post +exports.updateFileLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const link = this.parseRequired(options.link, 'string', 'stripe.updateFileLink: link is required.'); + delete options.link; + return stripe.fileLinks.update(link, options,__extra); +}; + +// /v1/files - get +exports.listFiles = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.files.list(options,__extra); +}; + +// /v1/files - post +exports.createFile = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.filePath) { + const fs = require('fs'); + if (fs.existsSync(options.filePath)) { + const fp = fs.readFileSync(options.filePath); + options.file = { + data: fp, + name: options.filePath.split('/').pop() + }; + } else { + throw new Error('createFile: File not found: ' + options.filePath); + } + delete options.filePath; + } else { + throw new Error('createFile: filePath is required!'); + } + + return stripe.files.create(options,__extra); +}; + +// /v1/files/{file} - get +exports.retrieveFile = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const file = this.parseRequired(options.file, 'string', 'stripe.retrieveFile: file is required.'); + delete options.file; + return stripe.files.retrieve(file, options,__extra); +}; + +// /v1/financial_connections/accounts - get +exports.listFinancialConnectionsAccounts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.financialConnections.accounts.list(options,__extra); +}; + +// /v1/financial_connections/accounts/{account} - get +exports.retrieveFinancialConnectionsAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.retrieveFinancialConnectionsAccount: account is required.'); + delete options.account; + return stripe.financialConnections.accounts.retrieve(account, options,__extra); +}; + +// /v1/financial_connections/accounts/{account}/disconnect - post +exports.disconnectFinancialConnectionsAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.disconnectFinancialConnectionsAccount: account is required.'); + delete options.account; + return stripe.financialConnections.accounts.disconnect(account, options,__extra); +}; + +// /v1/financial_connections/accounts/{account}/owners - get +exports.listFinancialConnectionsAccountOwners = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.listFinancialConnectionsAccountOwners: account is required.'); + delete options.account; + const ownership = this.parseRequired(options.ownership, 'string', 'stripe.listFinancialConnectionsAccountOwners: ownership is required.'); + return stripe.financialConnections.accounts.listOwners(account, options,__extra); +}; + +// /v1/financial_connections/accounts/{account}/refresh - post +exports.refreshFinancialConnectionsAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.refreshFinancialConnectionsAccount: account is required.'); + delete options.account; + return stripe.financialConnections.accounts.refresh(account, options,__extra); +}; + +// /v1/financial_connections/accounts/{account}/subscribe - post +exports.subscribeFinancialConnectionsAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.subscribeFinancialConnectionsAccount: account is required.'); + delete options.account; + return stripe.financialConnections.accounts.subscribe(account, options,__extra); +}; + +// /v1/financial_connections/accounts/{account}/unsubscribe - post +exports.unsubscribeFinancialConnectionsAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.unsubscribeFinancialConnectionsAccount: account is required.'); + delete options.account; + return stripe.financialConnections.accounts.unsubscribe(account, options,__extra); +}; + +// /v1/financial_connections/sessions - post +exports.createFinancialConnectionsSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.financialConnections.sessions.create(options,__extra); +}; + +// /v1/financial_connections/sessions/{session} - get +exports.retrieveFinancialConnectionsSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.retrieveFinancialConnectionsSession: session is required.'); + delete options.session; + return stripe.financialConnections.sessions.retrieve(session, options,__extra); +}; + +// /v1/financial_connections/transactions - get +exports.listFinancialConnectionsTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const account = this.parseRequired(options.account, 'string', 'stripe.listFinancialConnectionsTransactions: account is required.'); + return stripe.financialConnections.transactions.list(options,__extra); +}; + +// /v1/financial_connections/transactions/{transaction} - get +exports.retrieveFinancialConnectionsTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.retrieveFinancialConnectionsTransaction: transaction is required.'); + delete options.transaction; + return stripe.financialConnections.transactions.retrieve(transaction, options,__extra); +}; + +// /v1/forwarding/requests - get +exports.listForwardingRequests = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.forwarding.requests.list(options,__extra); +}; + +// /v1/forwarding/requests - post +exports.createForwardingRequest = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.forwarding.requests.create(options,__extra); +}; + +// /v1/forwarding/requests/{id} - get +exports.retrieveForwardingRequest = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveForwardingRequest: id is required.'); + delete options.id; + return stripe.forwarding.requests.retrieve(id, options,__extra); +}; + +// /v1/identity/verification_reports - get +exports.listIdentityVerificationReports = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.identity.verificationReports.list(options,__extra); +}; + +// /v1/identity/verification_reports/{report} - get +exports.retrieveIdentityVerificationReport = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const report = this.parseRequired(options.report, 'string', 'stripe.retrieveIdentityVerificationReport: report is required.'); + delete options.report; + return stripe.identity.verificationReports.retrieve(report, options,__extra); +}; + +// /v1/identity/verification_sessions - get +exports.listIdentityVerificationSessions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.identity.verificationSessions.list(options,__extra); +}; + +// /v1/identity/verification_sessions - post +exports.createIdentityVerificationSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.identity.verificationSessions.create(options,__extra); +}; + +// /v1/identity/verification_sessions/{session} - get +exports.retrieveIdentityVerificationSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.retrieveIdentityVerificationSession: session is required.'); + delete options.session; + return stripe.identity.verificationSessions.retrieve(session, options,__extra); +}; + +// /v1/identity/verification_sessions/{session} - post +exports.updateIdentityVerificationSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.updateIdentityVerificationSession: session is required.'); + delete options.session; + return stripe.identity.verificationSessions.update(session, options,__extra); +}; + +// /v1/identity/verification_sessions/{session}/cancel - post +exports.cancelIdentityVerificationSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.cancelIdentityVerificationSession: session is required.'); + delete options.session; + return stripe.identity.verificationSessions.cancel(session, options,__extra); +}; + +// /v1/identity/verification_sessions/{session}/redact - post +exports.redactIdentityVerificationSession = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const session = this.parseRequired(options.session, 'string', 'stripe.redactIdentityVerificationSession: session is required.'); + delete options.session; + return stripe.identity.verificationSessions.redact(session, options,__extra); +}; + +// /v1/invoiceitems - get +exports.listInvoiceItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoiceItems.list(options,__extra); +}; + +// /v1/invoiceitems - post +exports.createInvoiceItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoiceItems.create(options,__extra); +}; + +// /v1/invoiceitems/{invoiceitem} - delete +exports.deleteInvoiceItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoiceitem = this.parseRequired(options.invoiceitem, 'string', 'stripe.deleteInvoiceItem: invoiceitem is required.'); + delete options.invoiceitem; + return stripe.invoiceItems.del(invoiceitem, options,__extra); +}; + +// /v1/invoiceitems/{invoiceitem} - get +exports.retrieveInvoiceItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoiceitem = this.parseRequired(options.invoiceitem, 'string', 'stripe.retrieveInvoiceItem: invoiceitem is required.'); + delete options.invoiceitem; + return stripe.invoiceItems.retrieve(invoiceitem, options,__extra); +}; + +// /v1/invoiceitems/{invoiceitem} - post +exports.updateInvoiceItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoiceitem = this.parseRequired(options.invoiceitem, 'string', 'stripe.updateInvoiceItem: invoiceitem is required.'); + delete options.invoiceitem; + return stripe.invoiceItems.update(invoiceitem, options,__extra); +}; + +// /v1/invoices - get +exports.listInvoices = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoices.list(options,__extra); +}; + +// /v1/invoices - post +exports.createInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoices.create(options,__extra); +}; + +// /v1/invoices/search - get +exports.searchInvoices = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchInvoices: query is required.'); + return stripe.invoices.search(options,__extra); +}; + +// /v1/invoices/upcoming - get +exports.retrieveInvoicesUpcoming = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoices.retrieveUpcoming(options,__extra); +}; + +// /v1/invoices/upcoming/lines - get +exports.listInvoicesUpcomingLines = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.invoices.listUpcomingLineItems(options,__extra); +}; + +// /v1/invoices/{invoice} - delete +exports.deleteInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.deleteInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.del(invoice, options,__extra); +}; + +// /v1/invoices/{invoice} - get +exports.retrieveInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.retrieveInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.retrieve(invoice, options,__extra); +}; + +// /v1/invoices/{invoice} - post +exports.updateInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.updateInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.update(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/finalize - post +exports.finalizeInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.finalizeInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.finalize(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/lines - get +exports.listInvoiceLines = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.listInvoiceLines: invoice is required.'); + delete options.invoice; + return stripe.invoices.listLines(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/lines/{line_item_id} - post +exports.updateInvoiceLine = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.updateInvoiceLine: invoice is required.'); + delete options.invoice; + const line_item_id = this.parseRequired(options.line_item_id, 'string', 'stripe.updateInvoiceLine: line_item_id is required.'); + delete options.line_item_id; + return stripe.invoices.updateLine(invoice, line_item_id, options,__extra); +}; + +// /v1/invoices/{invoice}/mark_uncollectible - post +exports.markUncollectibleInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.markUncollectibleInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.markUncollectible(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/pay - post +exports.payInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.payInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.pay(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/send - post +exports.sendInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.sendInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.send(invoice, options,__extra); +}; + +// /v1/invoices/{invoice}/void - post +exports.voidInvoice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const invoice = this.parseRequired(options.invoice, 'string', 'stripe.voidInvoice: invoice is required.'); + delete options.invoice; + return stripe.invoices.void(invoice, options,__extra); +}; + +// /v1/issuing/authorizations - get +exports.listIssuingAuthorizations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.authorizations.list(options,__extra); +}; + +// /v1/issuing/authorizations/{authorization} - get +exports.retrieveIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.retrieveIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.issuing.authorizations.retrieve(authorization, options,__extra); +}; + +// /v1/issuing/authorizations/{authorization} - post +exports.updateIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.updateIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.issuing.authorizations.update(authorization, options,__extra); +}; + +// /v1/issuing/authorizations/{authorization}/approve - post +exports.approveIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.approveIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.issuing.authorizations.approve(authorization, options,__extra); +}; + +// /v1/issuing/authorizations/{authorization}/decline - post +exports.declineIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.declineIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.issuing.authorizations.decline(authorization, options,__extra); +}; + +// /v1/issuing/cardholders - get +exports.listIssuingCardholders = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.cardholders.list(options,__extra); +}; + +// /v1/issuing/cardholders - post +exports.createIssuingCardholder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.cardholders.create(options,__extra); +}; + +// /v1/issuing/cardholders/{cardholder} - get +exports.retrieveIssuingCardholder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const cardholder = this.parseRequired(options.cardholder, 'string', 'stripe.retrieveIssuingCardholder: cardholder is required.'); + delete options.cardholder; + return stripe.issuing.cardholders.retrieve(cardholder, options,__extra); +}; + +// /v1/issuing/cardholders/{cardholder} - post +exports.updateIssuingCardholder = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const cardholder = this.parseRequired(options.cardholder, 'string', 'stripe.updateIssuingCardholder: cardholder is required.'); + delete options.cardholder; + return stripe.issuing.cardholders.update(cardholder, options,__extra); +}; + +// /v1/issuing/cards - get +exports.listIssuingCards = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.cards.list(options,__extra); +}; + +// /v1/issuing/cards - post +exports.createIssuingCard = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.cards.create(options,__extra); +}; + +// /v1/issuing/cards/{card} - get +exports.retrieveIssuingCard = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const card = this.parseRequired(options.card, 'string', 'stripe.retrieveIssuingCard: card is required.'); + delete options.card; + return stripe.issuing.cards.retrieve(card, options,__extra); +}; + +// /v1/issuing/cards/{card} - post +exports.updateIssuingCard = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const card = this.parseRequired(options.card, 'string', 'stripe.updateIssuingCard: card is required.'); + delete options.card; + return stripe.issuing.cards.update(card, options,__extra); +}; + +// /v1/issuing/disputes - get +exports.listIssuingDisputes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.disputes.list(options,__extra); +}; + +// /v1/issuing/disputes - post +exports.createIssuingDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.disputes.create(options,__extra); +}; + +// /v1/issuing/disputes/{dispute} - get +exports.retrieveIssuingDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.retrieveIssuingDispute: dispute is required.'); + delete options.dispute; + return stripe.issuing.disputes.retrieve(dispute, options,__extra); +}; + +// /v1/issuing/disputes/{dispute} - post +exports.updateIssuingDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.updateIssuingDispute: dispute is required.'); + delete options.dispute; + return stripe.issuing.disputes.update(dispute, options,__extra); +}; + +// /v1/issuing/disputes/{dispute}/submit - post +exports.submitIssuingDispute = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const dispute = this.parseRequired(options.dispute, 'string', 'stripe.submitIssuingDispute: dispute is required.'); + delete options.dispute; + return stripe.issuing.disputes.submit(dispute, options,__extra); +}; + +// /v1/issuing/personalization_designs - get +exports.listIssuingPersonalizationDesigns = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.personalizationDesigns.list(options,__extra); +}; + +// /v1/issuing/personalization_designs - post +exports.createIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.personalizationDesigns.create(options,__extra); +}; + +// /v1/issuing/personalization_designs/{personalization_design} - get +exports.retrieveIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const personalization_design = this.parseRequired(options.personalization_design, 'string', 'stripe.retrieveIssuingPersonalizationDesign: personalization_design is required.'); + delete options.personalization_design; + return stripe.issuing.personalizationDesigns.retrieve(personalization_design, options,__extra); +}; + +// /v1/issuing/personalization_designs/{personalization_design} - post +exports.updateIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const personalization_design = this.parseRequired(options.personalization_design, 'string', 'stripe.updateIssuingPersonalizationDesign: personalization_design is required.'); + delete options.personalization_design; + return stripe.issuing.personalizationDesigns.update(personalization_design, options,__extra); +}; + +// /v1/issuing/physical_bundles - get +exports.listIssuingPhysicalBundles = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.physicalBundles.list(options,__extra); +}; + +// /v1/issuing/physical_bundles/{physical_bundle} - get +exports.retrieveIssuingPhysicalBundle = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const physical_bundle = this.parseRequired(options.physical_bundle, 'string', 'stripe.retrieveIssuingPhysicalBundle: physical_bundle is required.'); + delete options.physical_bundle; + return stripe.issuing.physicalBundles.retrieve(physical_bundle, options,__extra); +}; + +// /v1/issuing/tokens - get +exports.listIssuingTokens = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const card = this.parseRequired(options.card, 'string', 'stripe.listIssuingTokens: card is required.'); + return stripe.issuing.tokens.list(options,__extra); +}; + +// /v1/issuing/tokens/{token} - get +exports.retrieveIssuingToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const token = this.parseRequired(options.token, 'string', 'stripe.retrieveIssuingToken: token is required.'); + delete options.token; + return stripe.issuing.tokens.retrieve(token, options,__extra); +}; + +// /v1/issuing/tokens/{token} - post +exports.updateIssuingToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const token = this.parseRequired(options.token, 'string', 'stripe.updateIssuingToken: token is required.'); + delete options.token; + return stripe.issuing.tokens.update(token, options,__extra); +}; + +// /v1/issuing/transactions - get +exports.listIssuingTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.issuing.transactions.list(options,__extra); +}; + +// /v1/issuing/transactions/{transaction} - get +exports.retrieveIssuingTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.retrieveIssuingTransaction: transaction is required.'); + delete options.transaction; + return stripe.issuing.transactions.retrieve(transaction, options,__extra); +}; + +// /v1/issuing/transactions/{transaction} - post +exports.updateIssuingTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.updateIssuingTransaction: transaction is required.'); + delete options.transaction; + return stripe.issuing.transactions.update(transaction, options,__extra); +}; + +// /v1/mandates/{mandate} - get +exports.retrieveMandate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const mandate = this.parseRequired(options.mandate, 'string', 'stripe.retrieveMandate: mandate is required.'); + delete options.mandate; + return stripe.mandates.retrieve(mandate, options,__extra); +}; + +// /v1/payment_intents - get +exports.listPaymentIntents = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentIntents.list(options,__extra); +}; + +// /v1/payment_intents - post +exports.createPaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.paymentIntents.create(options,__extra); +}; + +// /v1/payment_intents/search - get +exports.searchPaymentIntents = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchPaymentIntents: query is required.'); + return stripe.paymentIntents.search(options,__extra); +}; + +// /v1/payment_intents/{intent} - get +exports.retrievePaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.retrievePaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.retrieve(intent, options,__extra); +}; + +// /v1/payment_intents/{intent} - post +exports.updatePaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.updatePaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.update(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/apply_customer_balance - post +exports.applyCustomerBalancePaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.applyCustomerBalancePaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.applyCustomerBalance(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/cancel - post +exports.cancelPaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.cancelPaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.cancel(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/capture - post +exports.capturePaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.capturePaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.capture(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/confirm - post +exports.confirmPaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.confirmPaymentIntent: intent is required.'); + delete options.intent; + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.paymentIntents.confirm(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/increment_authorization - post +exports.incrementAuthorizationPaymentIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.incrementAuthorizationPaymentIntent: intent is required.'); + delete options.intent; + return stripe.paymentIntents.incrementAuthorization(intent, options,__extra); +}; + +// /v1/payment_intents/{intent}/verify_microdeposits - post +exports.createPaymentIntentVerifyMicrodeposit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.createPaymentIntentVerifyMicrodeposit: intent is required.'); + delete options.intent; + return stripe.paymentIntents.createVerifyMicrodeposit(intent, options,__extra); +}; + +// /v1/payment_links - get +exports.listPaymentLinks = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentLinks.list(options,__extra); +}; + +// /v1/payment_links - post +exports.createPaymentLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentLinks.create(options,__extra); +}; + +// /v1/payment_links/{payment_link} - get +exports.retrievePaymentLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_link = this.parseRequired(options.payment_link, 'string', 'stripe.retrievePaymentLink: payment_link is required.'); + delete options.payment_link; + return stripe.paymentLinks.retrieve(payment_link, options,__extra); +}; + +// /v1/payment_links/{payment_link} - post +exports.updatePaymentLink = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_link = this.parseRequired(options.payment_link, 'string', 'stripe.updatePaymentLink: payment_link is required.'); + delete options.payment_link; + return stripe.paymentLinks.update(payment_link, options,__extra); +}; + +// /v1/payment_links/{payment_link}/line_items - get +exports.listPaymentLinkLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_link = this.parseRequired(options.payment_link, 'string', 'stripe.listPaymentLinkLineItems: payment_link is required.'); + delete options.payment_link; + return stripe.paymentLinks.listLineItems(payment_link, options,__extra); +}; + +// /v1/payment_method_configurations - get +exports.listPaymentMethodConfigurations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethodConfigurations.list(options,__extra); +}; + +// /v1/payment_method_configurations - post +exports.createPaymentMethodConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethodConfigurations.create(options,__extra); +}; + +// /v1/payment_method_configurations/{configuration} - get +exports.retrievePaymentMethodConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.retrievePaymentMethodConfiguration: configuration is required.'); + delete options.configuration; + return stripe.paymentMethodConfigurations.retrieve(configuration, options,__extra); +}; + +// /v1/payment_method_configurations/{configuration} - post +exports.updatePaymentMethodConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.updatePaymentMethodConfiguration: configuration is required.'); + delete options.configuration; + return stripe.paymentMethodConfigurations.update(configuration, options,__extra); +}; + +// /v1/payment_method_domains - get +exports.listPaymentMethodDomains = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethodDomains.list(options,__extra); +}; + +// /v1/payment_method_domains - post +exports.createPaymentMethodDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethodDomains.create(options,__extra); +}; + +// /v1/payment_method_domains/{payment_method_domain} - get +exports.retrievePaymentMethodDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method_domain = this.parseRequired(options.payment_method_domain, 'string', 'stripe.retrievePaymentMethodDomain: payment_method_domain is required.'); + delete options.payment_method_domain; + return stripe.paymentMethodDomains.retrieve(payment_method_domain, options,__extra); +}; + +// /v1/payment_method_domains/{payment_method_domain} - post +exports.updatePaymentMethodDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method_domain = this.parseRequired(options.payment_method_domain, 'string', 'stripe.updatePaymentMethodDomain: payment_method_domain is required.'); + delete options.payment_method_domain; + return stripe.paymentMethodDomains.update(payment_method_domain, options,__extra); +}; + +// /v1/payment_method_domains/{payment_method_domain}/validate - post +exports.validatePaymentMethodDomain = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method_domain = this.parseRequired(options.payment_method_domain, 'string', 'stripe.validatePaymentMethodDomain: payment_method_domain is required.'); + delete options.payment_method_domain; + return stripe.paymentMethodDomains.validate(payment_method_domain, options,__extra); +}; + +// /v1/payment_methods - get +exports.listPaymentMethods = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethods.list(options,__extra); +}; + +// /v1/payment_methods - post +exports.createPaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.paymentMethods.create(options,__extra); +}; + +// /v1/payment_methods/{payment_method} - get +exports.retrievePaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method = this.parseRequired(options.payment_method, 'string', 'stripe.retrievePaymentMethod: payment_method is required.'); + delete options.payment_method; + return stripe.paymentMethods.retrieve(payment_method, options,__extra); +}; + +// /v1/payment_methods/{payment_method} - post +exports.updatePaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method = this.parseRequired(options.payment_method, 'string', 'stripe.updatePaymentMethod: payment_method is required.'); + delete options.payment_method; + return stripe.paymentMethods.update(payment_method, options,__extra); +}; + +// /v1/payment_methods/{payment_method}/attach - post +exports.attachPaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method = this.parseRequired(options.payment_method, 'string', 'stripe.attachPaymentMethod: payment_method is required.'); + delete options.payment_method; + return stripe.paymentMethods.attach(payment_method, options,__extra); +}; + +// /v1/payment_methods/{payment_method}/detach - post +exports.detachPaymentMethod = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payment_method = this.parseRequired(options.payment_method, 'string', 'stripe.detachPaymentMethod: payment_method is required.'); + delete options.payment_method; + return stripe.paymentMethods.detach(payment_method, options,__extra); +}; + +// /v1/payouts - get +exports.listPayouts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.payouts.list(options,__extra); +}; + +// /v1/payouts - post +exports.createPayout = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.payouts.create(options,__extra); +}; + +// /v1/payouts/{payout} - get +exports.retrievePayout = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payout = this.parseRequired(options.payout, 'string', 'stripe.retrievePayout: payout is required.'); + delete options.payout; + return stripe.payouts.retrieve(payout, options,__extra); +}; + +// /v1/payouts/{payout} - post +exports.updatePayout = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payout = this.parseRequired(options.payout, 'string', 'stripe.updatePayout: payout is required.'); + delete options.payout; + return stripe.payouts.update(payout, options,__extra); +}; + +// /v1/payouts/{payout}/cancel - post +exports.cancelPayout = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payout = this.parseRequired(options.payout, 'string', 'stripe.cancelPayout: payout is required.'); + delete options.payout; + return stripe.payouts.cancel(payout, options,__extra); +}; + +// /v1/payouts/{payout}/reverse - post +exports.reversePayout = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const payout = this.parseRequired(options.payout, 'string', 'stripe.reversePayout: payout is required.'); + delete options.payout; + return stripe.payouts.reverse(payout, options,__extra); +}; + +// /v1/plans - get +exports.listPlans = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.plans.list(options,__extra); +}; + +// /v1/plans - post +exports.createPlan = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.plans.create(options,__extra); +}; + +// /v1/plans/{plan} - delete +exports.deletePlan = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const plan = this.parseRequired(options.plan, 'string', 'stripe.deletePlan: plan is required.'); + delete options.plan; + return stripe.plans.del(plan, options,__extra); +}; + +// /v1/plans/{plan} - get +exports.retrievePlan = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const plan = this.parseRequired(options.plan, 'string', 'stripe.retrievePlan: plan is required.'); + delete options.plan; + return stripe.plans.retrieve(plan, options,__extra); +}; + +// /v1/plans/{plan} - post +exports.updatePlan = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const plan = this.parseRequired(options.plan, 'string', 'stripe.updatePlan: plan is required.'); + delete options.plan; + return stripe.plans.update(plan, options,__extra); +}; + +// /v1/prices - get +exports.listPrices = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.prices.list(options,__extra); +}; + +// /v1/prices - post +exports.createPrice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.prices.create(options,__extra); +}; + +// /v1/prices/search - get +exports.searchPrices = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchPrices: query is required.'); + return stripe.prices.search(options,__extra); +}; + +// /v1/prices/{price} - get +exports.retrievePrice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const price = this.parseRequired(options.price, 'string', 'stripe.retrievePrice: price is required.'); + delete options.price; + return stripe.prices.retrieve(price, options,__extra); +}; + +// /v1/prices/{price} - post +exports.updatePrice = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const price = this.parseRequired(options.price, 'string', 'stripe.updatePrice: price is required.'); + delete options.price; + return stripe.prices.update(price, options,__extra); +}; + +// /v1/products - get +exports.listProducts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.products.list(options,__extra); +}; + +// /v1/products - post +exports.createProduct = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.products.create(options,__extra); +}; + +// /v1/products/search - get +exports.searchProducts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchProducts: query is required.'); + return stripe.products.search(options,__extra); +}; + +// /v1/products/{id} - delete +exports.deleteProduct = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.deleteProduct: id is required.'); + delete options.id; + return stripe.products.del(id, options,__extra); +}; + +// /v1/products/{id} - get +exports.retrieveProduct = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveProduct: id is required.'); + delete options.id; + return stripe.products.retrieve(id, options,__extra); +}; + +// /v1/products/{id} - post +exports.updateProduct = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.updateProduct: id is required.'); + delete options.id; + return stripe.products.update(id, options,__extra); +}; + +// /v1/promotion_codes - get +exports.listPromotionCodes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.promotionCodes.list(options,__extra); +}; + +// /v1/promotion_codes - post +exports.createPromotionCode = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.promotionCodes.create(options,__extra); +}; + +// /v1/promotion_codes/{promotion_code} - get +exports.retrievePromotionCode = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const promotion_code = this.parseRequired(options.promotion_code, 'string', 'stripe.retrievePromotionCode: promotion_code is required.'); + delete options.promotion_code; + return stripe.promotionCodes.retrieve(promotion_code, options,__extra); +}; + +// /v1/promotion_codes/{promotion_code} - post +exports.updatePromotionCode = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const promotion_code = this.parseRequired(options.promotion_code, 'string', 'stripe.updatePromotionCode: promotion_code is required.'); + delete options.promotion_code; + return stripe.promotionCodes.update(promotion_code, options,__extra); +}; + +// /v1/quotes - get +exports.listQuotes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.quotes.list(options,__extra); +}; + +// /v1/quotes - post +exports.createQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.quotes.create(options,__extra); +}; + +// /v1/quotes/{quote} - get +exports.retrieveQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.retrieveQuote: quote is required.'); + delete options.quote; + return stripe.quotes.retrieve(quote, options,__extra); +}; + +// /v1/quotes/{quote} - post +exports.updateQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.updateQuote: quote is required.'); + delete options.quote; + return stripe.quotes.update(quote, options,__extra); +}; + +// /v1/quotes/{quote}/accept - post +exports.acceptQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.acceptQuote: quote is required.'); + delete options.quote; + return stripe.quotes.accept(quote, options,__extra); +}; + +// /v1/quotes/{quote}/cancel - post +exports.cancelQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.cancelQuote: quote is required.'); + delete options.quote; + return stripe.quotes.cancel(quote, options,__extra); +}; + +// /v1/quotes/{quote}/computed_upfront_line_items - get +exports.listQuoteComputedUpfrontLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.listQuoteComputedUpfrontLineItems: quote is required.'); + delete options.quote; + return stripe.quotes.listComputedUpfrontLineItems(quote, options,__extra); +}; + +// /v1/quotes/{quote}/finalize - post +exports.finalizeQuote = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.finalizeQuote: quote is required.'); + delete options.quote; + return stripe.quotes.finalize(quote, options,__extra); +}; + +// /v1/quotes/{quote}/line_items - get +exports.listQuoteLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.listQuoteLineItems: quote is required.'); + delete options.quote; + return stripe.quotes.listLineItems(quote, options,__extra); +}; + +// /v1/quotes/{quote}/pdf - get +exports.retrieveQuotePdf = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const quote = this.parseRequired(options.quote, 'string', 'stripe.retrieveQuotePdf: quote is required.'); + delete options.quote; + return stripe.quotes.retrievePdf(quote, options,__extra); +}; + +// /v1/radar/early_fraud_warnings - get +exports.listRadarEarlyFraudWarnings = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.radar.earlyFraudWarnings.list(options,__extra); +}; + +// /v1/radar/early_fraud_warnings/{early_fraud_warning} - get +exports.retrieveRadarEarlyFraudWarning = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const early_fraud_warning = this.parseRequired(options.early_fraud_warning, 'string', 'stripe.retrieveRadarEarlyFraudWarning: early_fraud_warning is required.'); + delete options.early_fraud_warning; + return stripe.radar.earlyFraudWarnings.retrieve(early_fraud_warning, options,__extra); +}; + +// /v1/radar/value_list_items - get +exports.listRadarValueListItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const value_list = this.parseRequired(options.value_list, 'string', 'stripe.listRadarValueListItems: value_list is required.'); + return stripe.radar.valueListItems.list(options,__extra); +}; + +// /v1/radar/value_list_items - post +exports.createRadarValueListItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.radar.valueListItems.create(options,__extra); +}; + +// /v1/radar/value_list_items/{item} - delete +exports.deleteRadarValueListItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const item = this.parseRequired(options.item, 'string', 'stripe.deleteRadarValueListItem: item is required.'); + delete options.item; + return stripe.radar.valueListItems.del(item, options,__extra); +}; + +// /v1/radar/value_list_items/{item} - get +exports.retrieveRadarValueListItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const item = this.parseRequired(options.item, 'string', 'stripe.retrieveRadarValueListItem: item is required.'); + delete options.item; + return stripe.radar.valueListItems.retrieve(item, options,__extra); +}; + +// /v1/radar/value_lists - get +exports.listRadarValueLists = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.radar.valueLists.list(options,__extra); +}; + +// /v1/radar/value_lists - post +exports.createRadarValueList = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.radar.valueLists.create(options,__extra); +}; + +// /v1/radar/value_lists/{value_list} - delete +exports.deleteRadarValueList = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const value_list = this.parseRequired(options.value_list, 'string', 'stripe.deleteRadarValueList: value_list is required.'); + delete options.value_list; + return stripe.radar.valueLists.del(value_list, options,__extra); +}; + +// /v1/radar/value_lists/{value_list} - get +exports.retrieveRadarValueList = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const value_list = this.parseRequired(options.value_list, 'string', 'stripe.retrieveRadarValueList: value_list is required.'); + delete options.value_list; + return stripe.radar.valueLists.retrieve(value_list, options,__extra); +}; + +// /v1/radar/value_lists/{value_list} - post +exports.updateRadarValueList = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const value_list = this.parseRequired(options.value_list, 'string', 'stripe.updateRadarValueList: value_list is required.'); + delete options.value_list; + return stripe.radar.valueLists.update(value_list, options,__extra); +}; + +// /v1/refunds - get +exports.listRefunds = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.refunds.list(options,__extra); +}; + +// /v1/refunds - post +exports.createRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.refunds.create(options,__extra); +}; + +// /v1/refunds/{refund} - get +exports.retrieveRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const refund = this.parseRequired(options.refund, 'string', 'stripe.retrieveRefund: refund is required.'); + delete options.refund; + return stripe.refunds.retrieve(refund, options,__extra); +}; + +// /v1/refunds/{refund} - post +exports.updateRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const refund = this.parseRequired(options.refund, 'string', 'stripe.updateRefund: refund is required.'); + delete options.refund; + return stripe.refunds.update(refund, options,__extra); +}; + +// /v1/refunds/{refund}/cancel - post +exports.cancelRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const refund = this.parseRequired(options.refund, 'string', 'stripe.cancelRefund: refund is required.'); + delete options.refund; + return stripe.refunds.cancel(refund, options,__extra); +}; + +// /v1/reporting/report_runs - get +exports.listReportingReportRuns = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.reporting.reportRuns.list(options,__extra); +}; + +// /v1/reporting/report_runs - post +exports.createReportingReportRun = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.reporting.reportRuns.create(options,__extra); +}; + +// /v1/reporting/report_runs/{report_run} - get +exports.retrieveReportingReportRun = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const report_run = this.parseRequired(options.report_run, 'string', 'stripe.retrieveReportingReportRun: report_run is required.'); + delete options.report_run; + return stripe.reporting.reportRuns.retrieve(report_run, options,__extra); +}; + +// /v1/reporting/report_types - get +exports.listReportingReportTypes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.reporting.reportTypes.list(options,__extra); +}; + +// /v1/reporting/report_types/{report_type} - get +exports.retrieveReportingReportType = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const report_type = this.parseRequired(options.report_type, 'string', 'stripe.retrieveReportingReportType: report_type is required.'); + delete options.report_type; + return stripe.reporting.reportTypes.retrieve(report_type, options,__extra); +}; + +// /v1/reviews - get +exports.listReviews = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.reviews.list(options,__extra); +}; + +// /v1/reviews/{review} - get +exports.retrieveReview = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const review = this.parseRequired(options.review, 'string', 'stripe.retrieveReview: review is required.'); + delete options.review; + return stripe.reviews.retrieve(review, options,__extra); +}; + +// /v1/reviews/{review}/approve - post +exports.approveReview = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const review = this.parseRequired(options.review, 'string', 'stripe.approveReview: review is required.'); + delete options.review; + return stripe.reviews.approve(review, options,__extra); +}; + +// /v1/setup_attempts - get +exports.listSetupAttempts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const setup_intent = this.parseRequired(options.setup_intent, 'string', 'stripe.listSetupAttempts: setup_intent is required.'); + return stripe.setupAttempts.list(options,__extra); +}; + +// /v1/setup_intents - get +exports.listSetupIntents = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.setupIntents.list(options,__extra); +}; + +// /v1/setup_intents - post +exports.createSetupIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.setupIntents.create(options,__extra); +}; + +// /v1/setup_intents/{intent} - get +exports.retrieveSetupIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.retrieveSetupIntent: intent is required.'); + delete options.intent; + return stripe.setupIntents.retrieve(intent, options,__extra); +}; + +// /v1/setup_intents/{intent} - post +exports.updateSetupIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.updateSetupIntent: intent is required.'); + delete options.intent; + return stripe.setupIntents.update(intent, options,__extra); +}; + +// /v1/setup_intents/{intent}/cancel - post +exports.cancelSetupIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.cancelSetupIntent: intent is required.'); + delete options.intent; + return stripe.setupIntents.cancel(intent, options,__extra); +}; + +// /v1/setup_intents/{intent}/confirm - post +exports.confirmSetupIntent = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.confirmSetupIntent: intent is required.'); + delete options.intent; + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.setupIntents.confirm(intent, options,__extra); +}; + +// /v1/setup_intents/{intent}/verify_microdeposits - post +exports.createSetupIntentVerifyMicrodeposit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const intent = this.parseRequired(options.intent, 'string', 'stripe.createSetupIntentVerifyMicrodeposit: intent is required.'); + delete options.intent; + return stripe.setupIntents.createVerifyMicrodeposit(intent, options,__extra); +}; + +// /v1/shipping_rates - get +exports.listShippingRates = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.shippingRates.list(options,__extra); +}; + +// /v1/shipping_rates - post +exports.createShippingRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.shippingRates.create(options,__extra); +}; + +// /v1/shipping_rates/{shipping_rate_token} - get +exports.retrieveShippingRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const shipping_rate_token = this.parseRequired(options.shipping_rate_token, 'string', 'stripe.retrieveShippingRate: shipping_rate_token is required.'); + delete options.shipping_rate_token; + return stripe.shippingRates.retrieve(shipping_rate_token, options,__extra); +}; + +// /v1/shipping_rates/{shipping_rate_token} - post +exports.updateShippingRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const shipping_rate_token = this.parseRequired(options.shipping_rate_token, 'string', 'stripe.updateShippingRate: shipping_rate_token is required.'); + delete options.shipping_rate_token; + return stripe.shippingRates.update(shipping_rate_token, options,__extra); +}; + +// /v1/sigma/scheduled_query_runs - get +exports.listSigmaScheduledQueryRuns = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.sigma.scheduledQueryRuns.list(options,__extra); +}; + +// /v1/sigma/scheduled_query_runs/{scheduled_query_run} - get +exports.retrieveSigmaScheduledQueryRun = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const scheduled_query_run = this.parseRequired(options.scheduled_query_run, 'string', 'stripe.retrieveSigmaScheduledQueryRun: scheduled_query_run is required.'); + delete options.scheduled_query_run; + return stripe.sigma.scheduledQueryRuns.retrieve(scheduled_query_run, options,__extra); +}; + +// /v1/sources - post +exports.createSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.sources.create(options,__extra); +}; + +// /v1/sources/{source} - get +exports.retrieveSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const source = this.parseRequired(options.source, 'string', 'stripe.retrieveSource: source is required.'); + delete options.source; + return stripe.sources.retrieve(source, options,__extra); +}; + +// /v1/sources/{source} - post +exports.updateSource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const source = this.parseRequired(options.source, 'string', 'stripe.updateSource: source is required.'); + delete options.source; + return stripe.sources.update(source, options,__extra); +}; + +// /v1/sources/{source}/source_transactions - get +exports.listSourceSourceTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const source = this.parseRequired(options.source, 'string', 'stripe.listSourceSourceTransactions: source is required.'); + delete options.source; + return stripe.sources.listSourceTransactions(source, options,__extra); +}; + +// /v1/sources/{source}/verify - post +exports.verifySource = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const source = this.parseRequired(options.source, 'string', 'stripe.verifySource: source is required.'); + delete options.source; + return stripe.sources.verify(source, options,__extra); +}; + +// /v1/subscription_items - get +exports.listSubscriptionItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription = this.parseRequired(options.subscription, 'string', 'stripe.listSubscriptionItems: subscription is required.'); + return stripe.subscriptionItems.list(options,__extra); +}; + +// /v1/subscription_items - post +exports.createSubscriptionItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.subscriptionItems.create(options,__extra); +}; + +// /v1/subscription_items/{item} - delete +exports.deleteSubscriptionItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const item = this.parseRequired(options.item, 'string', 'stripe.deleteSubscriptionItem: item is required.'); + delete options.item; + return stripe.subscriptionItems.del(item, options,__extra); +}; + +// /v1/subscription_items/{item} - get +exports.retrieveSubscriptionItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const item = this.parseRequired(options.item, 'string', 'stripe.retrieveSubscriptionItem: item is required.'); + delete options.item; + return stripe.subscriptionItems.retrieve(item, options,__extra); +}; + +// /v1/subscription_items/{item} - post +exports.updateSubscriptionItem = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const item = this.parseRequired(options.item, 'string', 'stripe.updateSubscriptionItem: item is required.'); + delete options.item; + return stripe.subscriptionItems.update(item, options,__extra); +}; + +// /v1/subscription_items/{subscription_item}/usage_record_summaries - get +exports.listSubscriptionItemUsageRecordSummaries = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_item = this.parseRequired(options.subscription_item, 'string', 'stripe.listSubscriptionItemUsageRecordSummaries: subscription_item is required.'); + delete options.subscription_item; + return stripe.subscriptionItems.listUsageRecordSummaries(subscription_item, options,__extra); +}; + +// /v1/subscription_items/{subscription_item}/usage_records - post +exports.createSubscriptionItemUsageRecord = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_item = this.parseRequired(options.subscription_item, 'string', 'stripe.createSubscriptionItemUsageRecord: subscription_item is required.'); + delete options.subscription_item; + return stripe.subscriptionItems.createUsageRecord(subscription_item, options,__extra); +}; + +// /v1/subscription_schedules - get +exports.listSubscriptionSchedules = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.subscriptionSchedules.list(options,__extra); +}; + +// /v1/subscription_schedules - post +exports.createSubscriptionSchedule = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.subscriptionSchedules.create(options,__extra); +}; + +// /v1/subscription_schedules/{schedule} - get +exports.retrieveSubscriptionSchedule = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const schedule = this.parseRequired(options.schedule, 'string', 'stripe.retrieveSubscriptionSchedule: schedule is required.'); + delete options.schedule; + return stripe.subscriptionSchedules.retrieve(schedule, options,__extra); +}; + +// /v1/subscription_schedules/{schedule} - post +exports.updateSubscriptionSchedule = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const schedule = this.parseRequired(options.schedule, 'string', 'stripe.updateSubscriptionSchedule: schedule is required.'); + delete options.schedule; + return stripe.subscriptionSchedules.update(schedule, options,__extra); +}; + +// /v1/subscription_schedules/{schedule}/cancel - post +exports.cancelSubscriptionSchedule = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const schedule = this.parseRequired(options.schedule, 'string', 'stripe.cancelSubscriptionSchedule: schedule is required.'); + delete options.schedule; + return stripe.subscriptionSchedules.cancel(schedule, options,__extra); +}; + +// /v1/subscription_schedules/{schedule}/release - post +exports.releaseSubscriptionSchedule = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const schedule = this.parseRequired(options.schedule, 'string', 'stripe.releaseSubscriptionSchedule: schedule is required.'); + delete options.schedule; + return stripe.subscriptionSchedules.release(schedule, options,__extra); +}; + +// /v1/subscriptions - get +exports.listSubscriptions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.subscriptions.list(options,__extra); +}; + +// /v1/subscriptions - post +exports.createSubscription = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.subscriptions.create(options,__extra); +}; + +// /v1/subscriptions/search - get +exports.searchSubscriptions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const query = this.parseRequired(options.query, 'string', 'stripe.searchSubscriptions: query is required.'); + return stripe.subscriptions.search(options,__extra); +}; + +// /v1/subscriptions/{subscription_exposed_id} - delete +exports.deleteSubscription = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_exposed_id = this.parseRequired(options.subscription_exposed_id, 'string', 'stripe.deleteSubscription: subscription_exposed_id is required.'); + delete options.subscription_exposed_id; + return stripe.subscriptions.del(subscription_exposed_id, options,__extra); +}; + +// /v1/subscriptions/{subscription_exposed_id} - get +exports.retrieveSubscription = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_exposed_id = this.parseRequired(options.subscription_exposed_id, 'string', 'stripe.retrieveSubscription: subscription_exposed_id is required.'); + delete options.subscription_exposed_id; + return stripe.subscriptions.retrieve(subscription_exposed_id, options,__extra); +}; + +// /v1/subscriptions/{subscription_exposed_id} - post +exports.updateSubscription = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_exposed_id = this.parseRequired(options.subscription_exposed_id, 'string', 'stripe.updateSubscription: subscription_exposed_id is required.'); + delete options.subscription_exposed_id; + return stripe.subscriptions.update(subscription_exposed_id, options,__extra); +}; + +// /v1/subscriptions/{subscription_exposed_id}/discount - delete +exports.deleteSubscriptionDiscount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription_exposed_id = this.parseRequired(options.subscription_exposed_id, 'string', 'stripe.deleteSubscriptionDiscount: subscription_exposed_id is required.'); + delete options.subscription_exposed_id; + return stripe.subscriptions.deleteDiscount(subscription_exposed_id, options,__extra); +}; + +// /v1/subscriptions/{subscription}/resume - post +exports.resumeSubscription = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const subscription = this.parseRequired(options.subscription, 'string', 'stripe.resumeSubscription: subscription is required.'); + delete options.subscription; + return stripe.subscriptions.resume(subscription, options,__extra); +}; + +// /v1/tax/calculations - post +exports.createTaxCalculation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.calculations.create(options,__extra); +}; + +// /v1/tax/calculations/{calculation}/line_items - get +exports.listTaxCalculationLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const calculation = this.parseRequired(options.calculation, 'string', 'stripe.listTaxCalculationLineItems: calculation is required.'); + delete options.calculation; + return stripe.tax.calculations.listLineItems(calculation, options,__extra); +}; + +// /v1/tax/registrations - get +exports.listTaxRegistrations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.registrations.list(options,__extra); +}; + +// /v1/tax/registrations - post +exports.createTaxRegistration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.registrations.create(options,__extra); +}; + +// /v1/tax/registrations/{id} - get +exports.retrieveTaxRegistration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTaxRegistration: id is required.'); + delete options.id; + return stripe.tax.registrations.retrieve(id, options,__extra); +}; + +// /v1/tax/registrations/{id} - post +exports.updateTaxRegistration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.updateTaxRegistration: id is required.'); + delete options.id; + return stripe.tax.registrations.update(id, options,__extra); +}; + +// /v1/tax/settings - get +exports.retrieveTaxSetting = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.settings.retrieve(options,__extra); +}; + +// /v1/tax/settings - post +exports.createTaxSetting = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.settings.create(options,__extra); +}; + +// /v1/tax/transactions/create_from_calculation - post +exports.createTaxTransactionsCreateFromCalculation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.transactions.createFromCalculation.create(options,__extra); +}; + +// /v1/tax/transactions/create_reversal - post +exports.createTaxTransactionsCreateReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tax.transactions.createReversal.create(options,__extra); +}; + +// /v1/tax/transactions/{transaction} - get +exports.retrieveTaxTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.retrieveTaxTransaction: transaction is required.'); + delete options.transaction; + return stripe.tax.transactions.retrieve(transaction, options,__extra); +}; + +// /v1/tax/transactions/{transaction}/line_items - get +exports.listTaxTransactionLineItems = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.listTaxTransactionLineItems: transaction is required.'); + delete options.transaction; + return stripe.tax.transactions.listLineItems(transaction, options,__extra); +}; + +// /v1/tax_codes - get +exports.listTaxCodes = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.taxCodes.list(options,__extra); +}; + +// /v1/tax_codes/{id} - get +exports.retrieveTaxCode = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTaxCode: id is required.'); + delete options.id; + return stripe.taxCodes.retrieve(id, options,__extra); +}; + +// /v1/tax_ids - get +exports.listTaxIds = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.taxIds.list(options,__extra); +}; + +// /v1/tax_ids - post +exports.createTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.taxIds.create(options,__extra); +}; + +// /v1/tax_ids/{id} - delete +exports.deleteTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.deleteTaxId: id is required.'); + delete options.id; + return stripe.taxIds.del(id, options,__extra); +}; + +// /v1/tax_ids/{id} - get +exports.retrieveTaxId = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTaxId: id is required.'); + delete options.id; + return stripe.taxIds.retrieve(id, options,__extra); +}; + +// /v1/tax_rates - get +exports.listTaxRates = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.taxRates.list(options,__extra); +}; + +// /v1/tax_rates - post +exports.createTaxRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.taxRates.create(options,__extra); +}; + +// /v1/tax_rates/{tax_rate} - get +exports.retrieveTaxRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const tax_rate = this.parseRequired(options.tax_rate, 'string', 'stripe.retrieveTaxRate: tax_rate is required.'); + delete options.tax_rate; + return stripe.taxRates.retrieve(tax_rate, options,__extra); +}; + +// /v1/tax_rates/{tax_rate} - post +exports.updateTaxRate = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const tax_rate = this.parseRequired(options.tax_rate, 'string', 'stripe.updateTaxRate: tax_rate is required.'); + delete options.tax_rate; + return stripe.taxRates.update(tax_rate, options,__extra); +}; + +// /v1/terminal/configurations - get +exports.listTerminalConfigurations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.configurations.list(options,__extra); +}; + +// /v1/terminal/configurations - post +exports.createTerminalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.configurations.create(options,__extra); +}; + +// /v1/terminal/configurations/{configuration} - delete +exports.deleteTerminalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.deleteTerminalConfiguration: configuration is required.'); + delete options.configuration; + return stripe.terminal.configurations.del(configuration, options,__extra); +}; + +// /v1/terminal/configurations/{configuration} - get +exports.retrieveTerminalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.retrieveTerminalConfiguration: configuration is required.'); + delete options.configuration; + return stripe.terminal.configurations.retrieve(configuration, options,__extra); +}; + +// /v1/terminal/configurations/{configuration} - post +exports.updateTerminalConfiguration = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const configuration = this.parseRequired(options.configuration, 'string', 'stripe.updateTerminalConfiguration: configuration is required.'); + delete options.configuration; + return stripe.terminal.configurations.update(configuration, options,__extra); +}; + +// /v1/terminal/connection_tokens - post +exports.createTerminalConnectionToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.connectionTokens.create(options,__extra); +}; + +// /v1/terminal/locations - get +exports.listTerminalLocations = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.locations.list(options,__extra); +}; + +// /v1/terminal/locations - post +exports.createTerminalLocation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.locations.create(options,__extra); +}; + +// /v1/terminal/locations/{location} - delete +exports.deleteTerminalLocation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const location = this.parseRequired(options.location, 'string', 'stripe.deleteTerminalLocation: location is required.'); + delete options.location; + return stripe.terminal.locations.del(location, options,__extra); +}; + +// /v1/terminal/locations/{location} - get +exports.retrieveTerminalLocation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const location = this.parseRequired(options.location, 'string', 'stripe.retrieveTerminalLocation: location is required.'); + delete options.location; + return stripe.terminal.locations.retrieve(location, options,__extra); +}; + +// /v1/terminal/locations/{location} - post +exports.updateTerminalLocation = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const location = this.parseRequired(options.location, 'string', 'stripe.updateTerminalLocation: location is required.'); + delete options.location; + return stripe.terminal.locations.update(location, options,__extra); +}; + +// /v1/terminal/readers - get +exports.listTerminalReaders = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.readers.list(options,__extra); +}; + +// /v1/terminal/readers - post +exports.createTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.terminal.readers.create(options,__extra); +}; + +// /v1/terminal/readers/{reader} - delete +exports.deleteTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.deleteTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.del(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader} - get +exports.retrieveTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.retrieveTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.retrieve(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader} - post +exports.updateTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.updateTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.update(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader}/cancel_action - post +exports.cancelActionTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.cancelActionTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.cancelAction(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader}/process_payment_intent - post +exports.processPaymentIntentTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.processPaymentIntentTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.processPaymentIntent(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader}/process_setup_intent - post +exports.processSetupIntentTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.processSetupIntentTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.processSetupIntent(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader}/refund_payment - post +exports.refundPaymentTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.refundPaymentTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.refundPayment(reader, options,__extra); +}; + +// /v1/terminal/readers/{reader}/set_reader_display - post +exports.setReaderDisplayTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.setReaderDisplayTerminalReader: reader is required.'); + delete options.reader; + return stripe.terminal.readers.setReaderDisplay(reader, options,__extra); +}; + +// /v1/test_helpers/confirmation_tokens - post +exports.createTestHelpersConfirmationToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + if (options.return_url && !options.return_url.includes('://')) { + options.return_url = this.parse('{{$_SERVER.BASE_URL}}') + options.return_url; + } + return stripe.testHelpers.confirmationTokens.create(options,__extra); +}; + +// /v1/test_helpers/customers/{customer}/fund_cash_balance - post +exports.fundCashBalanceTestHelpersCustomer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const customer = this.parseRequired(options.customer, 'string', 'stripe.fundCashBalanceTestHelpersCustomer: customer is required.'); + delete options.customer; + return stripe.testHelpers.customers.fundCashBalance(customer, options,__extra); +}; + +// /v1/test_helpers/issuing/authorizations - post +exports.createTestHelpersIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.createIssuingAuthorizations(options,__extra); +}; + +// /v1/test_helpers/issuing/authorizations/{authorization}/capture - post +exports.captureTestHelpersIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.captureTestHelpersIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.testHelpers.issuing.authorizations.capture(authorization, options,__extra); +}; + +// /v1/test_helpers/issuing/authorizations/{authorization}/expire - post +exports.expireTestHelpersIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.expireTestHelpersIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.testHelpers.issuing.authorizations.expire(authorization, options,__extra); +}; + +// /v1/test_helpers/issuing/authorizations/{authorization}/increment - post +exports.incrementTestHelpersIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.incrementTestHelpersIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.testHelpers.issuing.authorizations.increment(authorization, options,__extra); +}; + +// /v1/test_helpers/issuing/authorizations/{authorization}/reverse - post +exports.reverseTestHelpersIssuingAuthorization = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const authorization = this.parseRequired(options.authorization, 'string', 'stripe.reverseTestHelpersIssuingAuthorization: authorization is required.'); + delete options.authorization; + return stripe.testHelpers.issuing.authorizations.reverse(authorization, options,__extra); +}; + +// /v1/test_helpers/issuing/cards/{card}/shipping/deliver - post +exports.createTestHelpersIssuingCardShipping = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const card = this.parseRequired(options.card, 'string', 'stripe.createTestHelpersIssuingCardShipping: card is required.'); + delete options.card; + return stripe.testHelpers.issuing.cards.createShipping(card, options,__extra); +}; + +// /v1/test_helpers/issuing/personalization_designs/{personalization_design}/activate - post +exports.activateTestHelpersIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const personalization_design = this.parseRequired(options.personalization_design, 'string', 'stripe.activateTestHelpersIssuingPersonalizationDesign: personalization_design is required.'); + delete options.personalization_design; + return stripe.testHelpers.issuing.personalizationDesigns.activate(personalization_design, options,__extra); +}; + +// /v1/test_helpers/issuing/personalization_designs/{personalization_design}/deactivate - post +exports.deactivateTestHelpersIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const personalization_design = this.parseRequired(options.personalization_design, 'string', 'stripe.deactivateTestHelpersIssuingPersonalizationDesign: personalization_design is required.'); + delete options.personalization_design; + return stripe.testHelpers.issuing.personalizationDesigns.deactivate(personalization_design, options,__extra); +}; + +// /v1/test_helpers/issuing/personalization_designs/{personalization_design}/reject - post +exports.rejectTestHelpersIssuingPersonalizationDesign = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const personalization_design = this.parseRequired(options.personalization_design, 'string', 'stripe.rejectTestHelpersIssuingPersonalizationDesign: personalization_design is required.'); + delete options.personalization_design; + return stripe.testHelpers.issuing.personalizationDesigns.reject(personalization_design, options,__extra); +}; + +// /v1/test_helpers/issuing/transactions/create_force_capture - post +exports.createTestHelpersIssuingTransactionsCreateForceCapture = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.issuing.transactions.createForceCapture.create(options,__extra); +}; + +// /v1/test_helpers/issuing/transactions/create_unlinked_refund - post +exports.createTestHelpersIssuingTransactionsCreateUnlinkedRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.issuing.transactions.createUnlinkedRefund.create(options,__extra); +}; + +// /v1/test_helpers/issuing/transactions/{transaction}/refund - post +exports.refundTestHelpersIssuingTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transaction = this.parseRequired(options.transaction, 'string', 'stripe.refundTestHelpersIssuingTransaction: transaction is required.'); + delete options.transaction; + return stripe.testHelpers.issuing.transactions.refund(transaction, options,__extra); +}; + +// /v1/test_helpers/refunds/{refund}/expire - post +exports.expireTestHelpersRefund = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const refund = this.parseRequired(options.refund, 'string', 'stripe.expireTestHelpersRefund: refund is required.'); + delete options.refund; + return stripe.testHelpers.refunds.expire(refund, options,__extra); +}; + +// /v1/test_helpers/terminal/readers/{reader}/present_payment_method - post +exports.presentPaymentMethodTestHelpersTerminalReader = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const reader = this.parseRequired(options.reader, 'string', 'stripe.presentPaymentMethodTestHelpersTerminalReader: reader is required.'); + delete options.reader; + return stripe.testHelpers.terminal.readers.presentPaymentMethod(reader, options,__extra); +}; + +// /v1/test_helpers/test_clocks - get +exports.listTestHelpersTestClocks = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.testClocks.list(options,__extra); +}; + +// /v1/test_helpers/test_clocks - post +exports.createTestHelpersTestClock = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.testClocks.create(options,__extra); +}; + +// /v1/test_helpers/test_clocks/{test_clock} - delete +exports.deleteTestHelpersTestClock = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const test_clock = this.parseRequired(options.test_clock, 'string', 'stripe.deleteTestHelpersTestClock: test_clock is required.'); + delete options.test_clock; + return stripe.testHelpers.testClocks.del(test_clock, options,__extra); +}; + +// /v1/test_helpers/test_clocks/{test_clock} - get +exports.retrieveTestHelpersTestClock = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const test_clock = this.parseRequired(options.test_clock, 'string', 'stripe.retrieveTestHelpersTestClock: test_clock is required.'); + delete options.test_clock; + return stripe.testHelpers.testClocks.retrieve(test_clock, options,__extra); +}; + +// /v1/test_helpers/test_clocks/{test_clock}/advance - post +exports.advanceTestHelpersTestClock = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const test_clock = this.parseRequired(options.test_clock, 'string', 'stripe.advanceTestHelpersTestClock: test_clock is required.'); + delete options.test_clock; + return stripe.testHelpers.testClocks.advance(test_clock, options,__extra); +}; + +// /v1/test_helpers/treasury/inbound_transfers/{id}/fail - post +exports.failTestHelpersTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.failTestHelpersTreasuryInboundTransfer: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.inboundTransfers.fail(id, options,__extra); +}; + +// /v1/test_helpers/treasury/inbound_transfers/{id}/return - post +exports.returnTestHelpersTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.returnTestHelpersTreasuryInboundTransfer: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.inboundTransfers.return(id, options,__extra); +}; + +// /v1/test_helpers/treasury/inbound_transfers/{id}/succeed - post +exports.succeedTestHelpersTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.succeedTestHelpersTreasuryInboundTransfer: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.inboundTransfers.succeed(id, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_payments/{id}/fail - post +exports.failTestHelpersTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.failTestHelpersTreasuryOutboundPayment: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.outboundPayments.fail(id, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_payments/{id}/post - post +exports.postTestHelpersTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.postTestHelpersTreasuryOutboundPayment: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.outboundPayments.post(id, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_payments/{id}/return - post +exports.returnTestHelpersTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.returnTestHelpersTreasuryOutboundPayment: id is required.'); + delete options.id; + return stripe.testHelpers.treasury.outboundPayments.return(id, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_transfers/{outbound_transfer}/fail - post +exports.failTestHelpersTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const outbound_transfer = this.parseRequired(options.outbound_transfer, 'string', 'stripe.failTestHelpersTreasuryOutboundTransfer: outbound_transfer is required.'); + delete options.outbound_transfer; + return stripe.testHelpers.treasury.outboundTransfers.fail(outbound_transfer, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_transfers/{outbound_transfer}/post - post +exports.postTestHelpersTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const outbound_transfer = this.parseRequired(options.outbound_transfer, 'string', 'stripe.postTestHelpersTreasuryOutboundTransfer: outbound_transfer is required.'); + delete options.outbound_transfer; + return stripe.testHelpers.treasury.outboundTransfers.post(outbound_transfer, options,__extra); +}; + +// /v1/test_helpers/treasury/outbound_transfers/{outbound_transfer}/return - post +exports.returnTestHelpersTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const outbound_transfer = this.parseRequired(options.outbound_transfer, 'string', 'stripe.returnTestHelpersTreasuryOutboundTransfer: outbound_transfer is required.'); + delete options.outbound_transfer; + return stripe.testHelpers.treasury.outboundTransfers.return(outbound_transfer, options,__extra); +}; + +// /v1/test_helpers/treasury/received_credits - post +exports.createTestHelpersTreasuryReceivedCredit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.createTreasuryReceivedCredits(options,__extra); +}; + +// /v1/test_helpers/treasury/received_debits - post +exports.createTestHelpersTreasuryReceivedDebit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.testHelpers.createTreasuryReceivedDebits(options,__extra); +}; + +// /v1/tokens - post +exports.createToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.tokens.create(options,__extra); +}; + +// /v1/tokens/{token} - get +exports.retrieveToken = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const token = this.parseRequired(options.token, 'string', 'stripe.retrieveToken: token is required.'); + delete options.token; + return stripe.tokens.retrieve(token, options,__extra); +}; + +// /v1/topups - get +exports.listTopups = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.topups.list(options,__extra); +}; + +// /v1/topups - post +exports.createTopup = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.topups.create(options,__extra); +}; + +// /v1/topups/{topup} - get +exports.retrieveTopup = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const topup = this.parseRequired(options.topup, 'string', 'stripe.retrieveTopup: topup is required.'); + delete options.topup; + return stripe.topups.retrieve(topup, options,__extra); +}; + +// /v1/topups/{topup} - post +exports.updateTopup = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const topup = this.parseRequired(options.topup, 'string', 'stripe.updateTopup: topup is required.'); + delete options.topup; + return stripe.topups.update(topup, options,__extra); +}; + +// /v1/topups/{topup}/cancel - post +exports.cancelTopup = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const topup = this.parseRequired(options.topup, 'string', 'stripe.cancelTopup: topup is required.'); + delete options.topup; + return stripe.topups.cancel(topup, options,__extra); +}; + +// /v1/transfers - get +exports.listTransfers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.transfers.list(options,__extra); +}; + +// /v1/transfers - post +exports.createTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.transfers.create(options,__extra); +}; + +// /v1/transfers/{id}/reversals - get +exports.listTransferReversals = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.listTransferReversals: id is required.'); + delete options.id; + return stripe.transfers.listReversals(id, options,__extra); +}; + +// /v1/transfers/{id}/reversals - post +exports.createTransferReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.createTransferReversal: id is required.'); + delete options.id; + return stripe.transfers.createReversal(id, options,__extra); +}; + +// /v1/transfers/{transfer} - get +exports.retrieveTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transfer = this.parseRequired(options.transfer, 'string', 'stripe.retrieveTransfer: transfer is required.'); + delete options.transfer; + return stripe.transfers.retrieve(transfer, options,__extra); +}; + +// /v1/transfers/{transfer} - post +exports.updateTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const transfer = this.parseRequired(options.transfer, 'string', 'stripe.updateTransfer: transfer is required.'); + delete options.transfer; + return stripe.transfers.update(transfer, options,__extra); +}; + +// /v1/transfers/{transfer}/reversals/{id} - get +exports.retrieveTransferReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTransferReversal: id is required.'); + delete options.id; + const transfer = this.parseRequired(options.transfer, 'string', 'stripe.retrieveTransferReversal: transfer is required.'); + delete options.transfer; + return stripe.transfers.retrieveReversal(id, transfer, options,__extra); +}; + +// /v1/transfers/{transfer}/reversals/{id} - post +exports.updateTransferReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.updateTransferReversal: id is required.'); + delete options.id; + const transfer = this.parseRequired(options.transfer, 'string', 'stripe.updateTransferReversal: transfer is required.'); + delete options.transfer; + return stripe.transfers.updateReversal(id, transfer, options,__extra); +}; + +// /v1/treasury/credit_reversals - get +exports.listTreasuryCreditReversals = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryCreditReversals: financial_account is required.'); + return stripe.treasury.creditReversals.list(options,__extra); +}; + +// /v1/treasury/credit_reversals - post +exports.createTreasuryCreditReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.creditReversals.create(options,__extra); +}; + +// /v1/treasury/credit_reversals/{credit_reversal} - get +exports.retrieveTreasuryCreditReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const credit_reversal = this.parseRequired(options.credit_reversal, 'string', 'stripe.retrieveTreasuryCreditReversal: credit_reversal is required.'); + delete options.credit_reversal; + return stripe.treasury.creditReversals.retrieve(credit_reversal, options,__extra); +}; + +// /v1/treasury/debit_reversals - get +exports.listTreasuryDebitReversals = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryDebitReversals: financial_account is required.'); + return stripe.treasury.debitReversals.list(options,__extra); +}; + +// /v1/treasury/debit_reversals - post +exports.createTreasuryDebitReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.debitReversals.create(options,__extra); +}; + +// /v1/treasury/debit_reversals/{debit_reversal} - get +exports.retrieveTreasuryDebitReversal = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const debit_reversal = this.parseRequired(options.debit_reversal, 'string', 'stripe.retrieveTreasuryDebitReversal: debit_reversal is required.'); + delete options.debit_reversal; + return stripe.treasury.debitReversals.retrieve(debit_reversal, options,__extra); +}; + +// /v1/treasury/financial_accounts - get +exports.listTreasuryFinancialAccounts = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.financialAccounts.list(options,__extra); +}; + +// /v1/treasury/financial_accounts - post +exports.createTreasuryFinancialAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.financialAccounts.create(options,__extra); +}; + +// /v1/treasury/financial_accounts/{financial_account} - get +exports.retrieveTreasuryFinancialAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.retrieveTreasuryFinancialAccount: financial_account is required.'); + delete options.financial_account; + return stripe.treasury.financialAccounts.retrieve(financial_account, options,__extra); +}; + +// /v1/treasury/financial_accounts/{financial_account} - post +exports.updateTreasuryFinancialAccount = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.updateTreasuryFinancialAccount: financial_account is required.'); + delete options.financial_account; + return stripe.treasury.financialAccounts.update(financial_account, options,__extra); +}; + +// /v1/treasury/financial_accounts/{financial_account}/features - get +exports.retrieveTreasuryFinancialAccountFeature = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.retrieveTreasuryFinancialAccountFeature: financial_account is required.'); + delete options.financial_account; + return stripe.treasury.financialAccounts.retrieveFeature(financial_account, options,__extra); +}; + +// /v1/treasury/financial_accounts/{financial_account}/features - post +exports.createTreasuryFinancialAccountFeature = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.createTreasuryFinancialAccountFeature: financial_account is required.'); + delete options.financial_account; + return stripe.treasury.financialAccounts.createFeature(financial_account, options,__extra); +}; + +// /v1/treasury/inbound_transfers - get +exports.listTreasuryInboundTransfers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryInboundTransfers: financial_account is required.'); + return stripe.treasury.inboundTransfers.list(options,__extra); +}; + +// /v1/treasury/inbound_transfers - post +exports.createTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.inboundTransfers.create(options,__extra); +}; + +// /v1/treasury/inbound_transfers/{id} - get +exports.retrieveTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryInboundTransfer: id is required.'); + delete options.id; + return stripe.treasury.inboundTransfers.retrieve(id, options,__extra); +}; + +// /v1/treasury/inbound_transfers/{inbound_transfer}/cancel - post +exports.cancelTreasuryInboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const inbound_transfer = this.parseRequired(options.inbound_transfer, 'string', 'stripe.cancelTreasuryInboundTransfer: inbound_transfer is required.'); + delete options.inbound_transfer; + return stripe.treasury.inboundTransfers.cancel(inbound_transfer, options,__extra); +}; + +// /v1/treasury/outbound_payments - get +exports.listTreasuryOutboundPayments = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryOutboundPayments: financial_account is required.'); + return stripe.treasury.outboundPayments.list(options,__extra); +}; + +// /v1/treasury/outbound_payments - post +exports.createTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.outboundPayments.create(options,__extra); +}; + +// /v1/treasury/outbound_payments/{id} - get +exports.retrieveTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryOutboundPayment: id is required.'); + delete options.id; + return stripe.treasury.outboundPayments.retrieve(id, options,__extra); +}; + +// /v1/treasury/outbound_payments/{id}/cancel - post +exports.cancelTreasuryOutboundPayment = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.cancelTreasuryOutboundPayment: id is required.'); + delete options.id; + return stripe.treasury.outboundPayments.cancel(id, options,__extra); +}; + +// /v1/treasury/outbound_transfers - get +exports.listTreasuryOutboundTransfers = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryOutboundTransfers: financial_account is required.'); + return stripe.treasury.outboundTransfers.list(options,__extra); +}; + +// /v1/treasury/outbound_transfers - post +exports.createTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.treasury.outboundTransfers.create(options,__extra); +}; + +// /v1/treasury/outbound_transfers/{outbound_transfer} - get +exports.retrieveTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const outbound_transfer = this.parseRequired(options.outbound_transfer, 'string', 'stripe.retrieveTreasuryOutboundTransfer: outbound_transfer is required.'); + delete options.outbound_transfer; + return stripe.treasury.outboundTransfers.retrieve(outbound_transfer, options,__extra); +}; + +// /v1/treasury/outbound_transfers/{outbound_transfer}/cancel - post +exports.cancelTreasuryOutboundTransfer = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const outbound_transfer = this.parseRequired(options.outbound_transfer, 'string', 'stripe.cancelTreasuryOutboundTransfer: outbound_transfer is required.'); + delete options.outbound_transfer; + return stripe.treasury.outboundTransfers.cancel(outbound_transfer, options,__extra); +}; + +// /v1/treasury/received_credits - get +exports.listTreasuryReceivedCredits = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryReceivedCredits: financial_account is required.'); + return stripe.treasury.receivedCredits.list(options,__extra); +}; + +// /v1/treasury/received_credits/{id} - get +exports.retrieveTreasuryReceivedCredit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryReceivedCredit: id is required.'); + delete options.id; + return stripe.treasury.receivedCredits.retrieve(id, options,__extra); +}; + +// /v1/treasury/received_debits - get +exports.listTreasuryReceivedDebits = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryReceivedDebits: financial_account is required.'); + return stripe.treasury.receivedDebits.list(options,__extra); +}; + +// /v1/treasury/received_debits/{id} - get +exports.retrieveTreasuryReceivedDebit = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryReceivedDebit: id is required.'); + delete options.id; + return stripe.treasury.receivedDebits.retrieve(id, options,__extra); +}; + +// /v1/treasury/transaction_entries - get +exports.listTreasuryTransactionEntries = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryTransactionEntries: financial_account is required.'); + return stripe.treasury.transactionEntries.list(options,__extra); +}; + +// /v1/treasury/transaction_entries/{id} - get +exports.retrieveTreasuryTransactionEntrie = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryTransactionEntrie: id is required.'); + delete options.id; + return stripe.treasury.transactionEntries.retrieve(id, options,__extra); +}; + +// /v1/treasury/transactions - get +exports.listTreasuryTransactions = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const financial_account = this.parseRequired(options.financial_account, 'string', 'stripe.listTreasuryTransactions: financial_account is required.'); + return stripe.treasury.transactions.list(options,__extra); +}; + +// /v1/treasury/transactions/{id} - get +exports.retrieveTreasuryTransaction = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const id = this.parseRequired(options.id, 'string', 'stripe.retrieveTreasuryTransaction: id is required.'); + delete options.id; + return stripe.treasury.transactions.retrieve(id, options,__extra); +}; + +// /v1/webhook_endpoints - get +exports.listWebhookEndpoints = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.webhookEndpoints.list(options,__extra); +}; + +// /v1/webhook_endpoints - post +exports.createWebhookEndpoint = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + return stripe.webhookEndpoints.create(options,__extra); +}; + +// /v1/webhook_endpoints/{webhook_endpoint} - delete +exports.deleteWebhookEndpoint = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const webhook_endpoint = this.parseRequired(options.webhook_endpoint, 'string', 'stripe.deleteWebhookEndpoint: webhook_endpoint is required.'); + delete options.webhook_endpoint; + return stripe.webhookEndpoints.del(webhook_endpoint, options,__extra); +}; + +// /v1/webhook_endpoints/{webhook_endpoint} - get +exports.retrieveWebhookEndpoint = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const webhook_endpoint = this.parseRequired(options.webhook_endpoint, 'string', 'stripe.retrieveWebhookEndpoint: webhook_endpoint is required.'); + delete options.webhook_endpoint; + return stripe.webhookEndpoints.retrieve(webhook_endpoint, options,__extra); +}; + +// /v1/webhook_endpoints/{webhook_endpoint} - post +exports.updateWebhookEndpoint = function(options) { + options = this.parse(options); + var __extra = options.__extra; + if (options.__extra) { + __extra = convertExtraOptions(__extra); + delete options.__extra; + } + + const webhook_endpoint = this.parseRequired(options.webhook_endpoint, 'string', 'stripe.updateWebhookEndpoint: webhook_endpoint is required.'); + delete options.webhook_endpoint; + return stripe.webhookEndpoints.update(webhook_endpoint, options,__extra); +}; diff --git a/lib/modules/upload.js b/lib/modules/upload.js new file mode 100644 index 0000000..feb5b79 --- /dev/null +++ b/lib/modules/upload.js @@ -0,0 +1,118 @@ +const fs = require('fs-extra'); +const debug = require('debug')('server-connect:upload'); +const { join, basename, extname } = require('path'); +const { toAppPath, toSystemPath, toSiteUrl, parseTemplate, getUniqFile } = require('../core/path'); +const diacritics = require('../core/diacritics'); + +module.exports = { + + upload: async function(options) { + let self = this; + let fields = this.parse(options.fields || this.parse('{{$_POST}}')); + let path = this.parseOptional(options.path, 'string', '/uploads'); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let createPath = this.parseOptional(options.createPath, 'boolean', true); + let throwErrors = this.parseOptional(options.throwErrors, 'boolean', false); + let template = typeof options.template == 'string' ? options.template : false; //this.parseOptional(options.template, 'string', ''); + let replaceSpace = this.parseOptional(options.replaceSpace, 'boolean', false); + let asciiOnly = this.parseOptional(options.asciiOnly, 'boolean', false); + let replaceDiacritics = this.parseOptional(options.replaceDiacritics, 'boolean', false); + + if (throwErrors) { + for (let field in this.req.files) { + if (Array.isArray(this.req.files[field])) { + for (let file of this.req.files[field]) { + if (file.truncated) { + throw new Error('Some files failed to upload.'); + } + } + } else { + let file = this.req.files[field]; + if (file.truncated) { + throw new Error('Some files failed to upload.'); + } + } + } + } + + path = toSystemPath(path); + + if (!fs.existsSync(path)) { + if (createPath) { + await fs.ensureDir(path); + } else { + throw new Error(`Upload path doesn't exist.`); + } + } + + let files = this.req.files; + let uploaded = []; + + if (files) { + await processFields(fields); + } + + return typeof fields == 'string' ? (uploaded.length ? uploaded[0] : null) : uploaded; + + async function processFields(fields) { + debug('Process fields: %O', fields); + + if (typeof fields == 'object') { + for (let i in fields) { + await processFields(fields[i]); + } + } else if (typeof fields == 'string' && files[fields]) { + let processing = files[fields]; + + if (!Array.isArray(processing)) processing = [processing]; + + for (let file of processing) { + debug('Processing file: %O', file); + + if (!file.processed) { + let name = file.name.replace(/[\x00-\x1f\x7f!%&#@$*()?:,;"'<>^`|+={}\[\]\\\/]/g, ''); + + if (replaceSpace) name = name.replace(/\s+/g, '_'); + if (replaceDiacritics) name = diacritics.replace(name); + if (asciiOnly) name = name.replace(/[^\x00-\x7e]/g, ''); + + let filepath = join(path, name); + + if (template) { + let _template = self.parse(template, self.scope.create({ + file: basename(filepath), + name: basename(filepath, extname(filepath)), + ext: extname(filepath) + })); + filepath = parseTemplate(filepath, _template); + } + + if (fs.existsSync(filepath)) { + if (overwrite) { + await fs.unlink(filepath); + } else { + filepath = getUniqFile(filepath); + } + } + + await file.mv(filepath); + + uploaded.push({ + name: basename(filepath), + path: toAppPath(filepath), + url: toSiteUrl(filepath), + type: file.mimetype, + size: file.size + }); + + file.processed = true; + } + } + } + + return uploaded; + } + + } + +}; \ No newline at end of file diff --git a/lib/modules/validator.js b/lib/modules/validator.js new file mode 100644 index 0000000..2c865a1 --- /dev/null +++ b/lib/modules/validator.js @@ -0,0 +1,18 @@ +const validator = require('../validator'); + +module.exports = { + + validate: function(options) { + const data = this.parse(options.data); + const noError = this.parseOptional(options.noError, 'boolean', false); + + return validator.validateData(this, data, noError); + }, + + error: function(options, name) { + const message = this.parseRequired(options.message, 'string', 'validator.error: message is required.'); + const error = { [options.fieldName ? 'form' : 'data']: {[options.fieldName || name]: message }}; + this.res.status(400).json(error); + }, + +}; \ No newline at end of file diff --git a/lib/modules/zip.js b/lib/modules/zip.js new file mode 100644 index 0000000..5abd1f7 --- /dev/null +++ b/lib/modules/zip.js @@ -0,0 +1,114 @@ +const fs = require('fs-extra'); +const debug = require('debug')('server-connect:zip'); +const { basename } = require('path'); +const { toSystemPath, toAppPath, getFilesArray, getUniqFile } = require('../core/path'); +const openZip = (zipfile) => require('unzipper').Open.file(zipfile); + +const Zip = function(zipfile, options) { + this.output = fs.createWriteStream(zipfile); + this.archive = require('archiver')('zip', options); + this.archive.on('warning', (err) => { + if (err.code == 'ENOENT') { + debug('error: %O', err); + } else { + throw err; + } + }); + this.archive.on('error', (err) => { + throw err; + }); + this.archive.pipe(this.output); +}; + +Zip.prototype.addFile = function(file) { + this.archive.file(file, { name: basename(file) }); +}; + +Zip.prototype.addDir = function(dir, recursive) { + if (recursive) { + this.archive.directory(dir, false); + } else { + this.archive.glob('*', { cwd: dir }); + } +}; + +Zip.prototype.save = function() { + return new Promise((resolve, reject) => { + this.output.on('close', resolve); + this.archive.on('error', reject); + this.archive.finalize(); + }); +}; + +module.exports = { + + zip: async function(options) { + let zipfile = toSystemPath(this.parseRequired(options.zipfile, 'string', 'zip.zip: zipfile is required.')); + let files = getFilesArray(this.parseRequired(options.files, 'object', 'zip.zip: files is requires.')); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let comment = this.parseOptional(options.comment, 'string', ''); + + if (!overwrite) zipfile = getUniqFile(zipfile); + + const zip = new Zip(zipfile, { comment }); + + for (let file of files) { + zip.addFile(file); + } + + await zip.save(); + + return toAppPath(zipfile); + }, + + zipdir: async function(options) { + let zipfile = toSystemPath(this.parseRequired(options.zipfile, 'string', 'zip.zipdir: zipfile is required.')); + let path = toSystemPath(this.parseRequired(options.path, 'string', 'zip.zipdir: path is required.')); + let overwrite = this.parseOptional(options.overwrite, 'boolean', false); + let recursive = this.parseOptional(options.recursive, 'boolean', false); + let comment = this.parseOptional(options.comment, 'string', ''); + + if (!overwrite) zipfile = getUniqFile(zipfile); + + const zip = new Zip(zipfile, { comment }); + + zip.addDir(path, recursive); + + await zip.save(); + + return toAppPath(zipfile); + }, + + unzip: async function(options) { + let zipfile = toSystemPath(this.parseRequired(options.zipfile, 'string', 'zip.unzip: zipfile is required.')); + let dest = toSystemPath(this.parseRequired(options.destination, 'string', 'zip.unzip: destination is required.')); + let overwrite = this.parseOptional(options.overwrite, 'boolean', true); + + // TODO: overwrite option + await openZip(zipfile).then(d => d.extract({ path: dest, concurrency: 4 })); + + return true; + }, + + dir: async function(options) { + let zipfile = toSystemPath(this.parseRequired(options.zipfile, 'string', 'zip.dir: zipfile is required.')); + + return openZip(zipfile).then(d => { + return d.files.map(file => ({ + type: file.type == 'Directory' ? 'dir': 'file', + path: file.path, + size: file.uncompressedSize, + compressedSize: file.compressedSize, + compressionMethod: file.compressionMethod == 8 ? 'Deflate' : 'None', + lastModified: file.lastModifiedDateTime + })); + }); + }, + + comment: async function(options) { + let zipfile = toSystemPath(this.parseRequired(options.zipfile, 'string', 'zip.comment: zipfile is required.')); + + return openZip(zipfile).then(d => d.comment); + }, + +}; \ No newline at end of file diff --git a/lib/oauth/index.js b/lib/oauth/index.js new file mode 100644 index 0000000..e80a480 --- /dev/null +++ b/lib/oauth/index.js @@ -0,0 +1,170 @@ +const { http, https } = require('follow-redirects'); +const querystring = require('querystring'); +const services = require('./services'); +const crypto = require('crypto'); + +class OAuth2 { + + constructor(app, opts, name) { + this.app = app; + this.name = name; + this.opts = opts; + + if (opts.service) { + Object.assign(this.opts, services[opts.service]); + } + + this.access_token = opts.access_token || app.getSession(`${name}_access_token`); + this.refresh_token = opts.refresh_token || app.getSession(`${name}_refresh_token`); + + if (this.access_token) { + let expires = app.getSession(`${name}_expires`); + + if (expires && expires < expires_in(-10)) { + this.access_token = null; + + app.removeSession(`${name}_access_token`); + app.removeSession(`${name}_expires`); + + if (this.refresh_token) { + this.refreshToken(this.refresh_token); + } + } + } + } + + async init() { + if (this.opts.jwt_bearer && !this.access_token) { + const assertion = this.app.getJSONWebToken(this.opts.jwt_bearer); + let response = await this.grant('urn:ietf:params:oauth:grant-type:jwt-bearer', { assertion }); + this.access_token = response.access_token; + } + + if (this.opts.client_credentials && !this.access_token) { + let response = await this.grant('client_credentials'); + this.access_token = response.access_token; + } + } + + async authorize(scopes = [], params = {}) { + let query = this.app.req.query; + + if (query.state && query.state == this.app.getSession(`${this.name}_state`)) { + this.app.removeSession(`${this.name}_state`); + + if (query.error) { + throw new Error(query.error_message || query.error); + } + + if (query.code) { + let params = { + redirect_uri: redirect_uri(this.app.req), + code: query.code + }; + + if (this.app.getSession(`${this.name}_code_verifier`)) { + params.code_verifier = this.app.getSession(`${this.name}_code_verifier`); + } + + return this.grant('authorization_code', params); + } + } + + if (this.opts.pkce || params.code_verifier) { + const code_verifier = params.code_verifier || crypto.randomBytes(40).toString('hex'); + this.app.setSession(`${this.name}_code_verifier`, code_verifier); + params.code_challenge_method = 'S256'; + params.code_challenge = crypto.createHash('sha256').update(code_verifier).digest('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); + } + + params = Object.assign({}, { + response_type: 'code', + client_id: this.opts.client_id, + scope: scopes.join(this.opts.scope_separator), + redirect_uri: redirect_uri(this.app.req), + state: generate_state() + }, this.opts.params, params); + + this.app.setSession(`${this.name}_state`, params.state); + + this.app.res.redirect(build_url(this.opts.auth_endpoint, params)); + } + + async refreshToken(refresh_token) { + return this.grant('refresh_token', { refresh_token }); + } + + async grant(type, params) { + const endpoint = new URL(this.opts.token_endpoint); + + return new Promise((resolve, reject) => { + const req = (endpoint.protocol == 'https:' ? https : http).request(endpoint, { + method: 'POST' + }, res => { + let body = ''; + res.setEncoding('utf8'); + res.on('data', chunk => body += chunk); + res.on('end', () => { + if (res.statusCode >= 400) { + return reject(new Error(`Http status code ${res.statusCode}. ${body}`)); + } + + try { + body = JSON.parse(body); + } catch (e) { } + + if (!body.access_token) { + return reject(new Error(`Http response has no access_token. ${JSON.stringify(body)}`)) + } + + this.app.setSession(`${this.name}_access_token`, body.access_token); + this.access_token = body.access_token; + + if (body.expires_in) { + this.app.setSession(`${this.name}_expires`, expires_in(body.expires_in)); + } + + if (body.refresh_token) { + this.app.setSession(`${this.name}_refresh_token`, body.refresh_token); + this.refresh_token = body.refresh_token; + } + + resolve(body); + }); + }); + + const body = querystring.stringify(Object.assign({ + grant_type: type, + client_id: this.opts.client_id, + client_secret: this.opts.client_secret + }, params)); + + req.on('error', reject); + req.setHeader('Content-Type', 'application/x-www-form-urlencoded'); + req.setHeader('Content-Length', body.length); // required by azure endpoint + req.write(body); + req.end(); + }); + } + +} + +function build_url(url, params) { + return url + (url.indexOf('?') != -1 ? '&' : '?') + querystring.stringify(params); +} + +function redirect_uri(req) { + let hasProxy = !!req.get('x-forwarded-host'); + return `${req.protocol}://${hasProxy ? req.hostname : req.get('host')}${req.path}`; +} + +function expires_in(s) { + return ~~(Date.now() / 1000) + s; +} + +function generate_state() { + const { v4: uuidv4 } = require('uuid'); + return uuidv4(); +} + +module.exports = OAuth2; \ No newline at end of file diff --git a/lib/oauth/services.js b/lib/oauth/services.js new file mode 100644 index 0000000..f23dda7 --- /dev/null +++ b/lib/oauth/services.js @@ -0,0 +1,98 @@ +module.exports = { + + 'google': { + auth_endpoint: 'https://accounts.google.com/o/oauth2/v2/auth', + token_endpoint: 'https://www.googleapis.com/oauth2/v4/token', + params: { access_type: 'offline' } + }, + + 'facebook': { + auth_endpoint: 'https://www.facebook.com/v3.2/dialog/oauth', + token_endpoint: 'https://graph.facebook.com/v3.2/oauth/access_token' + }, + + 'linkedin': { + auth_endpoint: 'https://www.linkedin.com/oauth/v2/authorization', + token_endpoint: 'https://www.linkedin.com/oauth/v2/accessToken' + }, + + 'github': { + auth_endpoint: 'https://github.com/login/oauth/authorize', + token_endpoint: 'https://github.com/login/oauth/access_token' + }, + + 'instagram': { + auth_endpoint: 'https://api.instagram.com/oauth/authorize/', + token_endpoint: 'https://api.instagram.com/oauth/access_token' + }, + + 'amazon': { + auth_endpoint: 'https://www.amazon.com/ap/oa', + token_endpoint: 'https://api.amazon.com/auth/o2/token' + }, + + 'dropbox': { + auth_endpoint: 'https://www.dropbox.com/oauth2/authorize', + token_endpoint: 'https://api.dropbox.com/oauth2/token', + scope_separator: ',' + }, + + 'foursquare': { + auth_endpoint: 'https://foursquare.com/oauth2/authenticate', + token_endpoint: 'https://foursquare.com/oauth2/access_token' + }, + + 'imgur': { + auth_endpoint: 'https://api.imgur.com/oauth2/authorize', + token_endpoint: 'https://api.imgur.com/oauth2/token' + }, + + 'wordpress': { + auth_endpoint: 'https://public-api.wordpress.com/oauth2/authorize', + token_endpoint: 'https://public-api.wordpress.com/oauth2/token' + }, + + 'spotify': { + auth_endpoint: 'https://accounts.spotify.com/authorize', + token_endpoint: 'https://accounts.spotify.com/api/token' + }, + + 'slack': { + auth_endpoint: 'https://slack.com/oauth/authorize', + token_endpoint: 'https://slack.com/api/oauth.access' + }, + + 'reddit': { + auth_endpoint: 'https://ssl.reddit.com/api/v1/authorize', + token_endpoint: 'https://ssl.reddit.com/api/v1/access_token', + scope_separator: ',' + }, + + 'twitch': { + auth_endpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', + token_endpoint: 'https://api.twitch.tv/kraken/oauth2/token' + }, + + 'paypal': { + auth_endpoint: 'https://identity.x.com/xidentity/resources/authorize', + token_endpoint: 'https://identity.x.com/xidentity/oauthtokenservice' + }, + + 'pinterest': { + auth_endpoint: 'https://api.pinterest.com/oauth/', + token_endpoint: 'https://api.pinterest.com/v1/oauth/token', + scope_separator: ',' + }, + + 'stripe': { + auth_endpoint: 'https://connect.stripe.com/oauth/authorize', + token_endpoint: 'https://connect.stripe.com/oauth/token', + scope_separator: ',' + }, + + 'coinbase': { + auth_endpoint: 'https://www.coinbase.com/oauth/authorize', + token_endpoint: 'https://www.coinbase.com/oauth/token' + } + +}; \ No newline at end of file diff --git a/lib/server.js b/lib/server.js new file mode 100644 index 0000000..04d1b2d --- /dev/null +++ b/lib/server.js @@ -0,0 +1,110 @@ +if (process.env.NODE_ENV !== 'production') { + require('dotenv').config(); +} + +process.on('uncaughtException', (e) => { + // prevent errors from killing the server and just log them + console.error(e); +}); + +const config = require('./setup/config'); +const debug = require('debug')('server-connect:server'); +const secure = require('./setup/secure'); +const routes = require('./setup/routes'); +const sockets = require('./setup/sockets'); +const upload = require('./setup/upload'); +const cron = require('./setup/cron'); +const http = require('http'); +const express = require('express'); +const endmw = require('express-end'); +const cookieParser = require('cookie-parser'); +const session = require('./setup/session'); //require('express-session')(Object.assign({ secret: config.secret }, config.session)); +const cors = require('cors'); +const app = express(); + +app.set('trust proxy', true); +app.set('view engine', 'ejs'); +app.set('view options', { root: 'views', async: true }); + +app.disable('x-powered-by') + +if (config.compression) { + const compression = require('compression'); + app.use(compression()); +} + +if (config.abortOnDisconnect) { + app.use((req, res, next) => { + req.isDisconnected = false; + req.on('close', () => { + req.isDisconnected = true; + }); + + next(); + }); +} + +app.use(cors(config.cors)); +app.use(express.static('public', config.static)); +app.use(express.urlencoded({ extended: true })); +app.use(express.json({ + verify: (req, res, buf) => { + req.rawBody = buf.toString() + } +})); +app.use(cookieParser(config.secret)); +app.use(session); +app.use(endmw); + +upload(app); +secure(app); +routes(app); + +const server = http.createServer(app); +const io = sockets(server, session); + +// Make sockets global available +global.io = io; + +module.exports = { + server, app, io, + start: function(port) { + // We add the 404 and 500 routes as last + app.use((req, res) => { + // if user has a custom 404 page, redirect to it + if (req.accepts('html') && req.url != '/404' && app.get('has404')) { + //res.redirect(303, '/404'); + req.url = '/404'; + app.handle(req, res); + } else { + res.status(404).json({ + status: '404', + message: `${req.url} not found.` + }); + } + }); + + app.use((err, req, res, next) => { + debug(`Got error? %O`, err); + // if user has a custom 500 page, redirect to it + if (req.accepts('html') && req.url != '/500' && app.get('has500')) { + //res.redirect(303, '/500'); + req.url = '/500'; + app.handle(req, res); + } else { + res.status(500).json({ + status: '500', + code: config.debug ? err.code : undefined, + message: config.debug ? err.message || err : 'A server error occured, to see the error enable the DEBUG flag.', + stack: config.debug ? err.stack : undefined, + }); + } + }); + + cron.start(); + + server.listen(port || config.port, () => { + console.log(`App listening at http://localhost:${config.port}`); + }); + } +}; diff --git a/lib/setup/config.js b/lib/setup/config.js new file mode 100644 index 0000000..258d8c9 --- /dev/null +++ b/lib/setup/config.js @@ -0,0 +1,105 @@ +const package = require('../../package.json'); +const fs = require('fs-extra'); +const debug = require('debug')('server-connect:setup:config'); +const { toSystemPath } = require('../core/path'); +const { mergeDeep } = require('../core/util'); +const Parser = require('../core/parser'); +const Scope = require('../core/scope'); + +const config = { + port: process.env.PORT || 3000, + debug: false, + secret: 'Need to be set', + tmpFolder: '/tmp', + abortOnDisconnect: false, + createApiRoutes: true, + compression: true, + redis: false, + cron: true, + static: { + index: false, + }, + session: { + name: package.name + '.sid', + resave: false, + saveUninitialized: false, + store: { $type: 'memory', ttl: 86400 }, + }, + cors: { // see https://github.com/expressjs/cors + origin: false, + methods: 'GET,POST', + credentials: true, + }, + csrf: { + enabled: false, + exclude: 'GET,HEAD,OPTIONS', + }, + rateLimit: { + enabled: false, + duration: 60, // duration of 60 second (1 minute) + points: 100, // limit to 100 requests per minute + private: { + provider: '', // security provider name + duration: 60, // duration of 60 second (1 minute) + points: 1000, // limit to 1000 requests per minute + }, + }, + globals: {}, + rateLimiter: {}, + mail: {}, + auth: {}, + oauth: {}, + db: {}, + s3: {}, + jwt: {}, + stripe: {}, + env: {}, +}; + +if (fs.existsSync('app/config/config.json')) { + mergeDeep(config, fs.readJSONSync('app/config/config.json')) +} + +if (fs.existsSync('app/config/user_config.json')) { + mergeDeep(config, fs.readJSONSync('app/config/user_config.json')); +} + +// folders are site relative +config.tmpFolder = toSystemPath(config.tmpFolder); + +if (config.env) { + for (let key in config.env) { + if (!Object.hasOwn(process.env, key)) { + process.env[key] = config.env[key]; + } else if (config.debug) { + debug(`"${key}" is already defined in \`process.env\` and will not be overwritten`); + } + } +} + +Parser.parseValue(config, new Scope({ + $_ENV: process.env +})); + +// we change the cors config a bit, * will become true +// and we split string on comma for multiple origins +if (typeof config.cors?.origin == 'string') { + if (config.cors.origin === '*') { + config.cors.origin = true; + } else if (config.cors.origin.includes(',')) { + config.cors.origin = config.cors.origin.split(/\s*,\s*/); + } +} + +if (config.debug) { + require('debug').enable(typeof config.debug == 'string' ? config.debug : 'server-connect:*'); +} + +if (config.redis) { + const Redis = require('ioredis'); + global.redisClient = new Redis(config.redis === true ? 'redis://redis' : config.redis); +} + +debug(config); + +module.exports = config; \ No newline at end of file diff --git a/lib/setup/cron.js b/lib/setup/cron.js new file mode 100644 index 0000000..30115fc --- /dev/null +++ b/lib/setup/cron.js @@ -0,0 +1,60 @@ +const fs = require('fs-extra'); +const { isEmpty } = require('./util'); +const config = require('./config'); +const debug = require('debug')('server-connect:cron'); + +exports.start = () => { + if (!config.cron || isEmpty('app/schedule')) return; + + debug('Start schedule'); + + processEntries('app/schedule'); +}; + +function processEntries(path) { + const schedule = require('node-schedule'); + const entries = fs.readdirSync(path, { withFileTypes: true }); + + for (let entry of entries) { + if (entry.isFile() && entry.name.endsWith('.json')) { + try { + const job = fs.readJSONSync(`${path}/${entry.name}`); + const rule = job.settings.options.rule; + + debug(`Adding schedule ${entry.name}`); + + if (rule == '@reboot') { + setImmediate(exec(job.exec)); + } else { + schedule.scheduleJob(rule, exec(job.exec)) + } + } catch (e) { + console.error(e); + } + } else if (entry.isDirectory()) { + processEntries(`${path}/${entry.name}`); + } + } +} + +function exec(action) { + return async () => { + const App = require('../core/app'); + const app = new App({ + params: {}, + session: {}, + cookies: {}, + signedCookies: {}, + query: {}, + headers: {} + }, { + headersSent: false, + set() {}, + status() { return this; }, + send() { this.headersSent = true; }, + json() { this.headersSent = true; }, + redirect() { this.headersSent = true; } + }); + return app.define(action, true); + } +} \ No newline at end of file diff --git a/lib/setup/database.js b/lib/setup/database.js new file mode 100644 index 0000000..b41f008 --- /dev/null +++ b/lib/setup/database.js @@ -0,0 +1,36 @@ +// allow Wappler to run queries on the server + +const crypto = require('crypto'); +const App = require("../core/app"); +const config = require('./config'); + +module.exports = function (app) { + app.post('/_db_', async (req, res) => { + const time = req.get('auth-time'); + const hash = req.get('auth-hash'); + if (!time || !hash || isNaN(time)) return res.status(400).json({ error: 'Auth headers missing' }); + + const diff = Math.abs(Date.now() - time) / 1000; + if (diff > 60) return res.status(400).json({ error: 'Auth time diff to high' }); + if (hash !== crypto.createHmac('sha256', config.secret).update(req.rawBody).digest('hex')) return res.status(400).json({ error: 'Auth hash invalid' }); + + const sc = new App(req, res); + const db = sc.getDbConnection(req.body.name); + + let results = []; + + try { + results = await db.raw(req.body.query); + } catch (error) { + return res.json({ error: error.sqlMessage }); + } + + if (db.client.config.client == 'mysql' || db.client.config.client == 'mysql2') { + results = results[0]; + } else if (db.client.config.client == 'postgres' || db.client.config.client == 'redshift') { + results = results.rows; + } + + res.json({ results }); + }); +} diff --git a/lib/setup/redis.js b/lib/setup/redis.js new file mode 100644 index 0000000..4bfa262 --- /dev/null +++ b/lib/setup/redis.js @@ -0,0 +1,9 @@ +const config = require('./config'); + +if (config.redis) { + const Redis = require('ioredis'); + //global.redisClient = redis.createClient(config.redis === true ? 'redis://redis' : config.redis); + global.redisClient = new Redis(config.redis === true ? 'redis://redis' : config.redis); +} + +module.exports = global.redisClient; \ No newline at end of file diff --git a/lib/setup/routes.js b/lib/setup/routes.js new file mode 100644 index 0000000..9602b34 --- /dev/null +++ b/lib/setup/routes.js @@ -0,0 +1,298 @@ +const fs = require('fs-extra'); +const debug = require('debug')('server-connect:setup:routes'); +const config = require('./config'); +const { map } = require('../core/async'); +const { posix, extname } = require('path'); +const { cache, serverConnect, templateView } = require('../core/middleware'); +const database = require('./database'); +const webhooks = require('./webhooks'); + +module.exports = async function (app) { + app.use((req, res, next) => { + req.fragment = (req.headers['accept'] || '*/*').includes('fragment'); + next(); + }); + + if (fs.existsSync('extensions/server_connect/routes')) { + const entries = fs.readdirSync('extensions/server_connect/routes', { withFileTypes: true }); + + for (let entry of entries) { + if (entry.isFile() && extname(entry.name) == '.js') { + let hook = require(`../../extensions/server_connect/routes/${entry.name}`); + if (hook.before) hook.before(app); + if (hook.handler) hook.handler(app); + debug(`Custom router ${entry.name} loaded`); + } + } + } + + database(app); + webhooks(app); + + if (config.createApiRoutes) { + fs.ensureDirSync('app/api'); + createApiRoutes('app/api'); + } + + if (fs.existsSync('app/config/routes.json')) { + const { routes, layouts } = fs.readJSONSync('app/config/routes.json'); + + parseRoutes(routes, null); + + function parseRoutes(routes, parent) { + for (let route of routes) { + if (!route.path) continue; + + createRoute(route, parent); + + if (Array.isArray(route.routes)) { + parseRoutes(route.routes, route); + } + } + } + + function createRoute({ auth, path, method, redirect, url, page, layout, exec, data, ttl, status, proxy }, parent) { + method = method || 'all'; + data = data || {}; + if (page) page = page.replace(/^\//, ''); + if (layout) layout = layout.replace(/^\//, ''); + if (parent && parent.path) path = parent.path + path; + + if (auth) { + app.use(path, (req, res, next) => { + if (typeof auth == 'string' && req.session && req.session[auth + 'Id']) { + next(); + } else if (typeof auth == 'object' && auth.user) { + const b64auth = (req.headers.authorization || '').split(' ')[1] || ''; + const [user, password] = Buffer.from(b64auth, 'base64').toString().split(':'); + if (user && password && user === auth.user && password === auth.password) { + next(); + } else { + res.set('WWW-Authenticate', 'Basic realm="401"'); + res.status(401).json({ error: 'Unauthorized' }); + } + } else { + res.status(401).json({ error: 'Unauthorized' }); + } + }); + } + + if (proxy) { + const httpProxy = require('http-proxy'); + const proxyServer = httpProxy.createProxyServer(proxy); + app.use(path, (req, res) => { + proxyServer.web(req, res); + }); + } else if (redirect) { + app.get(path, (req, res) => res.redirect(status == 302 ? 302 : 301, redirect)); + } else if (url) { + app[method](path, (req, res, next) => { + next(parent && !req.fragment ? 'route' : null); + }, (req, res) => { + res.sendFile(url, { root: 'public' }) + }); + + if (parent) { + createRoute({ + path, + method: parent.method, + redirect: parent.redirect, + url: parent.url, + page: parent.page, + layout: parent.layout, + exec: parent.exec, + data: parent.data + }); + } + } else if (page) { + if (path == '/404') { + app.set('has404', true); + } + + if (path == '/500') { + app.set('has500', true); + } + + if (exec) { + if (fs.existsSync(`app/${exec}.json`)) { + let json = fs.readJSONSync(`app/${exec}.json`); + + if (json.exec && json.exec.steps) { + json = json.exec.steps; + } else if (json.steps) { + json = json.steps; + } + + if (!Array.isArray(json)) { + json = [json]; + } + + + if (layout && layouts && layouts[layout]) { + if (layouts[layout].data) { + data = Object.assign({}, layouts[layout].data, data); + } + + if (layouts[layout].exec) { + if (fs.existsSync(`app/${layouts[layout].exec}.json`)) { + let _json = fs.readJSONSync(`app/${layouts[layout].exec}.json`); + + if (_json.exec && _json.exec.steps) { + _json = _json.exec.steps; + } else if (_json.steps) { + _json = _json.steps; + } + + if (!Array.isArray(_json)) { + _json = [_json]; + } + + json = _json.concat(json); + } else { + debug(`Route ${path} skipped, "app/${exec}.json" not found`); + return; + } + } + } + + app[method](path, (req, res, next) => { + next(parent && !req.fragment ? 'route' : null); + }, cache({ttl}), templateView(layout, page, data, json)); + } else { + debug(`Route ${path} skipped, "app/${exec}.json" not found`); + return; + } + } else { + let json = []; + + if (layout && layouts && layouts[layout]) { + if (layouts[layout].data) { + data = Object.assign({}, layouts[layout].data, data); + } + + if (layouts[layout].exec) { + if (fs.existsSync(`app/${layouts[layout].exec}.json`)) { + let _json = fs.readJSONSync(`app/${layouts[layout].exec}.json`); + + if (_json.exec && _json.exec.steps) { + _json = _json.exec.steps; + } else if (_json.steps) { + _json = _json.steps; + } + + if (!Array.isArray(_json)) { + _json = [_json]; + } + + json = _json.concat(json); + } else { + debug(`Route ${path} skipped, "app/${exec}.json" not found`); + return; + } + } + } + + app[method](path, (req, res, next) => { + next(parent && !req.fragment ? 'route' : null); + }, cache({ttl}), templateView(layout, page, data, json)); + } + + if (parent) { + createRoute({ + path, + method: parent.method, + redirect: parent.redirect, + url: parent.url, + page: parent.page, + layout: parent.layout, + exec: parent.exec, + data: parent.data + }); + } + } else if (exec) { + if (fs.existsSync(`app/${exec}.json`)) { + let json = fs.readJSONSync(`app/${exec}.json`); + + app[method](path, cache({ttl}), serverConnect(json)); + + return; + } + } + } + } + + if (fs.existsSync('extensions/server_connect/routes')) { + const entries = fs.readdirSync('extensions/server_connect/routes', { withFileTypes: true }); + + for (let entry of entries) { + if (entry.isFile() && extname(entry.name) == '.js') { + let hook = require(`../../extensions/server_connect/routes/${entry.name}`); + if (hook.after) hook.after(app); + debug(`Custom router ${entry.name} loaded`); + } + } + } + + function createApiRoutes(dir) { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + return map(entries, async (entry) => { + if (entry.name.startsWith('_')) return; + + let path = posix.join(dir, entry.name); + + if (entry.isFile() && extname(path) == '.json') { + let json = fs.readJSONSync(path); + let routePath = (json.settings?.options?.path) ? json.settings.options.path : path.replace(/^app/i, '').replace(/.json$/, '(.json)?'); + let routeMethod = (json.settings?.options?.method) ? json.settings.options.method : 'all'; + let ttl = (json.settings?.options?.ttl) ? json.settings.options.ttl : 0; + let csrf = (json.settings?.options?.nocsrf) ? false : config.csrf?.enabled; + let points = (json.settings?.options?.points) ? json.settings.options.points : 1; + + app[routeMethod](routePath.replace(/\/\(.*?\)\//gi, '/'), (req, res, next) => { + if (app.rateLimiter && points > 0) { + let isPrivate = false; + let key = req.ip; + + if (app.privateRateLimiter) { + if (req.session && req.session[config.rateLimit.private.provider + 'Id']) { + isPrivate = true; + key = req.session[config.rateLimit.private.provider + 'Id']; + } + } + + app[isPrivate ? 'privateRateLimiter' : 'rateLimiter'].consume(key, points).then(rateLimiterRes => { + const reset = Math.ceil(rateLimiterRes.msBeforeNext / 1000); + res.set('RateLimit-Policy', `${config.rateLimit.points};w=${config.rateLimit.duration}`); + res.set('RateLimit', `limit=${config.rateLimit.points}, remaining=${rateLimiterRes.remainingPoints}, reset=${reset}`); + next(); + }).catch(rateLimiterRes => { + const reset = Math.ceil(rateLimiterRes.msBeforeNext / 1000); + res.set('RateLimit-Policy', `${config.rateLimit.points};w=${config.rateLimit.duration}`); + res.set('RateLimit', `limit=${config.rateLimit.points}, remaining=${rateLimiterRes.remainingPoints}, reset=${reset}`); + res.set('Retry-After', reset); + if (req.is('json')) { + res.status(429).json({ error: 'Too Many Requests' }); + } else { + res.status(429).send('Too Many Requests'); + } + }); + } else { + next(); + } + }, (req, res, next) => { + if (!csrf) return next(); + if (config.csrf.exclude.split(',').includes(req.method)) return next(); + if (!req.validateCSRF()) return res.status(403).send('Invalid CSRF token'); + next(); + }, cache({ttl}), serverConnect(json)); + + debug(`Api route ${routePath} created`); + } + + if (entry.isDirectory()) { + return createApiRoutes(path); + } + }); + } +}; diff --git a/lib/setup/secure.js b/lib/setup/secure.js new file mode 100644 index 0000000..85ecf2e --- /dev/null +++ b/lib/setup/secure.js @@ -0,0 +1,112 @@ +const debug = require('debug')('server-connect:secure'); +const config = require('./config'); + +module.exports = function (app) { + const { randomBytes } = require('crypto'); + const generateToken = (req, overwrite) => { + const sessionToken = req.session.csrfToken; + + if (!overwrite && typeof sessionToken === 'string') { + return sessionToken; + } + + const token = randomBytes(32).toString('hex'); + req.session.csrfToken = token; + + return token; + } + const getTokenFromRequest = (req) => req.headers['x-csrf-token'] || req.body.CSRFToken; + const isValidToken = (req) => { + const token = getTokenFromRequest(req); + const sessionToken = req.session.csrfToken; + return typeof token === 'string' && typeof sessionToken === 'string' && token === sessionToken; + } + + app.get('/__csrf', (req, res) => { + res.json({ csrfToken: generateToken(req, true) }); + }); + + app.use((req, res, next) => { + req.csrfToken = (overwrite) => generateToken(req, overwrite); + req.validateCSRF = () => isValidToken(req); + next(); + }); + + debug('CSRF Protection initialized'); + + if (config.rateLimit?.enabled) { + const options = config.rateLimit; + const { RateLimiterMemory, RateLimiterRedis } = require('rate-limiter-flexible'); + + if (global.redisClient) { + app.rateLimiter = new RateLimiterRedis({ + duration: options.duration, + points: options.points, + storeClient: global.redisClient, + }); + if (options.private?.provider) { + app.privateRateLimiter = new RateLimiterRedis({ + duration: options.private.duration, + points: options.private.points, + storeClient: global.redisClient, + }); + } + } else { + app.rateLimiter = new RateLimiterMemory({ + duration: config.rateLimit.duration, + points: config.rateLimit.points, + }); + if (options.private?.provider) { + app.privateRateLimiter = new RateLimiterMemory({ + duration: options.private.duration, + points: options.private.points, + }); + } + } + + debug('Ratelimit initialized', options); + } + + if (config.passport) { + const passport = require('passport'); + const ServerConnectStrategy = require('../auth/passport'); + + passport.use(new ServerConnectStrategy({ provider: 'security' })); + + app.use(passport.initialize()); + app.use(passport.session()); + app.use(passport.authenticate('server-connect')); + + debug('Passport initialized', passport.strategies); + + app.use((req, res, next) => { + debug('auth', req.isAuthenticated()); + debug('Session', req.session); + if (req.user) { + debug('User', req.user); + } + next(); + }); + + app.use('/api/secure', restrict()); + } +}; + +// restrict middleware +function restrict (options = {}) { + return async function (req, res, next) { + if (req.isAuthenticated()) { + return next(); + } + + if (req.is('json')) { + return res.status(401).json({ error: 'Unauthorized' }); + } + + if (options.redirect) { + return res.redirect(options.redirect); + } + + res.status(401).send('Unauthorized'); + }; +} \ No newline at end of file diff --git a/lib/setup/session.js b/lib/setup/session.js new file mode 100644 index 0000000..efb7ed2 --- /dev/null +++ b/lib/setup/session.js @@ -0,0 +1,94 @@ +const session = require('express-session'); //(Object.assign({ secret: config.secret }, config.session)); +const debug = require('debug')('server-connect:setup:session'); +const Parser = require('../core/parser'); +const Scope = require('../core/scope'); +const db = require('../core/db'); +const { toSystemPath } = require('../core/path'); +const config = require('./config'); +const options = config.session; + +if (!options.secret) { + options.secret = config.secret; +} + +debug('init session store %o', options.store); + +if (options.store.$type == 'redis') { // https://www.npmjs.com/package/connect-redis + const RedisStore = require('connect-redis').default; + options.store = new RedisStore(Object.assign({ + client: global.redisClient + }, options.store)); +} else if (options.store.$type == 'file') { // https://www.npmjs.com/package/session-file-store + const FileStore = require('session-file-store')(session); + options.store = new FileStore(options.store); +} else if (options.store.$type == 'database') { // https://www.npmjs.com/package/connect-session-knex + const KnexStore = require('connect-session-knex')(session); + + if (typeof options.store.knex == 'string') { + if (!global.db) global.db = {}; + + if (!global.db[options.store.knex]) { + const fs = require('fs-extra'); + const action = fs.readJSONSync(`app/modules/connections/${options.store.knex}.json`); + const knex_options = Parser.parseValue(action.options, new Scope({ $_ENV: process.env })); + + if (knex_options.connection && knex_options.connection.filename) { + knex_options.connection.filename = toSystemPath(knex_options.connection.filename); + } + + if (knex_options.connection && knex_options.connection.ssl) { + if (knex_options.connection.ssl.key) { + knex_options.connection.ssl.key = fs.readFileSync(toSystemPath(knex_options.connection.ssl.key)); + } + + if (knex_options.connection.ssl.ca) { + knex_options.connection.ssl.ca = fs.readFileSync(toSystemPath(knex_options.connection.ssl.ca)); + } + + if (knex_options.connection.ssl.cert) { + knex_options.connection.ssl.cert = fs.readFileSync(toSystemPath(knex_options.connection.ssl.cert)); + } + } + + knex_options.useNullAsDefault = true; + + knex_options.postProcessResponse = function(result) { + if (Array.isArray(result)) { + return result.map(row => { + for (column in row) { + if (row[column] && row[column].toJSON) { + row[column] = row[column].toJSON(); + } + } + return row; + }); + } else { + for (column in result) { + if (result[column] && result[column].toJSON) { + result[column] = result[column].toJSON(); + } + } + return result; + } + }; + + global.db[options.store.knex] = db(knex_options); + } + + options.store.knex = global.db[options.store.knex]; + } + + if (options.store.ttl) { + options.cookie = { ...options.cookie, maxAge: options.store.ttl * 1000 }; + } + + options.store = new KnexStore(options.store); +} else { + const MemoryStore = require('../core/memoryStore')(session); + if (options.store.ttl) { + options.store.ttl = options.store.ttl * 1000; + } + options.store = new MemoryStore(options.store); +} + +module.exports = session(options); \ No newline at end of file diff --git a/lib/setup/sockets.js b/lib/setup/sockets.js new file mode 100644 index 0000000..0f7b9fa --- /dev/null +++ b/lib/setup/sockets.js @@ -0,0 +1,195 @@ +const fs = require('fs-extra'); +const { basename, extname } = require('path'); +const debug = require('debug')('server-connect:sockets'); +const { isEmpty } = require('./util'); +const config = require('./config'); +const cookieParser = require('cookie-parser'); +const { promisify } = require('util'); + +module.exports = function (server, appSession) { + //if (isEmpty('app/sockets')) return null; + + const io = require('socket.io')(); + + if (global.redisClient) { + const { createAdapter } = require('@socket.io/redis-streams-adapter'); + io.adapter(createAdapter(global.redisClient)); + } + + // user hooks + if (fs.existsSync('extensions/server_connect/sockets')) { + const entries = fs.readdirSync('extensions/server_connect/sockets', { withFileTypes: true }); + + for (let entry of entries) { + if (entry.isFile() && extname(entry.name) == '.js') { + const hook = require(`../../extensions/server_connect/sockets/${entry.name}`); + if (hook.handler) hook.handler(io); + debug(`Custom sockets hook ${entry.name} loaded`); + } + } + } + + // create socket connections for api endpoints + if (fs.existsSync('app/api')) { + + io.of('/api').on('connection', async (socket) => { + socket.onAny(async (event, params, cb) => { + try { + if (typeof cb == 'function' && global.redisClient && global.redisClient.isReady) { + const cached = await global.redisClient.get('ws:' + event + ':' + JSON.stringify(params)); + if (cached) return cb(JSON.parse(cached)); + } + + const req = Object.assign({}, socket.handshake); + const res = { + statusCode: 200, + getHeader: () => { }, + setHeader: () => { }, + sendStatus: (statusCode) => { res.statusCode = statusCode; }, + write: () => { }, + end: () => { } + }; + + cookieParser(config.secret)(req, res, () => { + appSession(req, res, async () => { + const App = require('../core/app'); + const app = new App(req, res); + const action = await fs.readJSON(`app/api/${event}.json`); + app.set('$_PARAM', params); + app.set('$_GET', params); // fake query params + app.socket = socket; + await app.define(action, true); + if (typeof cb == 'function') { + cb({ + status: res.statusCode, + data: res.statusCode == 200 ? app.data : null + }); + + if (global.redisClient && global.redisClient.isReady) { + let ttl = (action.settings && action.settings.options && action.settings.options.ttl) ? action.settings.options.ttl : null; + + if (ttl && res.statusCode < 400) { // only cache valid response, not error response + global.redisClient.setEx('ws:' + event + ':' + JSON.stringify(params), ttl, JSON.stringify({ + status: res.statusCode, + data: res.statusCode == 200 ? app.data : null + })); + } + } + } + }); + }); + } catch (e) { + debug(`ERROR: ${e.message}`); + console.error(e); + } + }); + }); + } + + if (fs.existsSync('app/sockets')) { + parseSockets(); + } + + function parseSockets(namespace = '') { + const entries = fs.readdirSync('app/sockets' + namespace, { withFileTypes: true }); + + io.of(namespace || '/').on('connection', async (socket) => { + if (fs.existsSync(`app/sockets${namespace}/connect.json`)) { + try { + const req = Object.assign({}, socket.handshake); + const res = { + statusCode: 200, + getHeader: () => { }, + setHeader: () => { }, + sendStatus: (statusCode) => { res.statusCode = statusCode; }, + write: () => { }, + end: () => { } + }; + + cookieParser(config.secret)(req, res, () => { + appSession(req, res, async () => { + const App = require('../core/app'); + const app = new App(req, res); + const action = await fs.readJSON(`app/sockets${namespace}/connect.json`); + app.socket = socket; + await app.define(action, true); + }); + }); + } catch (e) { + debug(`ERROR: ${e.message}`); + console.error(e); + } + } + + if (fs.existsSync(`app/sockets${namespace}/disconnect.json`)) { + socket.on('disconnect', async (event) => { + try { + const req = Object.assign({}, socket.handshake); + const res = { + statusCode: 200, + getHeader: () => { }, + setHeader: () => { }, + sendStatus: (statusCode) => { res.statusCode = statusCode; }, + write: () => { }, + end: () => { } + }; + + cookieParser(config.secret)(req, res, () => { + appSession(req, res, async () => { + const App = require('../core/app'); + const app = new App(req, res); + const action = await fs.readJSON(`app/sockets${namespace}/disconnect.json`); + app.socket = socket; + await app.define(action, true); + }); + }); + } catch (e) { + debug(`ERROR: ${e.message}`); + console.error(e); + } + }); + } + + socket.onAny(async (event, params, cb) => { + try { + const req = Object.assign({}, socket.handshake); + const res = { + statusCode: 200, + getHeader: () => { }, + setHeader: () => { }, + sendStatus: (statusCode) => { res.statusCode = statusCode; }, + write: () => { }, + end: () => { } + }; + + cookieParser(config.secret)(req, res, () => { + appSession(req, res, async () => { + const App = require('../core/app'); + const app = new App(req, res); + const action = await fs.readJSON(`app/sockets${namespace}/${event}.json`); + app.set('$_PARAM', params); + app.socket = socket; + await app.define(action, true); + if (typeof cb == 'function') cb(app.data); + }); + }); + } catch (e) { + debug(`ERROR: ${e.message}`); + console.error(e); + } + }); + }); + + for (let entry of entries) { + if (entry.isDirectory()) { + parseSockets(namespace + '/' + entry.name); + } + } + } + + io.attach(server, { + cors: config.cors + }); + + return io; +}; \ No newline at end of file diff --git a/lib/setup/upload.js b/lib/setup/upload.js new file mode 100644 index 0000000..afe939a --- /dev/null +++ b/lib/setup/upload.js @@ -0,0 +1,63 @@ +const config = require('./config'); +const debug = require('debug')('server-connect:setup:upload'); +const fs = require('fs-extra'); +const qs = require('qs'); + +module.exports = function(app) { + const fileupload = require('express-fileupload'); + const isEligibleRequest = require('express-fileupload/lib/isEligibleRequest'); + + // Make sure tmp folder exists and make it empty + fs.ensureDirSync(config.tmpFolder); + fs.emptyDirSync(config.tmpFolder); + + // Always use tmp folder + app.use(fileupload(Object.assign({}, config.fileupload, { + useTempFiles: true, + tempFileDir: config.tmpFolder, + defParamCharset: 'utf8' + }))); + + app.use((req, res, next) => { + if (!isEligibleRequest(req)) { + return next(); + } + + let encoded = qs.stringify(req.body); + if (req.files) { + for (let field in req.files) { + encoded += '&' + field + '=' + field; + } + } + + req.body = qs.parse(encoded, { + arrayLimit: 10000, + parameterLimit: 10000, + }); + + // Cleanup + res.once('close', () => { + if (req.files) { + for (let field in req.files) { + let file = req.files[field]; + + if (Array.isArray(file)) { + file.forEach((file) => { + if (file.tempFilePath && fs.existsSync(file.tempFilePath)) { + debug('delete %s', file.tempFilePath); + fs.unlink(file.tempFilePath); + } + }); + } else if (file.tempFilePath && fs.existsSync(file.tempFilePath)) { + debug('delete %s', file.tempFilePath); + fs.unlink(file.tempFilePath); + } + } + } + }); + + next(); + }); + + debug('Upload middleware configured.'); +} \ No newline at end of file diff --git a/lib/setup/util.js b/lib/setup/util.js new file mode 100644 index 0000000..c14f51c --- /dev/null +++ b/lib/setup/util.js @@ -0,0 +1,12 @@ +const fs = require('fs-extra'); + +exports.isEmpty = (path) => { + try { + let stat = fs.statSync(path); + if (!stat.isDirectory()) return true; + let items = fs.readdirSync(path); + return !items || !items.length; + } catch (e) { + return true; + } +} \ No newline at end of file diff --git a/lib/setup/webhooks.js b/lib/setup/webhooks.js new file mode 100644 index 0000000..58fab9e --- /dev/null +++ b/lib/setup/webhooks.js @@ -0,0 +1,22 @@ +const fs = require('fs-extra'); + +module.exports = function(app) { + app.all('/webhooks/:name', (req, res, next) => { + const name = req.params.name; + + if (!/^[a-zA-Z0-9-_]+$/.test(name)) { + res.status(400).json({error: `Invalid webhook name.`}); + } else if (fs.existsSync(`lib/webhooks/${name}.js`)) { + const webhook = require(`../webhooks/${name}`); + if (webhook.handler) { + webhook.handler(req, res, next); + } else { + res.status(400).json({error: `Webhook ${name} has no handler.`}); + } + } else { + const webhook = require('../core/webhook'); + const handler = webhook.createHandler(name); + handler(req, res, next); + } + }); +}; \ No newline at end of file diff --git a/lib/validator/core.js b/lib/validator/core.js new file mode 100644 index 0000000..5de27a9 --- /dev/null +++ b/lib/validator/core.js @@ -0,0 +1,243 @@ +const isValidString = (s) => typeof s == 'string' && s.length > 0; +const getLength = (s) => s && s.length || 0;; +const testRegexp = (re) => (v) => !isValidString(v) || re.test(v); +const reEmail = /^(?!\.)((?!.*\.{2})[a-zA-Z0-9\u0080-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u0300-\u036F\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u0530-\u058F\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\u0780-\u07BF\u07C0-\u07FF\u0900-\u097F\u0980-\u09FF\u0A00-\u0A7F\u0A80-\u0AFF\u0B00-\u0B7F\u0B80-\u0BFF\u0C00-\u0C7F\u0C80-\u0CFF\u0D00-\u0D7F\u0D80-\u0DFF\u0E00-\u0E7F\u0E80-\u0EFF\u0F00-\u0FFF\u1000-\u109F\u10A0-\u10FF\u1100-\u11FF\u1200-\u137F\u1380-\u139F\u13A0-\u13FF\u1400-\u167F\u1680-\u169F\u16A0-\u16FF\u1700-\u171F\u1720-\u173F\u1740-\u175F\u1760-\u177F\u1780-\u17FF\u1800-\u18AF\u1900-\u194F\u1950-\u197F\u1980-\u19DF\u19E0-\u19FF\u1A00-\u1A1F\u1B00-\u1B7F\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF\u1E00-\u1EFF\u1F00-\u1FFFu20D0-\u20FF\u2100-\u214F\u2C00-\u2C5F\u2C60-\u2C7F\u2C80-\u2CFF\u2D00-\u2D2F\u2D30-\u2D7F\u2D80-\u2DDF\u2F00-\u2FDF\u2FF0-\u2FFF\u3040-\u309F\u30A0-\u30FF\u3100-\u312F\u3130-\u318F\u3190-\u319F\u31C0-\u31EF\u31F0-\u31FF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FFF\uA000-\uA48F\uA490-\uA4CF\uA700-\uA71F\uA800-\uA82F\uA840-\uA87F\uAC00-\uD7AF\uF900-\uFAFF\.!#$%&'*+-/=?^_`{|}~\-\d]+)@(?!\.)([a-zA-Z0-9\u0080-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u0300-\u036F\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u0530-\u058F\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\u0780-\u07BF\u07C0-\u07FF\u0900-\u097F\u0980-\u09FF\u0A00-\u0A7F\u0A80-\u0AFF\u0B00-\u0B7F\u0B80-\u0BFF\u0C00-\u0C7F\u0C80-\u0CFF\u0D00-\u0D7F\u0D80-\u0DFF\u0E00-\u0E7F\u0E80-\u0EFF\u0F00-\u0FFF\u1000-\u109F\u10A0-\u10FF\u1100-\u11FF\u1200-\u137F\u1380-\u139F\u13A0-\u13FF\u1400-\u167F\u1680-\u169F\u16A0-\u16FF\u1700-\u171F\u1720-\u173F\u1740-\u175F\u1760-\u177F\u1780-\u17FF\u1800-\u18AF\u1900-\u194F\u1950-\u197F\u1980-\u19DF\u19E0-\u19FF\u1A00-\u1A1F\u1B00-\u1B7F\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF\u1E00-\u1EFF\u1F00-\u1FFF\u20D0-\u20FF\u2100-\u214F\u2C00-\u2C5F\u2C60-\u2C7F\u2C80-\u2CFF\u2D00-\u2D2F\u2D30-\u2D7F\u2D80-\u2DDF\u2F00-\u2FDF\u2FF0-\u2FFF\u3040-\u309F\u30A0-\u30FF\u3100-\u312F\u3130-\u318F\u3190-\u319F\u31C0-\u31EF\u31F0-\u31FF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FFF\uA000-\uA48F\uA490-\uA4CF\uA700-\uA71F\uA800-\uA82F\uA840-\uA87F\uAC00-\uD7AF\uF900-\uFAFF\-\.\d]+)((\.([a-zA-Z\u0080-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u0300-\u036F\u0370-\u03FF\u0400-\u04FF\u0500-\u052F\u0530-\u058F\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\u0780-\u07BF\u07C0-\u07FF\u0900-\u097F\u0980-\u09FF\u0A00-\u0A7F\u0A80-\u0AFF\u0B00-\u0B7F\u0B80-\u0BFF\u0C00-\u0C7F\u0C80-\u0CFF\u0D00-\u0D7F\u0D80-\u0DFF\u0E00-\u0E7F\u0E80-\u0EFF\u0F00-\u0FFF\u1000-\u109F\u10A0-\u10FF\u1100-\u11FF\u1200-\u137F\u1380-\u139F\u13A0-\u13FF\u1400-\u167F\u1680-\u169F\u16A0-\u16FF\u1700-\u171F\u1720-\u173F\u1740-\u175F\u1760-\u177F\u1780-\u17FF\u1800-\u18AF\u1900-\u194F\u1950-\u197F\u1980-\u19DF\u19E0-\u19FF\u1A00-\u1A1F\u1B00-\u1B7F\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF\u1E00-\u1EFF\u1F00-\u1FFF\u20D0-\u20FF\u2100-\u214F\u2C00-\u2C5F\u2C60-\u2C7F\u2C80-\u2CFF\u2D00-\u2D2F\u2D30-\u2D7F\u2D80-\u2DDF\u2F00-\u2FDF\u2FF0-\u2FFF\u3040-\u309F\u30A0-\u30FF\u3100-\u312F\u3130-\u318F\u3190-\u319F\u31C0-\u31EF\u31F0-\u31FF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FFF\uA000-\uA48F\uA490-\uA4CF\uA700-\uA71F\uA800-\uA82F\uA840-\uA87F\uAC00-\uD7AF\uF900-\uFAFF]){2,63})+)$/i; +const reUrl = /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i; +const reDateTime = /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])[T ]([01][0-9]|2[0-4]):[0-5][0-9](:([0-5][0-9]|60))?Z?$/; +const reDate = /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/; +const reTime = /^([01][0-9]|2[0-4]):[0-5][0-9](:([0-5][0-9]|60))?$/; +const reMonth = /^\d{4}-(0[1-9]|1[012])$/; +const reWeek = /^\d{4}-W(0[1-9]|[1-4][0-9]|5[0-3])$/; +const reColor = /^#[a-fA-F0-9]{6}$/; +const reNumber = /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/; +const reDigits = /^\d+$/; +const reAlphanumeric = /^\w+$/; +const reBic = /^([A-Z]{6}[A-Z2-9][A-NP-Z1-2])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/; +const reVat = /^((AT)?U[0-9]{8}|(BE)?0[0-9]{9}|(BG)?[0-9]{9,10}|(CY)?[0-9]{8}L|(CZ)?[0-9]{8,10}|(DE)?[0-9]{9}|(DK)?[0-9]{8}|(EE)?[0-9]{9}|(EL|GR)?[0-9]{9}|(ES)?[0-9A-Z][0-9]{7}[0-9A-Z]|(FI)?[0-9]{8}|(FR)?[0-9A-Z]{2}[0-9]{9}|(GB)?([0-9]{9}([0-9]{3})?|[A-Z]{2}[0-9]{3})|(HU)?[0-9]{8}|(IE)?[0-9]S[0-9]{5}L|(IT)?[0-9]{11}|(LT)?([0-9]{9}|[0-9]{12})|(LU)?[0-9]{8}|(LV)?[0-9]{11}|(MT)?[0-9]{8}|(NL)?[0-9]{9}B[0-9]{2}|(PL)?[0-9]{10}|(PT)?[0-9]{9}|(RO)?[0-9]{2,10}|(SE)?[0-9]{12}|(SI)?[0-9]{8}|(SK)?[0-9]{10})$/; +const reInteger = /^-?\d+$/; +const reIpv4 = /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/; +const reIpv6 = /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/; +const reLettersonly = /^[a-z]+$/i; +const reUnicodelettersonly = /^[A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+$/; +const reLetterswithbasicpunc = /^[a-z\-.,()'"\s]+$/; +const reNowhitespace = /^\S+$/; +const ibanCountryPatterns = { + "AL": "\\d{8}[\\dA-Z]{16}", + "AD": "\\d{8}[\\dA-Z]{12}", + "AT": "\\d{16}", + "AZ": "[\\dA-Z]{4}\\d{20}", + "BE": "\\d{12}", + "BH": "[A-Z]{4}[\\dA-Z]{14}", + "BA": "\\d{16}", + "BR": "\\d{23}[A-Z][\\dA-Z]", + "BG": "[A-Z]{4}\\d{6}[\\dA-Z]{8}", + "CR": "\\d{17}", + "HR": "\\d{17}", + "CY": "\\d{8}[\\dA-Z]{16}", + "CZ": "\\d{20}", + "DK": "\\d{14}", + "DO": "[A-Z]{4}\\d{20}", + "EE": "\\d{16}", + "FO": "\\d{14}", + "FI": "\\d{14}", + "FR": "\\d{10}[\\dA-Z]{11}\\d{2}", + "GE": "[\\dA-Z]{2}\\d{16}", + "DE": "\\d{18}", + "GI": "[A-Z]{4}[\\dA-Z]{15}", + "GR": "\\d{7}[\\dA-Z]{16}", + "GL": "\\d{14}", + "GT": "[\\dA-Z]{4}[\\dA-Z]{20}", + "HU": "\\d{24}", + "IS": "\\d{22}", + "IE": "[\\dA-Z]{4}\\d{14}", + "IL": "\\d{19}", + "IT": "[A-Z]\\d{10}[\\dA-Z]{12}", + "KZ": "\\d{3}[\\dA-Z]{13}", + "KW": "[A-Z]{4}[\\dA-Z]{22}", + "LV": "[A-Z]{4}[\\dA-Z]{13}", + "LB": "\\d{4}[\\dA-Z]{20}", + "LI": "\\d{5}[\\dA-Z]{12}", + "LT": "\\d{16}", + "LU": "\\d{3}[\\dA-Z]{13}", + "MK": "\\d{3}[\\dA-Z]{10}\\d{2}", + "MT": "[A-Z]{4}\\d{5}[\\dA-Z]{18}", + "MR": "\\d{23}", + "MU": "[A-Z]{4}\\d{19}[A-Z]{3}", + "MC": "\\d{10}[\\dA-Z]{11}\\d{2}", + "MD": "[\\dA-Z]{2}\\d{18}", + "ME": "\\d{18}", + "NL": "[A-Z]{4}\\d{10}", + "NO": "\\d{11}", + "PK": "[\\dA-Z]{4}\\d{16}", + "PS": "[\\dA-Z]{4}\\d{21}", + "PL": "\\d{24}", + "PT": "\\d{21}", + "RO": "[A-Z]{4}[\\dA-Z]{16}", + "SM": "[A-Z]\\d{10}[\\dA-Z]{12}", + "SA": "\\d{2}[\\dA-Z]{18}", + "RS": "\\d{18}", + "SK": "\\d{20}", + "SI": "\\d{15}", + "ES": "\\d{20}", + "SE": "\\d{20}", + "CH": "\\d{5}[\\dA-Z]{12}", + "TN": "\\d{20}", + "TR": "\\d{5}[\\dA-Z]{17}", + "AE": "\\d{3}\\d{16}", + "GB": "[A-Z]{4}\\d{14}", + "VG": "[\\dA-Z]{4}\\d{16}" +}; + +module.exports = { + + required: function(value) { + return value && value.length > 0; + }, + + email: testRegexp(reEmail), + url: testRegexp(reUrl), + datetime: testRegexp(reDateTime), + date: testRegexp(reDate), + time: testRegexp(reTime), + month: testRegexp(reMonth), + week: testRegexp(reWeek), + color: testRegexp(reColor), + number: testRegexp(reNumber), + digits: testRegexp(reDigits), + alphanumeric: testRegexp(reAlphanumeric), + bic: testRegexp(reBic), + vat: testRegexp(reVat), + integer: testRegexp(reInteger), + ipv4: testRegexp(reIpv4), + ipv6: testRegexp(reIpv6), + lettersonly: testRegexp(reLettersonly), + unicodelettersonly: testRegexp(reUnicodelettersonly), + letterswithbasicpunc: testRegexp(reLetterswithbasicpunc), + nowhitespace: testRegexp(reNowhitespace), + + pattern: function(value, param) { + const re = new RegExp(`^(?:${param})$`); + return !isValidString(value) || re.test(value); + }, + + creditcard: function(value) { + if (!isValidString(value)) { + return true; + } + + if (/[^0-9 \-]+/.test(value)) { + return false; + } + + value = value.replace(/\D/g, ''); + + if (value.length < 13 || value.length > 19) { + return false; + } + + let check = 0, digit = 0, even = false; + for (let i = value.length - 1; i >= 0; i--) { + digit = parseInt(value.charAt(i), 10); + if (even && (digit *= 2) > 9) { + digit -= 9; + } + check += digit; + even = !even; + } + + return (check % 10) === 0; + }, + + iban: function(value) { + if (!isValidString(value)) { + return true; + } + + const iban = value.replace(/ /g, '').toUpperCase(); + const country = iban.substr(0, 2); + const pattern = ibanCountryPatterns[country]; + + if (typeof patter !== 'undefined') { + const re = new RegExp(`^[A-Z]{2}\\d{2}${pattern}$`); + if (!re.test(iban)) { + return false; + } + } + + let leadingZeroes = true, digits = '', rest = ''; + + const lookup = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + const check = iban.substr(4) + iban.substr(0, 4); + + for (let i = 0; i < check.length; i++) { + const ch = check.charAt(i); + + if (ch !== '0') { + leadingZeroes = false; + } + + if (!leadingZeroes) { + digits += lookup.indexOf(ch); + } + } + + for (let i = 0; i < digits.length; i++) { + const ch = digits.charAt(i); + const op = rest + ch; + rest = op % 97; + } + + return rest === 1; + }, + + minlength: function(value, param) { + const length = getLength(value); + return length == 0 || length >= param; + }, + + maxlength: function(value, param) { + const length = getLength(value); + return length == 0 || length <= param; + }, + + rangelength: function(value, param) { + const length = getLength(value); + return length == 0 || (length >= param['0'] && length <= param['1']); + }, + + minitems: function(value, param) { + const length = getLength(value); + return length == 0 || (Array.isArray(value) && length >= param); + }, + + maxitems: function(value, param) { + const length = getLength(value); + return length == 0 || (Array.isArray(value) && length <= param); + }, + + rangeitems: function(value, param) { + const length = getLength(value); + return length == 0 || (Array.isArray(value) && length >= param['0'] && length <= param['1']); + }, + + min: function(value, param) { + return value != null && value != '' && Number(value) >= Number(param); + }, + + max: function(value, param) { + return value != null && value != '' && Number(value) <= Number(param); + }, + + range: function(value, param) { + return value != null && value != '' && Number(value) >= Number(param['0']) && Number(value) <= Number(param['1']); + }, + + equalTo: function(value, param) { + return this.parse(`{{ $_POST.${param.replace(/\[([^\]]+)\]/g, '.$1')} }}`) == value; + }, + + notEqualTo: function(value, param) { + return this.parse(`{{ $_POST.${param.replace(/\[([^\]]+)\]/g, '.$1')} }}`) != value; + }, + +}; \ No newline at end of file diff --git a/lib/validator/db.js b/lib/validator/db.js new file mode 100644 index 0000000..b7ef470 --- /dev/null +++ b/lib/validator/db.js @@ -0,0 +1,21 @@ +module.exports = { + + exists: async function(value, options) { + if (!value) return true; + + const db = this.getDbConnection(options.connection); + const results = await db.from(options.table).where(options.column, value).limit(1); + + return results.length > 0; + }, + + notexists: async function(value, options) { + if (!value) return true; + + const db = this.getDbConnection(options.connection); + const results = await db.from(options.table).where(options.column, value).limit(1); + + return results.length == 0; + } + +}; \ No newline at end of file diff --git a/lib/validator/index.js b/lib/validator/index.js new file mode 100644 index 0000000..e1c7b96 --- /dev/null +++ b/lib/validator/index.js @@ -0,0 +1,120 @@ +module.exports = { + + async init(app, meta) { + const errors = {}; + + if (meta['$_GET'] && Array.isArray(meta['$_GET'])) { + await this.validateFields(app, meta['$_GET'], app.scope.data['$_GET'], errors); + } + + if (meta['$_POST'] && Array.isArray(meta['$_POST'])) { + await this.validateFields(app, meta['$_POST'], app.scope.data['$_POST'], errors); + } + + if (Object.keys(errors).length) { + app.res.status(400).json(errors); + } + }, + + async validateData(app, data, noError) { + const errors = {}; + + if (Array.isArray(data)) { + for (let item of data) { + for (let rule in item.rules) { + const options = item.rules[rule]; + + rule = this.getRule(rule); + + if (!await this.validateRule(app, rule, item.value, options)) { + const t = item.fieldName ? 'form' : 'data'; + errors[t] = errors[t] || {}; + errors[t][item.fieldName || item.name] = this.errorMessage(rule, options); + } + } + } + } + + if (Object.keys(errors).length) { + if (noError) return false; + app.res.status(400).json(errors); + } + + return true; + }, + + async validateFields(app, fields, parent, errors, fieldname) { + if (parent == null) return; + + for (let field of fields) { + let value = parent[field.name]; + let curFieldname = fieldname ? `${fieldname}[${field.name}]` : field.name; + + if (field.type == 'array' && value == null) { + value = []; + } + + if (field.options && field.options.rules) { + await this.validateField(app, field, value, errors, curFieldname); + } + + if (field.type == 'object' && field.sub) { + await this.validateFields(app, field.sub, value, errors, curFieldname); + } + + if (field.type == 'array' && field.sub && field.sub[0] && field.sub[0].sub) { + if (Array.isArray(value) && value.length) { + for (let i = 0; i < value.length; i++) { + await this.validateFields(app, field.sub[0].sub, value[i], errors, `${curFieldname}[${i}]`); + } + } else { + await this.validateFields(app, field.sub[0].sub, null, errors, `${curFieldname}[0]`); + } + } + } + }, + + async validateField(app, field, value, errors, fieldname) { + for (let rule in field.options.rules) { + const options = field.options.rules[rule]; + + rule = this.getRule(rule); + + if (!await this.validateRule(app, rule, value, options)) { + const t = field.fieldName ? 'form' : 'data'; + errors[t] = errors[t] || {}; + errors[t][fieldname] = this.errorMessage(rule, options); + } + } + }, + + async validateRule(app, rule, value, options = {}) { + const module = require(`./${rule.module}`); + return module[rule.method].call(app, value, options.param); + }, + + errorMessage(rule, options = {}) { + let message = options.message; + + if (!message) { + const { validator: messages } = require('../locale/en-US'); + message = messages[rule.module][rule.method]; + } + + if (typeof options.param != 'object') { + options.param = { '0': options.param }; + } + + return message.replace(/{([^}]+)}/g, (m, i) => options.param[i]); + }, + + getRule(rule) { + const colon = rule.indexOf(':'); + + return { + module: colon > 0 ? rule.substr(0, colon) : 'core', + method: colon > 0 ? rule.substr(colon + 1) : rule + }; + } + +}; \ No newline at end of file diff --git a/lib/validator/unicode.js b/lib/validator/unicode.js new file mode 100644 index 0000000..61ed95c --- /dev/null +++ b/lib/validator/unicode.js @@ -0,0 +1,466 @@ +const unicode = { + 'L': { + bmp: 'A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC', + astral: '\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD83A[\uDC00-\uDCC4]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD80D[\uDC00-\uDC2E]|\uD87E[\uDC00-\uDE1D]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD809[\uDC80-\uDD43]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD808[\uDC00-\uDF99]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD811[\uDC00-\uDE46]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD82C[\uDC00\uDC01]|\uD873[\uDC00-\uDEA1]' + }, + 'M': { + bmp: '\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFC-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F', + astral: '\uD805[\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDEAB-\uDEB7\uDF1D-\uDF2B]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDCA-\uDDCC\uDE2C-\uDE37\uDEDF-\uDEEA\uDF00-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD82F[\uDC9D\uDC9E]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD83A[\uDCD0-\uDCD6]|\uDB40[\uDD00-\uDDEF]' + }, + 'N': { + bmp: '0-9\xB2\xB3\xB9\xBC-\xBE\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F\u0C78-\u0C7E\u0CE6-\u0CEF\u0D66-\u0D75\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19', + astral: '\uD800[\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDEE1-\uDEFB\uDF20-\uDF23\uDF41\uDF4A\uDFD1-\uDFD5]|\uD801[\uDCA0-\uDCA9]|\uD803[\uDCFA-\uDCFF\uDE60-\uDE7E]|\uD835[\uDFCE-\uDFFF]|\uD83A[\uDCC7-\uDCCF]|\uD81A[\uDE60-\uDE69\uDF50-\uDF59\uDF5B-\uDF61]|\uD806[\uDCE0-\uDCF2]|\uD804[\uDC52-\uDC6F\uDCF0-\uDCF9\uDD36-\uDD3F\uDDD0-\uDDD9\uDDE1-\uDDF4\uDEF0-\uDEF9]|\uD834[\uDF60-\uDF71]|\uD83C[\uDD00-\uDD0C]|\uD809[\uDC00-\uDC6E]|\uD802[\uDC58-\uDC5F\uDC79-\uDC7F\uDCA7-\uDCAF\uDCFB-\uDCFF\uDD16-\uDD1B\uDDBC\uDDBD\uDDC0-\uDDCF\uDDD2-\uDDFF\uDE40-\uDE47\uDE7D\uDE7E\uDE9D-\uDE9F\uDEEB-\uDEEF\uDF58-\uDF5F\uDF78-\uDF7F\uDFA9-\uDFAF]|\uD805[\uDCD0-\uDCD9\uDE50-\uDE59\uDEC0-\uDEC9\uDF30-\uDF3B]' + }, + 'P': { + bmp: '\x21-\x23\x25-\\x2A\x2C-\x2F\x3A\x3B\\x3F\x40\\x5B-\\x5D\x5F\\x7B\x7D\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65', + astral: '\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD809[\uDC70-\uDC74]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD836[\uDE87-\uDE8B]|\uD801\uDD6F|\uD82F\uDC9F|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]' + }, + 'S': { + bmp: '\\x24\\x2B\x3C-\x3E\\x5E\x60\\x7C\x7E\xA2-\xA6\xA8\xA9\xAC\xAE-\xB1\xB4\xB8\xD7\xF7\u02C2-\u02C5\u02D2-\u02DF\u02E5-\u02EB\u02ED\u02EF-\u02FF\u0375\u0384\u0385\u03F6\u0482\u058D-\u058F\u0606-\u0608\u060B\u060E\u060F\u06DE\u06E9\u06FD\u06FE\u07F6\u09F2\u09F3\u09FA\u09FB\u0AF1\u0B70\u0BF3-\u0BFA\u0C7F\u0D79\u0E3F\u0F01-\u0F03\u0F13\u0F15-\u0F17\u0F1A-\u0F1F\u0F34\u0F36\u0F38\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE\u0FCF\u0FD5-\u0FD8\u109E\u109F\u1390-\u1399\u17DB\u1940\u19DE-\u19FF\u1B61-\u1B6A\u1B74-\u1B7C\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2044\u2052\u207A-\u207C\u208A-\u208C\u20A0-\u20BE\u2100\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u214F\u218A\u218B\u2190-\u2307\u230C-\u2328\u232B-\u23FA\u2400-\u2426\u2440-\u244A\u249C-\u24E9\u2500-\u2767\u2794-\u27C4\u27C7-\u27E5\u27F0-\u2982\u2999-\u29D7\u29DC-\u29FB\u29FE-\u2B73\u2B76-\u2B95\u2B98-\u2BB9\u2BBD-\u2BC8\u2BCA-\u2BD1\u2BEC-\u2BEF\u2CE5-\u2CEA\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3004\u3012\u3013\u3020\u3036\u3037\u303E\u303F\u309B\u309C\u3190\u3191\u3196-\u319F\u31C0-\u31E3\u3200-\u321E\u322A-\u3247\u3250\u3260-\u327F\u328A-\u32B0\u32C0-\u32FE\u3300-\u33FF\u4DC0-\u4DFF\uA490-\uA4C6\uA700-\uA716\uA720\uA721\uA789\uA78A\uA828-\uA82B\uA836-\uA839\uAA77-\uAA79\uAB5B\uFB29\uFBB2-\uFBC1\uFDFC\uFDFD\uFE62\uFE64-\uFE66\uFE69\uFF04\uFF0B\uFF1C-\uFF1E\uFF3E\uFF40\uFF5C\uFF5E\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFFC\uFFFD', + astral: '\uD83E[\uDC00-\uDC0B\uDC10-\uDC47\uDC50-\uDC59\uDC60-\uDC87\uDC90-\uDCAD\uDD10-\uDD18\uDD80-\uDD84\uDDC0]|\uD83C[\uDC00-\uDC2B\uDC30-\uDC93\uDCA0-\uDCAE\uDCB1-\uDCBF\uDCC1-\uDCCF\uDCD1-\uDCF5\uDD10-\uDD2E\uDD30-\uDD6B\uDD70-\uDD9A\uDDE6-\uDE02\uDE10-\uDE3A\uDE40-\uDE48\uDE50\uDE51\uDF00-\uDFFF]|\uD83D[\uDC00-\uDD79\uDD7B-\uDDA3\uDDA5-\uDED0\uDEE0-\uDEEC\uDEF0-\uDEF3\uDF00-\uDF73\uDF80-\uDFD4]|\uD835[\uDEC1\uDEDB\uDEFB\uDF15\uDF35\uDF4F\uDF6F\uDF89\uDFA9\uDFC3]|\uD800[\uDD37-\uDD3F\uDD79-\uDD89\uDD8C\uDD90-\uDD9B\uDDA0\uDDD0-\uDDFC]|\uD82F\uDC9C|\uD805\uDF3F|\uD802[\uDC77\uDC78\uDEC8]|\uD81A[\uDF3C-\uDF3F\uDF45]|\uD836[\uDC00-\uDDFF\uDE37-\uDE3A\uDE6D-\uDE74\uDE76-\uDE83\uDE85\uDE86]|\uD834[\uDC00-\uDCF5\uDD00-\uDD26\uDD29-\uDD64\uDD6A-\uDD6C\uDD83\uDD84\uDD8C-\uDDA9\uDDAE-\uDDE8\uDE00-\uDE41\uDE45\uDF00-\uDF56]|\uD83B[\uDEF0\uDEF1]' + }, + 'Z': { + bmp: '\x20\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000' + }, + 'Ahom': { + astral: '\uD805[\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF3F]' + }, + 'Anatolian_Hieroglyphs' :{ + astral: '\uD811[\uDC00-\uDE46]' + }, + 'Arabic': { + bmp: '\u0600-\u0604\u0606-\u060B\u060D-\u061A\u061E\u0620-\u063F\u0641-\u064A\u0656-\u066F\u0671-\u06DC\u06DE-\u06FF\u0750-\u077F\u08A0-\u08B4\u08E3-\u08FF\uFB50-\uFBC1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFD\uFE70-\uFE74\uFE76-\uFEFC', + astral: '\uD803[\uDE60-\uDE7E]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB\uDEF0\uDEF1]' + }, + 'Armenian': { + bmp: '\u0531-\u0556\u0559-\u055F\u0561-\u0587\u058A\u058D-\u058F\uFB13-\uFB17' + }, + 'Avestan': { + astral: '\uD802[\uDF00-\uDF35\uDF39-\uDF3F]' + }, + 'Balinese': { + bmp: '\u1B00-\u1B4B\u1B50-\u1B7C' + }, + 'Bamum': { + bmp: '\uA6A0-\uA6F7', + astral: '\uD81A[\uDC00-\uDE38]' + }, + 'Bassa_Vah': { + astral: '\uD81A[\uDED0-\uDEED\uDEF0-\uDEF5]' + }, + 'Batak': { + bmp: '\u1BC0-\u1BF3\u1BFC-\u1BFF' + }, + 'Bengali': { + bmp: '\u0980-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09FB' + }, + 'Bopomofo': { + bmp: '\u02EA\u02EB\u3105-\u312D\u31A0-\u31BA' + }, + 'Brahmi': { + astral: '\uD804[\uDC00-\uDC4D\uDC52-\uDC6F\uDC7F]' + }, + 'Braille': { + bmp: '\u2800-\u28FF' + }, + 'Buginese': { + bmp: '\u1A00-\u1A1B\u1A1E\u1A1F' + }, + 'Buhid': { + bmp: '\u1740-\u1753' + }, + 'Canadian_Aboriginal': { + bmp: '\u1400-\u167F\u18B0-\u18F5' + }, + 'Carian': { + astral: '\uD800[\uDEA0-\uDED0]' + }, + 'Caucasian_Albanian': { + astral: '\uD801[\uDD30-\uDD63\uDD6F]' + }, + 'Chakma': { + astral: '\uD804[\uDD00-\uDD34\uDD36-\uDD43]' + }, + 'Cham': { + bmp: '\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA5C-\uAA5F' + }, + 'Cherokee': { + bmp: '\u13A0-\u13F5\u13F8-\u13FD\uAB70-\uABBF' + }, + 'Common': { + bmp: '\0-\x40\\x5B-\x60\\x7B-\xA9\xAB-\xB9\xBB-\xBF\xD7\xF7\u02B9-\u02DF\u02E5-\u02E9\u02EC-\u02FF\u0374\u037E\u0385\u0387\u0589\u0605\u060C\u061B\u061C\u061F\u0640\u06DD\u0964\u0965\u0E3F\u0FD5-\u0FD8\u10FB\u16EB-\u16ED\u1735\u1736\u1802\u1803\u1805\u1CD3\u1CE1\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u2000-\u200B\u200E-\u2064\u2066-\u2070\u2074-\u207E\u2080-\u208E\u20A0-\u20BE\u2100-\u2125\u2127-\u2129\u212C-\u2131\u2133-\u214D\u214F-\u215F\u2189-\u218B\u2190-\u23FA\u2400-\u2426\u2440-\u244A\u2460-\u27FF\u2900-\u2B73\u2B76-\u2B95\u2B98-\u2BB9\u2BBD-\u2BC8\u2BCA-\u2BD1\u2BEC-\u2BEF\u2E00-\u2E42\u2FF0-\u2FFB\u3000-\u3004\u3006\u3008-\u3020\u3030-\u3037\u303C-\u303F\u309B\u309C\u30A0\u30FB\u30FC\u3190-\u319F\u31C0-\u31E3\u3220-\u325F\u327F-\u32CF\u3358-\u33FF\u4DC0-\u4DFF\uA700-\uA721\uA788-\uA78A\uA830-\uA839\uA92E\uA9CF\uAB5B\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFEFF\uFF01-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\uFF70\uFF9E\uFF9F\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFF9-\uFFFD', + astral: '\uD83E[\uDC00-\uDC0B\uDC10-\uDC47\uDC50-\uDC59\uDC60-\uDC87\uDC90-\uDCAD\uDD10-\uDD18\uDD80-\uDD84\uDDC0]|\uD82F[\uDCA0-\uDCA3]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDFCB\uDFCE-\uDFFF]|\uDB40[\uDC01\uDC20-\uDC7F]|\uD83D[\uDC00-\uDD79\uDD7B-\uDDA3\uDDA5-\uDED0\uDEE0-\uDEEC\uDEF0-\uDEF3\uDF00-\uDF73\uDF80-\uDFD4]|\uD800[\uDD00-\uDD02\uDD07-\uDD33\uDD37-\uDD3F\uDD90-\uDD9B\uDDD0-\uDDFC\uDEE1-\uDEFB]|\uD834[\uDC00-\uDCF5\uDD00-\uDD26\uDD29-\uDD66\uDD6A-\uDD7A\uDD83\uDD84\uDD8C-\uDDA9\uDDAE-\uDDE8\uDF00-\uDF56\uDF60-\uDF71]|\uD83C[\uDC00-\uDC2B\uDC30-\uDC93\uDCA0-\uDCAE\uDCB1-\uDCBF\uDCC1-\uDCCF\uDCD1-\uDCF5\uDD00-\uDD0C\uDD10-\uDD2E\uDD30-\uDD6B\uDD70-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE10-\uDE3A\uDE40-\uDE48\uDE50\uDE51\uDF00-\uDFFF]' + }, + 'Coptic': { + bmp: '\u03E2-\u03EF\u2C80-\u2CF3\u2CF9-\u2CFF' + }, + 'Cuneiform': { + astral: '\uD809[\uDC00-\uDC6E\uDC70-\uDC74\uDC80-\uDD43]|\uD808[\uDC00-\uDF99]' + }, + 'Cypriot': { + astral: '\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F]' + }, + 'Cyrillic': { + bmp: '\u0400-\u0484\u0487-\u052F\u1D2B\u1D78\u2DE0-\u2DFF\uA640-\uA69F\uFE2E\uFE2F' + }, + 'Deseret': { + astral: '\uD801[\uDC00-\uDC4F]' + }, + 'Devanagari': { + bmp: '\u0900-\u0950\u0953-\u0963\u0966-\u097F\uA8E0-\uA8FD' + }, + 'Duployan': { + astral: '\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9C-\uDC9F]' + }, + 'Egyptian_Hieroglyphs': { + astral: '\uD80C[\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]' + }, + 'Elbasan': { + astral: '\uD801[\uDD00-\uDD27]' + }, + 'Ethiopic': { + bmp: '\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u137C\u1380-\u1399\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E' + }, + 'Georgian': { + bmp: '\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u10FF\u2D00-\u2D25\u2D27\u2D2D' + }, + 'Glagolitic': { + bmp: '\u2C00-\u2C2E\u2C30-\u2C5E' + }, + 'Gothic': { + astral: '\uD800[\uDF30-\uDF4A]' + }, + 'Grantha': { + astral: '\uD804[\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]' + }, + 'Greek': { + bmp: '\u0370-\u0373\u0375-\u0377\u037A-\u037D\u037F\u0384\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03E1\u03F0-\u03FF\u1D26-\u1D2A\u1D5D-\u1D61\u1D66-\u1D6A\u1DBF\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FC4\u1FC6-\u1FD3\u1FD6-\u1FDB\u1FDD-\u1FEF\u1FF2-\u1FF4\u1FF6-\u1FFE\u2126\uAB65', + astral: '\uD800[\uDD40-\uDD8C\uDDA0]|\uD834[\uDE00-\uDE45]' + }, + 'Gujarati': { + bmp: '\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AF1\u0AF9' + }, + 'Gurmukhi': { + bmp: '\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75' + }, + 'Han': { + bmp: '\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u3005\u3007\u3021-\u3029\u3038-\u303B\u3400-\u4DB5\u4E00-\u9FD5\uF900-\uFA6D\uFA70-\uFAD9', + astral: '\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD87E[\uDC00-\uDE1D]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD873[\uDC00-\uDEA1]' + }, + 'Hangul': { + bmp: '\u1100-\u11FF\u302E\u302F\u3131-\u318E\u3200-\u321E\u3260-\u327E\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC' + }, + 'Hanunoo': { + bmp: '\u1720-\u1734' + }, + 'Hatran': { + astral: '\uD802[\uDCE0-\uDCF2\uDCF4\uDCF5\uDCFB-\uDCFF]' + }, + 'Hebrew': { + bmp: '\u0591-\u05C7\u05D0-\u05EA\u05F0-\u05F4\uFB1D-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFB4F' + }, + 'Hiragana': { + bmp: '\u3041-\u3096\u309D-\u309F', + astral: '\uD82C\uDC01|\uD83C\uDE00' + }, + 'Imperial_Aramaic': { + astral: '\uD802[\uDC40-\uDC55\uDC57-\uDC5F]' + }, + 'Inherited': { + bmp: '\u0300-\u036F\u0485\u0486\u064B-\u0655\u0670\u0951\u0952\u1AB0-\u1ABE\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFC-\u1DFF\u200C\u200D\u20D0-\u20F0\u302A-\u302D\u3099\u309A\uFE00-\uFE0F\uFE20-\uFE2D', + astral: '\uD834[\uDD67-\uDD69\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD]|\uD800[\uDDFD\uDEE0]|\uDB40[\uDD00-\uDDEF]' + }, + 'Inscriptional_Pahlavi': { + astral: '\uD802[\uDF60-\uDF72\uDF78-\uDF7F]' + }, + 'Inscriptional_Parthian': { + astral: '\uD802[\uDF40-\uDF55\uDF58-\uDF5F]' + }, + 'Javanese': { + bmp: '\uA980-\uA9CD\uA9D0-\uA9D9\uA9DE\uA9DF' + }, + 'Kaithi': { + astral: '\uD804[\uDC80-\uDCC1]' + }, + 'Kannada': { + bmp: '\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2' + }, + 'Katakana': { + bmp: '\u30A1-\u30FA\u30FD-\u30FF\u31F0-\u31FF\u32D0-\u32FE\u3300-\u3357\uFF66-\uFF6F\uFF71-\uFF9D', + astral: '\uD82C\uDC00' + }, + 'Kayah_Li': { + bmp: '\uA900-\uA92D\uA92F' + }, + 'Kharoshthi': { + astral: '\uD802[\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F-\uDE47\uDE50-\uDE58]' + }, + 'Khmer': { + bmp: '\u1780-\u17DD\u17E0-\u17E9\u17F0-\u17F9\u19E0-\u19FF' + }, + 'Khojki': { + astral: '\uD804[\uDE00-\uDE11\uDE13-\uDE3D]' + }, + 'Khudawadi': { + astral: '\uD804[\uDEB0-\uDEEA\uDEF0-\uDEF9]' + }, + 'Lao': { + bmp: '\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF' + }, + 'Latin': { + bmp: 'A-Za-z\xAA\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02E0-\u02E4\u1D00-\u1D25\u1D2C-\u1D5C\u1D62-\u1D65\u1D6B-\u1D77\u1D79-\u1DBE\u1E00-\u1EFF\u2071\u207F\u2090-\u209C\u212A\u212B\u2132\u214E\u2160-\u2188\u2C60-\u2C7F\uA722-\uA787\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA7FF\uAB30-\uAB5A\uAB5C-\uAB64\uFB00-\uFB06\uFF21-\uFF3A\uFF41-\uFF5A' + }, + 'Lepcha': { + bmp: '\u1C00-\u1C37\u1C3B-\u1C49\u1C4D-\u1C4F' + }, + 'Limbu': { + bmp: '\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1940\u1944-\u194F' + }, + 'Linear_A': { + astral: '\uD801[\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]' + }, + 'Linear_B': { + astral: '\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA]' + }, + 'Lisu': { + bmp: '\uA4D0-\uA4FF' + }, + 'Lycian': { + astral: '\uD800[\uDE80-\uDE9C]' + }, + 'Lydian': { + astral: '\uD802[\uDD20-\uDD39\uDD3F]' + }, + 'Mahajani': { + astral: '\uD804[\uDD50-\uDD76]' + }, + 'Malayalam': { + bmp: '\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D75\u0D79-\u0D7F' + }, + 'Mandaic': { + bmp: '\u0840-\u085B\u085E' + }, + 'Manichaean': { + astral: '\uD802[\uDEC0-\uDEE6\uDEEB-\uDEF6]' + }, + 'Meetei_Mayek': { + bmp: '\uAAE0-\uAAF6\uABC0-\uABED\uABF0-\uABF9' + }, + 'Mende_Kikakui': { + astral: '\uD83A[\uDC00-\uDCC4\uDCC7-\uDCD6]' + }, + 'Meroitic_Cursive': { + astral: '\uD802[\uDDA0-\uDDB7\uDDBC-\uDDCF\uDDD2-\uDDFF]' + }, + 'Meroitic_Hieroglyphs': { + astral: '\uD802[\uDD80-\uDD9F]' + }, + 'Miao': { + astral: '\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]' + }, + 'Modi': { + astral: '\uD805[\uDE00-\uDE44\uDE50-\uDE59]' + }, + 'Mongolian': { + bmp: '\u1800\u1801\u1804\u1806-\u180E\u1810-\u1819\u1820-\u1877\u1880-\u18AA' + }, + 'Mro': { + astral: '\uD81A[\uDE40-\uDE5E\uDE60-\uDE69\uDE6E\uDE6F]' + }, + 'Multani': { + astral: '\uD804[\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA9]' + }, + 'Myanmar': { + bmp: '\u1000-\u109F\uA9E0-\uA9FE\uAA60-\uAA7F' + }, + 'Nabataean': { + astral: '\uD802[\uDC80-\uDC9E\uDCA7-\uDCAF]' + }, + 'New_Tai_Lue': { + bmp: '\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u19DE\u19DF' + }, + 'Nko': { + bmp: '\u07C0-\u07FA' + }, + 'Ogham': { + bmp: '\u1680-\u169C' + }, + 'Ol_Chiki': { + bmp: '\u1C50-\u1C7F' + }, + 'Old_Hungarian': { + astral: '\uD803[\uDC80-\uDCB2\uDCC0-\uDCF2\uDCFA-\uDCFF]' + }, + 'Old_Italic': { + astral: '\uD800[\uDF00-\uDF23]' + }, + 'Old_North_Arabian': { + astral: '\uD802[\uDE80-\uDE9F]' + }, + 'Old_Permic': { + astral: '\uD800[\uDF50-\uDF7A]' + }, + 'Old_Persian': { + astral: '\uD800[\uDFA0-\uDFC3\uDFC8-\uDFD5]' + }, + 'Old_South_Arabian': { + astral: '\uD802[\uDE60-\uDE7F]' + }, + 'Old_Turkic': { + astral: '\uD803[\uDC00-\uDC48]' + }, + 'Oriya': { + bmp: '\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B77' + }, + 'Osmanya': { + astral: '\uD801[\uDC80-\uDC9D\uDCA0-\uDCA9]' + }, + 'Pahawh_Hmong': { + astral: '\uD81A[\uDF00-\uDF45\uDF50-\uDF59\uDF5B-\uDF61\uDF63-\uDF77\uDF7D-\uDF8F]' + }, + 'Palmyrene': { + astral: '\uD802[\uDC60-\uDC7F]' + }, + 'Pau_Cin_Hau': { + astral: '\uD806[\uDEC0-\uDEF8]' + }, + 'Phags_Pa': { + bmp: '\uA840-\uA877' + }, + 'Phoenician': { + astral: '\uD802[\uDD00-\uDD1B\uDD1F]' + }, + 'Psalter_Pahlavi': { + astral: '\uD802[\uDF80-\uDF91\uDF99-\uDF9C\uDFA9-\uDFAF]' + }, + 'Rejang': { + bmp: '\uA930-\uA953\uA95F' + }, + 'Runic': { + bmp: '\u16A0-\u16EA\u16EE-\u16F8' + }, + 'Samaritan': { + bmp: '\u0800-\u082D\u0830-\u083E' + }, + 'Saurashtra': { + bmp: '\uA880-\uA8C4\uA8CE-\uA8D9' + }, + 'Sharada': { + astral: '\uD804[\uDD80-\uDDCD\uDDD0-\uDDDF]' + }, + 'Shavian': { + astral: '\uD801[\uDC50-\uDC7F]' + }, + 'Siddham': { + astral: '\uD805[\uDD80-\uDDB5\uDDB8-\uDDDD]' + }, + 'SignWriting': { + astral: '\uD836[\uDC00-\uDE8B\uDE9B-\uDE9F\uDEA1-\uDEAF]' + }, + 'Sinhala': { + bmp: '\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2-\u0DF4', + astral: '\uD804[\uDDE1-\uDDF4]' + }, + 'Sora_Sompeng': { + astral: '\uD804[\uDCD0-\uDCE8\uDCF0-\uDCF9]' + }, + 'Sundanese': { + bmp: '\u1B80-\u1BBF\u1CC0-\u1CC7' + }, + 'Syloti_Nagri': { + bmp: '\uA800-\uA82B' + }, + 'Syriac': { + bmp: '\u0700-\u070D\u070F-\u074A\u074D-\u074F' + }, + 'Tagalog': { + bmp: '\u1700-\u170C\u170E-\u1714' + }, + 'Tagbanwa': { + bmp: '\u1760-\u176C\u176E-\u1770\u1772\u1773' + }, + 'Tai_Le': { + bmp: '\u1950-\u196D\u1970-\u1974' + }, + 'Tai_Tham': { + bmp: '\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA0-\u1AAD' + }, + 'Tai_Viet': { + bmp: '\uAA80-\uAAC2\uAADB-\uAADF' + }, + 'Takri': { + astral: '\uD805[\uDE80-\uDEB7\uDEC0-\uDEC9]' + }, + 'Tamil': { + bmp: '\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BFA' + }, + 'Telugu': { + bmp: '\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C78-\u0C7F' + }, + 'Thaana': { + bmp: '\u0780-\u07B1' + }, + 'Thai': { + bmp: '\u0E01-\u0E3A\u0E40-\u0E5B' + }, + 'Tibetan': { + bmp: '\u0F00-\u0F47\u0F49-\u0F6C\u0F71-\u0F97\u0F99-\u0FBC\u0FBE-\u0FCC\u0FCE-\u0FD4\u0FD9\u0FDA' + }, + 'Tifinagh': { + bmp: '\u2D30-\u2D67\u2D6F\u2D70\u2D7F' + }, + 'Tirhuta': { + astral: '\uD805[\uDC80-\uDCC7\uDCD0-\uDCD9]' + }, + 'Ugaritic': { + astral: '\uD800[\uDF80-\uDF9D\uDF9F]' + }, + 'Vai': { + bmp: '\uA500-\uA62B' + }, + 'Warang_Citi': { + astral: '\uD806[\uDCA0-\uDCF2\uDCFF]' + }, + 'Yi': { + bmp: '\uA000-\uA48C\uA490-\uA4C6' + } +}; + +const isValidString = (s) => typeof s == 'string' && s.length > 0; +const buildRegexp = (re) => new RegExp(re.replace(/\\p\{([^}]+)\}/g, function(a, b) { + if (!unicode[b]) throw Error('Invalid expression'); + var i = unicode[b], c = ''; + if (i.bmp) { + c = '[' + i.bmp + ']' + (i.astral ? '|' : ''); + } + if (i.astral) { + c += i.astral; + } + return '(?:' + c + ')'; +})); + +module.exports = { + + unicodelettersonly: function(value, param) { + const arr = ['L']; + + for (let key in param) { + arr.push(key); + } + + return !isValidString(value) || (buildRegexp('^(\\p{' + arr.join('}|\\p{') + '})+$')).test(value) + }, + + unicodescript: function(value, param) { + const arr = param.scripts.slice(0); + + for (let key in param) { + if (key != 'scripts') { + arr.push(key); + } + } + + return !isValidString(value) || (buildRegexp('^(\\p{' + arr.join('}|\\p{') + '})+$')).test(value) + }, + +}; \ No newline at end of file diff --git a/lib/validator/upload.js b/lib/validator/upload.js new file mode 100644 index 0000000..0396794 --- /dev/null +++ b/lib/validator/upload.js @@ -0,0 +1,82 @@ +// IMPROVE: Improve this with the actual file field being checked, for now we check all uploaded files +function getFiles(app, value) { + const files = []; + + if (app.req.files) { + for (let field in app.req.files) { + if (Array.isArray(app.req.files[field])) { + for (let file of app.req.files[field]) { + if (!file.truncated) { + files.push(file); + } + } + } else { + let file = app.req.files[field]; + if (!file.truncated) { + files.push(file); + } + } + } + } + + return files; +} + +module.exports = { + + accept: function(value, param) { + const files = getFiles(this, value); + const allowed = param.replace(/\s/g, '').split(','); + + if (!files.length) { + return true; + } + + for (let file of files) { + if (!allowed.some(allow => { + if (allow[0] == '.') { + const re = new RegExp(`\\${allow}$`, 'i'); + if (re.test(file.name)) { + return true; + } + } else if (/(audio|video|image)\/\*/i.test(allow)) { + const re = new RegExp(`^${allow.replace('*', '.*')}$`, 'i'); + if (re.test(file.mimetype)) { + return true; + } + } else if (allow.toLowerCase() == file.mimetype.toLowerCase()) { + return true; + } + })) { + return false; + } + } + + return true; + }, + + minsize: function(value, param) { + return !getFiles(this, value).some(file => file.size < param); + }, + + maxsize: function(value, param) { + return !getFiles(this, value).some(file => file.size > param); + }, + + mintotalsize: function(value, param) { + return getFiles(this, value).reduce((size, file) => size + file.size, 0) >= param; + }, + + maxtotalsize: function(value, param) { + return getFiles(this, value).reduce((size, file) => size + file.size, 0) <= param; + }, + + minfiles: function(value, param) { + return getFiles(this, value).length >= param; + }, + + maxfiles: function(value, param) { + return getFiles(this, value).length <= param; + }, + +}; \ No newline at end of file diff --git a/lib/webhooks/stripe.js b/lib/webhooks/stripe.js new file mode 100644 index 0000000..7c8c3d0 --- /dev/null +++ b/lib/webhooks/stripe.js @@ -0,0 +1,22 @@ +const webhook = require('../core/webhook'); +const config = require('../setup/config'); +const fs = require('fs-extra') + +if (fs.existsSync('app/webhooks/stripe')) { + const stripe = require('stripe')(config.stripe.secretKey); + const endpointSecret = config.stripe.endpointSecret; + + exports.handler = webhook.createHandler('stripe', (req, res, next) => { + const sig = req.headers['stripe-signature']; + + try { + stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret); + } catch (err) { + res.status(400).send(`Webhook Error: ${err.message}`); + return false; + } + + // return the action name to execute + return req.body.type; + }); +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..c0aa070 --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "ertfast_tiller", + "version": "2.0.0", + "private": true, + "description": "", + "main": "index.js", + "engines": { + "node": ">=16.9.0" + }, + "scripts": { + "start": "node ./index.js" + }, + "author": "Wappler", + "license": "ISC", + "dependencies": { + "archiver": "^7.0.1", + "compression": "^1.7.4", + "connect-session-knex": "^4.0.0", + "cookie-parser": "^1.4.6", + "cors": "^2.8.5", + "debug": "^4.3.2", + "dotenv": "^16.0.3", + "ejs": "^3.1.6", + "express": "^4.17.1", + "express-end": "0.0.8", + "express-fileupload": "^1.2.1", + "express-session": "^1.17.2", + "follow-redirects": "^1.14.5", + "fs-extra": "^11.2.0", + "http-proxy": "^1.18.1", + "knex": "^3.0.1", + "mime-types": "^2.1.34", + "node-schedule": "^2.0.0", + "nodemon": "^3.0.1", + "pdf-lib": "^1.17.1", + "qs": "^6.10.1", + "session-file-store": "^1.5.0", + "socket.io": "^4.7.5", + "unzipper": "^0.12.1", + "uuid": "^10.0.0" + }, + "nodemonConfig": { + "watch": [ + "app", + "lib", + "views", + "extensions", + "tmp/**/restart.txt" + ], + "ext": "ejs,js,json" + } +} diff --git a/public/PDF/ERT_Template_1.pdf b/public/PDF/ERT_Template_1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e02a7efacaa05efdc64743bf2862dbb0a3b0a06b GIT binary patch literal 633426 zcmeFY1yo#HmoAD0mq2iW2Mg|1xVyW%1c##V;O_43kl-N@G(d0<7CcyRcMIGi=X9U^ z|9`q~-@ZM@8*jX~_kg|E-fOKn*Ehc@t0<_H#3UG5nYhuY*4D=+(U5^G05TRb2NP>F zK0anSD|>Tekd>#o85t|H92plY7m%HmS&Ddzy;u_X8q>gZnQTV&L{SG@7dv}rGEQbC zkhz(aDcAu-#>yrjz${^93pNKaOV}EN&Hos}EG?$X$H5`a$pPR70>l9<01k0>E^%RD zVGc0>fJ+1@#=#~cz$YxiDJI6r2IS%v=a!J*5C^h}a{xI-xH$nF9FXon0exm^doy!) z-KUkaa&SL2EgZ;zEKfSBFw2?STY}$^v2wCKY2^CHI@y?292~%qKEPjnR3QkFv9fS- zGOKwwnlo#um|HMw>avotld(cTa0Y|SjqT8o-z=M$m>E4cGBPnT@`j}#NcET6^e}2* zPg{&0wMav;7=unEL`D%~*%hBN15#6o>OmEi`0^mHt%nPkT9ojX_{{khujKG7B0q6_vQ6 z1RC=1HvIqOJsL8^#vFe!_wOi4#ti`c8)vxKfXu37?Cc!O8f08t5EB2!1S2CO_<|u+ zTU69FbJQ+WL^4$Dbt5C=m2Jq2rW$+Jz<%7`VR-luznHvDSm|tMJ|cWaC{^-GYBX?pcKmg<~z0tDI~KnL*}k^o4W)ni6-ix%!Yw(X5xBvYQD_!c0et)$>HFe9MTSUb@EZW zyjse~A>-01gX;N#KTDM6SO5BXW1#w|@pCgXvvIzTd%yR7s|YMY1o)o#i}>dRa6|y~ z8ZnlO_WO>SH1bdWKb2ZU@8PDA$??#EDSdLa(c$ufxZUBS?vw`$+P!!~WJMz+PBR1h z&`Pq;Ch7BPeNcB%1CX;Yv`AQRSahJC!4Pu13?ljj=Lf?W#f6GOa8EFWfynJhVsz7? z290}cr)2pY+KSak1={AsR0#x?;RIDC9;_Ue|H775Y3Sq!Z(fMVGxQESZD?G-d6Ta3 zP9uzZ4Kn{Tm~kZ}3EmJ~cuV+b_}*uHC|Zp8*7!yE8U8b`S;M$%U@M=Y9J8r$z9B&s z7~fN}&L{4?LewR+Hd;yYgeoc~z^@QmP3!FVI=?tEm6DlgOwqk%WNT-xrmAVEG4)(U zKRri1EnPn@H6tr20iXB@s{&P&ymqY}+tB0Eh>p!-3c8~VfY)x$}jV<0-t(e(c zACiHI9QC~d21WoiEu0wK{@!yM#n?`fgBvoL0~KMr(iAy`B7BR1##s1c&nya2T*Sd$_h~p6D;pU%kWHUi66E0G_>`4Y znZ?XqtxU~TBt@82nbknX_RfyRAai?Dk3T~Gua1!Q$wP9M@xM$1Np27&|9zP%=FSc- zAk$wEiMxX(Rl$&)29YAE%1s6Y{sD!gDgbi-OSh-w2U#Yhg9Pi7CH`2s1l!a7uSGFS za6aAtF;Ig0>Hd$rB%$>WBOomn$Vo<{zgo>~(BIPllFf0xPi)br0(X#LXtuQ?&<_%{i_zglk2j^>8Hmd5&P zp%8>1yfalb2kSCJ${A)gb9XSav>k+7B7a;&|F}r&GixcDSeu(Zf%)q_gdITkU%&}K z4oFsiG6fk6vnm+mVhaAlZq8(E%xcCa&QFGaO7Bmc{hggpMElc+M4n8~$_-!wK$Z_# z7NkC6Vg&+$oUCMQtUxA?r>8(NPEH^bH{>Y*p#OJe&A(L~9Bj<;#$b>YWJZVth~=Js z>ofnx9*D-OE+*h7rJnrq&+6lk75qRtZA(};-@Tx69PQ4ITvf{!=8SDB*lb1~`PM|aw-;M-FQq`k#kBdk(yGmR zjF=ml1ir^MD3e3}@50b8lm8Ck?_m7}AqNM?F9_K<$hcWJm>^*V0_=YVA>e-sKv*cq z0sXhCKXLbW)&GC%{~ufbx033Y1O30q`cHA}ck8n-L3|YQ!wTSJf^2fwSeaP4A$jC) zD#bs&oQ?f=hyJ%fFJ*4)N(SWmmBO9^(_hXk6+VGcuH<;?qOk(HaM^@GWP!q+g1P!z zYCgrazg3P6zywK`5an1n{-4tAw+-MgoBu0tzjT8{wWpZ-wEO&H9|rs@DE#JM|qJbApbQo%=aw$;_CwzlP zTm79zMkc#7yOuXaQv3B)J?~q30kK`uUA-Y@#SqvMzBS4YsD##+fWPA(+f(83cerwJ zGC^R1)Onmt+??E;f5$)o@V|rNlk0FlG4OxbRXN!IRG9sj!vE~85Rs57x1!m#@J^$FA{N-ojBOo{?_dv`>Fp9 zPVycgD`PXrHi}Wj+|tF?7zBar*Dn8mkXr12C=a35lk&eq_OH@^M-Rd>ZcgC;0ertz zl>c$*1^K+18}RQ*U(wOr{;AaXz0}f6K>=e=6ZanRxh@~g>64jwL|~#|Zsg-)OG$~4 zy^;^0AY+-!-nXC4mZxr^7AC(gz$6tiW!x?2kb;hdk?HKnH&TA{8Z-_|AufijVo=Oi zgpEeg#@@oqdy=)k|9JJ;?eJ&2gR6y#hPpbudkvyd-rKJ0!SGw+*aIl2w`hXFH^IT) z7bj;@P;vfHS=*XQz;eV(Oeh81FAnd@S}RWPGj0ThnGo`#UcbxYf^Ks*ViF3GM-WQa z*GT7nd%5zX4?5v9&S)-Cn3W6F-Qfs=l(3@vZE#K1vvP!LT-YM)(DlNu8esu>J~$85 z4gXcG0;{)^0akHaFdcY(^(8zNF!sU2S_pkeqLDkKxY^4vH~e8ZE+-+~m6b*SAr70i z_7oMNP7HK7KZyX(#i%}1KRj%Oau}e$1v@fGq(lH?L+oP^GzbdX?*Jp%(g~{RBCQRE zZ-rLqxfq=t7o|?kViZRCxadHch0fAqmB#o5-^53_1Dycx?=L>H?)MJq>+7rXwMkxL zdByHCyDg#%ZDBvF42*=rVo{2M@$?tS6U2w>iFvjz%t{MgoCm`PW8n|XBitr~+ZD#D z_tQA$9Y>!DJXkqec@BVP^3OyJ+#qu!^XUKCFIMz2J$ze=zYfYuT!U;c4t>L+-*^KR zL`*eK@`lNiR3~6m$mT1}SMzNdFv=}vGwDxc{z!u_N^%{Zrh&Czwa7JspVjbGJkyP- z>|O42HOe&#tyP`H*{2>|YdaRcmf|C=?$}y^Z%1_avT%ZS?fRDa0pp&-XFAZ+Op#PE zXG?fYZHz&2YI4o$to6s-#@r9!3UKEpMhGz&UuaQQ?iC4jm)MKiAT4QI@`e{)VPj#Y z-QrkrY(T%4A~965{X1r1wy$!jd}XVsza=j8FKq~%qMXv+FnYwdN<2t;(1u{qr8h_K zO8!vsm_pozwtmT(o*^+(#PM2eT2)>R<>KYXs9XMNg*I6iCEhXxYeAPV9+~Wt2T}cE zpFHU3NxsU7w(W;=eJ}}s$Y^Qlg!(zEPN8VMF!yBScF(yinCPdZCu5uc1AANaL)@|~ zX;JKW>Gs(<&Mi+M3pOQ-KnXW{*bNnX7QqlUYq1>F8SM)r>vYeOc#jN^94pghvt(w9gL z$@+usgYBISA8h92_g!7_T4MP-NQ`4YY|Res%g3yURnc(!5%tQilz`Zq$Wm8uL6wVa zhn$D<_Bf3x3mtZ=4NktRu1;Q`+7`DKp$}0GWe@S)vUmrwS4W*SKRtrJH8GK_YPTbN z4;}jZe|ox99en~xK> zF@efB&fPvHRcSOaSbY)C*34=}-l}EMhlq*FMJPs$g~@jRSVMLy{v@HtY>(R*NgAFT zmKzz`6MJnj;mE3xRFDz*^rP1M_RSmYJ!1|Y+B|*C2@*^F~uOk zAbvScL)t<1NSb&;!78?1k%!isDm!jeCbj5Mn%i8@!d(22>MHptDRhwX2P>#ukK$1$ zhiNR{W+N>tE;r7XvN~?zNA#(MM`o+)gVte^VB+Vb*n!dw-=XgVg0w>9IKn4%ZOPDs zM&@at+|xLfvZUAQgL1_>;j}I?g>_0E3LeTvV6_fO#*KAIh_o znO=WOB2||&SJOTyzf?vj7oEb~t)4>Kef3e#TP(YB*ILa^Y3sdWdF8Ikh57}?uJr}6 zOJcM9&-e4?hng7WhVQ?>+nT1|z1n4DUj5N#aAddqu-wvm`-|1>`2BT z^h))J^~n54K6is?#j8VechVc>ZS~FamCJ4WiT7#{4r{YA|BKun{pYg*z30hRI zvlUTI?t%==C^*y+PwUK+=9(PG5{)hkDFo@qLnUT^H4)?id@_6dqcUzK8753 z7ZH#;NJPcqN>PK`$T7n^?E)@?+&O0B-o)%WiMs8InKtAiiV~ntcPColQp8$H5>m*| z>G_k1F3py1Z6)3dd5E@T{^mMRBx+Lkk+_ZA-8?HAuNKp5p-_IMB2S2ROBtvw^HcOa34K*lp)jM&iK!JVLyZGCy zX18H`-UU}miD*-9RbAKY`G!pLg(}#TB~-0njr^n~)Hzr7xQ5J5$X)RPPaxz~(E-mM zz_rEY*U;7AZ}N=sr+a?p0F(w(1iK;aIzC>r^wEHW`OYLe1iSC(#nHT3phKg1PuJPg zL#6q*uB?6DS1EJ){s)3(L?>F^{jXL;=wZ3%={^y z_(t4!Ha1Gy_c-cCfM&ghTUq!T?Q#idRPu3l%b5`I)$;g3VkG4e_?1@aQk?+=#ls4uC+6IXnW ze2%<*bFW&y4|$E51x|6tIqObK1*s_v#2r7AA9(dK9+N_vFkD1|eCt!f08x>xg#at3 zs*oO1gGBCCM24uMzQ#v2D^Tdm zbY;0mxell?7wR7-Vk%0sGVzqW$LelXfb(d7&)05KmS>*uTHEAHtXhbRs=k|t8h3#! zr8RdlLk6z;_J010qkQ%N`gu_i1_Cz2AgIqtm(0aKWofd(dHB1<-OKgD?Fys#BeW9v zc;g11wyHZIK6*bTOF@@a@xxpmDgl76xCaAi6sl6MUotCHFBBFLOC-C{2cjY;1bS$M zL_%#s4@N<$h;|Kq^&M?-;w19p_vQ93H$&oPyGpb=(DKlgpaQDxG`MV=0xW=XOhS?V zYqWv_{XHy0rgf`GvfoV7`MY)w^b2fQZICcAHY}1i4Q#Nh6DAH@|vD8syQbk

m><@6<*6m75BTw5H1mv!eF*P3TZm#y8m4m5Hn+ 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�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

uT`oZ|!oQj9&D@6V099-@)&0rzVB7Y&=;V zs?Jk~Ihu+|O;@u7R=bHd6F{hb<<{iUF%U6eF(NcgJb>E%2Rot8^W3n)Cj8&jHTWMe z{xEXQh|ZeZw_vn>m@Rx>h38#Y%1xeqs;f;w)wL|4)s5>AYlv%5Yp`ozYXlpsF1SxB zTNn+k*c}{RS?52GaC2?HRfTpTwN?pNS;n4PJXL{LK~{ZZ1xtoV&Rs7Yb`@W8TOH6x zwZaS@Z?Z;cUw6@8%4~A>Xgk;9S#^mXZpu8jamt=wJK~+~9PDgzkK*!*`mSrJt=Mr8 z$ue%qtG4?G5l-`5XP-CUJ;}4sv(;1h*5Ofb#iML~p;e?MnT4%I8u1!Y68DY}SI9AN z*!G7vZjcD#Uue%jg4(&xe(lYUerd^{bmm$++6Fim+I61cB9cOCma7w$k`<-FdW$E( zxWByv7r__80FbIJ=eZCMkQQISJ$;pvNE)HvgWjQvSN8QcnCi1mj^Bs(<9am>xWt*(uP_@ zO)=Zx?XKqrW(GEdn}?AiF9Wh-mG>SR+bY^CH#MY4O~;hb{jESOvf23w+%EW`ebcP2!Bu6f{$) zWi#4Qs%D=|=T#@r2JUa<+;;5IWuKU%?Bp@-Zj!m}?(-$ZmFU z0Glh8Rw)|)86UBh!V()#VBaI&%fy_b{aMDpwT{fXYS~ARz`&=?%U{i7QDx>N#5azZ z*~_Jsa23ACRlveGkC^+)Dabkr_mWmud-nIw^d8x1wj4~rX8)*a?RC6?)86iU#UOCZ z)ZBNzNzIfr-TIa9Q#$)3QYytfoTuoq@A|HgxUU*0|MSomC)D3$q3yVSQqh+RKLe*--lImk#b5s2X5V6n)1V67ku5Y*l`Qh!B16p)=B)-SYS=#X!L z{`{cddf%o9k6`Yg>il?r+Y+JLfp_|4eVwv$#ex+;tbc=XC?Rl^-|Ro2gTRS@|M~%$0ybO1is$?r0`j*K?G|dN zUm&O&L=OVn(gjU|`24f|-a#=SWY8nX71Ru311*Ah_2{qAuaLJSx8S!fL5HACzeztH zkgmV1|2l{qGz1a_g@Rx}uOLrQJBSOk3ep6ngT59akR_-bL=B?bGTs7i32#Adxo>f7 zsc&I#nQu{U(SyF$9FWYG{T9QP{1(cV{uZ$w%oLmz3;`s;wNV;!nez*R&euy9sJ$&UD3$&C^@LOHI5BM=(|7ZGNpHK(gpf}9_ zeIX544fOX4FmOvh5DwA*Rro)j{R-}=t9JoE=I!4~|LYOzpbNBynetWmIjIx+`LFV9 z@&e@BD(~&x9+lcqkjfNB@g$NbnC$w5ogD7OIj>0CwJk(4UJl_0-BZq|j&sP-L^uqJgd@o(X zwvKU!5*CWTy^&3EE2mcCQxkuqmztgXcu9qdES?3N&ZT#OL_T zbEq<^_HWD`UR@!~Y&qLAxi(1M1mbU z;ZMPl711MX55J52*JFM|n(|*3+$0uS7EugZK@4h9)ZE3|G45}F2?;72TU^|&r9Y^( z!9JJIHE3>Q@*}xn;xd9S1IOwPO5$1|7eOz>==R5OyDu?rt1+Xt?(=S1n>I#OOcDzC z=R9#;@iriY4s215GKF>U@spo|&5xUI>@M~4T}d|itM>F4%!vjMYhd2w>%pzw$SkuM zUij1J=(pZZ)%J%Sa{|08603IPgazkw7EiwA7j7SwKHZl!rZ7B0$h)xakuC;>?f~7< zTMDn3*G^e%UOGN;Uw(BF`(tbm4C*=fJCaVJ>Zw_ZbH?b&+&ijH8QUo?62QsVJKs>? zUD-3!pFH`er_UdQ&mL*~Odo8j&H3XQQm_rJ=Z8)E)_kXiB`S#q5Uo(I=D6BnvS;T$ zA4*FUV|S{5imk2rj%PtUNL{vBjz@o+Y=veqb%QIwpm=g!5-P%UmCyw2NNNOd; z3-ovVVE{L`f*vq@A^2e;E@%1zVqx5>4dM}NSs59-OS}|j`2>n{R35hfU^l|n-6wit zkS|6#pRT)>a@Ajj7DTgs-b(k|wHXBKp}k%Xir-%}w690vR!^s#VhugliFnKmGYJw% zSK9?n9kevA206*ZN9})JI$Vr8TsV+K)!TM6q9kA#67fpFBSj-SIGyJso@6n&f&4y5 z8~Gua@U-)p``GNRV&h)?K1$OFs<}y7)G=O8yZ@e|B0-y5;&UiMrzj@m?~(?NX(Sgc zZII5;CKMON93=oMU|^09Vz&qc9ojJ#(Ve{c*na|v8Gx*0WUN32V&KoGEtrE6h|TnT zpAn|{_d0Q89A4VubRTE6FcUa93u)<-HA^>*>BJ-U2ukh=Kmry34;OWs)OV%l9#-2Z znhq8=<8+H1p*a8C&%Baj$WZhGb6yvZ)i4%kbiHKI_c zxySIO>WL+)iX2B*&eS*1ViO&CM@ZUB8sApL-b*5!M;SmugR=4-J%nG)?j#ySO2TTa zP_cSel#!9Klq*RJ>Xmof{`k*r+U&Pc;zB8f-sFeT7!%m`vz<1|mW>hkQXDrS9+?y}p#6!mE zwcEpV#cWH=2+O@#x>sQJQjDjmT1PuVGPq1Lee)w+PyrIjB{K0X-BmSa4-)ePRjWi+ z?E7S5Pbwq3BDCq~mK1iz^ABDj#nlOz97@A)7qcot0p063=Z9i}zAHHTSD2c8KuZkO zy@RXFZ4EbvN5a>8_zROab>9(?3d#mROM>IoArhNO$w>Nh4(sN_mQ#Hgdkb$=LnfzY z&u87YLJ%%uthRDO0YFp#bP@}VNc~;w0bWebGCCKLW z&>5+M=;Lw6&ihDQP7ryJsBvb?TwnjI-e#edJu|&rs~`B2c1mcWG_VaX?E;H;drP#B zo75__t{owj%-5%_?p#wbhy(pJs&>KZT%a%WL*D5{6s1Eon&ion7^vso|9)TH??zL}eYLcy^V}{6ZS`>Q~Tk?U5A)8WH>oJ^DCRr3+PLGX+kB3oU+e|hf zGVpk2L)&@Yj%)LA|6vaMptDfYvbXd^XP>)X--cP?YjBSplf^+rN=-`fQ~84r_C~!6 zYtn&c`N(2dTxLWZ)&Ax=GGWqGi(7<>4x?}({``ei8n(H}Or*#^Gu_I8I$BMKv*GeHcKaP*cxFEm@q`6AB&4_+ z4!HUE*pR=4ztuG;)@?LbW^`7_TnuucJZvcnX+QTj_})EN!I6)_TN{d|KBj{DN8cyL z%xYoLE(Rt^z2gpW$K5BNC)_7FHW5+?h(`$-5L=6{kMuJwB7`AjG0ttqyqALX_Q-FG zi1G2<$*O11q*+)DqDvXjL!2Lnn>T$LuKpURC~xw-C*_t7X2#&ZY;SwVXlDWmJ}z2q zk;|c(U&d#V&r>*;Ty@UauM~|a#T*<6<2#@|@%DJn{+PP}W()q9D-1A~8l)-JnhT5_ z-z)rvBih!QWlv`!2-}$yxlYb=W0v5`eKx`SliS?qWPToX-h~sMslc6*Q^{YGRpJ0n z7eg~4lea8V3?6777J`a5GP`fwY~Pn+^1B}fLx-JW0yqAZA}}tq6WNcrsa8a#SdbnH zhp(5UTjY4!jl;iQ1Y+LOUQ-R7Y+}KgA9>gyif8A{?Jhrp+C4~6wAwx-zzA-ewv<&MGXkV7*Qnew}PSq;Bh92 z4ux!OjdVCL!+{lsy@RDjfVGCfwc?CSqQx3uk>xW^)Gqi*BB`qf4FmRPQR87?+Y?dN zSVw42Oh3o4urrZ5`jJN) zsjA1>a%A9uSqcz{De=#GJ&)AZq5@5kj)J%FhHmTt+Auf-L^&&P`weQr*I@5J+d!kyi(NSce z2}{I~93^wK59pbzcN=$X_jLO>8l8DEJ>IP|)-D<6u!`w~M=b~^Z+_fOcux|XU3*-2 z=6+ZB_79FglHWnNlQ@;|wP*E_@9iUowY?WsRjmHE1<-(*^2~Xv-^dh#;DE)=9Z^Sz zN#H*zFd1gR4G#s0dC32OeF_gIZ5LE;|3eU8Z6XBRRwe+GzaORs?}fnew?Ayd=|V1<40GR*_DJwuCSa+7tX@W#Nd*E8pEhpW1AE9 zK_?4wR=Mjjjcvd_^V^B&hqmLq*XyRlfVohDa6KWUL9eIWZ6SnTuK!%>hj*{&Vdr0 z+J_U1oK{c^{!AZsBT}0V@Ncnpu^k~yKEEp#D$X*cz<&W9IgII~crO9XuR93!cNNa> zVaJLD^k;Kw%;lDtDXehNSX*RaXVXKiXWbOwm)uSU>JG;Hn7x$Y1{?+L%vl76k45#B z`8<2$D8wHDWGL?{%>|-h57&7Jhh4dtJY~GcWPeJnmwYK+&{&<%PKYA!@1vDQ<;ee4 z9mj|_{LbtWgP6nxbz}cm;ziKAqNpF{=y@TP7R)&NRkRnvxk5;IQ%%`cEdxJV#rWON zz{k&lCYKpde_kpD?a<7SXrX(D=kQGCh~a`2I)?x>cXN{|QK@6*hQP@GCQhzizIpsz zf<}HgHe=kf@woy|{%9bM&~bEF%3>`U>V&k8U^BlD*`CgK&b@G%_S*Qf=6&rw(w~^J zKTICyUGDf+w4Ahq`hu5Fg7>7{#ld65ayD<&Co`i8cnlb>;xF(NK0gO42=f&4 z4M_EL`F^Ef0Bv{8x=s7{9Nw9@aFr$U#!WLv>~!r~yCsztnyG!_?*U0~b!W{rELHlp zW0&aH7`BRS{DUaeiElEVpxy@!9*n=YMTY&B%d*XM-ZhtRd=l_CwQj>(Vo#>N8!gVW zY8C^Jsy}4&JFrnC_sYIood#o{G|+`pQ7a?E#df|EhLI3s=oz?ZK+xdBs;7XhINBgBvyS=M{0Y&?9pxl+O zgII?9Dd{$pEt>abW0BC~GZa^}>pkXD`#yo|Z*|vg>A5PO=g`(kyKjY&#;_}2%t9NC zq@J!KmQ&DzP$7%FxwZT3%1zN1_(c+W1of@+TaM~h)RP}36|~OMT}-(0?_0Ap(H(nH zYe6rlMiuD_#>1TLqf&0pHRvJPgCyA7WoI|u!Xl~}x|`>=IfBj|0lEjMyM(>Sd*Z380ID!N{W^jVc5qld;AY>|JBm%Ykc$(*lw|k5j>`l;&xs}xLNBpuPv+Na?j*S za{3x>;*AzsQ4M?Aa zUsOUG1uyDCce2uzH((*rL~Bd&#;^nAW2))vnF{vTJC#5)^`IY%)r|XMPTr;BJ8rY{ zn4#H%(8kHo#u?MrNIag=J5T#*;V|MPQ?~Bg_Pmc#j& zAOs=wl5Z1y!Rt9!+NnAJXlNp4_0(2;g|!el^Uo|A6=K}YF?#nJ4CUbrTW^krHR6F) ztgn5ot)GfZ11v36_#_P_H#$x#8kPyXCB>o0P`;a@edB<0Njwu%9vp`!OLZd6Z1+kNEO8m+D~H zfxfPTq|}4`O2pLY`F2%=b!G)imlvQ+A}~U9#S{eF*pWBvPBw=)Pbdw<|#gsdBzGL+FP5h|mjF>L=Oq+C^D|x|GKxaNUw8)!FU0G+N`R=VT_HGD5k!)nMrG!2z@jbeZA-XV{VjXqCjQtT| z9yVL>5z@$3)P2HI?1A;nCD>4O)ebFXau35lzf=Hvyq74ew;Xamfr^`~j|ioA{~Lk@ z?$wG%2W!A$$o0YY$+jJjs0xPm52g#D6_HPVMe$e1!?D{K+L@!ex7gU&foU(Y1B1Nw zBmg&THs3LWe6VZSqj&-}%J&7gF{5n;9L#($Um=6<+>yH~3bvIu?g1)1;pE@fBxe+s zPT}EO=qv#Kq!B-Fe6wWHhvpvf>Ap?$XL9j3+L?$wjjy$4%U41(FQjTA1?9?a$EZ}` ztiF#><8y29A{F91SVf24sLj`;Hz9n<%WNePyt*B?$CoPYJsC&Jc{v-UrKJ_dkJBj5 z>fmZVffq9k1$0qy6?2_PwSN~4bo|<##8SuqaKihT6fkeys{YXpaZEXAMzPkqwX$&b zb>yH3jSAtgisFb8yXKphXSUw2JW^~V8X1FA49i{YDg<%lNN17qQ$s!tz4At7XyP}I zb+1F}1!E-v%)Y`}YP@WgK<+@iGi-GU#Be<%b6`y7Nb;c>3wue#ggM<{z}VUWNg*@l z$RAn95;>w#}H%qq4?~dK4G@D^?_ODI()a7Jjnn0 z3`w(hr4YgsIMJ$+&=9VKZf$dtZ56;OyIZ73(DPD@zXNuE@#*uyA@-Z+;g9@lHSjev z(<{@#9Avs=T%~^XBA3nWq1;x|IRC@e|B=IdU#2q&zbkdH&dvE^*mye}(R4d(>%kbY zXmw8Y=4wf1FtCgJ7&9jpS7hh#3-=|x(#3xDv8YblUbDR2U3qf0n(k=C?OJMgc*jZX z3VOsAsxvpMQoA9#GT*hmaO48 z+Wq|SMn^tXZ#ZHVCyePC8akRUUKI7f`RHJ-m9IijbYrv)Cpn7cFlMcx;1M+KWzkSQ zUs#L<(I?C`xLel6R zMfdgrW~hmqJ?lEyyWt3I5J|<3Kpo8KfOF0p6%ZGEfU7@#e1D@p+wxjrHxymtn{IqFO+P)9-GCS@tdHv(IeS*>g&Gz%Sl85h4bt|p!CC@67wu0c(<=cEN zow8vBGH<=_)5JOL;ig5(6Uh;D)nrgrHMFMYS#~ab{RxD^@7}wrFXi>KNPBLV5V}=h zmWTYgFQp2lu=eK|HElJM`!0Kw3bD3+Vs_4~aUNE5mN$eE5e>o+k!deOu=JBlNMIKa z4quQj%r#FLOzwEe&98t}1N*e)-fFBKV7QDwrB^yi#vdch4e||<_~6>4KSrD=Wc668H_XxB*e}=b>vz0fe2|ZUJshl+Z zyM5tKW0Un0;{XA^_mb23sCwgDGjfwvecrv@muOlM@l;fsL2PJ{ibAww$mTdTs)pX6 zQJ1kM&lpF1^``tB`c)LNB~kFd0@M>F=KPSAof(qN83vEhwO@AopyBg$-1KH2n-Uwx zvua`d9o%y;euOG5p7@sIV6NoKmcw9NOzqC1WecLq#5M5K%9|$b4-in3V66*e)8{Sb zoue>#N^jNZfgK^a1AXzfjL7!B%0q4C)4jLo6j5S}CaHglwjSc{*6&B!+Y?yvOY~j{ z5-;cjf>?-hzEByFurh0lw2bgsZ|RB58~28-0@0jJ&lrxkunjw?%}ve)x`O5dW)Md% zpfo>da1xGofZ_QuKGSxfTLI4!u=6e^fQvEh1|B~~Bt=n2v>xznw41l#enz(ggGURj z?e7eF>}SqA3gTo3<}`ifqIXD`K@4fL5URYqd%$~e^L4fSmC*a(&84UGHY~PRy-lcw zu4*+YH`qAZp1(8KpTc|JX^H1F%k4oORqx&iI_P~nkC4VTQaar={oi){nEnH&B2^kX zeRe;izrwIx*2t}1KCO0zzj2fORhPktmvEt`&C7vqIr3c$y(Dl^B+Yx@rP}~9RzIR zQ0lVn#YB(}3&wR*=`1{_V^3F}eDSAyoeP{f6c%e5mPZjMw8YMaMp6n|TbE3K`vlZZ z1zc-ziF)MkZv2CkMk&PcSc$g|twcBT{v$z4X`1EhR!h&&O`TiY9&;kGWZecU0>_>K z1}}(qk<)9RK9olekBkxt+;V!Ml~S5`wG5~ukd<>wbA?9DvYrrgpFiX9jXjL3o!RTU z&8GH#@uK*49=0s9bJ%5piTNGk5A0*9T_?3<>ghNURHH?>KDI z#asE@;rZu@^nP~H__)97z;%h+cGpAw-vCQMw7*-scRc_+4QcFhWWip<)|abXoqh^= z4-s8_8&a7VdsnC9tKPGX^Osc?<%?`V7efJ7$J?sj%PWWct1>*4IGM`2CV6?|*1IR} zFS?7Uj2pu}ftE{ip`+a$wv_z~Uu|91;3WRM>Xu7Wg?lSiLNk13`9$B+Euv0WxYFO; zuj*nw^$gNQ6_&*|DI7u{5To#9BFtS6Ptz0wdOitY+qW%%H;P9(IWbeLp(E~~70%Id z_N1a7`!*g9J(`DO<<1kya>ZO*e;28kBYT7j?mVSELz_5p@75Ke#DT#Zj_)n$&QjGB zT5x@r$yE6Fk`|Yt_ZtU7>uo+;U3+KP5$XBtFAweiCDP<=&mUZL;}z>7bzL^Pis)Z^ z|CLCqUs$>Ri}|+hUH55PeFSMWgP7E~>KhG&ZL&c~ijDBFrf91|3eBFR_$icGzp@auZvQ>ONkB$sv-^**qUc9UwyGU8Pl#MwMBo!GM`3P&P^0^(dxwPK9V4%TWTNM;8F))9%)%WKf_N{n$Yjmu|u` z6>W?*n!NCAD$Imjl`*H!cAWJt(j~Vc_bna^Nc9pSA-`@^7O;|>A!wAnlnO{HA2^&< z<{I}&T9eoMG?lg0vEJ!H7b9r61s*E?;Q5TD+dp3Zd_aOV~ zK=u{DS`?pwQOKN*+Rc=JV(d}(xZyIb)O+Hc=b*w%x#5e+yz))C!b^X6fw#kKDxA_= zf=-v;$`(!)Qz7Cs=$ia}Rq*^RC9UsgMU)M>MTSxL`h88wBBIEn<d|gEu98rI$TIQ+3muboz z3HZZShQ!kZVg!0?$mVXChR#;=9?#~C+fpc-Yh38P~9v_n<0*0V;~L$z?5M5jIdYy+)~;_NrdmK}bi8ybGRX7m+cDU3%U> zA=8!#y0{Ed?__@s^1})!ef2MDz-2QaoO&tbaH7RqnB*CAG~}zY>8bO?f1(8t_JpcT zIu3uJQ1UtWeqx1M4MnL;J^m8Q(G+EN!Yb&&FtQ5y#)O)qKYXBC&ROSuGhf%ox4)c+^UA4;UF*so<1sZ4p<$UUSWrRuG7B`4r+q>x zL4i>twjIhyWT)V9yZolsdxv_j8LI8L_Paaw*&CK;+cz(3;8|WzG4Ae>ZH=2hyE^)< zyL!gE{gxChxot_cRf^yRdVaVw*o1tkzkkTQVqdOMub1xAFvS@f%H>iYCqzFg0v?r{L{FwW= zIxh+nbNakGRR?pCSr4kg7NPCPRz%#G?Sr9L_1?uyh!O2}g+~55v-O^_R8+C#NW8?se?#-kN(W#)UW4TQmqRXH1!Jlk*LK&R|^Xk4)plq|`_Lr2#tcdgD@P z4bX@6gTOwMGs;V81N{kYV^og!JKxBwCy?L_$MaHO1NoOcoMZ}4qKC5_$?FGI8me>* zE`C!Ft&&c9PQxHT=on{P|Ju~%2PYPE?SANzCC9RKgPziJRC}3=~qvlJSD0wE9+Uw31FNN+*;li$iX>U$eHZd}YN zWgm&4{qp*tX(sENAVNte-VVgX=$Q!u-+uP#F*a~@vAxxD>}9b zP7Cj>@+j7D@F&-28OF1yru*vE4LXK18MT!~PazxFgZ)mifsy68OTY&F(VYF-r->Lg zf$_)!?ASIUe{7c>{Vg{MLrd363|@B_#4+Ak={@mwqDRBHwotEm2esDA!tb$W`i2HozkY@!PX=jlI`D(nBc zn?Hg6@imOa8Iqld;F-*YC-I%ULD^77GDbcA_kwZ04J=%f`hT&3^RM8aG8pwF$#8mJ z779-mszjS=7rJM3JF)%hoxqdWo*gSlFpQ_tZ_E@9qz?s_jbPlf= zChEs?yXkm1dz7gVbAg zGs3B~@UxSrgcG7-4=(etf0)3=g>hsA%K7(=zL7`2aJBLi#yt9sc6la`ex(+E2F1ME zL3Yth9{rS3-$Ry>o@*cABT8yT98S|I)KcsfWyu=al*jOD)lZ?p|OS^lw=6rk17D{hq_Bd<16Y=gkr>|bGBP_hJYGKKF z6diqKSU|?J!j>c_-95?fox^o?BR8+vz=$p>5-9j~Z`2#~I+JYig^Wgysp`LG3;uo} zBDqASuWhv1?MW}GUO1FCS)7%oqlO4;WiUaevycW(x(UVNM|4+WQ7nu7SmC7p&Q_dr zXMu@kVFyv-FoL}JiX=KjVo@=F;+h?AN`|?KHbxacn;z{UBUL;l9;nd zbM_354BmQp%e7A(>|MNw;EFrqXBQ)evt?hd=avb?cosDvbjJ|74nD(=(`*;L7)Z@Bh}n-}+7^X0YDE%(E z)m@BjxNRh1H1m=hyg(J=4J`<%3G8}xKj&n1wJnkY)|fS{-v$52qam9uyA2`(hxK%1 z4ZC-(r*v09He%h}H?qVx)7M+qGSjoPaUil(7`V1FsZoF`<~AoX3SZ2eN~_H}*ko{A zJ=F9|zjk`RLp;f2hQbMlhB^D+Z=yakdsdFMF1Fd1`5 zhLDhL;LN;ioH_|_-V9_xynm_cZb>aKFqWlOx2pcCy4!#6fA9a@xd*o-WeSBttyF8( z*NVUpzm>(R8(Kaq!*D`|LmLFbIX zgG(YQ5$6XiE(Ni|UKM`g1oF6*mwS zRxd+|WgsaDBq<>L?m)7)p%PbEV1SNfPYq;`1+r&;9vB6g$?&`+drSOEDU5IjD(Uq$ zHLwqAozfOaxW)#_S64^|71}lTz)e_=RIB9Z*1BY>@DiR%_9B+du1!6=S4-whO5=vV??V4-eX*ecp-*>`|6?!uMP=PH%ZOa$8Btd@o1=ieMMhs;D@M^-7^T$$mf zgkm|&=7c^jS@YDi$QX+v1ugRBj>}M_I#5zvlq1j0N`Z&x7N-Orp52!d`1<*dhI|yI z`AcHdhvIUu@v-bXknny$_;oK~Q}NObvg=e7e3}}u0iTj)i~)}l67T~Pepll&kq2@) zm;nwFPa__5bo9U?kUyTg+2XuFi{!VYk^Cv@{(=e*p6~sn5p!P2ov18 zT849qv0=mJ!?5s52Tp}TmSBW}b6|*lXbcw*2bqZrJQxOq5`5D@j$*T!Ul#kdi2gsY zxfd4})4My76(76s103)CS(P56Nw*s>`ArODQvPjBKT zguzN6Ep!{0ZZ1}HBISSeicyec#a6eX-Z#Ev2mX_Zda@Aaj!4Vs?1m6Lggc)Y&zmr%ViW`g~=85 zZ0M&HE~o4tJux^!%;SkVRVrun&+S*EC!Rf(Dz(PwFaBfzjk3u@0OV6cw;8q>JpiYCYK0h?IC$?wMyf?D0@1vZ7VMdM$yDQtdS)p3a1p0&# zuA?RqyFTv$!+J#`xwH!RGSW+Ia!X+_R0)i=`>4Rf(W7#^WUr~C19SKaFttFhUW9yg8ldl=v>v$$Rge|LhK^e+NQ@;Ll z;L-Wv`QgH`!v{8Yoct{JDdBi|$MgZ=n1MOQ?XAt$#sRG__D*HFwnn@o+Cr$HBI~Y5 zo+;#BlY5HBJpL(xhoc@lfb1h4w-*i!=1MrUv#mL@5zQvl)z%DU)?kyVWr_6%QRUSt z`<@P}o+BM`B0lOACMVz{QavaNYyyh{tpK86um@e~Q|*J9oTKJsCV6mo8KG&jWkPx6n%X2M3r2sse}-d65FPH~Ci zFPL)RYjgM#HC^&J=Vv$f3VSNtW?`P2tPOE(HF-qqt7)r5S*k#(FDTU=E96$aU3jxe zO!o8&JQ!wvv%s$&$k7-{T5W0t##4`K$q(f_)0-fhU|z!!aymjR%xip;T+phO7L+|P z$d?|Fm`N!WntWHDy|xG46&F2dW)Xh+mmjvsXQ)~xPHxs!!L&q8J)AylBAc<$g5YSP&P7zQZr@aPZmN1s)DR z%?bRn3*d06K6@9hk}mteE^_OsyvaRZA#6&I(h_nt!~xCbKpeC=EDqQ0Bd@Rk*e~yj zvj$r7%gN0!#Rf#trq=6$C?3{oTBq1P(5l5UTL1)UrGMU1Qn7^5Uj{Z~Be4(J48K2I zR>L`FqcaSr6>DEdZoF&}ij^>4&~S2$(EiGW6;~n)td0CA7 zK|^Bt%O65>QS9sd5PFF=1z>CY&p<$-;c(w$5 zybRfBmNsCX*?|Ud@%0fr8G(mmuv&pfqc}tc;$c$Ds~cOera5UNG3S~oW^(_kBu(Yu zShVcZDrj1+WRzBz)9DBp$S0Jr>Lg%R{tAL{m<_+~=GdsmrT_=YR7PJi$5!@~ymWSr zG!7|b*lQPHNkWsIaN5_v$QhLl4`LBVNeUA7?WS@qHT$3>Obw0j* zyN=hx2-P*^`q+I?#oBZSbuRD~{{+#Vihqpbxg$r~ZxgmuCOJ;%Dc|ST$Axw-ShLn1 zg3*gYDrze15Q=OR^~$$GBV?6! zHOg)slYmcic$-H1dJB4R_Q2SdjDORSS^Yb0^s$H3-C`x#T59p zq%)Yw+LT*n$kQDP1?8Qza8Y-0_$Jz9lPcAz?8VPxrjk?U&Cq0RL2#Vvb6&#gkUKq z@S*Wa3j9w=KalH%#?_Afl-Js9Gr!^C;DhvUc{qIU`q%m1vA`{_^MNU8BXkiLx*A`s zCnbS^NFH}AE(ehqYZno!q(H3))~s~*NR=zB9YknkXRKcQ3Sk#w@wlEA-_eg)4EKaR zg}F~o_Uv|>?Ze$~jVw%OI}dzi@!(Uprj5b&Kzptb^+!6l-&0Fg{G`!jT3XsWm#^gP zdv9sya`u@Wn|>QeI#l=HIlgzuO&<;WBU^KmADu~iS##DG&QfYBxNh6P(88AXXlYw# zaHzM#>8z#K?Tkg|)=%8MIiperm)_ofd!VV&J4=qsQCKf*cmlnBVDs{L7#qR*^2s!Q9LJF6jCK} z<#W}Wubxh6E1e%R>&Hn~3MG*%G}UlUSdosC%IgkkTU@SyWNJ_?ZT#Z(rs|mvDp0e1BWA4}4>}0Rh^#ny>-Lch9NC822yR?M7G+u(i*&006`II{n0Q8ufA!-&?lPu026S83p^Ym+}F}5 zyjVy{16|85tlGBL(;!9@Pnf3P>A&jS_t*Q{=cn533W}03YPl-8Vb5rB`#N7}LwPK= z?XHSXn$fMP^*MLny3x4g0>1Y>z{j@Kz(5-{3V*_P*l3unCWGFpH@Sh)up^^!YqUFJ z8o2G@393V){d@n?zhfj~N*43fQR%a~1W4Zm_5P2R9w+wz{zQm8@hqwpo+`lZE~uOy zy#8M(+fk{k47PoNLLP;zOKA!5xF)D6Ec> za$_d+FpyEV`(287L8&e-l8ZI`IAt00SHd}0iQKFd>#kKgSa*F$*LqQ?F?F%=S0@>KIWMdon_vH^}XdCy6?}~W^_*olCAL6sy3~}I5ua+3X z(u*S?y@hGAyt*$3#v5(w-|@S;pWySc5OdjAzzE_9OB}(7BXp;D}-Dk$3 z<8h839~(xa>G|cuN#Z1;L0ruhS<5%EiMt#{fn#35E();P>CtT85mX@p*JRlANVe~2 z-D81p7_8T*s6ATk-8PyxW;TsiB3tjQ`WwDWxNn8;vig(8e0tZGDOYYd(LUN{0ZvU# z^8WzW3&bVFbpz%){2{u6D>=JHXBN0=&8IOMC65p_#%St^LB4xYbPAD|r_PMEIU`kx zT`;O@bV)ESXXraZP?KC;!^6VV%%VPF%w^Tt-Yl#GTMHg!|Bb38)BB zM_xj7^c0h1!b~Pj5Q#IUC1Xl5rXbr8h+_*}@;6%IlEdJ}ms6AKCcyK+a=`QFDK$js zy=WjN763b)mq_xgVJv)!q{OOOxHnBCYpxDsJ}3RduqWg>-Y^DXPpEoT?DfE&(7)_I z^0#*!{%?o6`;L752zc%JJNMB3DGIVYF6Nnb+DR*+T5r-X!_hd}4IC6lsz_tIcgzBx@6!GO(Wa?`$1t zlVmwQdiTiCwjLMx+dIE?U&R>e3@;5yR$KO4DYW}o+fdS0yZ_l^8}8XNXaTX^(tn+q z9o%yQ#m}cu{0ymk5nFaZUcV*<)ikBnN*hd(IZnYn8Er%d7R07*alaG0#YtV{q;7HQ zlG+xnYSyLvGGi9f$2Haq_hc9+p!?E#PlmK5LpsluMYXrCF=H>RsWNEddW+>t16sIzMSoi22dF@q?!jkZjp_!>mHHbC(;H^CS~IVM~{s-q=UYAj~31**0hAx>q;^+`42 zFxw+ofnv z_7}WK{T5G{jHZBz*3%*&`Ogp<`543gMluz3C~~;B<%6J+Q!jVS-#eM#x*^Z1We{yV zRlKRUZM5Kymo{$MREj63k55Oq{-jMo)2JRI)LqTBm6EnZX?n{{DNgD)@IXrJPHV(( z0ZzU<;5LW5qp{9}Ka?7}X|QWoHLW$+xnaG!~+Jw$vpz29u=Oonz#QM;eu-)O18av z*^F0O&A7zh_)7?Xg~2sCk8{WT0*@~=Jq|cCZJZ8F1J0mp?eBPr2Uja^{S6PU=QIes zlT$r{f+>|a=Plm{m@;9XuX$5;%iVs|N`Bd{%HO6vqiZbxGY2V|LhDbUY>n@@!3@#V z?{;R_Cle!iSJvePVOluOK$Nlpqol04CO? z(HNu@K=W?E_Hwc`<_Y*(NJszGWvhSJhL~LL;9~0!Z(6nmv5^FTNISo$4$A(P=qDaQ z_A^PC!Wq#fWJFt$5p6|AoP;u>UCp56pIw&`!kkwp*tzyb%1F2Z4QGeA6Y$Dqv^5_L zE%4x2d&#a7csr+PZ%zTFT*fQKW!Sx|Qao#LjXJ0_29jBOc~8mvam=&-B86PPh8b@_ z?VgG~;;||j6|+3uf@yt~L!;(4av&74YQ@0)+ft;GZCk~Tu>yNEBx**G9v3p~i z)_28NHrBO%6GQBqKYr-=p?zxM;mSSSQEnveniuv#BoNuz*$G%ie134EzV8+DuYj{c zfOF6y;rYEOr7v;Pg$90DE%5cB_st9ZK5n*xSUEc}2*N(75FHNb zuDyGA4W+$KU2Ya0WL8nyU$ltw8DtU9VT;I6l0}pc1I^!$*OAZqho+Dr++WzQ-#<6E zUr)OyVHMW;cDT-YHtL>%^NxMb1Xmm4+PPFJ(3|h2dZ!4t5andh1lUAZQ8WNWJl8|Z ziDU=~59(sO2My)p8)Ff@#$I3!dZSZB)GhE)&L%@vz$STu4I5Ce>bY_5Uxz^^_cpWG zQWCtOx z1bhei6@=q!VRP6-HK=h_HMK_17x0fr&7qaW6#ftU1~~sANkr`p=Zw z5_H>rCI$UBGL>4Z_{)EX*pM=%UQKV+npHHAdkXwez30+usb4|7LrSTEeCc}sPt5&* zFB`~q%$Lgh-zlvFMCgU3NXcHZ2fjwLWGqO=0%Xij#(ZSVOU6B9LPjQOvVVXa=qCp< zSgQ|6H0(!@s5B%7)Rxa<`}NN>dxar}i51AAbaCK6klTyaH&BIn!w2gqt|we~2X| z?{L*%^c&%&WLkWk61vdFG)be2n88J2>FzW|0a2CV%K(o32mGeKs%s<2_n!y%a(;UZ6HfJfG%8s=!y=~E?O^E`iLb} zP*d7q?hc+aNRYi@vRgxH0x&%S`K-~j=abd2#^kM<>M0Q1b1PTxw*?gk>kjOm+l z=uipxnr;ak+5{UqqO`>QzOYRr`^k@G8e7QYjhaXm=~#M4Nm}9oZ`i7qU3o>OHu>G& zsF_kN{V}b#Xc-z}Z0=q90)+Qvv=%*ik^HLOqLa~bwPNWEIVFc3ay3@N(mb?lOQ+}) zD61{@mU071bU~ZsCX;T`fzpW%GN$j=Q*jmPg3EOMF4EZx$?7EiRj1lgRgcT2h;cEE z2fJ~lusMNd1w!jJA0#H{ASr@0rbLegHj|EZ*5P$6cpI8kL3Mmo-d=D8OqBeD%1AH$ zz0w%*`9f9|Lz48na#JYaiJ0U|=Zz*tYt@r|GP9cAZgc1vT4~U|m!)2_Xc#yrLQAoY zAgCYH7l{aujLuz|gT1lc zXQNJd$ax+d!^CkKX8Z;bMtv0ZW4ThVeD5`z8%jL+nWaw{EwGC;CDWL+3i#R5G4d-) z5D%0gsHi6x(zC47NbL_sAz-sy&zb^yhuvj-?+Jy`O;E%r^ZPR`m+@@4(%}4r`!t_eRQEj2qcOxhfQbIt=zSAf6Zx~TxYi8(NtN8b zpes7ppC0e_C&u}a%{qTaEIOF+DRpN3z@BxZbA7G{rxOD)b0M8BMyP+$YBjoiG|8rm zZP^VOHtcTm=*%`#*kiH!9NzAU+$XhcfQ`o^aX>2`(CUkF3lSrFi0z10YJdI(aw|*; zlAkJ>2#a5>Pd^u0bRN_n={Un2l@d^WeIkTEA__bzlzxuK$1xpecyOeYZt7bBPqWf@ z!Sb#Uv!FJdY@)sh@xeTWY{uaZ8Cg9eH@d7=mys!K>nPpa>-wzDUx-A@xkM!yF8Gb~ zJLScVDK#5*3~F_-G?Df&5WO7ySb8ZF&28L28XfHlB)h+z&GFykU`kDxG**aTEeYL9onv8?6%SwLQsu4WZBThQ&1f6rWZkp5_%t zmo?Bry~OGqZCV(YW459vrW}+^5xRNc!B3^e_YGN7iI`m@r=fXLs1wCtr8Yj68jfic z3J_s;>dZQ|BlyH;rjCq9MdWrBM;Iq+0>%T2(Jbz$t_ zvnprT3*;;aWisR=C8NoI(5a#m&#{YzFKC-&y$_?9OWcG9v5Dt+c2QgS01C!@!ig#i8aV`y9U?a(&q{c?xGksjdR6vh00MbAl8*%zRi_MICZkmQI?J>)T_+LQt(% z>O-map1|qT@v#G=We^BGB-=0=?u=L{8R2rq*R`=4gVy5mIQ3c;^VsR~;$&N*Jl}09 zkJ}TSK4{Y;)c48faeC@sVgDqB*5rUmImx?W5{$>?I(dr(|+kp0dMe@0eDv z)7&N3yRA0B>`4uRxw=oH?r? zCZI8M6PwAKN{OiKRNvvmrc))`MjK`0P8zZbx*NDPT5X_ovU@SGmE3x=6d(f-FjuMJ z0(TO{)SR%0WdsE0!fSIvp9uN->dzsvCx-fb6Cs9|+WTaucyIyRrc*p&GlENKwcJS_ zUF8jQmG06>9$p8CRDjgcO&4;YKte>21D8q}Uz|(z)v99_MZw_NC*VK-k4PLql6TJUQD1jpp0^J?^Ai z*Wc+$xpmpD+(Ti<_-Lv>Y>@rT$y!p5ELU*pv}(K2?4ab7E!G=KjC6Y0SXUr1?9=63 z;Q>3_m&$P+ZaL%ln|zPS8@J{=jo!%8exHw$x#Mg&U~mLb|2FFT)W^Y$B--UO2{YuN zhtL3VHV__D!mdA)S_~buA7PG2h1q@0?PS0aDg7C~evDL&-M1_u52o_*xfER?-D-@mEAQvX_))?TiX%PgIi-Z#%bZHdUxOdqgRyMUfj%%4#u zb_q)_Oa2r*9048?f{VD$IgJbGxqjK`@B<5^T^D#~3H=E?j_YM87nW7`-qTe1`R|Ou zWs~mM<}xt4P(B+BW%I$tJyQ<3Qch9e$Hg|EFWKhz5!CxXA>Sdj;2uN3nvRPxKKawL zNi6}V5j%+dT^C~B1?yAHozQ-w`@D>AG?u;uJ!3vr@-8ser+5a*wRpRYAu3VOR`* z9!CpJy;%*;CxFw;gg-BZ2iGpw>!!YxoVtUXTBwHNwPTZGhpN%f7^3U4ZR-+N_&T|T zerIH1IuoxQtc)DqlunKxtR^a5K2K*Q-B#}O&cnOq$#%px9O$q(%4r?9^1gM^XMeX_A)BzOhq&mNy z$--C%LJnjRb#qUepaDOLst*w6qKob4)Y>vaW(cMdr->8wX? zU`;_M4iAEQ4TyVTcwms8nN)(nPK*#x-}w7=RHzH&AAsPFQEI^SWdPn3+iY1JZLzRL zA7m9E8u_8gp;s_+M&n3$m%g@K-zxruT`5z_^|05^dg{BR0G^_Vv3$Qt^pTs-2hxGG z)_IBCQt}eowv%tP|Dl~~?|#hL$3z#^C%EXDeK5Um$I4L1$t3TlhHI_Gaqyvs*f0eD;XJSZjhop4wh`kD^9-ZlO{75hx zGB7MF*J~AOImxNmGM~k%*ZVVZUqetQlPhR&hnJHIP7yHKTn3#-*g4w;+Upeg1X3mH zMDvKt?t{I~saeK8>wDD5JgS}zep({(sEa~KgqQyOz1IXCUu&OL`yS01)fqMOXihyV z34Xe$;tP&8r8sSZdbd{lS*0OCWglKcPf@ha=42-}u4foV-a{32-}}HBbDyK!SK0| znU&(i3plMQvsheO8oxl2GR~sWSyU8$62tGtq@0=50GuUf6$PafMlX;uQuHr!UM?ju z;cd36)#wuvDTRagsK8#vzYE@@8M#qdC*yEd>LL+M2)s;&acWfc8mT0e(xjm!l9y-_ zbCHw=H~Z(z2TN}zZtJ?Vg_*EzS!oY5xSyHOhimh#+~0NWOZ%?>{wsds#{fx6aN>)& zRP8jGomv^T55;!iG7UV_(D;)CF5yiYy@i)xH(}TfsDv|WwMHd{AIGo*sFW9*iD8v1 znrVkzUii#%qbWINsnO(e^a+swq*P&?DW1X6>j!9Q6KKN-a!^DUW#|H8K-@rD%fbfm zLG+9~kKVtaV_5efB zbMsOutdMIxGTT_EC z&%CVIv28sbEZ19El<0Xwf+}q$1JGNDhq1dbinkgJHWno@R>2Tb1$`RRu^KsnN#so7 zFou3iLW3d2s5wNCTt7kon^b~v-B+A(K!1MDrU4(MERX(MhLJh>Pa9`h*JI=XAgEph z|5t5fmQgy5{HJpu1?hS$2hspG{37^Yl0Zu{3~JZX3Vv?83b)AWd3w z>DP=>K)72uo@U>ESJ(zr%4!r6oz_6JEXSbTCbJTptMo>-MN?QH6&i>k5&XQcikd;> zh`+S2_x=S+hQs*Fzq}}_RtqvbBg9iS{qo|n#a~ox(f-mR<-#fAVsS}j;WR9paG+h} ziPdEv5z5}artIz4DZ8s8Sz8`V)&>h_Nl#5MP*V%aehI-)dhs3f4RQ;Bx&g!^cwmwq za}Ni@uDuDvlAa?y8Q~ln7T$biC4j@dOG`>uLv-|;K#SFCEh-6*O0_PF*`<{#Wk$c< z9x%vchJfAfH_Fh%rBw;|3v!j5qyW2oKj{pb8O9uRIzvW=F^0tT>F?l!poTGIooK&> zZ?8IQGO>XGnMZ$ohGpY!Xd6JEaP0gbKQo9(K|k%Ltu z$9g0#pVp?5QYhdADqYi9(&WfYX6pts0b0UH2}+afpN#E!YY|ID&*ZWq<+t!hc2WvnW>wbg618Nhhc8H{SXNtN1l{|EJJ8xcb;EnY%T z5nF{`t`#|^4%?165G|HG$8hDK&u0+OPTWf+ZR{Mpm@&hBN=@+kDnmx>dL92s3E*Zr zGe@C3rFL7)Won5`rgK{@UcF4F_gbuOoeYh`9bItng)PcCnj{%k{(-~lGcb(7XSMo` zG;Q>QIsi&QwZGrIcnQad?MN-sElSFNLXy}HAT~+#$LIKF@CjP-m@1=2f?hA<{7p>v z6eug!!>ns8z6Jn0U16WYNx7vyFD}pVU8m2f zYxX%7TE8bl0vTvl>uf;T0l)N|v~HkUXNvVDVgn6cDQE)%!%42XV=y{;_h_i_zD(z_ zTD>~ZXkM$;qm$vD_WgYk>a4(rNSX9ijfIoAYCHU)&bZZJHAn!EVmP^swV70vn-72R zhqdj9BISi&q6d-xK+K3$z)g(af^e_G_$~8H2K)xd!E~`W9b0ncMFvhH=VW1%EI)#UqQCiT^@=2};!?E~G`YMH{eN5E~-Ljv^}1KDV5w00K0#@F~^R7ptlS zTb&?@&Vw5n;f?{^GJ$(*9!g&4H#!q(s0+hC^t)mBtG+&jzErxFYm0hTp44Wrj`v=N@5obX81_w{z=zjsYWdYCPaq?NX zp)v(^;$C5EWgvY)`RKichcFNM7%1N;v>^wVI>HACzYOV>PR7exALfy$Y_jB z9vlnvCBaVpa4FydsDCTEfQ7LX0+6`4$C`w>L?Cct6?W7D!CcD0T~U{2N=2d&TtLDq zzOY3F{-IAw6*3Zi*JrbPy*7$BfqrPkT0prMF$sPEXt)m|C_prq2n;(0p5zsGxRx;A zKrP7f!%h=S9<`jl-t!1dt^OdbR;kqZcVw)L#1dYY%j;XRZ5`h_N}@`uQDsug@x(x_#hx6fMuGaN(_1)GbB*0 z$myT_{LTIQ-}twm?j$G*r)U9T-wbN_J5WO>(k$X9mFRJ)Dy~I>$Fs1eD!9=oboN3_ zv{>U{Rk6zO!ZHm=N*$yNMaI#fwiI!(&fI|zl%pfai%ydoRY8N%6e z3@EJtQLzN^@uv!3Id!`5)i2RJO+uY(%5(0Dc+eL-K5t-u%{xlA?%N_2Lt>n3p4 zst02B<*A;vJu&Z z+=ASS#N*3GJn>p}RYI717p*74fj~7v zNQ@Fl#7m2}!#GHQ=4_dr9sZ<;D<40(Yv}e(LGQ*p#>@H!M=LZAgIvPdjXJwVrgm1^ zLd_97O{*Bt*K&tR6U}T&mXFUIY)FZ`-z1{nXfKRbY5z&~I+-=?l1Ng$$_X ztio8E+quQ>j@smy)L=B)SX!!dHH6FR0(xDbv3Cl`%(bbuV4yXVaa-d7gE`c2#UI<8 zcJbDLJ~TNs8Fr-88T>@C|AFHO%I*qV_8Q54Y5V9q?1Ak&@~GlW`%GmXjh{}< zh^wVuh5aq4cW;RcZ8%~1EU!;i^F>O~ttyEfeJ2OXYKMg_KR?-3`fm1aiDGK%&0YiKYu(A(`T%cg3K*<4S#yJ;vM?CtIebew4O&D6L603b1` zQf}LDe>zo($_xgzk(0?7nXM+`hOD_o)4GG}cxq%%dzG&+Cc%K3_Q30sxl z-_zCOH&<0wVm~bQxg<|5g5JIpc?5YB`Lnn}e){;)L1N>~#;zmm!8{f@KY5a*8;Pz5 zE?_}q1|Z4c{E3lCFeKm4$l1x66D)gTW|HVKUqIhRLP!jiW(=Br;^rxfBry0jhbh^YlqhbUqMQUIA)I7HI~?>a^Nu|NAjp_J~y(lDUZre=Qcvn5D{o(`B^4z$%~=LDPou~k3|$$ zQbVf1Mf(7uQx&Ob^}0Lgyi=Mlsaf_eM^_Qe((EUkutrHu!{pI)#q{jq`kK+Sd-S>Y z9>4i}kL~YpY0Wx?#$eD;jN0WiR!%(l&wu&Ex6$aU`~98Msi7}q%s#VP3TKi~f{;Z_8ylSEZCk1{o&M2J>>Rmmz;k6T(0j16?{n>`8xQUdyBk9$^jGTg z=F0fGkfyG>db~aO{$qr*eRinRRkeL`*c-q5OW&RS5{jN0tsc7JqaSbWnr?E*B)p1O zN;S#h!>yASzVg7Xy4fEWUj5c>Z(R6nW3-l{NJ6exsZG2LhHvU=?pUV_qwd5s=fug!;vgE*{d^-s-tbzWaB#24>h-~iqG>h!P?;LGx(+vBdR;Yj=A!v?lie|)fBW4Hb%FQW5;0F7~Pp1xc_0p#t`v_ zqZf{SIn?XN)O&yMsgZ$)esDMkk0*a{+u);f znT9ZnI0ZD{azf4nRAg64j50yn zVQ75#VFqF)-Ut}bw=oh!q0I*3$ti%s?g8!Fh#V3TkPe$USM32GB$>zVTwolIq&bf! zGqPGjO zY+Gz<=7zR!%f!9n`U?g8CeS`3$OB^gRAU_(Mc*cWU)JB(=g;CwYaW|9k0{kCb;?ki zM-3UeYg2f!tc>g$Gvra@X>w8w+!48yjJL0S+j?-YRxSaU6R;2_HZa?o z-PTf8vEiofo8_#GAf+rbm!0YGnc{u%`l-$;nvzKfjF4*UH|+D#_iczA z>%VVTgU)Ve6k3~3ZDc8@*HPBExjHeFE|YL3Ef7YOD?OYDbR_JqfQ#hJI#$OkH6E8f zyzy{bYG$BTj*(RZ`@pC#Tcn99!i$t6;i4Bq*vaKFYQaYkNGOky=XF8OIT|+m@~HkS zH%?Cw<47^+Jq+Fo$PpTI;HWw;CgfnsbGc$tLoup-F>ov8axJ-7#+RtE6nl?{)(jWw zB&yV^m!)uVol`Bjl4n#33U0T8{*BZ)Lv~lCP5CmbE9}AwA@sHOGSBNkVJ1nSFux86 zOs}z9EF9h>gG#!Tm3?r(3-^3b5aX60_-?`hMqo~)up0DdXZ=tO1$Igq>preDZajF7sOL{#!%_FOvx$kGZ0zNkRM z*==XhRTAIbdh2sLlhgfi4Nbxj9-i!u&h(z%P+8ZFo9z*wTWvAet(Z-wWJry=(BNF> zo_%tr`s|*kW@}lkLF03B0Ff9hdPmDlM|!-`hLeQJgK-XrRBcha{e@2wcw+Ky$oG#e zUc!GtI*=-)4cRGh&p;}VwJgX@CV6!pYdw$1!;7`Gq#L~M8TD{Bk7`R2V7XCB9^SHA zFrYwUHB7ybQxB6c{jx|{a_U}c5~6xdKy=BPKv|^#W50;+`||#QTgKB~ULEPZ>FIsG z?xqMYMJc(A_9oX?Z@Fu85I1Gl4@GyM9Q8eKs2$0Ay4%wxXJ%`rX`<1Feqqz+ZtU=P z=I;ICmJQE-{=S_JGNp>Q0+pMSDmi7(@vn@rHUpd7ao+=8N;O;#K&B?*k zj(tH_>(18nMB4t*6Sv)kc5k|4Boyesu{SlnxvR|H+BcSHJ~C1r>EGL!*t&6@-PO5i zD>k`ta%z*WIhC|kAHVWMxGU4>bfmH!6_dMngAp(W_(C2?o+y$y7%D`Uo7|Z!JED?!i2Po+WYcXM~fC*a#OsE$zq5hhfP+!7?`hN%$;yRqV>#95+r!;S<9Z#+E}Pm#`Q>J!>Ijda|$0-816wJEgouZF1_|E~CFI zm#x{cy9gIiWG6t2CqWwwmC#}()|pYP-{4)J@vdL*&EQHP({}^3SkKqp@7wb{(5LJ@&5=GPc|NUc2Cp(p*mJd;Yx)pzG1c{ zyS=3>xZ$RrqoBPc6r+^w7ocLSKVCQ4T}8w7xHu_M)@?l4JaYFafQlpa&9i->JBJ?J zS);YtSf$3Qb(`&8ds*YA>csFeP%O&~*96uj>}3EbvSuBxSIL!bw|N;TN@D$cATMwL z3HYlbQrxnP6!k@0RY ziX4YshVkbI|Qk%1G$)_L+6fbNyjo|1FzS zLtX(irl6LlQ5CGNj_B;uv&l2No|vsu84U^rZ{k&EUTU-%?b%(Oja$=p`D)NeV)4m) z0WItSa^af*GeYl@?-!%+FTR-3vslkWCmKAQMyJzga~h4MQFj_m=dtFDMsBglZ;qq8 z<7ihLt&5|lUL?(klYcma710 zaQxL^aD4nNA(nxFMTK8gaIrWL$Nu1Oj(||1D!F52p(E z9(j77r#n-j1WFq!VPuRq(NhigAj3^fJ)0|M9vt?5U7rA;w6hg}()5;eW=o?LeQML= zH?0Pxtcp>vYNY^6d1d$UXNs`&o{74xSvQ2Gw|{*m6z$&u8f_aGlcxZsT34e|EkLC^ z=MgQbf~eGRO;l=738>WYcTwrm+x34^;Rs%@-m+L@MzuPMS zQXClo)bfIWM{AIFv=~Vt0mHlSXmtqo%U$!`;od<9&3-agyq`?Nk=$L~P9H{LN4E+4qDr-&cy-`5(djM}50Nnd&0nN9FmbTSszInj|**D+?8CiJ--~mbb zN=Uzgc`w6y$h*shEb^Qq12F)|v>Mr~R(U^N4enPh+aRujyiaSQ{YRex=sp|KNC7^S z(&g#?%E|kNDzNy&6S;?meO0@^c%c8*v5b#DRhFGdH;vU>j6lWpb~dNbmp6V@#P=GF zQv(2`lvA;tw|!~M9D?Y{m}>SgIvbSgJHgm|V=cn3 z5fFZjfbf-P*l7Uam8Z#>s}a5uApB{F@Rfj{&U|=;|3>n_v$K1j%EjyEo}Gioulw7l z>RNU*m-*VK>fsSH-2Tdg-A#A?=>FSZd7wLU=gSWt`rL%SZucW&;25ai{Rj+jzW5&c z2H``TNZpdvS3U0(tiDb2mL23SX!ZR*WP7j|&&Z+Cw`n0K+<`pr``5Agmev=dZ_pZt zL2uVe|2Lr%x_hd_PjM_{Ppv>!|QFH220&Q#(a-Z}R%)-dIMK6LUagZyX@@oke4B^4iAU zn!j)C)qzS%71AqrE42!Wq*aQawhUEk^r7Z(^~Q8i21B}Fgp^OLpNwz5bs%7BIyCxa z^bHl?#+y_!3SdU9&8S!Wq-A!b+gVm$VKA4OB)mnV(C`Y*X4O`7ZBNFwAG~MqNgs@q z0_e>n;PyL8xV`o&+@8tkO2GYH0NQ^IG556YU6BI+K4Q-pi;(?Y2-klt!1ZZT_il+i zS7UYb{~N1&>T-`w)@kwo9YHIkx!7R3OkJLhR1dX= zV0c~Fpj-`T(Dg_91Gac~s9_@OM$6aTFc9MOCK!}d<1hjkW{PC}<*i{Or8H_(MkQ&A zwgwzE0fVW`L@JGHR?8_^n?>Vlo@%Vx)EuI5vOGHsMzweG1N;Qx1^_i$wCYBkY#v=) z@R1}E$)nF?Se?%ty-b9h97j8M(7S}CS`k$e;`LQvJ;MQ{9=)7fk6_ zXx)YhaQ%de{hUN&*TE=ug$ke^l2ETj@1ZpIpsg%yQ+y5#C#vu`R`?XU8?ASiy;X90 zeT(F*29?F2S7QB)k`{wC9g%QGtnbQyK+Hb5_$S-|kWdzxE3M>udI_`FiI}}Mk8&A# zMdNP(QB-UL}O8Ty;8#Kct>q-Xkr8>af2QGhQ%xu3<+vk;SVT|qQTxdKok2G|A_Ab+^7o3#hhs8 z1>#Usvh!*(YDAv+Z8)qZ?e>iOvU)o6PLbsoH?m!0>eYu6zs(hstKFA#>S;3b&XN=; z=K0XZUhgVky{l^517jWgH`KW}KG-(%z*bvRU67Mv6fLElmD%$3v+XWSThrX>8@g@4 z_w3|OYpOn`wbk^;V(Y66=*I3lx7Ik*BXf6kwtxI1a~q-(hUJU~xR8pL({+=_TI4#O zjtm^?8`~-4b;@lg`pe4d*F!prEWU>;S0n1CBBD+~M9l+^J8#&|?79Y`hW7xXhBvQ; zsAH=UwGxIgE*vJ5I+xMu<4N>OS1$9sio+g+ZfW>0R2HLN{=svM;B3e-_>9->hI2X4 z!rKM624Bk=r^)u$mil(hlGN+ldPRx95+WA+cP6pxQ&UqXr=s@E)+PW4jiIso zwvOC67&OE-)MvmT_|e4ns?Mlh6YZ^^8gv+IMzYyWl>oMFPBjn5wJ6iHyVLJ!ooER6 zt;^Vr$xK&8?Y7RKCp%Uf?9A3%EDdeF=pWNPT2IVwtE#9lhDHmVx3)5Bvev|67DugL zZ>bRC+zf!0>;^3vL)ygX%%|-@D^)Kb9EYUgs89v8T2tL$!`a`u-IDOM>9S-k*H~fx z)q%9(y1(Y!+27`bSIH2C zJ>MEj>mU+!iSiR&W6-tw`Bcen1_nDX+0 z(TDf!`ou`!Ia8uPk?xDA%<=x3bbpw~R7d|Wao+(aM{(xc)jd6-yJxz4dUDQjC(mq} z4YS%^X@hdkIe-{T2nmb`5(0s3vTd+2e(z`Q*+-NmE5VY%jE%vT4~ziYcYfFp8{79Y zcLsxXhtcR&Rrl=9t^#n+?>%8pPghsXR#$yh-@oen>ifTZ=N!0%-u>0Z*SvJ+oRM2z zzIo?;8`3@1yO+Rgs=InO9LcxAfzkreVVaoJ*#9*?<`G50#!7JQTX{6NDK37f;4S1E zP{T?Oxd%vsa;aCukNCF0=GoZ;NJ5h2}7z zrQ!PCX!97=&uZY)KBJx2Xn4EPXcsi(U$55z-fI;tJWGF@Qc)=UhUVj)P}!Y1Lu`U? zRfK(^re4t@f9mxRPo*KAQd6($kUV$7`Kx-iJr^`~>Ar(CfBKXv`RO!&8fyOJ|AFS0 z_vxwtkg70k8rj{Qg%ptVX4RAFtB9 z%Vu)hEF8I>6=hX;`eGkBdir&=2Ml$7s#)ikq0Ubf6Gf!++hv{KiFJOu=j}j%PH(h* z_(?iH-6`w*K+oG%T!glL_^EaNxoo;v=_~tlsq*LUUDvf{dV)nKSX5By2f9}Db*`Eg z@=V)2Z~}gJu^LP zSE(qA4~#*z4*`MbH0tXAMv}?7mm+ysr;J^s^>Hk^!%3+1sbgq24r+a(!V_Hk@6u^H z@*&jt?=+OcS=wGoxBm`mds>Qoh&BB?6WZWY?4M1J&@evMQ>*V-J96p#c0Vsf`!Bk4 zU3jo9!l`Khg+rt9m1eZe-ZsNWC2m^0;_8{c@0d~}t%Exg&enOQw%IugScqt&g1swt z+%VjK^9`3RDrh;LW%Lp!Ie0azXFJwkH)Ky_7VVmw9xl3~_VqW-4~4sCAuYcej*`B~ z*4&C>&FxXFxuv6yb82~qtmPdS)bdT%+&=g+bbTkShiX0$71ZE6r{9A;s}c?5*YzSZ zuIL|OIgO&|8OdpsRXs(_gZ(!P_Fs_55q%9!p8#hFBN2kV$GG%fml6J_%|A&}a7ykX zsfwu0ceP~_AvG0!-MYE=4eAp#eIvIu`g+y6nd*I`N=?!9>T@c3^@VM^vwryOvRl{Y zy*;aXTIZIW$t5?fTz=jB=+@8mtnT%`xP0}h73TD4>*B>eXZzxEX=cv0b^9e-f%(p_ z`=@Q}&(53C?X-3D&1@-e7)nG2R+bjun00jx%m#nxoi(fCDP)q?$ojfD*pbgVteJc{ zFgkCpqN{ood0#B={y;G|9x)KzNZJk2X}=2uH0|%SY_`3lX%_b1oa|5}JuvNe*o52u zPPJvT#`aD_fq#1At!k?AwnvB$UUJv^_;5KQ=twmSm)&inxp|ij1%av9JCazlca}f3 z>h^Ubmo97z7#?)O_G+J^!DbaVXyKWGH*!04k z(}(xraj<=VZUfZZ;jY=S)NsKSZ20uiegq4N5m|Q3WCn_fm&_a?7{?f3j_?aY@N$@5 ztmyBWd>rX8kDeZ@@(YmX2XmwfGn{XK$9%S1U+3TCHzD| z9;bd%tmv#F%dxKc^dfZJ_5=#Lhi!EnTfvF=%|=5vw8@iJHhC7{ zO`h11DlFN&$#X8_tn?|I`jBF8&b*>%CFz}seQmqp^7jNrXecP(E zWyb!`ogg#FKs#b|Nji<-v6wtos~#{bzjEdJWO7z#AQ1IyMYq{t6%9f-; zI}h%7LuXWYzo^}?m71)y#j|#MmN!{xS!|{K3);0=V1c)aSbkb7Eh}4T|AM!D79cAv zJ1)3?@139B9XlS`(Rb;R4pB=}dIMXWyKQ>Ip>KD?O1pjBp>N$tL9gumNTns)`&N8u z!l4gropH1>XK<2W!szW38o(WQI-fW6(-idX~qS7{6w!WY(rjt;M;8R$z&=}lqQ>CNf_%0X;S&?sD*r)C<$a{P7r?I49o9_lVH~>v zo8dpG1}g7kCBFcfadbzjnsasP|3e=w@`(ip?z?qM7b}>JZim?^sIiaM11kj=^3l3w zKK9Z2_S-x6eoyw%>RQ(yn()zrEg=y}z~Yv$Pa&HPH^^pFD2RU;*-KNPiAEN2GMf-z zhY6W8O*ZuHNiu~N_%gkkPcgUp6z)%-LfQeZe?X_ELnyv0nX2d*AMB6{K_Pn9iVl{@ zFmo7^zweu)C!3%^;_%ma*#m`#ke&IU*c ze-cQ6ouz8RC@Sj3%;&zh@2UqjC$rV>U3WFSe$U`ccFoEz+S+aL^lchyU(_A5>ByV! z{_Ej&OCCCP-(9Eh^}xD&uUOP(pL^?5)i3|}y3SDl>dS5<8vbSwtE2&kdm$9^fRG!6 zTp;KKAqNQA0Sb0+1##RvMil=)i`qhuIwT7S)c8bPVYG`YO&(WxQsPRJ#}$Hs_!vY& z29FIDva#rs6&2evykc9lsMwyH(w{>=fvG`su*7{L5Ybjh@6q7=xG)AZ4S0ffLlnA> z6Ukb#fGO6KKTr(F6TA_t8e1j3$E)xs)aW-Rkjl9>OaAVza@5c7W@J}60Rc3X3aB)! z`fqn2S2@K~4x_Y}ai8eptsvsEjpSRw}@PMuP?0^esNj-$16u;hfwf9Z!&Z z307`G7V&bhh}HU2!#re~SCC%A6ptA={_lgnDK2?`TXJ!S(N&sT>|QsV7D=z6oC7sG} zfG{$3920CH2clzWb}=YoZ%{^(0d=K6El1X3q+hCh^No64TP;AVgV5|h?oF`5l~1%Gzh6c(h2ZfYLw^Fl*IazttHQENEt(erF!gK1Q@|4=9%$seh^Ng%wTjLzDslkz~7y8-pXxMyZdMXUxoKd2G`I8O1eodx(YY4ayx{!jMLY~??T ze%TA0 z$s(L%8PN}Bih_(0{-8{P7;cCP%@@4>2bTCnJC_ku%&-O$QH$Ssr!|c$?x$9k>Y8P zpg|mSf+Zp#!hiuZS@}?t3_z8rZ)ijJRXNa+8_r#vqmnrQ)goN85(a~hKn8p~k;yKG z$IwV9Bi#p!mJyyaqT-jAL2G$>d2^Wxl|gxoOjZn;FbG%P_xUufA0*JCNUJQ0lwD8k zGz*6XY(m9vHs3rp;OMCEKDbmu9&d2fl)%d+?f7!(f?J{&a(QcQc)TU&^|hFE$m5NT zbbjD?S~;;T zZ|*85wxwfL{Am2dHhm%Qz00*mCkhCxx!Yi5ki}8+1jRzsYO?E9V;UU|(Tz?+?wYG1 z8FV~W&(Pa}iUcSo46NbknO1TOY|~bG=lpTlq8}Vj`BQ$PFh)+RFgokcW4pQH3)EHe z;hM%yE^b?W%=+^xd`uNzsKN~LdI;B7J;g8YyoYHl!**QHkv(1NgSQ$?3%lF4;&bQ@;vO_3Nol3)K z1{W;t>JIhh{BTK0QZ(069*Xp=>UPhb6&ozN%1L$dmx-QfJ^9bapz``errtdD`}Tvq&VfpY8mM!T>-n zOfA?sJ=j-@a4Hpr7&SEZcyA`!m$sRbGYXNS!^le(uoL27l|f(sXDMx)e$ikcS6Sa5 z)EY#FflFkl-!;6^zz14mzO2`vHCn(dt4V9H8r;5PB=D9OAsxR0w!yt{JXaNW z(_PP=r#%|egzA&+>*T$mk?PRGZ51cyDbBfc)S5~7I4zp1(I>6ajk7S=JpW z1tptlaCxiM5-vc=$ip$P7D|R!=HWpxiQkuf=fDRL$CxSOn~u-3S5MtduIMO-@y#lJ zbRoYv1+kH9DN3uV|C&Of|6D$v0_pl~dX7>vYVhw6wb7)?U^W`r(_h!>)Ns<*Ysp<+ z7o7IBwBCw+=i=l&uONd7qpf(OjI`A%;qd8QL|9-vU4uG)eAf8G<*u&_CJplaStbsnfvZb5b zO*XAtEX}6%NtSi;o^Rbg?UK>hvZdj+h$%3zWnf@KuV?1)qE!#~^j3O!e?Gj+E=9Xz z;Zi^to;7nAT=F?Y4mM*$T#RSh{Vk&K;h~=Zrih($%5YG zAtwbY2jv6O&sAG?Yi!R}HD}4WYRXo})Lr*9)l6lwpYs;yZXflurTq}BlPssU$J@hg z^NJ2ih@=Yc>Fc_j;o)s)AUN(MP2o}~Tnh3Ae=(Ar`HSr4wTt>fYSzFo9M1+UEXRZ^ zi}N~BuMPGsDYUJf5pUn{#m>Iv?RHDF;PMxP65@?mTR#LgkgqqQ*`Zi_dvL71>rkvc z@W@fKm)%eF)+U1O9YG1U-kKaA90ek3ghQVkY;SG3v#VVNSeuzQ^ExtE3W~;1(GRqG zt7x=y6#3W{A6#$U6+Zm5Cr!vrf$5hF4PP|P z>mRsec+Mpi$E||DH5@GZ1qr@Ktl-sy8MF2*%cqxIJ9qe+<;B(&R}Hr>>2$l=7qkzo zD49Lw1+YbPXFdWqkY9mlwY`B>4^`rT0z3suc!-)sL(A!3ARrjU&|?w4&3D zh*%L)RRO4kpH%7eX2ERdX%PjSB2jep|Cp_zbS4p|GpcQX1ON&ELaJSGpgLfCwh{{) zNcH5S2&twJR8>_3onQslL$SSjFH}c|sOwPM=5gaTM}&u%^-vXNqKb#AFwZz<4SyEQ z>d-Uw4IGaGL9*q>K+Zy0D=u0j;#H93JrCJhK53& z6k=|9z2YOdQYkKELH6N(ZydxQ^zyvP>!thmdQHIeh$3DXQ)PsAcfkKOEWlGvlfYl; z&purB#viOUet|YU(hx!U2c{g^Rb610wsmsjP3+M^3@gNjHunU2i(y`^(`j9au0$~B z<&DvvmT4MBrm*RoF;eVs7vpZV3Ibg~(M+hdKhi$G%_#*7o>+I>@_1^b=++rT!R~Mx zdC?%){h~GG&?63GUSd^+R4^k*yo!?yEYE2fNz~hu-C=h=?$)XtG3*mbtC9 zu>LawW0vTH5mP_|@N-aZ%aNx9rfr{VR~g5Gd)?ylikH?HrXH7qL%f{9aUG-^+A zFk&=$OCz~bhs5VcGFGE6i6kNbrJsiqF>wL}yr!VoH!2)qrUfG~9<*J{B9Vu}J_*Uv zHsir6el`ioQjHkX&GDIelGM_tC{{u-E(B5q==zJSUeAK#n1FtP-Xt2CI)|ozm5$Sb zGY+@MMzLmyVZ*S`^03d4S7?8O%_RZ$6DA@A_M`J3e);@I{z>o%@(Kk9Dn=&NMhj68 z{YU#=Zy)Hpzo`SygIi$%d-NZx_Puo9{Y^dCF{J~~i(FB(#Ysm$TFJa`+Qxxt>nc8T zq&?`#28_<~>i)hJ?T(?Y!3F;<-PM^Zb(z9WK@P?D+yON z5fONAH0=)bX5F@AXRtWi?VS%)!9+ahP1zZ<-C2KG@;Dp@&gS$QMUT~xgi;=aE#`wQ zmVi<&uh{I#F>v4*!Ere3Umy1Ti{$K3fv*(2Eps}ZOa z9-J!UXOVMG-4i{FmCxrLx^k(jm~e4$#ig?aGrm+`LLz;$TDlh$tbx9@z5Q#tUAGF} zq|1?T^Q=AQ@$`e$Bl|aXYFJJ$8hmz!WuRn7jV5<#D3O+hzoTs=V-kHyhcoWhi)bIT zoxGCVM7v3i3em890jA{0ec%S#4pYoZN*DPB(t-0>l#~=KCEygRl42tFk=?Wjrr4B} zJj^4}P~=s1B_#_}Qn-{uNeRGGJkH}(Qo=A#9_Mi>DM6Ux!zpf*LVz=)Xa0rSMwbY4 z1A|Ftp2J7xCFM-Lq(wF&0nF0kAnyRh8%<6M^*$a$8Z_8=0<^*8GFhA~rKR@M2D8&_ zc5;+ftJ7&HEuu!#X<0R;F_>7mJlsqySFKRZCNu<3SmF9HN@R#OqL-LK%psN#Yltnx zcH;BIUN945+HG?$sxGXyUwKW}HL*)~we0e(-5A=a9h%9_Bq{@{0U=v76|2|m+BkEd zSR9zSao06fjce&jn``8Zf}yoeswSEA2TDED3H8}+q30zbJz z{mnul_ZGYXUx!~7qhGrket@U*nbPSY`0eg&wn+MvOmz+X@&{Dx7sYHb4ZpzA;v!Fw zC+RFNTVxE}ToLqha%*yL|@0GduIf_UO3~4Mxeq18%2hG(Cz)T z#qSQ(e{PT@1Nnm2rY!L#c?S)Lfo^cMn@uR%I9yZR zYRb+ncg@Y4rR@(b3NYpd?r+WFIGp5B%WYt>qV3AVu!*ao|C8~hN<(R4V*I$;|O z%FNA=L(sMKcqkXj=^bO>3l%+~=L>wn+HttcLC4WRvnqtRvX_|b*)B)o#w=TM_IStP zs#4%MZ5^k@l^w#GUD+deBMFT&9A6(TJfjo13=h7>*pv4ZVe51zhBuYFR=EsD9c5)5 z9P5o{-Q^)`q~HtC6+v?Kt62n+b0|uIPmm-dc*AAbxj=;84!M2-^+wMgyPfK9i z9mMkbBixn_`20uo@1c(El$nKf4mi4wm?#4pWRy%S+Y2&i5IzobH$SpX;o*xm(eO>M z@clsJk6S%PmO7L@>!FOqtLh0$H-B}prt6z zW+pI-`LQZP{~p%)C0u7T2XBXU_RCzpF!qrD)l$jH! zPhjH7$6@IyS!@%$lZ}aE{6)OV))9V_o4t{U?`MpL^?A$=L8sc1$!<{Tc$?%4!1cX~ ztiR3cbUbi_2hBD;icziopO;_uE3ICuf^UMLT|;{i0U|7~c^(VZf^Lp8x?x?88pnAb z;o9I`d4DTZt4_&aYvjQ5-Z(Q$)IS+>!A{V?b^K3hzXzKKl zF(@LoA|=l>R_s_DJm*)^d!^uPyx($JETPy1Ws>S*V(ZOk*JuiJ}`69kKcVd=CVwA-u8+RDiRHMeTtge=XvQoASMojOSVqEV>5MUn7W8 z7t}-|Y=T#bYN-SN-~iDVyXpuyf)%X1p3V!pdvX_lv#Dz(LvqMzw~~w(DIaOLqfV2i z_o!B01ViD<4sR&&sVmFawVZigtpMre7Xu{kb!>}hou<)51c}AJ@^bupv(g-QIGG1@ zQ6}2KMscDpva|X~t8Ka1-QjNwHAR_U(ftJC-4!Q%12E!qZ0^BS{jX_1@qud!+1(}L z^t)x{j7nW#yOo83$R50lW+!}0F`6pgYX{lON6&+isd-^LN8NizwKg8JyH2<<);)&S zS1zW<^ZuB$Zz)N974(_C_0-6fA5Ahe)?L=d8zK3tr#lc(i~|`zEDZ`oHR8DoB<|sO zaz#$?o7G5uqAAcmMsb{ld%`iakE+yR`#+MA)FlG1!Y9NjX=83LdXNu$=|NZBOXHF0;L_7be=N0rS`gc@3FIY@VlFpXd?7(&5M%BOdCWFX=J5#U@ zXYtw$f>umd-EmL_ET-Ll@o>-D5crBpGC~S5f@z*nzXaxwpmhFQs$Y5X0=G4LY%`M5 zGI|jH*A#x;QU#MIv{GdO*eGj>CVQi$!C!N$Cs|8fotESP8CAB%GCLbK7vPv4@u!(} zo}rD_@r2aR&DMgYg+s1BCJcNxKb3o^X8uLptEW6{G6JohE;ja3O5(E9;Hr2f4V~qo zu@r}|l>E4UCM=j63M_H7ie9qyC@kF#`&%{Ds-EKnhJFL&V9Y7VTX=1O@py>jqA(m#aS+QJ>D#Wfm2`KA%`_CjWB&q&(6LLWPmK8n9hm!h% zLf{MKNxQ6>TRLC&XNIPolo;|}>peF4&|I3uFH+y=ljyism^2N6=pZ3#b>T;+YSEx+&0DvC4a+t$B3N=!dd72IA> z4)k2I9#?|0*{psBnFvC0{w)L>x|5<2s?|vEEzS!Iww(q4>1{ax_%JQCJfah5YK-{%aQ! z<@90ObU~9pcPESP#r~F*!P;J>3SBziPzq|$`i597s^7N=6#nStGy%U`bc+>$3Q+(f zlnm(k9DMuX79(%M6V?hFY8B601$x2`IRHkBY~k^%xnGUfe+Pkra0Q$ze*FH27zlbI zW==Oo3_2~-LNxv5hP!Fu`a@=^>%f}0vPr40YDMU|zdgS~inlUS>4jMzWFrEhYd=~B z&w^^o-Ow!OpBbp*Mldzmp3EQH$(g-QHCc(P4Wn8(J_G7lkD!c~f(&d^uLXY?Xo z^xTLU0$>DLQjn2?(4UYX(ZKILKF3(Lt+Ph>v(&N`HnGMnt^tXx7%#jY{C8-)ZihE& zxb~%rMVgn7;M)gIe3KXSz0+sO?z#R-bJ@khlb?5Rp(iQUCZTHs*y>(Iye1xKbOG`O z1TF}o7mn&V?G|QVpUecWzs*902f7?MDsz(d_kT)B?A4MvjOz4~4O6bxPI6-ASZNO) z1*4ky+dlQ9LtAq?GJgrU zebo|SVay3Oi9ifu71GJpT^rc1dV5A(TEpVjWSUF0{SiC2~b({c3HrX0Yq>GuP)WAkq zq{Goc5i&?rv&Uh^Q8$-8As7O2grfwRve>g&sSrbk5KSS_%)=Gg=96cWjpicKQMAnL zr!!QI%7#VSc6I4vJA)BTv-m4t&J_n(+Q)0CKdDwbpO`d>{vG%`;JkX8*dR|u}qUYGZ6`pvpAj9 zrax&{8@7v@{gtBGKw|S54$`IusQ*2!aP%b&nAnE&)7^_FUdjB;SZwxLq z*)@K(Re5q}F9K)ANPaj+5*}&**9!elV*Dejisl85sb<*y3f=x|Uq)CB#J{l>Vg5?C zIRB-*cWQT3ymMm6RN>ypTiQ2~?zeZN-S2pR3{UNzhcFiMI0Gm7_|F`ytjVGaI2<9u163EjH3NH>3>D_` z5%npk@L=|KcXr5dDPad~FB*=z>n0D6JBRhH>Wk}xzd#=y4K)piyNnt3i&!vW2zPqG z=03}L8iPUqJ^+$!(BnKl0h7mPZUAA>hkof3%o*TNoJXF929_vnm#9lKJ?jvD;qY4B z#lS${pGH`^f7Ax9En=lK(ae(;aY z{mR)I!W~&OC?z}h_<-e^Wlh@wuKMjAxlQV>J^O%u;JEiswNm__o5i8H>uLo4ldrQP zy`;;V}1zfTxxh z+QvyXJ={V>)t)zau*|6Of6NjRSp~TrM*uU1?&<2`a ztJnKH!c{8}R8QC1jJIA?O@}dVt$TrcI>Vi_6=So`s}1?MU&>3%NU4M}kB4oU6s?;r znyNTs9XZ=>E!jb69yO$EXAVp@F`vs;>zbBGahQY>#Qkf;3hV6aJKfaX%#LEtOuMe2k?0&PBn8^dQL2GDIK`LfZf)}qGshSP&06^kMMPMS zK%f1V8A59-v<(7V?d~`Bm`TlEScJs5YvHkHp(*`w-`jN?OUL>y(uzy=HbBgRE- zuUh3@%N(O3{?xvNko*L;N#mi^Pu-ul7mYix7_iN~nGO23huL$kz z!73YSWWXkMl}w^Z6_qs}xXRI3#FVQB+nPVoDwYV}#89Mc&>==}b8vtm;Mg;)*f ziOyL8)|o`h;$q_UL_;_j!{Zva$D@ex4+08XxfU$NLy2 zlptE;5VY5KS9UJNb#8kFn%~dJYz1y`BHLL!U)9&g(5fD;;Y|}H96BvBVfKnY1ju#~Q%3DS@K^%x9+K+&!ndY<=2YwS>rE|7>PIFSyX2;y{XvOm@ z1@e*7Vcp`nY<66OeP-M)fuczE;n9i_Ot|Qy_j2`wAAD1;Z6ZDa)CPAwHOFYIhQqq7 z?WuC++>LI1k~A1839s~&pJ=76LqV@Pa~|1$!`f+RJw8Gn=HJ<(1&ftTlNXIro@-TN+sD?xlr?eCr7#xk7DqC!!c~uq_ za!MBP>YQb!!owrW8EflL(Dw{+E6^O23=N{|--1@TqG@LGHWrn7xic25#JsSWW9=}z zqG$BmjgUz9M>r3tFPN{$#bn+UJa0AV7@V+Z4{6Y#&=QrR?rTp9mNK5%gmit5eh;u- zWZKquI>wrjkg$nAluMq@T{zEuN91^0t30YL-XR~`938&jNJlGqF;ZY50Evi5T7x9H zwK{1?dSPK*6QyimCv)0&%&NG&kqp7B@{e$zbiT&_O&_}fD~X`yNpFle%T6p@ zETtcNY=(F69%mcTal_TRN$H+y%CN@EXidhG!TO3xfRYK)EL0gO{!vnE%_wEG(5j;B zoQmzb+=S&?GJKR|dR#TV*f<*bZ^bkf;nJp@mu(KCQ1KLT)xdn|HCjt`)TAi2HMz!C zCZ>8vQJ)1f300tpsk9B@lZ7IOIeQPW<63?i(O+Jj%Rl~9I;WduG2=B>dHmFU4tJT! z7g;E*o0cMjQCfmM>&w_Fh&ZE?E=rCm&b<=pH5uMQ62Nimnys4IW+3XrPeiH|3Cf;B zogh{W(oCMHW_P7|L(v}0s&>`Wyd}7U4NXw2+~m_y**EiD)Ywe|4;)-hE3dj5j)1PPItU^N7+C^Qg6eA;cUmx zEvGN4D|{zCwgkg=kTgE(uMycZ*E3sH*nupokp)W)IgZGVPr*+{pCg-f zb4FeV(*)x56?kf>kdG6>L&ON|!;RHo@$Ylt-}^;M!}5m3uGA^H!vcR*^U_HjY@@gmkIqiZEO0i4jIxBaiJX&(J6IqJ*D^{33ex^0ErcVhZ8+`VprV zRZFIwa^tChW{VK77j96Yp~|1Bj21_kpajlBD>bnUFKJ8l5 z?mRih*pM?F^T&tzSo!K}{NI7%_)e=boClnTYRnHCHX`+8VyXC!6G)O_+mxxvhBr`+ zWj6WOIka>=R=u~x2HJ@nDo=w;>|`ZDYTvv9SO2c&E@hwmJ9w3Dl`pNNGs-|kN7*E2 zXc3K_W}iv4*I2~Zr5^QvnZt_DO~dtBi?!BN$YYGS8{l_tWo*_~Z<3BG_k;At^v_K> zW(!T$)}9UB9a~%ZXK5!~3u~*d%4#+%=v$Q?wwIsMe~vvDJdvMzK6mKx_BoeB$?3j8 zUNPc(niKHPzc16krJsDA{$BpQ(w9nHE9B$Jj03>8!pTZFkPg%%@G`#H!J3p`o1OFP zY8VF;TP<`%jzwqcXpOY`JYFYSqi(d={u_MibS=7UJ8sCBwCU*Dy%TpGx$YX7(CX#u z@_6xV5pL02H@t9-r4fe~*CXKNufgk`R=D7@6Wzw#S;y?P{Hhu3jNwk6(V}diDAD#< zp|AOJ;%w7%%BV3Ba=W^m1`nmFVWH39_*YU+QbSK9;>oRB) zFQqPsoK7Eh@=Jufz<(3rHTIlC(8uxP>D;#G^RZAX*5_{sCg&#P>uw0L&89oR-Rfqw z&=FG~R$6@k?SE~*=I1f~ZzumN^NrhNdLGPFw0YZr2LiUR4@^oCA z7PsO5It`KY{fqTTuhN>;mRy=3TFmh+!vELf|C@csoc|W@{SW5bD`{mL-QA&3(b(`T z>#vW^IWv!;g+G6zRezBsyHjZ01}UosEh?~34BHkp&>VDStX04Krm;**p72)Gn1ne4 zSL6=p06oDUodNl$0>utwhJcBr z2?Y!Oa%~!MR_?MvR=QUcIR@3!_z4ozdO8QJ$?v2JrkwOUR}pvr|CjyWSN|mna}@bf zG?7`?#t<4+z17ysY)=5^|R_(mCEJPFp-uXRE|Sh0_G~eY2WMmt_mCGj zeeTfB{qBO^V${(7<)gPuHF1rqQu_JP2D~=oivCY5;guW~wE$PRXOGi#$wl1!MTjh- z(&LUajuVj+t`l#DiRO@P+fw$6DTp_+B=M8RpXOvMm?!Q;kLBPUW83iSf;Ip0gTYk|`Qg`dB2cmPTqC6=rmpG4h|4a-1bAWD(zTNy5IFb-IlIK(a_28xSuX2vx0C z>_UrmFS>VNL^m6ie03wE*WqFn^u*kFP*4;^l!zs?h)ly{(Nb`wjK)QS-B&z7tEP}& ziTM0@7!&oTA8j53T!=mtN;gc(tRXFA2u2IjlPo^X{u8Nm{o59HWK+g@Z)S$YTavCKOdt`Dd3R`PfFd0YCf$8k16_#|;ma(EiI~KZ z5~)BG?TFm0Q}lC3ysIPli8zLBA*o4Xgy^P4TZ{0A)%>bV!zKVH&V!JeMrhO=`S2`Z z1iBux)5$7%#E6$b0e?|Qp;Nyi^x|-s@Lt+A>~>$r!GsMM_?jRFX0M8!5BQkBAU!Hw zt>vEmV*o#-;1uD|Fz2NivzBC39%z!0c$Ad(D!rEIIh0OL^azh7a&uQ5$@5-qC44WO zwFPPJ^Q81=4Qnbfk{)IzBJ!~UHXPI-+$^$9Gj3GM7I?@kbgcV>K)mWllX4W_c|aN3 zD0u4wohEq-rFl=-{sK()DE|B^NsF)r&*_QpUu;Zq`_zMU;JHHx*LP&JeWxx*iLCK5 zLeWv_PNTrCfjDZ$C=1rdsN@uJYTz_n2IiZ;~;%_Rf1rChnEwk*sdCs6;Z>4+(Ry`{Rz@) z*CE19r!DJZmx*`-3^Xkijj*yvjqz>HYxz!}6 zt~y(VD?P41gBy^vmuXEE~W=@G~j6EBYu8=XJE|VURE5QCL z*cp)qd6SL_i`T%H2fIEu_$b_*+F-)gXaZkZ{Qb#Zjf^HTPI zW*a(O8{s=LihK^@;4=t&X-QfqR8&tZ(VatlBAINI5gACkT_|;S`Ks1U;u?(%_>cJ? zs;3o@)~$0-VV^}`CYo1BCu?-(7Wq*UlSR`|W{Z-sV{3x%SEAiM>%GD2bQT*j)@CEl zb14)Rpr`E;4JuXQF9?!qVXQO)SQB~47U%~nemmW>Sxq|umEcYxC;i#ci}OBG zwUP#6RhLELf|Vk2?DCc{Dg$|_hYasc5UY=KrV6P&am!c@8zQyPmf=qtMaNK5HKByz z+h%#<;EQSp6&iQOM9qfwwV7sQ9CMmj7WTeBg)+<^BGxD8sGsK03&K~4Zmr809F*aI zjHA|LzP{xhoEB@tIKvwYJ)9vwc;%_2IKAhlLX z)Ny4CK^v|9g|Y3Ur*5jsBzi+x~ay1Id#nROA{(#_vyzHnNt zk`+8db>2KtzGIFk?x7f|vWq`NADnE+> z5o*?+hW^l1qdNhi0pX?L&{Zo>R!;#`4Ju5PnMmJS0~qB6N#a(Fkegtd6vKi3PKOBeXaqrhu+({9wQ zefs~Ry$7+g4bs=m1@~+g4%8V&anQLOv|iutn~XAe#{W!DKSrmhsxj~9fK83?K=)$! z{iLZMx$>7(BSe_pslFw&4~-|<4!xfc)Wl7d15k_#ww5eX4%y?GXF;9{$g>uoh} z{s^^Kp?TGjF*LYN{F8)%icwRk1EhP$lAvBry}L7vV6oVL3?JMcncLcN&?|NDiuNq4 z=}^lX@}^*R&$;ts-=}v}O-K9JUdw9)#Q|@mop8PH`aa>kCg1+T@Us38PI*MejAZ5# z=a~yAmjxv6y^O~%S!l3OZCbRJHcCM&%`_31SIN4B1&AQTn#Yalfv~}2L6AtKM>m2t z3t>!NAFaP)ovs6ap9|annp-5}$NUa%v-dn4pgQgET67ueA1j)+rTE*1_na}#=Ky{4 zXR&RNgRo!?gU7keYDRL%N8Ym)K0e`^3T1($h)1(6t;GO~c=@ku?#_8#I4HI2?|pfj z$`G_M*SsGG5I9m@WR7B+N(`kNJPmO3X0mrVO_W_pc|^ z(B`pA0$K|3;WGDjwi1lKZZ2!7yn5VLxJVVjN}vSFPgDwQga z{G`3hsVYiv6@Tu=QC-5z?;QQH@57Hhw==r{0Kd7tf3r|8o8niMp9PzcxbPhEE1S_UTo}>sJTVnktvy{&UHOe0{guCd4Akl@vL^5~p zgCY#_x;pHTe-}n!E}r;%VIG1i!$IPy2J;d{hIbP0f-cW^MRepTU1n+=dvv?Ew0Q)2upiwnQru9HO_8w%*w1=i4|hyugr7KE;Fr~ zpQ3v}|MT#C%bfKl&A7BVx49B#l+lsd0#9@3w3ju*9q^ysu{gab($Rp*_a<7Z%`bJ8 z*3*(c_nTe+{y`2(e3Y4;QruZ1bAc?x<)OFMG0C5sH#RpfaDB{6=GT;&w^6c0=L`O% zds$(cU0#rxGCMgXJQ>W#QGG&$u-RH%-Z8?W^S1tgkZ{yP&Y*_0HTeC!*To9U6Zo47^~tV>i_-nW z$NlH~-S3w}|C>qhNb&m{X$W{dlDN|#Jqnm!0E$og?pJ3jY+UBv7dtR57BEF@(hg18 zO`5BJf6MxNXU(@hR4ASZXS@K(b*XqcHiG2w^_Zc#M_2BQwvn;f2Y^ssyY5*2hzO;q z_pvWsP4px54erxBDvc?wn8g^taC6+xY+`~(v1<mzZCY#Kxkh@S`h?{I{f~UPpMQvF?YhC{Y4n2ZpR^0-zw3*o3$3zS zP*1*6lURq_3;fjnoL$w1Si8du^O*tuapwj(N8VK;yZwvUcFCUX%)gy&crSui5CTJ6 zl4RuhyFjU5Wo-ulIbWJ=;0m1fG$4V?A?s{#d?fy^4IYdO>|bZ%_61 ztKHh(O}~FrmHEc(jDN-F?(Q5t-(BB?z6#uO-uoV1@9rED9qjD)_V@nD75nvWMy&L? z)*E;#$>a?N$OTc#G_PtfUueVaNXz`a)H9@@h9h9Xb`dBpJ94SCZNPp91|)1)2De zN?iR;!GUk|Odp_Wp9udmRKHWs2Q$%d>%FAc4?U0)m7i+JCEdUd0)RE}EC>+rt#>}q zlGecgM$!Vl88Ur9rsaSy#J+H1&b@0zuzh9?OnoURfR|hXJ0SnFEgS&Z|J0u811xR3 zHO_o$kof=D-?Fa>9MI~2%D}`2@t+jm;6wxDLNBcJJqVWs${2Cff7c)cFhVO?)vkU3 zJrDrb;0xD*ry5Lr&?U!f*bEsmM~bUXGgC*7DprhpcC@XS09XH0T_!%blJ9Py{|6!@ zfG=c;t>3A;OH3?K?k4<#)QiD0gV8wm;C!|8RqzS=ohi+u-$@{C#{ZN6=s}d`0}sgk z#_fN261^f#?1sa>Xh8WDG5}t}3gr1#df=%r&;u8c z3%<|@^oa3Q8^5`xCVhQQrr^v3A zYnJG$5OafXbpP*Dk<+#zN{|CiUHc!nnY;sU9Z{!rhlobm8$4=|0pfj&@Fgn&-@5FE z2M~cS5c!=l_CLrhPqSW-5lvnl!LL@n!ac$44C($st`~o0X3Bmd z|2=^r0Rp=_@@Frc$>r}_6DsfAhftrf6V~q=Qj}jYcNU*=6L_xzw7oDT--iBf5ej^B zY~Y_SyJe)H5a_+F0EcFiQZ3}?<#e@iae2b$mUlNXy-9mCQ?3$ow0Y9}Whd%Sdu(EI zV{6jik5H|;#>MKm(Hx!p0K(}daW!98CB{t3$M&mG0cLNX?b6ic>S>|%&-8S{a*|@T zBQ9cPxZ7DdHX~)JU>!q3Y zbRJ?8n^LiHGv#YZp0SLGX;czuBsPvC!`tFHX56jjfo3639nUSXFecKeSx-|_PtWmg z8uCiT+=Ywu?Ba;B{AapT`C_1o6mtEb@E=PFOyz<{zQ*h6sf#%(zx<*rZ36ea1gU0I z8=K=OaiiE2!Z+e}QIXLwd6Jh{wyr*iUDQky6PWIeV=N|Bb9I%WLIIut=u1T_=`#Z4 zf@CTBGq~6|a0S6=vEfFVVU2oB_58c3EQWCIN>wI{;4&*%>yX9IsWz$cvLq6OKi_7^_4HJ?cY* z#z+#}l~opo7X2ni17(`xv67khv*uU7EW?AzqlPK4@_WS6M|t8XVxc1z}@nCe=jEl zEko2+?SB!&61bPcR;x5cUiO-1 z!rJ;Fi`J;adfb$5+&T}E_hfl=Qpu=;P2_kr`FM1@P$l^|pI4R%rx0!078i^OXE%vy ztJ_awPNaaf=s=)LViCL%XFJ-MO+As^Zba6WR7`l@noj9KI?S;7B0p{NPfhy5C5tpS zm|OqgHMv6Ze|p0@?!S_>hUm^_4tfy+@<{x6d{9u zqjqfsL)nIKBoj?c>tcmp8%)wc#_eQx#URySvm<89yFcKQ90I`M8HBhR&{2pmNH8c6 z^H~*m@C<}nATZm!!r)#c&{lWt%8ZiW9%sON`t$M-aIue+G1Y~as3p*@lUUI0(85Ke zCP0g_$Y>yo2QDR$b8{zt5+gKV;Nc5O)39UWf)cUqelSr0;eK>L5WUbD5a~dnfRJDt zC4u&fkhnuduo%(oDVu&S7h`da1a<}C?Vqxo(`Uv-N9cV7fZuNzaJi_jmFp^ef(p0 z#{IXqVD~r*1w24-b~lBfhn(_Ieq)fuat2 zFV|9{`ndtoIUw~FCCIq(6@G^UXud*&&=krm`?XXkp)a`IbY|JMx;J+ggv{OL8 z`uO4^SWems1yN4@5#}$mhZ^h}Mqe*ccY89S%4P#R-%yL8)iN-EFK&ii*XvDel6tGP z$k!imM3Q$_ubO=y!x=pgC1a|vY1Sc~u_$cN>^5DBc z{Y|NM%eh$~dAozc48wJUM(+*T-~lkjcOYYlISg^DndEoQ8_>6e95MFa=USVM^nDq6GnEj(&oQCvLrbPU4jwjZrwQ8Dg>|dlipz8pwUkN6g*CtsZJch>*NcNN4$qLF@lA> zCp-`{5o$t{*ub7U#H^pCkX9H$Eg*_vbD3CM@M{j*H07$e1guQsVxDF7p24n2-25QA93_al} zBW6@AB~XRmA-n05$9I9jlYLOISMlm>Ny8`QH8nM7tZ!A>&Z$PQLJTr9=Uz}5+qUl(4Q|2u#2R-G+K^g6M=;wdrD zh&t%nZ+8Mq4w_ZDMg4LXL_LDa@;7;L0?7o-H0*SES!5aB8jyV{rZl8npH7S@l5ha| zuNDcDrT(|E(( zdO|xD9Yi|)7=lc&ss#ovt~Ofr1ogNcgfxBU3OnkP`iEm`5fTnik*Wv?Q_n_aF%e7$94EPXmO4D@!0U#qY%YMOXJ6)1}i z=zCxbP6d_*iYidWVx=6b3JU?r#3_S{WI%SBfkbHN%~_8?O?wbbg}eY%4WKT28Jarn0ZN!b^B84PPy%%}Raf@v=<)9{G)^o^yYRosFUe$r zJUD5{)kqW^=*EqgsLa=>VFU)WQJ}i;x75mJlu(@EaQ~Xe3|8-O-*n26YbC0o&cJD* z3=&l2FR1cbcraa6A$Jifem4#FlU?mTq#t31CT=nk@ z@k@w!ua`^<*Nz@RqVIu{r=8+|as9P_YpXMO4Y4s8bvl74rnoJs?QkR8SKnuI36D3L zbb*+SQPx`|k;j399w(CK=T;Xc@f#L`T)J+^g3y<&L!OTYRnLxnB&7!Hf*i$)xd*Uk zgeDr!w3b^Lgy;vEfo3Q*7SOI>A1`@puZ>g!LR&7S+!qDt z8EG!eqaZ0Gh8z}w$oVJ-7cMh<>uf*J1XbToTCb3XxH7W7yzan5wLXDa-*o3|Y-|+; zdkP?*7a`IUr~%~wXfH-MhD-Uy)_1&hp0<4lwR350sbg<$s;_5mc5c8KCkQzW=bnEd z7ysM~^=Agi8>EnhgGIy=5y3 z!@z&x6VT7!^bc!e>(Y7LB^nvtrpcp!_QWfMXe-P)QrSKfth&Kv`?dsRg_N$du9C0V({RNm<3gj-+TkSlm^5S0;F=9H+Vh3X)^j4^Uj?d=Q&4+s zf9vVA&NZ7BrbKSxR#Q?c%peDUmB7}qkM(vi5ahPxFo=0G3vfo!VaK+O^!*Xp=n<;p}`wIAWIf~9K?y3kwXfO6~<)e zRO-IOL`*IG7^&sEUgffg9Ur1g^j^52$G$)FFhL-}1oCtKQ|DFvh2s*>mhm+F4^s1! zY=(TZ)4VEOD`0LQlV5?qqU+AGcg+=npRN94ObA}U+rh^s@HlX#W~-gJV!`A?!Q_{_ zaQ;q|6tT%~Z%@|%L5KE}NiNH$-EiLaRKhJqqK&{ZKrva-}YeX^7(WkW` zD%;w_;VgVFAh~U+%&NPS_lDu9aU--H*xlFy5gjcjzJOh4GBCMazYg(Or|8YPKOi(3 zYb!d1H`p@`Q*k zFAri!@4%$Gl(3YXaI`TWI;fu9wU`DnlJZFl)DJ`a!Q0*Pi;qrwRDcG}*A0&ftEV|I zvM?RdWb#lG_){x<4xl=e@R|H5c{0?S_eGCuY?3?wLt(|{h_dnl0r^UDitY8u-qhzv z>^YIQc4mf!$lAQ5Ip&$(xwi#`FULWpJHTzY`*FU6af@vFnHJkyct2uCJf_incb>?S>P59X}($8PNFxt_&i z^K@)aM^p<{c{uC%{S$A|vg3ekh4Vez)AM3=Gh?ZM-tFm?iRdM}r*08)MVDu>JbLx( zRlAybw{cHRspyL62f^6f{ij;WC=O(?^Xka0K$f+VLCaGiDv<=43IgZg&rJ!^dk9>b zN96*1ZJlmj$`6m5GEVRKAEujCe4aN&66GNGfd`;uexjYfm|Am5qcKfDBaO%n;iVDw zHL*k24OAVg2899DV$=03%?));?9I*i=K8oMmebKN^>uYE5ilR1V0`-n8Q$Zq5YSL- zs|ep`*4Gn410lU3eR?FyUISqGkpAr;yilqL2-xe_RxkPyYiR~8To55+g1bft7pez8 zXD@9jPsIDbSVYlA0^rqz=J?S-t;T$vObh({NHA0`LzNibmd{=<(uO)$wjV!Y9bJR2 zdyrtQo@=A;e(scatUpzmD$RzhfmJc94V1N|CzI_}42*PGuw=T+Hp z&AP&7ry4lVGljC$aay~^3Le$l+iiCNQt48%)g~w7kX6U7N+Gl{t0yfh2JIhW|EP5% z9~(R-x!BjH*^(twqe>kE#ilpfY}HI2#0FnnRm8Wm9PyMnM|m&Z{5nQ60=DW0xB|}@ zz%SQbvOlI8TvyUE?{lU%u$sHv4!YH|w=rGcL`v_(=RHSh)Uc?of0!&f!_XkhF1sr5 zUbP3LDttX2afJ3iCpYR=Gh|ifa=T7_;`Th>sIhIq=I^wA*09XoxdH3`blaC&uRNsl zF=Td6lE1NBd#tUUG*23;QV_Z|c6Q#>zilb->8BcAnopNcP9mvY{JP(GjP~zTEYT%6 zVLmRg5s%fuOnn{R-@v5sGlw8ryB{{0NmoD|G3D?HCv-1>Gv7T9Si!Bvu?0RbW zQ><>a@vGLG9`DCePNB+U%&U`;KjNX;?qh%JAD@ZN2E~0oU&1p-1Bvy3J1s$B0x>xi z%PB8Bjl<@Z_qjS1L(`>JjrN*_D)muKvbyUs3nqFB)MYoHOE*B9>?UXc2quI-l06DA zsBETrpk8nPnEt{FUl!-#aj?O)>K6_=Jt*pS)gY|?Ilk)P0y zl4+|ftZX)}$c~^iSkiGHy)YqxxXq9w4bx=2xvphM4Aic5cTZFr0qe`x^Q3q1d0;QC zVI(x?T)apu6Vb=xYVBS+lj8K1u;)pAT1)TtGP;|S0$+B(393o)8p2Bcb^>p&>rT+old8cPP*QDYHQe86Kj;_r<*;`mFydaWu z%EyZjlB5A$F^LEslQ=-v!R>o;n>5H)S{MmxO&5G5e~y$<04$gVGDU@8yvP3lWbnF(gpYgnKk z2E@K>tayxaaNl&_G=RqBi@-`kJzF6U6msXoiC&*lt-odlTd7y7AEx^Jrr^XO{Cz8Seu}>*rwk!rk>Pnf`G{aY_|Y zb0vBXKV$Bz{S?eL5%UA)S^aNt#%l}9eAEzJX@sbCG;=qz`2KVjCVT~0r4vq${4#CN ziv1|TmvqO|x|=CDzCD(@3D1035+d$Jh31B$*cPi+3Uo;g+< zF2+uu?aYBB6Mn6cPV`p4m9RjwC^FetE8XPB>e~#8dC=kAoZzE=a;$$lHnrL-SjX%f zhdFLCtHQ$)N3qbRx4bODY@Zw!tNx9>2Oew9p7KICPQE$8rJ>(wll%I9PnLiQ{l~U0 z97Y8@hV7JFiuVzIwW&;*J)XAh&gjY8zgM3kZI*AQKis*R?j2#dP;5hCF4RAWI|#8o z*~Fb9s}#{y*#O`o%PBGdxy$%c$c=S}hXNnWj`}rC`GQ5zGDuy;iTp$gPRM|2A+lQ0 zGF-|ZVEWag{c=1VPT6EA z7~<5Y4eLYyDav3u_Qo{BY7l-nSpD$@C73E>$KkX45`awgF%xyPts_#qb{d(4mOG2R zIsdig#wqG)khdR`G*seWDC;q8VP@;9-un4%E9yIm;jr67V|sE)lk4>SI()LIa}>kK zW5DseApW5fsd+q9Z!9XgvPsw79&P(D&SjTf^UN?`)>dcyZQ4SGhgX&A^L&VDFmgfP z$aVQecXU);g?K>*msxG%Rxr~y=c3kMW=~vOno`YIkl*U+E2J6r(p4IwvuR?=i_Q9& za^vRNTXCp}^Aw)VrVuc7d&VQ1R?a@|ZSRi$&~$((!0?eTSNypZx0yKFi=|w(R&c7E z?=r!%d@Wo|!B|XrpX_r8_9~sqNoOJD-sa`JDMW8|+s_hhEf7q~6DvuaqkK~Owc^w2 z{Ygz<*ewy_`9c?d)H{_knCZR5{k4-+{fCZ*mDy_*kD5u#vSDR@r1aq&dbNt`JxH?E zwKI*}`VDDycro3gfccNxpou5`vvmy(?B;x$)3sL_7sl!l@&yN<1zZa zEE>13I~^v?b)FPKeG^6)-}hYy%)yhP)|aAO3g10RMhuyqqqYonVs~!Oy5?YaeyVY> zG&%&E8_aU4nZf)UY^|@;BE%c*oyK0(g5bE-ltW7}7~YA=crXXw6#8QdjN{CYX4(h+ zA-TSJu-!dpQIi)9ErO&bIcfstZ%NG4W9DoFOo2sm*mcxg2~RyImB z$0T_EzN#h~C+2u4$j@1=Jx^eh!&@lCUR5H@Bs8%dx8HQ$x1s4VC$;bf@wb0m(3!zV zDl@kFDyg4^x;7U!Nbv)Jg`)2Ak?VRNlb40cH+x_)<;+jwCapbHChvB0e)b z4ai+?1O5yyI;K`Ryk>9pY2EO!P;oCE2I=Za-Fj-1|Y}qBEgDzf-a?mG3g~c*-=T`Am_Q_KK`UJ z_hP71h_M_Mxf~kHjNknuN9vfcsiYYR0Kb@=;Kg#{VZ^LPRn2YWiZPnN za|+7u?h`OK0991uz5fTznq)puPHDT=UborknzVsv9oAS`W^=uwkP^pypLPs<>9;}P`7XWIMlX!`y#WH6hT;$ORFuewK1?dZLeW(0(X1U z-Ic=9p1{@3NuS#pByl+1R&KI1Hn2NRCIS3+a%}#dPHLioF}~%@?OnkE|B&mS)iy90$5v7v;q@veBtA~#(_Z^i} zrR7u+hr{Gud*)p3u-xu$OJlE@InlAQeF;^*BhyILz(it)fuTh2VShh38v*%#;~PYT zK0f~}G=db_iYx;tSrb7i4sPpBW*?9@4@AAGILe6_sW+gc4SHBq!1EP}) z;7(I~gT9R>Kzg02YGGI_MM%NZaVUNDO@=T__GZpvG}?EFOFX>S-a^qE#iJd$+Av>p za5^2_v_CF*5>tjBUkZyNZc)i)S6iBVdz+Bl?=G%d>ay8Jmxh+6ZfwqZ3l(y0u@`Nm z+;{iu9e&m7G}=z2@#RP_Lwji_ietOk%TAS4PjCM%yrbjdIv|`i6wFp|dR6ICs+n{? zhsRpDX%w42?|c2qiC8xz%gu1DH;5fRl$!y>E3U>(<1jmz-l%_BM^L zA4y3`?~tT7^kNj~IZy^qDG%A=#2#sA+UaL}lSew4i{m$Luhx|~yTwRsS?_C2(~>9n zaxw{M?t4(v5*tYqT1a81<}OXB@y?7XY?qmwghI^j_Db`lwF=)h3m^5VtTaNns(2z~ zvU!;vntWGDm#~S>K&$Tf{1;YjsE?6;u*tGJzcjrZr4QpTXs+<7Ys*haSt@Us z@Hyx_t#qWzuxYq#ySFudJ_m+YkxD3HXGzx1}>7}OzQ-> zBxx~w^xCF>-WrFxIz*`)Ao2Sio^GGY~Kk{Kw+vrWY}{ zax!+H7qQZJG8Qs6v^6rOmom07buuGhVPO8l_}>mA0V5L&3mYph?|-*~cFVf*g3?P= zUU_N1&8%L}V3y8cZE|098f-$Lkc^xQv5}itpiLu?lthn6sV;_ujG&lYavsqZ70$M= zj4_v^Y4eNVj46mYEIYh8W6xAJwKw5SI^lce5l5$(e4F-q^!?dQ%;J8{@tkRY&FSo+ z1j6{0b|(n*Y&=}-yps1Qs0Y#Y%RbzA*ijO#M5hgNZRg{c5*uyVbatEbWDmrzHJhdi zTdn0fm9-=em^_TD??zHV-((GPoRTvj||=UYVJKlXy$(OGFOuG#ft zBlv(=L9OKObX4dcZX9VI-i$n`ETyWj)Y`UPLWs^nygV38*=lXqRRE-?^I`Qidx0^0 z4>wqFF9*ri!oT*fQ&(EpYP+und8Jmo;q`KUaidk3b?;kR~8 z-B(9Mi;I%udWvJ$rUL^H5g_MhL%;juuH0~No|nN-`fmdZj@89q4qXSc^?_Ow6cj@_ z=%f3+SXHU^Dah~ZulgY|Ph=*$pD_i?dBM3r=K&F_P(lX$rKaEoGhBl)M_1_n^=LsF zUfLrshrQGagGs?YHaSF|Y_g;HWP@V`=ZPBnF>^P&rz;jQ+t0^L|83*;ZA8Kg$|Av2 z#hVScc*ZTVUz;Jz z?I0~V_TJuh-Be&V90K1CZ@BkFTbnY15k_Uaiya|rQ;3%#vGz$z#|)~^OjF9=;S;g1 z^gTW%jsj}vIr$wl+=7ovZXpF+S4!Fu=j4n$Jluy;SIXEkQnqQ*uq*No2Dn?P2a5vf z8{q&W3f>(ZZXBNnH_QuE;l-=z{-^OXBhG_uIX6yRWn2`v>f4|ecD?WO3;dE?oNow1 zB^X(_S8B3DcQ#;3Uv4eBPtX;(M*wD(q7!^-c2p9YT4_y?bA@hpI&X{Pb^4h-QZIsL zQn%eR)C0^wZTPDHv(%Rz(oP6OknRBPfK;2P%r4Yxed(aC;k(X0Zd)L2Xs1y(wmyO; zv?q$Ae(W9HLuT0rniao~pLa0e-j4DE6mPVlG#K};>W#FE$jZNXU

;@IzIv`n^bt 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�X{a^qH5~4{BR_<%K+<^V98vxe1cv9u-^b@q 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%0D3=uv31XhyXpX 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