@trifrost/core
Version:
Blazingly fast, runtime-agnostic server framework for modern edge and node environments
130 lines (129 loc) • 5.85 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Sym_TriFrostMiddlewareCors = void 0;
exports.Cors = Cors;
const array_1 = require("@valkyriestudios/utils/array");
const boolean_1 = require("@valkyriestudios/utils/boolean");
const number_1 = require("@valkyriestudios/utils/number");
const object_1 = require("@valkyriestudios/utils/object");
const string_1 = require("@valkyriestudios/utils/string");
const constants_1 = require("../types/constants");
/* Specific symbol attached to cors mware to identify them by */
exports.Sym_TriFrostMiddlewareCors = Symbol('TriFrost.Middleware.Cors');
const METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'CONNECT', 'TRACE'];
const METHODSSet = new Set(METHODS);
/**
* Cors defaults
*/
const CorsDefaults = {
origin: '*',
methods: ['GET', 'HEAD', 'POST'],
headers: [],
expose: [],
credentials: false,
};
/**
* CORS - Cross Origin Resource Sharing
*
* @param {TriFrostCorsOptions} options - Options to apply
* @param {TriFrostCorsConfig} config - Additional behavioral config
*
* @see origin - https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Origin
* @see methods - https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Methods
* @see headers - https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Headers
* @see expose - https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Expose-Headers
* @see credentials - https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Credentials
* @see maxage - https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Max-Age
*/
function Cors(options = {}, config) {
const use_defaults = !(0, boolean_1.isBoolean)(config?.use_defaults) ? true : config.use_defaults;
const { origin, methods, headers, expose, credentials, maxage } = use_defaults === true ? { ...CorsDefaults, ...((0, object_1.isObject)(options) && options) } : (0, object_1.isObject)(options) ? options : {};
let originWhiteList = null;
let originFn = null;
const computed = { vary: 'Origin' };
/* Access-Control-Allow-Origin */
if ((0, string_1.isNeString)(origin)) {
computed['access-control-allow-origin'] = origin;
}
else if ((0, array_1.isNeArray)(origin)) {
originWhiteList = new Set();
for (let i = 0; i < origin.length; i++) {
const val = origin[i];
if ((0, string_1.isNeString)(val))
originWhiteList.add(val.trim());
}
if (!originWhiteList.size)
throw new Error('TriFrostMiddleware@Cors: Invalid origin');
originFn = reqOrigin => {
if (typeof reqOrigin === 'string' && originWhiteList.has(reqOrigin))
return reqOrigin;
return null;
};
}
else if (typeof origin === 'function') {
originFn = origin;
}
/* Access-Control-Allow-Methods */
if ((0, array_1.isNeArray)(methods)) {
const normalized = new Set();
for (let i = 0; i < methods.length; i++) {
const method = methods[i];
if (!METHODSSet.has(method))
throw new Error('TriFrostMiddleware@Cors: Invalid method "' + method + '"');
normalized.add(method);
}
computed['access-control-allow-methods'] = [...normalized].join(', ');
}
else if (methods === '*') {
computed['access-control-allow-methods'] = '*';
}
/* Access-Control-Allow-Headers */
if ((0, array_1.isNeArray)(headers)) {
const normalized = new Set();
for (let i = 0; i < headers.length; i++) {
const el = headers[i];
if (!(0, string_1.isNeString)(el))
throw new Error('TriFrostMiddleware@Cors: Invalid header "' + el + '"');
normalized.add(el.trim());
}
computed['access-control-allow-headers'] = [...normalized.values()].join(', ');
}
/* Access-Control-Expose-Headers */
if ((0, array_1.isNeArray)(expose)) {
const normalized = new Set();
for (let i = 0; i < expose.length; i++) {
const el = expose[i];
if (!(0, string_1.isNeString)(el))
throw new Error('TriFrostMiddleware@Cors: Invalid expose "' + el + '"');
normalized.add(el.trim());
}
computed['access-control-expose-headers'] = [...normalized.values()].join(', ');
}
/* Access-Control-Allow-Credentials */
if (credentials === true) {
computed['access-control-allow-credentials'] = 'true';
}
/* Access-Control-Max-Age */
if ((0, number_1.isIntGte)(maxage, 0)) {
computed['access-control-max-age'] = `${maxage}`;
}
/* Baseline Middleware function */
const mware = function TriFrostCorsMiddleware(ctx) {
/* Add computed headers */
ctx.setHeaders(computed);
/* If origin is function we should check to see if we need to add it */
if (originFn) {
const value = originFn(ctx.headers.origin ?? ctx.headers.Origin ?? null);
if (value !== null)
ctx.setHeader('access-control-allow-origin', value);
}
/* If it's an options call simply return a 204 status */
if (ctx.method === constants_1.HttpMethods.OPTIONS)
return ctx.status(204);
};
/* Add symbols for introspection/use further down the line */
Reflect.set(mware, constants_1.Sym_TriFrostName, 'TriFrostCors');
Reflect.set(mware, constants_1.Sym_TriFrostDescription, 'Middleware for Cross Origin Resource Sharing');
Reflect.set(mware, constants_1.Sym_TriFrostFingerPrint, exports.Sym_TriFrostMiddlewareCors);
return mware;
}