diff --git a/out/auth-proxy.d.ts b/out/auth-proxy.d.ts new file mode 100644 index 0000000..68ae7b7 --- /dev/null +++ b/out/auth-proxy.d.ts @@ -0,0 +1,5 @@ +/// +import { RequestHandler } from 'express'; +export declare const AuthProxy: { + router: RequestHandler; +}; diff --git a/out/auth-proxy.js b/out/auth-proxy.js new file mode 100644 index 0000000..20b53c8 --- /dev/null +++ b/out/auth-proxy.js @@ -0,0 +1,49 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthProxy = void 0; +const _1 = require("."); +const node_fetch_1 = require("node-fetch"); +const router = (req, res, next) => { + const resolvable = new _1.Resolvable(() => __awaiter(void 0, void 0, void 0, function* () { + if (!_1.DefaultConfig.USERINFO_HEADER) { + return undefined; + } + const token = req.header(_1.DefaultConfig.USERINFO_HEADER); + const url = _1.DefaultConfig.AUTH_PROXY_USERINFO_URL || + _1.DefaultConfig.AUTH_PROXY_URL && _1.urlJoin(_1.DefaultConfig.AUTH_PROXY_URL, "userinfo"); + if (token === undefined || url === undefined) { + return undefined; + } + try { + const res = yield node_fetch_1.default(url, { headers: [[_1.DefaultConfig.USERINFO_HEADER, token]] }); + return yield res.json(); + } + catch (e) { + _1.Logger.warn(e); + return undefined; + } + })); + req.getUserInfo = () => resolvable.resolve(); + res.initLogout = function () { + const url = _1.DefaultConfig.AUTH_PROXY_INIT_LOGOUT_URL || + _1.DefaultConfig.AUTH_PROXY_URL && _1.urlJoin(_1.DefaultConfig.AUTH_PROXY_URL, "init-logout"); + if (url === undefined) { + return false; + } + this.redirect(307, url); + return true; + }; + next(); +}; +exports.AuthProxy = { + router, +}; diff --git a/out/auto-reload.d.ts b/out/auto-reload.d.ts new file mode 100644 index 0000000..9586b23 --- /dev/null +++ b/out/auto-reload.d.ts @@ -0,0 +1,3 @@ +export declare const AutoReloader: { + router: import("express-serve-static-core").Router; +}; diff --git a/out/auto-reload.js b/out/auto-reload.js new file mode 100644 index 0000000..1fdbfc9 --- /dev/null +++ b/out/auto-reload.js @@ -0,0 +1,61 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AutoReloader = void 0; +const express_1 = require("express"); +const _1 = require("."); +const uuid_1 = require("uuid"); +const router = express_1.Router(); +if (!_1.DefaultConfig.isProduction) { + let uuid = uuid_1.v4(); + let updateTimeout = undefined; + Promise.resolve().then(() => require("node-watch")).then((watch) => { + watch.default('public', { recursive: true }, () => { + if (updateTimeout !== undefined) + clearTimeout(updateTimeout); + updateTimeout = setTimeout(() => { + uuid = uuid_1.v4(); + }, 200); + }); + }).catch((err) => { _1.Logger.error(err); }); + router.get("/auto-reload/client.js", (req, res) => { + _1.Logger.debug(req.url, req.originalUrl, req.baseUrl); + res.setHeader('Content-Type', "application/javascript"); + // language=JavaScript + res.send(` + const loc = window.location; + const url = loc.protocol+'//'+loc.host+'${_1.urlJoin(req.baseUrl, "/auto-reload")}'; + // const parse = async res => (await res.json()).uuid; + const parse = function(res) { + return res.json() + .then(function(json) {return json.uuid;}) } + let hash = undefined; + let hadError = false; + setInterval(function() { + try { + fetch(url) + .then(function(res) { return parse(res) }) + .then(function(data) { + if (data) { + hash = hash === undefined ? data : hash; + if (hash !== data) { + window.location.reload(); + } + } + }); + } catch (e) { + if (hadError === false) { + console.log(e); + hadError = true; + } + } + }, 3000); + `.replace(/\t/g, "")); + }); + router.get("/auto-reload", (req, res) => { + req.noLogging = true; + res.json({ uuid }); + }); +} +exports.AutoReloader = { + router, +}; diff --git a/out/config.d.ts b/out/config.d.ts new file mode 100644 index 0000000..e3b7bef --- /dev/null +++ b/out/config.d.ts @@ -0,0 +1,17 @@ +declare function requireEnv(name: string, onlyInProduction?: boolean): void; +export declare const DefaultConfig: { + EXTERNAL_BASE_URL: string; + isProduction: boolean; + requireEnv: typeof requireEnv; + NODE_ENV: string; + PORT: number; + HOSTNAME: string; + BASE_PATH: string; + REDIS_URL: string | undefined; + SESSION_SECRET: string | undefined; + USERINFO_HEADER: string | undefined; + AUTH_PROXY_URL: string | undefined; + AUTH_PROXY_USERINFO_URL: string | undefined; + AUTH_PROXY_INIT_LOGOUT_URL: string | undefined; +}; +export {}; diff --git a/out/config.js b/out/config.js new file mode 100644 index 0000000..dcced38 --- /dev/null +++ b/out/config.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DefaultConfig = void 0; +const env = require("env-var"); +const _1 = require("."); +const NODE_ENV = env.get('NODE_ENV').default("development").asString(); +const isProduction = NODE_ENV === 'production'; +const envs = { + NODE_ENV, + // port of the server + PORT: env.get('PORT').default('3000').asPortNumber(), + // hostname of the server ('0.0.0.0' listens on all network interfaces) + HOSTNAME: env.get('HOSTNAME').default('0.0.0.0').asString(), + // base path + BASE_PATH: env.get('BASE_PATH').default('/').asString(), + // external base url + EXTERNAL_BASE_URL: env.get('EXTERNAL_BASE_URL').asString(), + // url of redis session store (required in production using InMemory) + REDIS_URL: env.get('REDIS_URL').asString() || undefined, + // cookie secret for the session id (required in production using Session) + SESSION_SECRET: env.get('SESSION_SECRET').asString() || undefined, + // header where user info token is stored to request auth proxy + USERINFO_HEADER: env.get('USERINFO_HEADER').asString() || undefined, + // base url to init a logout or request user info + AUTH_PROXY_URL: env.get('AUTH_PROXY_URL').asString() || undefined, + // override base url to request user info + AUTH_PROXY_USERINFO_URL: env.get('AUTH_PROXY_USERINFO_URL').asString() || undefined, + // override base url to init a logout + AUTH_PROXY_INIT_LOGOUT_URL: env.get('AUTH_PROXY_INIT_LOGOUT_URL').asString() || undefined, +}; +function requireEnv(name, onlyInProduction = false) { + env.get(name).required(!onlyInProduction || isProduction).asString(); +} +exports.DefaultConfig = Object.assign(Object.assign({}, envs), { EXTERNAL_BASE_URL: envs.EXTERNAL_BASE_URL || + _1.urlJoin(`http://${envs.HOSTNAME}${envs.PORT !== 80 ? `:${envs.PORT}` : ""}`, envs.BASE_PATH), isProduction, + requireEnv }); diff --git a/out/helpers/resolvable.d.ts b/out/helpers/resolvable.d.ts new file mode 100644 index 0000000..b5bec50 --- /dev/null +++ b/out/helpers/resolvable.d.ts @@ -0,0 +1,27 @@ +declare enum ResolvableState { + WAITING = 0, + PENDING = 1, + ERROR = 2, + DONE = 3 +} +declare class FetchOnce> { + protected fetchMethod?: ((...args: U) => Promise) | undefined; + protected data: T | undefined; + protected error: unknown | undefined; + protected state: ResolvableState; + protected pendings: [(res: Promise | T) => void, (reason: unknown) => void][]; + constructor(fetchMethod?: ((...args: U) => Promise) | undefined); + resolve(...args: U): Promise; + protected isFinished(): boolean; + protected parsePromise(promise: Promise): void; +} +export declare class Resolvable> extends FetchOnce { + constructor(fetchMethod: (...args: U) => Promise); +} +export declare class WaitForSync extends FetchOnce { + protected state: ResolvableState; + constructor(); + setData(data: T): void; + setError(error: unknown): void; +} +export {}; diff --git a/out/helpers/resolvable.js b/out/helpers/resolvable.js new file mode 100644 index 0000000..c155b3c --- /dev/null +++ b/out/helpers/resolvable.js @@ -0,0 +1,85 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WaitForSync = exports.Resolvable = void 0; +var ResolvableState; +(function (ResolvableState) { + ResolvableState[ResolvableState["WAITING"] = 0] = "WAITING"; + ResolvableState[ResolvableState["PENDING"] = 1] = "PENDING"; + ResolvableState[ResolvableState["ERROR"] = 2] = "ERROR"; + ResolvableState[ResolvableState["DONE"] = 3] = "DONE"; +})(ResolvableState || (ResolvableState = {})); +class FetchOnce { + constructor(fetchMethod) { + this.fetchMethod = fetchMethod; + this.state = ResolvableState.WAITING; + this.pendings = []; + } + resolve(...args) { + // eslint-disable-next-line promise/avoid-new + return new Promise((resolve, reject) => { + switch (this.state) { + case ResolvableState.WAITING: + this.state = ResolvableState.PENDING; + this.pendings.push([resolve, reject]); + if (this.fetchMethod) + this.parsePromise(this.fetchMethod(...args)); + break; + case ResolvableState.PENDING: + this.pendings.push([resolve, reject]); + break; + case ResolvableState.DONE: + resolve(this.data); + break; + case ResolvableState.ERROR: + reject(this.error); + break; + } + }); + } + isFinished() { + return this.state === ResolvableState.DONE || this.state === ResolvableState.ERROR; + } + parsePromise(promise) { + promise.then((data) => { + this.data = data; + this.state = ResolvableState.DONE; + this.pendings.forEach(pending => pending[0](data)); + }).catch(err => { + this.error = err; + this.state = ResolvableState.ERROR; + this.pendings.forEach(pending => pending[1](err)); + }); + } +} +class Resolvable extends FetchOnce { + constructor(fetchMethod) { + super(fetchMethod); + } +} +exports.Resolvable = Resolvable; +class WaitForSync extends FetchOnce { + constructor() { + super(undefined); + this.state = ResolvableState.PENDING; + } + setData(data) { + if (!this.isFinished()) { + this.parsePromise((() => __awaiter(this, void 0, void 0, function* () { return data; }))()); + } + } + setError(error) { + if (!this.isFinished()) { + this.parsePromise((() => __awaiter(this, void 0, void 0, function* () { throw error; }))()); + } + } +} +exports.WaitForSync = WaitForSync; diff --git a/out/helpers/urlJoin.d.ts b/out/helpers/urlJoin.d.ts new file mode 100644 index 0000000..a972bb6 --- /dev/null +++ b/out/helpers/urlJoin.d.ts @@ -0,0 +1,2 @@ +import * as properUrlJoin from 'proper-url-join'; +export declare const urlJoin: properUrlJoin.default; diff --git a/out/helpers/urlJoin.js b/out/helpers/urlJoin.js new file mode 100644 index 0000000..2103c9b --- /dev/null +++ b/out/helpers/urlJoin.js @@ -0,0 +1,5 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.urlJoin = void 0; +const properUrlJoin = require("proper-url-join"); +exports.urlJoin = properUrlJoin; diff --git a/out/helpers/userinfo.d.ts b/out/helpers/userinfo.d.ts new file mode 100644 index 0000000..eb054cb --- /dev/null +++ b/out/helpers/userinfo.d.ts @@ -0,0 +1,10 @@ +export declare type UserInfo = { + email: string; + email_verified: boolean; + family_name: string; + given_name: string; + groups: string[]; + name: string; + preferred_username: string; + sub: string; +}; diff --git a/out/helpers/userinfo.js b/out/helpers/userinfo.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/out/helpers/userinfo.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/out/index.js b/out/index.js new file mode 100644 index 0000000..9e7d2af --- /dev/null +++ b/out/index.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Permissions = exports.Session = exports.Polyfill = exports.AutoReloader = exports.urlJoin = exports.WaitForSync = exports.Resolvable = exports.AuthProxy = exports.HttpLogger = exports.Logger = exports.Redis = exports.DefaultConfig = void 0; +var config_1 = require("./config"); +Object.defineProperty(exports, "DefaultConfig", { enumerable: true, get: function () { return config_1.DefaultConfig; } }); +var redis_1 = require("./redis"); +Object.defineProperty(exports, "Redis", { enumerable: true, get: function () { return redis_1.Redis; } }); +var logging_1 = require("./logging"); +Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logging_1.Logger; } }); +Object.defineProperty(exports, "HttpLogger", { enumerable: true, get: function () { return logging_1.HttpLogger; } }); +var auth_proxy_1 = require("./auth-proxy"); +Object.defineProperty(exports, "AuthProxy", { enumerable: true, get: function () { return auth_proxy_1.AuthProxy; } }); +var resolvable_1 = require("./helpers/resolvable"); +Object.defineProperty(exports, "Resolvable", { enumerable: true, get: function () { return resolvable_1.Resolvable; } }); +Object.defineProperty(exports, "WaitForSync", { enumerable: true, get: function () { return resolvable_1.WaitForSync; } }); +var urlJoin_1 = require("./helpers/urlJoin"); +Object.defineProperty(exports, "urlJoin", { enumerable: true, get: function () { return urlJoin_1.urlJoin; } }); +var auto_reload_1 = require("./auto-reload"); +Object.defineProperty(exports, "AutoReloader", { enumerable: true, get: function () { return auto_reload_1.AutoReloader; } }); +var polyfill_1 = require("./polyfill"); +Object.defineProperty(exports, "Polyfill", { enumerable: true, get: function () { return polyfill_1.Polyfill; } }); +var session_1 = require("./session"); +Object.defineProperty(exports, "Session", { enumerable: true, get: function () { return session_1.Session; } }); +var permissions_1 = require("./permissions"); +Object.defineProperty(exports, "Permissions", { enumerable: true, get: function () { return permissions_1.Permissions; } }); diff --git a/out/logging.d.ts b/out/logging.d.ts new file mode 100644 index 0000000..f2b5179 --- /dev/null +++ b/out/logging.d.ts @@ -0,0 +1,13 @@ +import * as winston from 'winston'; +import { RequestHandler } from 'express'; +export declare const Logger: { + error: (...args: unknown[]) => winston.Logger; + http: (...args: unknown[]) => winston.Logger; + info: (...args: unknown[]) => winston.Logger; + silly: (...args: unknown[]) => winston.Logger; + warn: (...args: unknown[]) => winston.Logger; + verbose: (...args: unknown[]) => winston.Logger; + debug: (...args: unknown[]) => winston.Logger; + log: (...args: unknown[]) => winston.Logger; +}; +export declare const HttpLogger: RequestHandler; diff --git a/out/logging.js b/out/logging.js new file mode 100644 index 0000000..5509f56 --- /dev/null +++ b/out/logging.js @@ -0,0 +1,47 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HttpLogger = exports.Logger = void 0; +const winston = require("winston"); +const colors = require("colors"); +const _1 = require("."); +const prune = require("json-prune"); +const logger = winston.createLogger({ + level: _1.DefaultConfig.isProduction ? "info" : "silly", + format: winston.format.json(), + transports: [ + new winston.transports.Console({ + format: winston.format.simple(), + }), + ], +}); +const levels = ["error", "warn", "info", "http", "verbose", "debug", "silly"]; +const wrapper = (original) => { + return (...args) => { + return original(args.map((obj) => typeof obj === "string" ? obj : prune(obj)).join(" ")); + }; +}; +exports.Logger = {}; +for (const level of levels) { + exports.Logger[level] = wrapper(logger[level]); +} +exports.Logger.log = wrapper(logger["silly"]); +exports.HttpLogger = (req, res, next) => { + const start = Date.now(); + const path = req.path; + const end = res.end; + res.end = function (...args) { + const statusCode = res.statusCode; + const colorFunction = statusCode >= 500 ? colors.red + : statusCode >= 400 ? colors.yellow + : statusCode >= 300 ? colors.cyan + : statusCode >= 200 ? colors.green + : colors.gray; + const status = colorFunction(res.statusCode.toString(10)); + const method = req.method.toUpperCase().padEnd(6, " "); + const responseTime = (Date.now() - start).toString(10).padStart(3, " "); + if (!req.noLogging) + exports.Logger.http(`${status} ${method} ${responseTime}ms ${path}`); + end.apply(res, args); + }; + next(); +}; diff --git a/out/permissions.d.ts b/out/permissions.d.ts new file mode 100644 index 0000000..6a1297b --- /dev/null +++ b/out/permissions.d.ts @@ -0,0 +1,24 @@ +import { AccessControl, IQueryInfo, Permission } from 'role-acl'; +import { Query } from 'role-acl/lib/src/core/Query'; +import { Request, RequestHandler } from 'express'; +declare class PermissionManager extends AccessControl { + can(roleOrRequest: Request | string | string[] | IQueryInfo): PermQuery; + getRouter(resource: string, opts: Partial): RequestHandler; +} +export declare type RermRouterOpts = { + context: unknown; + action: string; + skipConditions: boolean; +}; +export declare class PermQuery extends Query { + protected resolveRequest: Request | undefined; + constructor(grants: unknown, roleOrRequest: Request | string | string[] | IQueryInfo); + on(resource: string, skipConditions?: boolean): Promise; + context(context: unknown): PermQuery; + skipConditions(value: boolean): PermQuery; + with(context: unknown): PermQuery; + execute(action: string): PermQuery; + sync(): PermQuery; +} +export declare const Permissions: PermissionManager; +export {}; diff --git a/out/permissions.js b/out/permissions.js new file mode 100644 index 0000000..4178f92 --- /dev/null +++ b/out/permissions.js @@ -0,0 +1,92 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Permissions = exports.PermQuery = void 0; +const role_acl_1 = require("role-acl"); +const Query_1 = require("role-acl/lib/src/core/Query"); +// see https://www.npmjs.com/package/role-acl +class PermissionManager extends role_acl_1.AccessControl { + can(roleOrRequest) { + return new PermQuery(this.getGrants(), roleOrRequest); + } + getRouter(resource, opts) { + return (req, res, next) => __awaiter(this, void 0, void 0, function* () { + let query = this.can(req); + if (opts.context) + query = query.context(opts.context); + if (opts.action) + query = query.execute(opts.action); + if (opts.skipConditions) + query = query.skipConditions(opts.skipConditions); + const permission = yield query.on(resource); + if (permission.granted) { + req.permissionDetails = permission; + next(); + } + else { + res.sendStatus(403); + } + }); + } +} +class PermQuery extends Query_1.Query { + constructor(grants, roleOrRequest) { + function isRequest(obj) { + // eslint-disable-next-line no-prototype-builtins + return typeof obj === 'object' && obj && obj.hasOwnProperty('path') || false; + } + if (isRequest(roleOrRequest)) { + super(grants, []); + this.resolveRequest = roleOrRequest; + } + else { + super(grants, roleOrRequest); + } + } + on(resource, skipConditions) { + const _super = Object.create(null, { + on: { get: () => super.on } + }); + var _a; + return __awaiter(this, void 0, void 0, function* () { + if (this.resolveRequest) { + const userInfo = yield this.resolveRequest.getUserInfo(); + this.role((_a = userInfo === null || userInfo === void 0 ? void 0 : userInfo.groups) !== null && _a !== void 0 ? _a : []); + } + if (typeof this._.role === 'object' && this._.role.includes('noaccess') || + typeof this._.role === 'string' && this._.role === 'noaccess') { + this.role([]); + } + return _super.on.call(this, resource, skipConditions); + }); + } + context(context) { + super.context(context); + return this; + } + skipConditions(value) { + super.skipConditions(value); + return this; + } + with(context) { + super.with(context); + return this; + } + execute(action) { + super.execute(action); + return this; + } + sync() { + throw new role_acl_1.AccessControlError("Sync method is not allowed on PermissionManager!"); + } +} +exports.PermQuery = PermQuery; +exports.Permissions = new PermissionManager(); diff --git a/out/polyfill-worker.d.ts b/out/polyfill-worker.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/out/polyfill-worker.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/out/polyfill-worker.js b/out/polyfill-worker.js new file mode 100644 index 0000000..ab2ec1b --- /dev/null +++ b/out/polyfill-worker.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +// workers/add.js +const worker_1 = require("threads/worker"); +const fs = require("fs"); +const polyfill_analyzer_1 = require("@10xjs/polyfill-analyzer"); +const polyfills_1 = require("@10xjs/polyfill-analyzer/dist/polyfills"); +worker_1.expose(() => { + const exclude = [ + "console.markTimeline", + "console.timeline", + "console.timelineEnd", + ]; + const featureList = polyfill_analyzer_1.analyze({ + source: fs.readFileSync('./public/js/bundle.js', 'utf-8'), + include: polyfills_1.default.filter(x => !exclude.includes(x)), + // Not all features listed by polyfillLibrary.listAllPolyfills()` can be detected. + unsupportedPolyfill: 'ignore', + }); + const feats = {}; + for (const feature of featureList) { + feats[feature] = {}; + } + return feats; +}); diff --git a/out/polyfill.js b/out/polyfill.js new file mode 100644 index 0000000..e1db6ba --- /dev/null +++ b/out/polyfill.js @@ -0,0 +1,39 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Polyfill = void 0; +const polyfillLibrary = require("polyfill-library"); +const _1 = require("."); +const threads_1 = require("threads"); +const features = new _1.WaitForSync(); +(() => __awaiter(void 0, void 0, void 0, function* () { + const worker = yield threads_1.spawn(new threads_1.Worker("./polyfill-worker")); + const feats = yield worker(); + yield threads_1.Thread.terminate(worker); + return feats; +}))() + .then(feats => { + feats["fetch"] = {}; + _1.Logger.debug("Polyfill analysed:", Object.keys(feats)); + features.setData(feats); +}) + .catch(err => features.setError(err)); +function getRouter(opts) { + const options = Object.assign({ minify: _1.DefaultConfig.isProduction, unknown: "polyfill" }, opts); + return (req, res) => __awaiter(this, void 0, void 0, function* () { + const polyfillBundle = yield polyfillLibrary.getPolyfillString(Object.assign(Object.assign({}, options), { uaString: req.header("user-agent"), features: yield features.resolve(), stream: false })); + res.setHeader('Content-Type', 'text/javascript'); + res.send(polyfillBundle); + }); +} +exports.Polyfill = { + getRouter: getRouter, +}; diff --git a/out/redis.d.ts b/out/redis.d.ts new file mode 100644 index 0000000..e28d55f --- /dev/null +++ b/out/redis.d.ts @@ -0,0 +1,9 @@ +import * as redis from 'redis'; +interface RedisType { + client: redis.RedisClient | undefined; + get(key: string): Promise; + set(key: string, value: string): Promise; + del(...keys: string[]): Promise; +} +export declare const Redis: RedisType; +export {}; diff --git a/out/redis.js b/out/redis.js new file mode 100644 index 0000000..6d0587e --- /dev/null +++ b/out/redis.js @@ -0,0 +1,76 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Redis = void 0; +const redis = require("redis"); +const _1 = require("."); +const util_1 = require("util"); +class RealRedis { + get client() { + if (this._client !== undefined) + return this._client; + _1.DefaultConfig.requireEnv('REDIS_URL'); + this._client = redis.createClient({ url: _1.DefaultConfig.REDIS_URL }); + return this._client; + } + get(key) { + return __awaiter(this, void 0, void 0, function* () { + if (!this._promGet) + this._promGet = util_1.promisify(this.client.get); + const value = yield this._promGet(key); + // eslint-disable-next-line no-null/no-null + return value === null ? undefined : value; + }); + } + set(key, value) { + return __awaiter(this, void 0, void 0, function* () { + if (!this._promSet) + this._promSet = util_1.promisify(this.client.set); + yield this._promSet(key, value); + }); + } + del(...keys) { + return __awaiter(this, void 0, void 0, function* () { + if (!this._promDel) + this._promDel = util_1.promisify(this.client.del); + return yield this._promDel(...keys); + }); + } +} +class MockRedis { + constructor() { + this.inMemory = {}; + } + get client() { + return undefined; + } + get(key) { + return __awaiter(this, void 0, void 0, function* () { + return this.inMemory[key] || undefined; + }); + } + set(key, value) { + return __awaiter(this, void 0, void 0, function* () { + this.inMemory[key] = value; + }); + } + del(...keys) { + return __awaiter(this, void 0, void 0, function* () { + for (const key of keys) { + delete this.inMemory[key]; + } + return keys.length; + }); + } +} +exports.Redis = _1.DefaultConfig.REDIS_URL || _1.DefaultConfig.isProduction + ? new RealRedis() + : new MockRedis(); diff --git a/out/session.d.ts b/out/session.d.ts new file mode 100644 index 0000000..03fd7d3 --- /dev/null +++ b/out/session.d.ts @@ -0,0 +1,7 @@ +import * as session from 'express-session'; +import { RequestHandler } from 'express'; +declare function getRouter(options?: Partial): RequestHandler; +export declare const Session: { + getRouter: typeof getRouter; +}; +export {}; diff --git a/out/session.js b/out/session.js new file mode 100644 index 0000000..e7f5889 --- /dev/null +++ b/out/session.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Session = void 0; +const session = require("express-session"); +const _1 = require("."); +const redisStore = require("connect-redis"); +let sessionStore = undefined; +function getRouter(options) { + _1.DefaultConfig.requireEnv('SESSION_SECRET', true); + if (_1.Redis.client && sessionStore !== undefined) { + const RedisStore = redisStore(session); + sessionStore = new RedisStore({ client: _1.Redis.client }); + } + return session(Object.assign({ store: sessionStore, secret: _1.DefaultConfig.SESSION_SECRET || 'keyboard cat', resave: false, saveUninitialized: true, cookie: { secure: false } }, options)); +} +exports.Session = { + getRouter, +};