Socket
Socket
Sign inDemoInstall

@mocks-server/core

Package Overview
Dependencies
215
Maintainers
1
Versions
52
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.5.3 to 3.0.0

src/files-loader/FilesLoader.js

6

index.js

@@ -12,7 +12,3 @@ /*

const Core = require("./src/Core");
const Behavior = require("./src/mocks-legacy/Behavior");
module.exports = {
Core,
Behavior,
};
module.exports = Core;
{
"name": "@mocks-server/core",
"version": "2.5.3",
"version": "3.0.0",
"description": "Pluggable mock server supporting multiple route variants and mocks",

@@ -23,3 +23,7 @@ "keywords": [

"license": "Apache-2.0",
"repository": "https://github.com/mocks-server/main",
"repository": {
"type": "git",
"url": "https://github.com/mocks-server/main.git",
"directory": "packages/core"
},
"homepage": "https://www.mocks-server.org",

@@ -36,24 +40,23 @@ "publishConfig": {

"dependencies": {
"@babel/register": "7.16.9",
"@babel/register": "7.17.7",
"@hapi/boom": "9.1.4",
"ajv": "8.9.0",
"ajv-errors": "3.0.0",
"body-parser": "1.19.2",
"commander": "8.3.0",
"@mocks-server/config": "1.0.0",
"ajv": "8.11.0",
"better-ajv-errors": "1.2.0",
"body-parser": "1.20.0",
"cors": "2.8.5",
"express": "4.17.2",
"deepmerge": "4.2.2",
"express": "4.18.1",
"express-request-id": "1.4.1",
"fs-extra": "10.0.0",
"fs-extra": "10.1.0",
"globule": "1.3.3",
"handlebars": "4.7.7",
"is-promise": "4.0.0",
"lodash": "4.17.21",
"md5": "2.3.0",
"node-watch": "0.7.3",
"require-all": "3.0.0",
"route-parser": "0.0.5",
"winston": "3.5.0",
"winston-array-transport": "1.1.7"
"winston": "3.7.2",
"winston-array-transport": "1.1.9"
},
"engines": {
"node": ">=12.x"
"node": ">=14.x"
},

@@ -63,3 +66,3 @@ "scripts": {

},
"readme": "<p align=\"center\"><a href=\"https://mocks-server.org\" target=\"_blank\" rel=\"noopener noreferrer\"><img width=\"120\" src=\"https://www.mocks-server.org/img/logo_120.png\" alt=\"Mocks Server logo\"></a></p>\n\n<p align=\"center\">\n <a href=\"https://github.com/mocks-server/main/actions?query=workflow%3Abuild+branch%3Amaster\"><img src=\"https://github.com/mocks-server/main/workflows/build/badge.svg?branch=master\" alt=\"Build Status\"></a>\n <a href=\"https://codecov.io/gh/mocks-server/main\"><img src=\"https://codecov.io/gh/mocks-server/main/branch/master/graph/badge.svg?token=2S8ZR55AJV\" alt=\"Coverage\"></a>\n <a href=\"https://sonarcloud.io/project/overview?id=mocks-server_main_core\"><img src=\"https://sonarcloud.io/api/project_badges/measure?project=mocks-server_main_core&metric=alert_status\" alt=\"Quality Gate\"></a>\n <a href=\"https://www.npmjs.com/package/@mocks-server/core\"><img src=\"https://img.shields.io/npm/dm/@mocks-server/core.svg\" alt=\"Downloads\"></a>\n <a href=\"https://renovatebot.com\"><img src=\"https://img.shields.io/badge/renovate-enabled-brightgreen.svg\" alt=\"Renovate\"></a>\n <a href=\"https://github.com/mocks-server/main/blob/master/packages/core/LICENSE\"><img src=\"https://img.shields.io/npm/l/@mocks-server/core.svg\" alt=\"License\"></a>\n</p>\n\n---\n\n# Mocks Server Core\n\nThis package provides the plugabble core of the [Mocks Server][website-url] project.\n\nUse it if you want to start Mocks Server programmatically by your own, or even create your own Mocks Server distribution with your desired plugins. If not, you would probably prefer using the [main distribution of the Mocks Server project][main-url].\n\n## Documentation\n\nPlease refer to the [project documentation website][website-url]:\n\n* [Get started](https://www.mocks-server.org/docs/get-started-intro)\n* [Guides](https://www.mocks-server.org/docs/guides-organizing-files)\n* [Configuration](https://www.mocks-server.org/docs/configuration-options)\n* [Plugins](https://www.mocks-server.org/docs/plugins-adding-plugins)\n* [Integrations](https://www.mocks-server.org/docs/integrations-cypress)\n* [API](https://www.mocks-server.org/docs/api-mocks-server-api)\n\n[website-url]: https://www.mocks-server.org\n[logo-url]: https://www.mocks-server.org/img/logo_120.png\n[main-url]: https://www.npmjs.com/package/@mocks-server/main\n"
"readme": "<p align=\"center\"><a href=\"https://mocks-server.org\" target=\"_blank\" rel=\"noopener noreferrer\"><img width=\"120\" src=\"https://www.mocks-server.org/img/logo_120.png\" alt=\"Mocks Server logo\"></a></p>\n\n<p align=\"center\">\n <a href=\"https://github.com/mocks-server/main/actions?query=workflow%3Abuild+branch%3Amaster\"><img src=\"https://github.com/mocks-server/main/workflows/build/badge.svg?branch=master\" alt=\"Build Status\"></a>\n <a href=\"https://codecov.io/gh/mocks-server/main\"><img src=\"https://codecov.io/gh/mocks-server/main/branch/master/graph/badge.svg?token=2S8ZR55AJV\" alt=\"Coverage\"></a>\n <a href=\"https://sonarcloud.io/project/overview?id=mocks-server_main_core\"><img src=\"https://sonarcloud.io/api/project_badges/measure?project=mocks-server_main_core&metric=alert_status\" alt=\"Quality Gate\"></a>\n <a href=\"https://www.npmjs.com/package/@mocks-server/core\"><img src=\"https://img.shields.io/npm/dm/@mocks-server/core.svg\" alt=\"Downloads\"></a>\n <a href=\"https://renovatebot.com\"><img src=\"https://img.shields.io/badge/renovate-enabled-brightgreen.svg\" alt=\"Renovate\"></a>\n <a href=\"https://github.com/mocks-server/main/blob/master/packages/core/LICENSE\"><img src=\"https://img.shields.io/npm/l/@mocks-server/core.svg\" alt=\"License\"></a>\n</p>\n\n---\n\n# Mocks Server Core\n\nThis package provides the plugabble core of the [Mocks Server][website-url] project.\n\nUse it if you want to start Mocks Server programmatically by your own, or even create your own Mocks Server distribution with the plugins of your choice. If this is not the case, you would probably prefer using the [main distribution of the Mocks Server project][main-url].\n\n## Documentation\n\nPlease refer to the [project documentation website][website-url]:\n\n* [Get started](https://www.mocks-server.org/docs/get-started-intro)\n* [Guides](https://www.mocks-server.org/docs/guides-organizing-files)\n* [Configuration](https://www.mocks-server.org/docs/configuration-options)\n* [Plugins](https://www.mocks-server.org/docs/plugins-adding-plugins)\n* [Integrations](https://www.mocks-server.org/docs/integrations-cypress)\n* [API](https://www.mocks-server.org/docs/api-mocks-server-api)\n\n[website-url]: https://www.mocks-server.org\n[logo-url]: https://www.mocks-server.org/img/logo_120.png\n[main-url]: https://www.npmjs.com/package/@mocks-server/main\n"
}

@@ -18,3 +18,3 @@ <p align="center"><a href="https://mocks-server.org" target="_blank" rel="noopener noreferrer"><img width="120" src="https://www.mocks-server.org/img/logo_120.png" alt="Mocks Server logo"></a></p>

Use it if you want to start Mocks Server programmatically by your own, or even create your own Mocks Server distribution with your desired plugins. If not, you would probably prefer using the [main distribution of the Mocks Server project][main-url].
Use it if you want to start Mocks Server programmatically by your own, or even create your own Mocks Server distribution with the plugins of your choice. If this is not the case, you would probably prefer using the [main distribution of the Mocks Server project][main-url].

@@ -21,0 +21,0 @@ ## Documentation

@@ -13,3 +13,3 @@ // Use this file only as a guide for first steps using middlewares. Delete it when no more needed.

id: "enabled",
response: (req, res, next, mocksServer) => {
response: (_req, res, next, mocksServer) => {
res.set("x-mocks-server-example", "some-value");

@@ -24,3 +24,3 @@ mocksServer.tracer.info(

id: "disabled",
response: (req, res, next) => next(),
response: (_req, _res, next) => next(),
},

@@ -27,0 +27,0 @@ ],

@@ -11,23 +11,11 @@ /*

const deepMerge = require("deepmerge");
const EventEmitter = require("events");
const {
INIT,
START,
STOP,
CHANGE_LEGACY_MOCKS,
CHANGE_MOCKS,
CHANGE_SETTINGS,
CHANGE_ALERTS,
LOAD_LEGACY_MOCKS,
LOAD_MOCKS,
LOAD_ROUTES,
} = require("./eventNames");
const Config = require("@mocks-server/config");
const { CHANGE_MOCKS, CHANGE_ALERTS } = require("./eventNames");
const tracer = require("./tracer");
const Config = require("./Config");
const Orchestrator = require("./Orchestrator");
const Loaders = require("./Loaders");
const Alerts = require("./Alerts");
const RoutesHandlers = require("./routes-handlers/RoutesHandlers");

@@ -37,11 +25,46 @@ const Mocks = require("./mocks/Mocks");

const Server = require("./server/Server");
const LegacyMocks = require("./mocks-legacy/Mocks");
const Settings = require("./settings/Settings");
const FilesLoader = require("./files-loader/FilesLoader");
const { scopedAlertsMethods, addEventListener } = require("./support/helpers");
const { scopedAlertsMethods, addEventListener, arrayMerge } = require("./support/helpers");
const Scaffold = require("./scaffold/Scaffold");
const MODULE_NAME = "mocks";
const CONFIG_PLUGINS_NAMESPACE = "plugins";
const CONFIG_MOCKS_NAMESPACE = "mocks";
const CONFIG_SERVER_NAMESPACE = "server";
const CONFIG_FILES_LOADER = "files";
const ROOT_OPTIONS = [
{
description: "Log level. Can be one of silly, debug, verbose, info, warn or error",
name: "log",
type: "string",
default: "info",
},
{
description: "Array of RouteHandlers to be added",
name: "routesHandlers",
type: "array",
default: [],
},
];
class Core {
constructor(programmaticConfig) {
constructor(programmaticConfig = {}) {
this._programmaticConfig = programmaticConfig;
this._eventEmitter = new EventEmitter();
this._loadedMocks = false;
this._loadedRoutes = false;
this._config = new Config({ moduleName: MODULE_NAME });
this._configPlugins = this._config.addNamespace(CONFIG_PLUGINS_NAMESPACE);
this._configMocks = this._config.addNamespace(CONFIG_MOCKS_NAMESPACE);
this._configServer = this._config.addNamespace(CONFIG_SERVER_NAMESPACE);
this._configFilesLoader = this._config.addNamespace(CONFIG_FILES_LOADER);
[this._logOption, this._routesHandlersOption] = this._config.addOptions(ROOT_OPTIONS);
this._logOption.onChange(tracer.set);
this._alerts = new Alerts({

@@ -53,12 +76,8 @@ onChange: (alerts) => {

// TODO, remove v1 legacy code
this._legacyMocksLoaders = new Loaders({
onLoad: () => {
this._eventEmitter.emit(LOAD_LEGACY_MOCKS);
},
});
this._mocksLoaders = new Loaders({
onLoad: () => {
this._eventEmitter.emit(LOAD_MOCKS);
this._loadedMocks = true;
if (this._loadedRoutes) {
this._mocks.load();
}
},

@@ -69,19 +88,12 @@ });

onLoad: () => {
this._eventEmitter.emit(LOAD_ROUTES);
this._loadedRoutes = true;
if (this._loadedMocks) {
this._mocks.load();
}
},
});
this._config = new Config({
programmaticConfig,
...scopedAlertsMethods("config", this._alerts.add, this._alerts.remove),
});
// TODO, refactor. Pass specific callbacks instead of objects
this._settings = new Settings(this._eventEmitter, this._config);
this._plugins = new Plugins(
{
createLegacyMocksLoader: () => {
return this._legacyMocksLoaders.new();
},
config: this._configPlugins,
createMocksLoader: () => {

@@ -107,6 +119,5 @@ return this._mocksLoaders.new();

{
config: this._configMocks,
getLoadedMocks: () => this._mocksLoaders.contents,
getLoadedRoutes: () => this._routesLoaders.contents,
getCurrentMock: () => this._settings.get("mock"),
getDelay: () => this._settings.get("delay"),
onChange: () => this._eventEmitter.emit(CHANGE_MOCKS),

@@ -120,16 +131,7 @@ ...scopedAlertsMethods(

},
this //To be used only by routeHandlers
this // To be used only by routeHandlers
);
// TODO, remove v1 legacy code
this._legacyMocks = new LegacyMocks(
this._eventEmitter,
this._settings,
this._legacyMocksLoaders,
this,
scopedAlertsMethods("legacy-mocks", this._alerts.add, this._alerts.remove)
);
// TODO, refactor. Pass specific callbacks instead of objects
this._server = new Server(this._eventEmitter, this._settings, this._legacyMocks, this, {
this._server = new Server({
config: this._configServer,
mocksRouter: this._mocks.router,

@@ -139,10 +141,19 @@ ...scopedAlertsMethods("server", this._alerts.add, this._alerts.remove),

// TODO, refactor. Pass specific callbacks instead of objects
this._orchestrator = new Orchestrator(
this._eventEmitter,
this._legacyMocks,
this._server,
this._mocks
);
this._filesLoader = new FilesLoader({
config: this._configFilesLoader,
loadMocks: this._mocksLoaders.new(),
loadRoutes: this._routesLoaders.new(),
...scopedAlertsMethods("files", this._alerts.add, this._alerts.remove),
});
this._scaffold = new Scaffold({
config: this._config, // It needs the whole configuration to get option properties and create scaffold
...scopedAlertsMethods(
"scaffold",
this._alerts.add,
this._alerts.remove,
this._alerts.rename
),
});
this._inited = false;

@@ -173,29 +184,44 @@ this._stopPluginsPromise = null;

async init(options) {
async init(programmaticConfig) {
if (this._inited) {
// in case it has been initializated manually before
return Promise.resolve();
}
await this._config.init(options);
this._inited = true;
// Register plugins, let them add their custom settings
await this._plugins.register(this._config.coreOptions.plugins);
if (programmaticConfig) {
this._programmaticConfig = deepMerge(this._programmaticConfig, programmaticConfig, {
arrayMerge,
});
}
// Init config
await this._config.init(this._programmaticConfig);
tracer.set(this._logOption.value);
// Register plugins, let them add their custom config
await this._plugins.register();
// Register routes handlers
await this._routesHandlers.register(this._config.coreOptions.routesHandlers);
// Init settings, read command line arguments, etc.
await this._settings.init(this._config.options);
// Settings are ready, init all
await this._routesHandlers.register(this._routesHandlersOption.value);
await this._scaffold.init({
filesLoaderPath: this._filesLoader.path,
});
// load config
await this._config.load();
// Config is ready, init all
this._mocks.init(this._routesHandlers.handlers);
await this._legacyMocks.init();
await this._server.init();
return this._plugins.init().then(() => {
this._eventEmitter.emit(INIT, this);
});
await this._filesLoader.init();
return this._plugins.init();
}
async start() {
await this.init(); // in case it has not been initializated manually before
await this.init();
await this._server.start();
return this._startPlugins().then(() => {
this._eventEmitter.emit(START, this);
});
this._filesLoader.start();
return this._startPlugins();
}

@@ -205,11 +231,6 @@

await this._server.stop();
return this._stopPlugins().then(() => {
this._eventEmitter.emit(STOP, this);
});
this._filesLoader.stop();
return this._stopPlugins();
}
addSetting(option) {
return this._settings.addCustom(option);
}
addRoutesHandler(RoutesHandler) {

@@ -225,6 +246,2 @@ this._routesHandlers.add(RoutesHandler);

onChangeSettings(listener) {
return addEventListener(listener, CHANGE_SETTINGS, this._eventEmitter);
}
onChangeAlerts(listener) {

@@ -254,10 +271,2 @@ return addEventListener(listener, CHANGE_ALERTS, this._eventEmitter);

get settings() {
return this._settings;
}
get lowLevelConfig() {
return { ...this._config.coreOptions };
}
get mocks() {

@@ -271,23 +280,7 @@ return this._mocks;

// TODO, remove v1 legacy code
addFixturesHandler(Handler) {
return this._legacyMocks.addFixturesHandler(Handler);
get config() {
return this._config;
}
// TODO, remove v1 legacy code
onChangeLegacyMocks(listener) {
return addEventListener(listener, CHANGE_LEGACY_MOCKS, this._eventEmitter);
}
// TODO, remove v1 legacy code
get behaviors() {
return this._legacyMocks.behaviors;
}
// TODO, remove v1 legacy code
get fixtures() {
return this._legacyMocks.fixtures;
}
}
module.exports = Core;

@@ -12,12 +12,4 @@ /*

module.exports = {
INIT: "init",
START: "start",
STOP: "stop",
LOAD_LEGACY_MOCKS: "load:mocks:legacy",
LOAD_MOCKS: "load:mocks",
LOAD_ROUTES: "load:routes",
CHANGE_LEGACY_MOCKS: "change:mocks:legacy",
CHANGE_MOCKS: "change:mocks",
CHANGE_SETTINGS: "change:settings",
CHANGE_ALERTS: "change:alerts",
};

@@ -78,3 +78,3 @@ /*

`${alertScope}:from`,
`Mock with invalid "from" property detected, "${mock.from}" was not found`
`Mock with invalid 'from' property detected, '${mock.from}' was not found`
);

@@ -161,3 +161,3 @@ }

newCustomVariants.forEach((customVariantId, index) => {
// TODO, use better method, store base id and variant id in an object to avoid conflicts with possible routes ids containing :
// TODO, use better method, store base id and variant id in an object to avoid conflicts with possible routes ids containing ":"
if (!inserted && customVariantId.split(":")[0] === variantId.split(":")[0]) {

@@ -258,3 +258,3 @@ newCustomVariants.splice(index, 1, variantId);

`${alertScope}:duplicated`,
`Route with duplicated id "${route.id}" detected. It has been ignored`
`Route with duplicated id '${route.id}' detected. It has been ignored`
);

@@ -281,3 +281,3 @@ return null;

`${alertScope}:variant:${variantIndex}:duplicated`,
`Route variant with duplicated id "${variantHandler.id}" detected in route "${route.id}". It has been ignored`
`Route variant with duplicated id '${variantHandler.id}' detected in route '${route.id}'. It has been ignored`
);

@@ -369,3 +369,3 @@ return null;

`process:mocks:${index}:duplicated`,
`Mock with duplicated id "${mock.id}" detected. It has been ignored`
`Mock with duplicated id '${mock.id}' detected. It has been ignored`
);

@@ -372,0 +372,0 @@ return null;

@@ -33,3 +33,3 @@ /*

const httpMethod = HTTP_METHODS[method.toUpperCase()];
this._router[httpMethod](routeVariant.url, (req, res, next) => {
this._router[httpMethod](routeVariant.url, (_req, _res, next) => {
const delay = routeVariant.delay !== null ? routeVariant.delay : this._getDelay();

@@ -36,0 +36,0 @@ if (delay > 0) {

@@ -12,4 +12,24 @@ /*

const express = require("express");
const tracer = require("../tracer");
const OPTIONS = [
{
description: "Selected mock",
name: "selected",
type: "string",
extraData: {
scaffold: {
commented: false,
},
},
},
{
description: "Global delay to apply to routes",
name: "delay",
type: "number",
default: 0,
},
];
const {

@@ -24,21 +44,15 @@ getPlainMocks,

} = require("./helpers");
const { getIds, compileRouteValidator, catchInitValidatorError } = require("./validations");
const { getIds, compileRouteValidator } = require("./validations");
class Mocks {
constructor(
{
getLoadedMocks,
getLoadedRoutes,
getCurrentMock,
getDelay,
onChange,
addAlert,
removeAlerts,
},
{ config, getLoadedMocks, getLoadedRoutes, onChange, addAlert, removeAlerts },
core
) {
this._config = config;
[this._currentMockOption, this._currentDelayOption] = this._config.addOptions(OPTIONS);
this._currentMockOption.onChange(this._setCurrent.bind(this));
this._getLoadedMocks = getLoadedMocks;
this._getLoadedRoutes = getLoadedRoutes;
this._getCurrentMock = getCurrentMock;
this._getDelay = getDelay;
this._onChange = onChange;

@@ -61,4 +75,9 @@ this._addAlert = addAlert;

this.router = this.router.bind(this);
this.getDelay = this.getDelay.bind(this);
}
getDelay() {
return this._currentDelayOption.value;
}
_reloadRouter() {

@@ -83,3 +102,3 @@ if (this._customVariantsMock) {

routeVariants: this._routesVariants,
getGlobalDelay: this._getDelay,
getGlobalDelay: this.getDelay,
});

@@ -110,14 +129,6 @@ }

this._plainMocks = getPlainMocks(this._mocks, this._mocksDefinitions);
this.current = this._getCurrentMock();
this.current = this._currentMockOption.value;
}
init(routesHandlers) {
const initValidationError = catchInitValidatorError();
if (initValidationError) {
this._addAlert(
"validation:init",
"Error loading ajv-errors dependency, validations won't be executed. Visit https://mocks-server.org/docs/how-to-fix-ajv-errors-installation for further info.",
initValidationError
);
}
compileRouteValidator(routesHandlers);

@@ -131,3 +142,3 @@ this._routesVariantsHandlers = routesHandlers;

set current(id) {
_setCurrent(id) {
tracer.verbose(`Trying to set current mock as "${id}"`);

@@ -171,2 +182,6 @@ let current;

set current(id) {
this._setCurrent(id);
}
_stopUsingVariants() {

@@ -188,3 +203,3 @@ this._customVariants = [];

routeVariants: this._routesVariants,
getGlobalDelay: this._getDelay,
getGlobalDelay: this.getDelay,
addAlert: this._addAlert,

@@ -191,0 +206,0 @@ removeAlerts: this._removeAlerts,

@@ -13,2 +13,3 @@ /*

const { compact } = require("lodash");
const betterAjvErrors = require("better-ajv-errors").default;

@@ -44,7 +45,5 @@ const ajv = new Ajv({ allErrors: true });

type: "string",
errorMessage: 'Property "id" should be string',
},
from: {
type: "string",
errorMessage: 'Property "from" should be string',
},

@@ -57,17 +56,6 @@ routesVariants: {

},
errorMessage: 'Property "routesVariants" should be an array of strings with unique items',
},
},
required: ["id", "routesVariants"],
additionalProperties: {
not: true,
errorMessage: "Extra property ${0#} is not allowed",
},
errorMessage: {
type: "Should be an object",
required: {
id: 'Should have a string property "id"',
routesVariants: 'Should have a property "routesVariants"',
},
},
additionalProperties: false,
};

@@ -80,3 +68,2 @@

type: "string",
errorMessage: 'Property "id" should be string',
},

@@ -92,3 +79,2 @@ url: {

],
errorMessage: 'Property "url" should be a string or a RegExp',
},

@@ -110,5 +96,2 @@ method: {

],
errorMessage: `Property "method" should be a string or an array with unique items. Allowed values for "method" are "${validHttpMethods.join(
","
)}"`,
},

@@ -118,3 +101,2 @@ delay: {

minimum: 0,
errorMessage: `Property "delay" should be a positive integer`,
},

@@ -128,3 +110,2 @@ variants: {

type: "string",
errorMessage: 'Property "id" should be string in variant ${1#}',
},

@@ -134,3 +115,2 @@ handler: {

enum: [], // this enum is defined when validator is compiled
errorMessage: "", // this error message is defined when validator is compiled
},

@@ -147,77 +127,16 @@ delay: {

],
errorMessage:
'Property "delay" should be a positive integer or "null" in variant ${1#}',
},
},
required: ["id"],
errorMessage: {
type: "Variant ${0#} should be an object",
required: {
id: 'Should have a string property "id" in variant ${0#}',
},
},
},
errorMessage: `Property "variants" should be an array`,
},
},
required: ["id", "url", "method", "variants"],
additionalProperties: {
not: true,
errorMessage: "Extra property ${0#} is not allowed",
},
errorMessage: {
type: "Should be an object",
required: {
id: 'Should have a string property "id"',
url: 'Should have a property "url"',
method: 'Should have a property "method"',
variants: 'Should have a property "variants"',
},
},
additionalProperties: false,
};
let validatorInited, mockValidator, routeValidator, originalMockValidator, originalRouteValidator;
const mockValidator = ajv.compile(mocksSchema);
function undoInitValidator() {
if (validatorInited) {
validatorInited = false;
originalMockValidator = mockValidator;
mockValidator = null;
originalRouteValidator = routeValidator;
routeValidator = null;
}
}
let routeValidator, routeSchema;
function restoreValidator() {
if (!validatorInited) {
validatorInited = true;
mockValidator = originalMockValidator;
routeValidator = originalRouteValidator;
}
}
function fooValidator() {
return true;
}
function initValidator() {
const initAjvErrors = require("ajv-errors");
initAjvErrors(ajv, { singleError: true, keepErrors: false });
mockValidator = ajv.compile(mocksSchema);
validatorInited = true;
}
function catchInitValidatorError() {
let error = null;
if (!validatorInited) {
try {
initValidator();
} catch (err) {
error = err;
mockValidator = fooValidator;
}
}
return error;
}
function getIds(objs) {

@@ -228,13 +147,7 @@ return objs.map((obj) => obj.id);

function compileRouteValidator(routesHandlers) {
if (validatorInited) {
const supportedRouteHandlersIds = getIds(routesHandlers);
const schema = { ...routesSchema };
schema.properties.variants.items.properties.handler.enum = supportedRouteHandlersIds;
schema.properties.variants.items.properties.handler.errorMessage = `Property "handler" should be one of "${supportedRouteHandlersIds.join(
","
)}" in variant \${1#}`;
routeValidator = ajv.compile(schema);
} else {
routeValidator = fooValidator;
}
const supportedRouteHandlersIds = getIds(routesHandlers);
const schema = { ...routesSchema };
schema.properties.variants.items.properties.handler.enum = supportedRouteHandlersIds;
routeSchema = { ...schema };
routeValidator = ajv.compile(schema);
}

@@ -258,3 +171,3 @@

function validationSingleMessage(errors) {
function customValidationSingleMessage(errors) {
return errors

@@ -270,2 +183,17 @@ .reduce((messages, error) => {

function validationSingleMessage(schema, data, errors) {
const formattedJson = betterAjvErrors(schema, data, errors, {
format: "js",
});
return formattedJson.map((result) => result.error).join(". ");
}
function routeValidationMessage(data, errors) {
return validationSingleMessage(routeSchema, data, errors);
}
function mockValidationMessage(data, errors) {
return validationSingleMessage(mocksSchema, data, errors);
}
function findRouteVariantByVariantId(routesVariants, variantId) {

@@ -275,5 +203,11 @@ return routesVariants.find((routeVariant) => routeVariant.variantId === variantId);

function traceId(id) {
return `with id '${id}'`;
}
function notFoundRouteVariantMessage(variantId) {
return {
message: `routeVariant with id "${variantId}" was not found, use a valid "routeId:variantId" identifier`,
message: `routeVariant ${traceId(
variantId
)} was not found, use a valid 'routeId:variantId' identifier`,
};

@@ -284,3 +218,3 @@ }

return {
message: `route with id "${routeId}" is used more than once in the same mock`,
message: `route ${traceId(routeId)} is used more than once in the same mock`,
};

@@ -314,5 +248,5 @@ }

function mockErrorsMessage(mock, errors) {
const idTrace = mock && mock.id ? `with id "${mock.id}" ` : "";
return `Mock ${idTrace}is invalid: ${validationSingleMessage(errors)}`;
function mockErrorsMessagePrefix(mock) {
const idTrace = mock && mock.id ? `${traceId(mock.id)} ` : "";
return `Mock ${idTrace}is invalid:`;
}

@@ -324,3 +258,5 @@

return {
message: mockErrorsMessage(mock, invalidRouteVariants.errors),
message: `${mockErrorsMessagePrefix(mock)} ${customValidationSingleMessage(
invalidRouteVariants.errors
)}`,
errors: invalidRouteVariants.errors,

@@ -336,3 +272,6 @@ };

return {
message: mockErrorsMessage(mock, mockValidator.errors),
message: `${mockErrorsMessagePrefix(mock)} ${mockValidationMessage(
mock,
mockValidator.errors
)}`,
errors: mockValidator.errors,

@@ -347,5 +286,8 @@ };

if (!isValid) {
const idTrace = route && route.id ? `with id "${route.id}" ` : "";
const idTrace = route && route.id ? `${traceId(route.id)} ` : "";
return {
message: `Route ${idTrace}is invalid: ${validationSingleMessage(routeValidator.errors)}`,
message: `Route ${idTrace}is invalid: ${routeValidationMessage(
route,
routeValidator.errors
)}`,
errors: routeValidator.errors,

@@ -358,3 +300,3 @@ };

function variantValidationErrors(route, variant, Handler) {
if (!Handler.validationSchema || !validatorInited) {
if (!Handler.validationSchema) {
return null;

@@ -365,7 +307,11 @@ }

if (!isValid) {
const idTrace = variant && variant.id ? `with id "${variant.id}" ` : "";
const idTrace = variant && variant.id ? `${traceId(variant.id)} ` : "";
return {
message: `Variant ${idTrace}in route with id "${
message: `Variant ${idTrace}in route ${traceId(
route.id
}" is invalid: ${validationSingleMessage(variantValidator.errors)}`,
)} is invalid: ${validationSingleMessage(
Handler.validationSchema,
variant,
variantValidator.errors
)}`,
errors: variantValidator.errors,

@@ -387,7 +333,3 @@ };

mockRouteVariantsValidationErrors,
catchInitValidatorError,
initValidator,
// use only for testing
undoInitValidator,
restoreValidator,
customValidationSingleMessage,
};

@@ -16,20 +16,22 @@ /*

// FilesLoader built-in plugin
const FilesLoaderLegacy = require("./files-loader-legacy/FilesLoaderLegacy");
const FilesLoader = require("./files-loader/FilesLoader");
const { scopedAlertsMethods } = require("../support/helpers");
const OPTIONS = [
{
description: "Plugins to be registered",
name: "register",
type: "array",
default: [],
},
];
class Plugins {
constructor(
{
addAlert,
removeAlerts,
renameAlerts,
createLegacyMocksLoader,
createMocksLoader,
createRoutesLoader,
},
{ config, addAlert, removeAlerts, renameAlerts, createMocksLoader, createRoutesLoader },
core
) {
this._config = config;
[this._pluginsToRegister] = this._config.addOptions(OPTIONS);
this._addAlert = addAlert;

@@ -40,6 +42,6 @@ this._removeAlerts = removeAlerts;

this._createRoutesLoader = createRoutesLoader;
this._createLegacyMocksLoader = createLegacyMocksLoader;
this._core = core;
this._pluginsInstances = [];
this._pluginsMethods = [];
this._pluginsOptions = [];
this._pluginsRegistered = 0;

@@ -51,6 +53,4 @@ this._pluginsInitialized = 0;

register(plugins = []) {
this._plugins = plugins;
this._plugins.unshift(FilesLoader);
this._plugins.unshift(FilesLoaderLegacy);
register() {
this._plugins = this._pluginsToRegister.value;
return this._registerPlugins().then(() => {

@@ -86,13 +86,16 @@ tracer.verbose(`Registered ${this._pluginsRegistered} plugins without errors`);

pluginDisplayName(index) {
return (this._pluginsInstances[index] && this._pluginsInstances[index].displayName) || index;
_pluginId(index) {
const plugin = this._pluginsInstances[index];
if (!plugin) {
return index;
}
if (plugin.constructor.id) {
return plugin.constructor.id;
}
return plugin.id || index;
}
_catchRegisterError(error, index) {
const pluginDisplayName = this.pluginDisplayName(index);
this._addAlert(
`register:${pluginDisplayName}`,
`Error registering plugin "${pluginDisplayName}"`,
error
);
const pluginId = this._pluginId(index);
this._addAlert(`register:${pluginId}`, `Error registering plugin "${pluginId}"`, error);
return {};

@@ -102,9 +105,17 @@ }

_registerPlugin(Plugin, pluginMethods, pluginIndex) {
let pluginInstance;
let pluginInstance, pluginConfig;
const pluginOptions = { core: this._core, ...pluginMethods };
if (isObject(Plugin) && !isFunction(Plugin)) {
pluginInstance = Plugin;
this._pluginsInstances.push(pluginInstance);
this._pluginsRegistered++;
} else {
try {
pluginInstance = new Plugin(this._core, pluginMethods);
if (Plugin.id) {
pluginConfig = this._config.addNamespace(Plugin.id);
}
const pluginFinalOptions = { ...pluginOptions, config: pluginConfig };
pluginInstance = new Plugin(pluginFinalOptions);
this._pluginsOptions.push(pluginFinalOptions);
this._pluginsInstances.push(pluginInstance);
this._pluginsRegistered++;

@@ -115,3 +126,4 @@ } catch (error) {

const pluginFunc = Plugin;
pluginInstance = pluginFunc(this._core, pluginMethods) || {};
pluginInstance = pluginFunc(pluginOptions) || {};
this._pluginsInstances.push(pluginInstance);
this._pluginsRegistered++;

@@ -126,9 +138,20 @@ } catch (err) {

}
if (typeof pluginInstance.register === "function") {
try {
pluginInstance.register(this._core, pluginMethods);
} catch (error) {
this._catchRegisterError(error, pluginIndex);
this._pluginsRegistered = this._pluginsRegistered - 1;
if (
isFunction(pluginInstance.register) ||
isFunction(pluginInstance.init) ||
isFunction(pluginInstance.start)
) {
if (!pluginConfig && pluginInstance.id) {
pluginConfig = this._config.addNamespace(pluginInstance.id);
}
const pluginFinalOptions = { ...pluginOptions, config: pluginConfig };
this._pluginsOptions.push(pluginFinalOptions);
if (isFunction(pluginInstance.register)) {
try {
pluginInstance.register(pluginFinalOptions);
} catch (error) {
this._catchRegisterError(error, pluginIndex);
this._pluginsRegistered = this._pluginsRegistered - 1;
}
}
}

@@ -142,11 +165,11 @@ return pluginInstance;

}
const loadLegacyMocks = this._createLegacyMocksLoader();
const loadMocks = this._createMocksLoader();
const loadRoutes = this._createRoutesLoader();
const pluginMethods = {
loadLegacyMocks,
loadMocks,
loadRoutes,
...scopedAlertsMethods(
() => this.pluginDisplayName(pluginIndex),
() => {
return this._pluginId(pluginIndex);
},
this._addAlert,

@@ -158,4 +181,3 @@ this._removeAlerts,

this._pluginsMethods.push(pluginMethods);
const plugin = this._registerPlugin(this._plugins[pluginIndex], pluginMethods, pluginIndex);
this._pluginsInstances.push(plugin);
this._registerPlugin(this._plugins[pluginIndex], pluginMethods, pluginIndex);
return this._registerPlugins(pluginIndex + 1);

@@ -166,8 +188,4 @@ }

this._pluginsInitialized = this._pluginsInitialized - 1;
const pluginDisplayName = this.pluginDisplayName(index);
this._addAlert(
`init:${pluginDisplayName}`,
`Error initializating plugin "${pluginDisplayName}"`,
error
);
const pluginId = this._pluginId(index);
this._addAlert(`init:${pluginId}`, `Error initializating plugin "${pluginId}"`, error);
tracer.debug(error.toString());

@@ -185,18 +203,11 @@ return Promise.resolve();

};
const pluginDisplayName = this.pluginDisplayName(pluginIndex);
const pluginId = this._pluginId(pluginIndex);
if (!this._pluginsInstances[pluginIndex].init) {
this._pluginsInitialized = this._pluginsInitialized - 1;
this._addAlert(
`init:${pluginDisplayName}`,
`Plugin "${pluginDisplayName}" has not init method`
);
return initNextPlugin();
}
tracer.debug(`Initializing plugin "${pluginDisplayName}"`);
tracer.debug(`Initializing plugin "${pluginId}"`);
let pluginInit;
try {
pluginInit = this._pluginsInstances[pluginIndex].init(
this._core,
this._pluginsMethods[pluginIndex]
);
pluginInit = this._pluginsInstances[pluginIndex].init(this._pluginsOptions[pluginIndex]);
} catch (error) {

@@ -218,8 +229,4 @@ return this._catchInitError(error, pluginIndex).then(initNextPlugin);

this._pluginsStarted = this._pluginsStarted - 1;
const pluginDisplayName = this.pluginDisplayName(index);
this._addAlert(
`start:${pluginDisplayName}`,
`Error starting plugin "${pluginDisplayName}"`,
error
);
const pluginId = this._pluginId(index);
this._addAlert(`start:${pluginId}`, `Error starting plugin "${pluginId}"`, error);
tracer.debug(error.toString());

@@ -237,18 +244,11 @@ return Promise.resolve();

};
const pluginDisplayName = this.pluginDisplayName(pluginIndex);
const pluginId = this._pluginId(pluginIndex);
if (!this._pluginsInstances[pluginIndex].start) {
this._pluginsStarted = this._pluginsStarted - 1;
this._addAlert(
`start:${pluginDisplayName}`,
`Plugin "${pluginDisplayName}" has not start method`
);
return startNextPlugin();
}
tracer.debug(`Starting plugin "${pluginDisplayName}"`);
tracer.debug(`Starting plugin "${pluginId}"`);
let pluginStart;
try {
pluginStart = this._pluginsInstances[pluginIndex].start(
this._core,
this._pluginsMethods[pluginIndex]
);
pluginStart = this._pluginsInstances[pluginIndex].start(this._pluginsOptions[pluginIndex]);
} catch (error) {

@@ -270,8 +270,4 @@ return this._catchStartError(error, pluginIndex).then(startNextPlugin);

this._pluginsStopped = this._pluginsStopped - 1;
const pluginDisplayName = this.pluginDisplayName(index);
this._addAlert(
`stop:${pluginDisplayName}`,
`Error stopping plugin "${pluginDisplayName}"`,
error
);
const pluginId = this._pluginId(index);
this._addAlert(`stop:${pluginId}`, `Error stopping plugin "${pluginId}"`, error);
tracer.debug(error.toString());

@@ -290,18 +286,11 @@ return Promise.resolve();

const pluginDisplayName = this.pluginDisplayName(pluginIndex);
const pluginId = this._pluginId(pluginIndex);
if (!this._pluginsInstances[pluginIndex].stop) {
this._pluginsStopped = this._pluginsStopped - 1;
this._addAlert(
`stop:${pluginDisplayName}`,
`Plugin "${pluginDisplayName}" has not stop method`
);
return stopNextPlugin();
}
tracer.debug(`Stopping plugin "${pluginDisplayName}"`);
tracer.debug(`Stopping plugin "${pluginId}"`);
let pluginStop;
try {
pluginStop = this._pluginsInstances[pluginIndex].stop(
this._core,
this._pluginsMethods[pluginIndex]
);
pluginStop = this._pluginsInstances[pluginIndex].stop(this._pluginsOptions[pluginIndex]);
} catch (error) {

@@ -308,0 +297,0 @@ return this._catchStopError(error, pluginIndex).then(stopNextPlugin);

@@ -15,4 +15,2 @@ /*

const FUNCTION = "function";
class DefaultRoutesHandler {

@@ -34,11 +32,19 @@ static get id() {

type: "object",
errorMessage: 'Property "response.headers" should be an object',
},
status: {
type: "number",
},
body: {
oneOf: [
{
type: "object",
},
{
type: "array",
},
],
},
},
required: ["status"],
errorMessage: {
required: {
status: 'Should have an integer property "response.status"',
},
},
additionalProperties: false,
},

@@ -49,12 +55,5 @@ {

],
errorMessage: 'Property "response" should be a valid object or a function',
},
},
required: ["response"],
errorMessage: {
type: "Should be an object",
required: {
response: 'Should have a property "response"',
},
},
};

@@ -90,3 +89,3 @@ }

return isFunction(this._response)
? FUNCTION
? null
: {

@@ -93,0 +92,0 @@ body: this._response.body,

@@ -22,6 +22,7 @@ /*

const addRequestId = expressRequestId();
const jsonBodyParser = bodyParser.json();
const formBodyParser = bodyParser.urlencoded({ extended: true });
const traceRequest = (req, res, next) => {
const jsonBodyParser = (options) => bodyParser.json(options);
const urlEncodedBodyParser = (options) => bodyParser.urlencoded(options);
const traceRequest = (req, _res, next) => {
tracer.verbose(`Request received | ${req.method} => ${req.url} | Assigned id: ${req.id}`);

@@ -31,3 +32,3 @@ next();

const notFound = (req, res, next) => {
const notFound = (req, _res, next) => {
tracer.debug(`Sending Not found response | ${req.id}`);

@@ -56,3 +57,3 @@ next(Boom.notFound());

jsonBodyParser,
formBodyParser,
urlEncodedBodyParser,
traceRequest,

@@ -59,0 +60,0 @@ notFound,

@@ -12,8 +12,5 @@ /*

"use strict";
const http = require("http");
const express = require("express");
const { delay } = require("lodash");
const cors = require("cors");

@@ -23,12 +20,109 @@ const tracer = require("../tracer");

const ALL_HOSTS = "0.0.0.0";
const LOCALHOST = "localhost";
const OPTIONS = [
{
description: "Port number for the server to be listening at",
name: "port",
type: "number",
default: 3100,
},
{
description: "Host for the server",
name: "host",
type: "string",
default: ALL_HOSTS,
},
];
const CORS_NAMESPACE = "cors";
const CORS_OPTIONS = [
{
description: "Use CORS middleware or not",
name: "enabled",
type: "boolean",
default: true,
},
{
description:
"Options for the CORS middleware. Further information at https://github.com/expressjs/cors#configuration-options",
name: "options",
type: "object",
default: {
preflightContinue: false,
},
},
];
const JSON_BODY_PARSER_NAMESPACE = "jsonBodyParser";
const JSON_BODY_PARSER_OPTIONS = [
{
description: "Use json body-parser middleware or not",
name: "enabled",
type: "boolean",
default: true,
},
{
description:
"Options for the json body-parser middleware. Further information at https://github.com/expressjs/body-parser",
name: "options",
type: "object",
default: {},
},
];
const URL_ENCODED_BODY_PARSER_NAMESPACE = "urlEncodedBodyParser";
const URL_ENCODED_BODY_PARSER_OPTIONS = [
{
description: "Use urlencoded body-parser middleware or not",
name: "enabled",
type: "boolean",
default: true,
},
{
description:
"Options for the urlencoded body-parser middleware. Further information at https://github.com/expressjs/body-parser",
name: "options",
type: "object",
default: { extended: true },
},
];
class Server {
constructor(eventEmitter, settings, legacyMocks, core, { addAlert, removeAlerts, mocksRouter }) {
// TODO, deprecate, the core is being passed only to maintain temporarily backward compatibility with API plugin. This is not published in documentation.
this._core = core; // Use this reference only to provide it to external functions for customization purposes
this._legacyMocks = legacyMocks;
constructor({ config, addAlert, removeAlerts, mocksRouter }) {
this._config = config;
const corsConfigNamespace = this._config.addNamespace(CORS_NAMESPACE);
const jsonBodyParserConfigNamespace = this._config.addNamespace(JSON_BODY_PARSER_NAMESPACE);
const formBodyParserConfigNamespace = this._config.addNamespace(
URL_ENCODED_BODY_PARSER_NAMESPACE
);
[this._portOption, this._hostOption] = this._config.addOptions(OPTIONS);
[this._corsEnabledOption, this._corsOptionsOption] =
corsConfigNamespace.addOptions(CORS_OPTIONS);
[this._jsonBodyParserEnabledOption, this._jsonBodyParserOptionsOption] =
jsonBodyParserConfigNamespace.addOptions(JSON_BODY_PARSER_OPTIONS);
[this._urlEncodedBodyParserEnabledOption, this._urlEncodedBodyParserOptionsOption] =
formBodyParserConfigNamespace.addOptions(URL_ENCODED_BODY_PARSER_OPTIONS);
this.restart = this.restart.bind(this);
this._hostOption.onChange(this.restart);
this._portOption.onChange(this.restart);
this._corsEnabledOption.onChange(this.restart);
this._corsOptionsOption.onChange(this.restart);
this._jsonBodyParserEnabledOption.onChange(this.restart);
this._jsonBodyParserOptionsOption.onChange(this.restart);
this._urlEncodedBodyParserEnabledOption.onChange(this.restart);
this._urlEncodedBodyParserOptionsOption.onChange(this.restart);
this._mocksRouter = mocksRouter;
this._eventEmitter = eventEmitter;
this._customRouters = [];
this._settings = settings;
this._error = null;

@@ -60,17 +154,26 @@ this._addAlert = addAlert;

this._express.use(middlewares.addRequestId);
if (this._settings.get("cors")) {
// TODO, move to variants router. Add options to routes to configure it
if (this._corsEnabledOption.value) {
this._express.use(cors(this._corsOptionsOption.value));
}
// TODO, move to middleware variant handler. Add options to variant to configure it
if (this._jsonBodyParserEnabledOption.value) {
this._express.use(middlewares.jsonBodyParser(this._jsonBodyParserOptionsOption.value));
}
if (this._urlEncodedBodyParserEnabledOption.value) {
this._express.use(
cors({
preflightContinue: !this._settings.get("corsPreFlight"),
})
middlewares.urlEncodedBodyParser(this._urlEncodedBodyParserOptionsOption.value)
);
}
this._express.use(middlewares.jsonBodyParser);
this._express.use(middlewares.formBodyParser);
// TODO, move to variants router. Add options to routes to configure it
this._express.use(middlewares.traceRequest);
this._registerCustomRouters();
this._express.use(this._mocksRouter);
// TODO, remove v1 legacy code
this._express.use(this._fixturesMiddleware.bind(this));
// TODO, Add options to allow to disable or configure it
this._express.use(middlewares.notFound);
this._express.use(middlewares.errorHandler);

@@ -105,5 +208,5 @@

_startServer(resolve, reject) {
const host = this._settings.get("host");
const port = this._settings.get("port");
const hostName = host === "0.0.0.0" ? "localhost" : host;
const host = this._hostOption.value;
const port = this._portOption.value;
const hostName = host === ALL_HOSTS ? LOCALHOST : host;

@@ -147,15 +250,2 @@ try {

// TODO, remove v1 legacy code
_fixturesMiddleware(req, res, next) {
const fixture = this._legacyMocks.behaviors.current.getRequestMatchingFixture(req);
if (fixture) {
delay(() => {
// TODO, deprecate passing the core to handlers. Fixtures handlers already have a reference that is passed to the constructor.
fixture.handleRequest(req, res, next, this._core);
}, this._settings.get("delay"));
} else {
next();
}
}
stop() {

@@ -162,0 +252,0 @@ if (this._serverStopping) {

@@ -11,3 +11,3 @@ /*

const { isFunction } = require("lodash");
const { isFunction, isUndefined } = require("lodash");

@@ -19,3 +19,3 @@ function alertContext(contextScope, context) {

/*
When registering plugins, their displayName is not still available, so its index is used as context.
When registering plugins, their id is not still available, so its index is used as context.
Afterwards it may change, so old alerts have to be renamed

@@ -31,3 +31,3 @@ */

const replacePreviousContextAlerts = (contextScope) => {
if (!previousContext) {
if (isUndefined(previousContext)) {
previousContext = contextScope;

@@ -96,5 +96,10 @@ return;

function arrayMerge(_destinationArray, sourceArray) {
return sourceArray;
}
module.exports = {
scopedAlertsMethods,
addEventListener,
arrayMerge,
};

@@ -73,2 +73,3 @@ /*

module.exports = {
_logger: logger, // exposed for testing purposes
store,

@@ -75,0 +76,0 @@ silly: logger.silly,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc