├── .github └── settings.yml ├── nodemon.json ├── renovate.json ├── jest.config.js ├── .snyk ├── CODEOWNERS ├── .babelrc ├── .gitignore ├── src ├── index.js ├── __tests__ │ ├── __snapshots__ │ │ ├── operation.js.snap │ │ └── action.js.snap │ ├── index.js │ ├── setup.js │ ├── operation.js │ └── action.js ├── operation.js ├── setup.js └── action.js ├── .editorconfig ├── .npmignore ├── Makefile ├── .eslintrc.yml ├── LICENSE ├── package.json ├── README.md ├── .circleci └── config.yml └── secret-squirrel.js /.github/settings.yml: -------------------------------------------------------------------------------- 1 | _extends: github-apps-config-next 2 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": { 3 | "restart": "clear" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>financial-times/renovate-config-next" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', // temporary fix for jsdom issue in Jest 3 | }; 4 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, which patches or ignores known vulnerabilities. 2 | version: v1.13.5 3 | ignore: {} 4 | patch: {} 5 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/about-codeowners/ for more information about this file. 2 | 3 | * @Financial-Times/cp-retention-team 4 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", { "targets": { "node": "12.22.5" } } 5 | ] 6 | ], 7 | "plugins": [ 8 | "transform-object-rest-spread" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules/ 3 | 4 | # test output 5 | coverage/ 6 | reports/ 7 | 8 | # dist 9 | lib/ 10 | dist/ 11 | 12 | # dot files 13 | .DS_Store 14 | .cache 15 | *.env* 16 | 17 | # log files 18 | *.log 19 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default as logger } from '@financial-times/n-auto-logger'; 2 | 3 | export { monitorService, monitorModule } from './action'; 4 | export { default as monitor } from './operation'; 5 | export { default as setupMonitor } from './setup'; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | 8 | [**{.js,.scss,.html,.mustache,Makefile,.vcl}] 9 | indent_style = tab 10 | trim_trailing_whitespace = true 11 | 12 | [**{.json}] 13 | indent_style = space 14 | indent_size = 2 15 | trim_trailing_whitespace = true 16 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/operation.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`monitor disable metricsOperation if metrics instance is not set 1`] = `Array []`; 4 | 5 | exports[`monitor enable metricsOperation if metrics instance is set 1`] = ` 6 | Array [ 7 | Array [ 8 | [Function], 9 | ], 10 | ] 11 | `; 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules/ 3 | 4 | # tests 5 | coverage/ 6 | reports/ 7 | tests/ 8 | test/ 9 | 10 | # src 11 | src/ 12 | 13 | # dev dot files 14 | .circleci 15 | .babelrc 16 | .editorconfig 17 | .eslintrc.yml 18 | .gitattributes 19 | .gitignore 20 | ft.yml 21 | secret-squirrel.js 22 | Makefile 23 | README.md 24 | nodemon.json 25 | yarn.lock 26 | 27 | # hidden dot file 28 | .idea/ 29 | .DS_Store 30 | -------------------------------------------------------------------------------- /src/__tests__/index.js: -------------------------------------------------------------------------------- 1 | import { monitor, monitorService, monitorModule, setupMonitor } from '../index'; 2 | 3 | describe('index', () => { 4 | it('exports monitor', () => { 5 | expect(typeof monitor).toBe('function'); 6 | }); 7 | 8 | it('exports monitorService', () => { 9 | expect(typeof monitorService).toBe('function'); 10 | }); 11 | 12 | it('exports monitorModule', () => { 13 | expect(typeof monitorModule).toBe('function'); 14 | }); 15 | 16 | it('exports setupMonitor', () => { 17 | expect(typeof setupMonitor).toBe('function'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/operation.js: -------------------------------------------------------------------------------- 1 | import { 2 | logOperation, 3 | autoNext, 4 | compose, 5 | } from '@financial-times/n-auto-logger'; 6 | import { metricsOperation } from '@financial-times/n-auto-metrics'; 7 | 8 | import { getConfig } from './setup'; 9 | 10 | const noDecoration = inputFunction => inputFunction; 11 | 12 | const monitor = controllerOrBundle => { 13 | const { metrics } = getConfig(); 14 | return compose( 15 | autoNext, 16 | metrics ? metricsOperation : noDecoration, 17 | logOperation, 18 | )(controllerOrBundle); 19 | }; 20 | 21 | export default monitor; 22 | -------------------------------------------------------------------------------- /src/setup.js: -------------------------------------------------------------------------------- 1 | import { initAutoMetrics } from '@financial-times/n-auto-metrics'; 2 | import { 3 | setupLoggerInstance, 4 | requestIdMiddleware, 5 | enhancedRender, 6 | } from '@financial-times/n-auto-logger'; 7 | 8 | let config = {}; 9 | 10 | const setupMonitor = ({ app, metrics, logger }) => { 11 | config = { app, metrics, logger }; 12 | 13 | if (metrics) { 14 | initAutoMetrics(metrics); 15 | } 16 | 17 | if (logger) { 18 | setupLoggerInstance(logger); 19 | } 20 | 21 | app.use(/^\/(?!_{2}).*$/, [requestIdMiddleware, enhancedRender]); 22 | }; 23 | 24 | export const getConfig = () => config; 25 | 26 | export default setupMonitor; 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | node_modules/@financial-times/n-gage/index.mk: 2 | npm install --no-save @financial-times/n-gage 3 | touch $@ 4 | 5 | -include node_modules/@financial-times/n-gage/index.mk 6 | 7 | build: $(shell find src -type f) 8 | @echo 'Building…' 9 | @rm -rf dist 10 | @babel src -d dist --ignore '**/__tests__/*.js' 11 | 12 | unit-test: 13 | @echo 'Unit Testing…' 14 | @jest 15 | 16 | unit-test-cover: 17 | @jest --coverage 18 | 19 | lint: 20 | @echo 'linting…' 21 | @eslint src 22 | 23 | test: verify lint unit-test 24 | 25 | test-cover: verify lint unit-test-cover 26 | 27 | lint-fix: 28 | @eslint src --fix 29 | 30 | watch: 31 | @watchman-make -s 1 -p 'src/**/*.js' -t lint-fix -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/action.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`monitorModule disable metricsAction if metrics instance is not set 1`] = `Array []`; 4 | 5 | exports[`monitorModule enable metricsAction if metrics instance is set 1`] = ` 6 | Array [ 7 | Array [ 8 | Object { 9 | "method": [Function], 10 | }, 11 | ], 12 | ] 13 | `; 14 | 15 | exports[`monitorService disable metricsAction if metrics instance is not set 1`] = `Array []`; 16 | 17 | exports[`monitorService enable metricsAction if metrics instance is set 1`] = ` 18 | Array [ 19 | Array [ 20 | Object { 21 | "method": [Function], 22 | }, 23 | ], 24 | ] 25 | `; 26 | -------------------------------------------------------------------------------- /src/action.js: -------------------------------------------------------------------------------- 1 | import { logAction, compose } from '@financial-times/n-auto-logger'; 2 | import { metricsAction, tagService } from '@financial-times/n-auto-metrics'; 3 | 4 | import { getConfig } from './setup'; 5 | 6 | const noDecoration = inputFunction => inputFunction; 7 | 8 | export const monitorService = (serviceName, functionBundle) => { 9 | const { metrics } = getConfig(); 10 | return compose( 11 | tagService(serviceName), 12 | metrics ? metricsAction : noDecoration, 13 | logAction, 14 | )(functionBundle); 15 | }; 16 | 17 | export const monitorModule = functionBundle => { 18 | const { metrics } = getConfig(); 19 | return compose( 20 | metrics ? metricsAction : noDecoration, 21 | logAction, 22 | )(functionBundle); 23 | }; 24 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parser: babel-eslint 3 | parserOptions: 4 | ecmaVersion: 6 5 | sourceType: module 6 | env: 7 | browser: true 8 | es6: true 9 | node: true 10 | jest/globals: true 11 | extends: 12 | - airbnb-base 13 | - plugin:jest/recommended 14 | - prettier 15 | plugins: 16 | - import 17 | - jest 18 | - json 19 | - prettier 20 | rules: 21 | import/extensions: [warn, {js: never}] 22 | import/no-extraneous-dependencies: 'off' 23 | import/no-named-as-default: 'off' 24 | jest/no-disabled-tests: warn 25 | jest/no-focused-tests: error 26 | jest/no-identical-title: error 27 | jest/valid-expect: error 28 | prettier/prettier: 29 | - error 30 | - { 31 | singleQuote: true, 32 | trailingComma: all, 33 | useTabs: true 34 | } 35 | -------------------------------------------------------------------------------- /src/__tests__/setup.js: -------------------------------------------------------------------------------- 1 | import * as nAutoMetrics from '@financial-times/n-auto-metrics'; 2 | import * as nAutoLogger from '@financial-times/n-auto-logger'; 3 | 4 | import setupMonitor from '../setup'; 5 | 6 | jest.mock('@financial-times/n-auto-metrics'); 7 | jest.mock('@financial-times/n-auto-logger'); 8 | 9 | describe('setupMonitor', () => { 10 | afterEach(() => { 11 | jest.resetAllMocks(); 12 | }); 13 | 14 | afterAll(() => { 15 | jest.resetModules(); 16 | }); 17 | 18 | const app = { 19 | use: jest.fn(), 20 | }; 21 | const metrics = {}; 22 | const logger = {}; 23 | 24 | it('initAutoMetrics if metrics instance is provided', () => { 25 | setupMonitor({ app, metrics }); 26 | expect(nAutoMetrics.initAutoMetrics).toBeCalledWith(metrics); 27 | }); 28 | 29 | it('setupLoggerInstance to override the default if logger is provided', () => { 30 | setupMonitor({ app, logger }); 31 | expect(nAutoLogger.setupLoggerInstance).toBeCalledWith(logger); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/__tests__/operation.js: -------------------------------------------------------------------------------- 1 | import * as nAutoMetrics from '@financial-times/n-auto-metrics'; 2 | 3 | import setupMonitor from '../setup'; 4 | import monitor from '../operation'; 5 | 6 | jest.mock('@financial-times/n-auto-metrics', () => ({ 7 | metricsOperation: jest.fn(inputFunction => inputFunction), 8 | initAutoMetrics: jest.fn(), 9 | })); 10 | 11 | describe('monitor', () => { 12 | afterEach(() => { 13 | jest.clearAllMocks(); 14 | }); 15 | 16 | const app = { 17 | use: jest.fn(), 18 | }; 19 | 20 | const metrics = {}; 21 | 22 | it('disable metricsOperation if metrics instance is not set', () => { 23 | setupMonitor({ app }); 24 | const targetOperation = () => {}; 25 | monitor(targetOperation); 26 | expect(nAutoMetrics.metricsOperation.mock.calls).toMatchSnapshot(); 27 | }); 28 | 29 | it('enable metricsOperation if metrics instance is set', () => { 30 | setupMonitor({ app, metrics }); 31 | const targetOperation = () => {}; 32 | monitor(targetOperation); 33 | expect(nAutoMetrics.metricsOperation.mock.calls).toMatchSnapshot(); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Financial Times 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@financial-times/n-express-monitor", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "repository": "https://github.com/Financial-Times/n-express-monitor.git", 6 | "main": "dist/index.js", 7 | "scripts": { 8 | "lint": "eslint src", 9 | "watch": "nodemon --watch src -q --exec 'eslint src --fix'", 10 | "test": "jest", 11 | "cover": "jest --coverage", 12 | "prepublish": "make build", 13 | "prepare": "npx snyk protect || npx snyk protect -d || true", 14 | "preinstall": "[ \"$INIT_CWD\" != \"$PWD\" ] || npm_config_yes=true npx check-engine" 15 | }, 16 | "dependencies": { 17 | "@financial-times/n-auto-logger": "^4.1.2", 18 | "@financial-times/n-auto-metrics": "^4.0.0-beta.2" 19 | }, 20 | "devDependencies": { 21 | "@financial-times/n-gage": "^8.2.0", 22 | "babel-cli": "^6.26.0", 23 | "babel-core": "^6.26.3", 24 | "babel-eslint": "^10.0.0", 25 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 26 | "babel-preset-env": "^1.7.0", 27 | "check-engine": "^1.10.1", 28 | "coveralls": "^3.0.2", 29 | "eslint": "^5.6.0", 30 | "eslint-config-airbnb-base": "13.1.0", 31 | "eslint-config-prettier": "^3.1.0", 32 | "eslint-plugin-import": "^2.14.0", 33 | "eslint-plugin-jest": "^21.22.1", 34 | "eslint-plugin-json": "^1.2.0", 35 | "eslint-plugin-prettier": "^2.6.2", 36 | "jest": "^23.6.0", 37 | "nodemon": "^1.18.4", 38 | "prettier": "^1.14.3", 39 | "snyk": "^1.167.2", 40 | "supertest": "^3.3.0" 41 | }, 42 | "engines": { 43 | "node": "14.x || 16.x", 44 | "npm": "7.x || 8.x" 45 | }, 46 | "husky": { 47 | "hooks": { 48 | "commit-msg": "node_modules/.bin/secret-squirrel-commitmsg", 49 | "pre-commit": "node_modules/.bin/secret-squirrel", 50 | "pre-push": "make lint unit-test && make verify -j3" 51 | } 52 | }, 53 | "volta": { 54 | "node": "16.15.0", 55 | "npm": "7.20.2" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/__tests__/action.js: -------------------------------------------------------------------------------- 1 | import * as nAutoMetrics from '@financial-times/n-auto-metrics'; 2 | 3 | import setupMonitor from '../setup'; 4 | import { monitorService, monitorModule } from '../action'; 5 | 6 | jest.mock('@financial-times/n-auto-metrics', () => ({ 7 | tagService: () => inputFunction => inputFunction, 8 | metricsAction: jest.fn(inputFunction => inputFunction), 9 | initAutoMetrics: jest.fn(), 10 | })); 11 | 12 | describe('monitorService', () => { 13 | afterEach(() => { 14 | jest.clearAllMocks(); 15 | }); 16 | 17 | const app = { 18 | use: jest.fn(), 19 | }; 20 | 21 | const metrics = {}; 22 | 23 | it('disable metricsAction if metrics instance is not set', () => { 24 | setupMonitor({ app }); 25 | const serviceActions = { 26 | method: () => {}, 27 | }; 28 | monitorService('mock-service', serviceActions); 29 | expect(nAutoMetrics.metricsAction.mock.calls).toMatchSnapshot(); 30 | }); 31 | 32 | it('enable metricsAction if metrics instance is set', () => { 33 | setupMonitor({ app, metrics }); 34 | const serviceActions = { 35 | method: () => {}, 36 | }; 37 | monitorService('mock-service', serviceActions); 38 | expect(nAutoMetrics.metricsAction.mock.calls).toMatchSnapshot(); 39 | }); 40 | }); 41 | 42 | describe('monitorModule', () => { 43 | afterEach(() => { 44 | jest.clearAllMocks(); 45 | }); 46 | 47 | const app = { 48 | use: jest.fn(), 49 | }; 50 | 51 | const metrics = {}; 52 | 53 | it('disable metricsAction if metrics instance is not set', () => { 54 | setupMonitor({ app }); 55 | const moduleFunctions = { 56 | method: () => {}, 57 | }; 58 | monitorModule(moduleFunctions); 59 | expect(nAutoMetrics.metricsAction.mock.calls).toMatchSnapshot(); 60 | }); 61 | 62 | it('enable metricsAction if metrics instance is set', () => { 63 | setupMonitor({ app, metrics }); 64 | const moduleFunctions = { 65 | method: () => {}, 66 | }; 67 | monitorModule(moduleFunctions); 68 | expect(nAutoMetrics.metricsAction.mock.calls).toMatchSnapshot(); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # n-express-monitor 2 | 3 | a configurable [express](https://github.com/expressjs/express) decorator to automate log, metrics to ensure monitor consistency and make debugging more predictable across micro-services 4 | 5 | [![npm version](https://badge.fury.io/js/%40financial-times%2Fn-express-monitor.svg)](https://badge.fury.io/js/%40financial-times%2Fn-express-monitor) 6 | [![CircleCI](https://circleci.com/gh/Financial-Times/n-express-monitor.svg?style=shield)](https://circleci.com/gh/Financial-Times/n-express-monitor) 7 | [![Coverage Status](https://coveralls.io/repos/github/Financial-Times/n-express-monitor/badge.svg?branch=main)](https://coveralls.io/github/Financial-Times/n-express-monitor?branch=main) 8 | [![gitter chat](https://badges.gitter.im/Financial-Times/n-express-monitor.svg)](https://gitter.im/Financial-Times/n-express-monitor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 9 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FFinancial-Times%2Fn-express-monitor.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FFinancial-Times%2Fn-express-monitor?ref=badge_shield) 10 | 11 | ![node version](https://img.shields.io/node/v/@financial-times/n-express-monitor.svg) 12 | [![Known Vulnerabilities](https://snyk.io/test/github/Financial-Times/n-express-monitor/badge.svg)](https://snyk.io/test/github/Financial-Times/n-express-monitor) 13 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Financial-Times/n-express-monitor/badges/quality-score.png?b=main)](https://scrutinizer-ci.com/g/Financial-Times/n-express-monitor/?branch=main) 14 | [![Dependencies](https://david-dm.org/Financial-Times/n-express-monitor.svg)](https://david-dm.org/Financial-Times/n-express-monitor) 15 | [![devDependencies](https://david-dm.org/Financial-Times/n-express-monitor/dev-status.svg)](https://david-dm.org/Financial-Times/n-express-monitor?type=dev) 16 | 17 |
18 | 19 | - [Install](#install) 20 | - [Demo](#demo) 21 | - [Usage](#usage) 22 | * [setupMonitor](#setupmonitor) 23 | * [monitor](#monitor) 24 | * [monitorService](#monitorservice) 25 | * [monitorModule](#monitormodule) 26 | - [Configuration](#configuration) 27 | - [Convention](#convention) 28 | * [operation function](#operation-function) 29 | * [action function](#action-function) 30 | - [How It Works](#how-it-works) 31 | - [Licence](#licence) 32 | 33 |
34 | 35 | ## Install 36 | ```shell 37 | npm install @financial-times/n-express-monitor --save 38 | ``` 39 | 40 | ## Demo 41 | [next-monitor-express](https://github.com/Financial-Times/next-monitor-express) 42 | 43 | ## Usage 44 | 45 | ### setupMonitor 46 | ```js 47 | import express, { metrics } from '@financial-times/n-express'; 48 | import { setupMonitor } from '@financial-times/n-express-monitor'; 49 | 50 | const app = express(); 51 | 52 | setupMonitor({ app, metrics }); // if metrics is not set, it would only do the log 53 | 54 | // ...middlewares and routes 55 | ``` 56 | 57 | > it uses [n-logger](https://github.com/Financial-Times/n-logger) by default, use custom logger instance, check [example](https://github.com/Financial-Times/next-monitor-express/blob/use-custom-logger/server/app.js) of setupMonitor with [n-mask-logger](https://github.com/Financial-Times/n-mask-logger) 58 | 59 | ### monitor 60 | ```js 61 | import { monitor } from '@financial-times/n-express-monitor'; 62 | 63 | const getUserProfileBySession = async (req, res) => { 64 | const { meta } = req; 65 | const { sessionId } = req.params; 66 | if (sessionId === 'uncovered') { 67 | throw Error('an uncovered function has thrown an error'); 68 | } 69 | const { userId } = await SessionApi.verifySession({ sessionId }, meta); 70 | const userProfile = await UserProfileSvc.getUserProfileById({ userId }, meta); 71 | res.json(userProfile); 72 | }; 73 | 74 | export default monitor(getUserProfileBySession); 75 | ``` 76 | 77 | ### monitorService 78 | ```js 79 | import { monitorService } from '@financial-times/n-express-monitor'; 80 | 81 | /* 82 | SHORTHAND DEFAULT: in case we don't need to add extra error handling, 83 | the default method from n-api-factory can be used to setup a client method 84 | */ 85 | const getUserProfileById = async ({ userId }, meta) => 86 | userProfileSvc.get({ 87 | endpoint: `/user-profile/${userId}`, 88 | meta, 89 | }); 90 | 91 | export default monitorService('user-profile-svc', { 92 | getUserProfileById, 93 | }); 94 | ``` 95 | 96 | ### monitorModule 97 | ```js 98 | import { monitorModule } from '@financial-times/n-express-monitor'; 99 | 100 | export default monitorModule({ 101 | validateUserId: () => {}, 102 | mapUserProfileToView: () => {}, 103 | }); 104 | ``` 105 | 106 | ## Configuration 107 | 108 | Apart from the configuration you need for the logger, metrics instance you used, you can config `n-express-monitor` with the following ENV_VAR for the log automation behaviour: 109 | 110 | | ENV_VAR | values | function | 111 | |---------|--------|----------| 112 | | AUTO_LOG_LEVEL | `verbose`, `standard`(default), `concise`, `error` | control how operation and action would be logged, check [example](https://github.com/Financial-Times/next-monitor-express) | 113 | | LOGGER_MUTE_FIELDS | key names of values in the `meta` or error | use to reduce amount of log information | 114 | 115 | ## Convention 116 | 117 | ### operation function 118 | 119 | same as express middleware/controller but without next: `(req, res) => {}` 120 | 121 | ### action function 122 | 123 | `(param, meta) => {}` 124 | 125 | ## How It Works 126 | 127 | If you are interested in how it works under the hood, it is using [n-auto-logger](https://github.com/Financial-Times/n-auto-logger), [n-auto-metrics](https://github.com/Financial-Times/n-auto-metrics), and you can build your own customised decorator and add it into the chain with [n-express-enhancer](https://github.com/Financial-Times/n-express-enhancer). 128 | 129 | ## Licence 130 | [MIT](/LICENSE) 131 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # generator: n-circle2-cli 2 | # template: component 3 | --- 4 | references: 5 | 6 | container_config_node: 7 | &container_config_node 8 | working_directory: ~/project/build 9 | docker: 10 | - image: cimg/node:<< parameters.node-version >>-browsers 11 | parameters: 12 | node-version: 13 | default: "16.14" 14 | type: string 15 | 16 | container_config_lambda_node: 17 | &container_config_lambda_node 18 | working_directory: ~/project/build 19 | docker: 20 | - image: lambci/lambda:build-nodejs<< parameters.node-version >> 21 | parameters: 22 | node-version: 23 | default: "14.19" 24 | type: string 25 | 26 | workspace_root: &workspace_root ~/project 27 | 28 | attach_workspace: 29 | &attach_workspace 30 | attach_workspace: 31 | at: *workspace_root 32 | 33 | npm_cache_keys: 34 | &npm_cache_keys 35 | keys: 36 | - v2-dependency-npm-{{ checksum "package.json" }}- 37 | - v2-dependency-npm-{{ checksum "package.json" }} 38 | - v2-dependency-npm- 39 | 40 | cache_npm_cache: 41 | &cache_npm_cache 42 | save_cache: 43 | key: v2-dependency-npm-{{ checksum "package.json" }}-{{ epoch }} 44 | paths: 45 | - ./node_modules/ 46 | 47 | restore_npm_cache: 48 | &restore_npm_cache 49 | restore_cache: 50 | <<: *npm_cache_keys 51 | 52 | filters_only_main: 53 | &filters_only_main 54 | branches: 55 | only: main 56 | 57 | filters_ignore_main: 58 | &filters_ignore_main 59 | branches: 60 | ignore: main 61 | 62 | filters_ignore_tags: 63 | &filters_ignore_tags 64 | tags: 65 | ignore: /.*/ 66 | 67 | filters_version_tag: 68 | &filters_version_tag 69 | tags: 70 | only: 71 | - /^v?\d+\.\d+\.\d+(?:-beta\.\d+)?$/ 72 | branches: 73 | ignore: /.*/ 74 | 75 | version: 2.1 76 | 77 | orbs: 78 | node: circleci/node@4.6.0 79 | 80 | jobs: 81 | 82 | build: 83 | <<: *container_config_node 84 | steps: 85 | - checkout 86 | - run: 87 | name: Checkout next-ci-shared-helpers 88 | command: git clone --depth 1 89 | git@github.com:Financial-Times/next-ci-shared-helpers.git 90 | .circleci/shared-helpers 91 | - *restore_npm_cache 92 | - node/install-npm: 93 | version: "7" 94 | - run: 95 | name: Install project dependencies 96 | command: make install 97 | - run: 98 | name: Run the project build task 99 | command: make build 100 | - run: 101 | name: shared-helper / generate-build-state-artifacts 102 | command: .circleci/shared-helpers/helper-generate-build-state-artifacts 103 | when: always 104 | - *cache_npm_cache 105 | - store_artifacts: 106 | path: build-state 107 | destination: build-state 108 | - persist_to_workspace: 109 | root: *workspace_root 110 | paths: 111 | - build 112 | 113 | test: 114 | <<: *container_config_node 115 | steps: 116 | - *attach_workspace 117 | - run: 118 | name: Run tests 119 | command: make test-cover 120 | environment: 121 | JEST_JUNIT_OUTPUT: test-results/jest/results.xml 122 | MOCHA_FILE: test-results/mocha/results.xml 123 | - run: 124 | name: Report test coverage 125 | command: | 126 | cat coverage/lcov.info | node_modules/coveralls/bin/coveralls.js 127 | - store_test_results: 128 | path: test-results 129 | - store_artifacts: 130 | path: test-results 131 | destination: test-results 132 | 133 | publish: 134 | <<: *container_config_node 135 | steps: 136 | - *attach_workspace 137 | - run: 138 | name: shared-helper / npm-store-auth-token 139 | command: .circleci/shared-helpers/helper-npm-store-auth-token 140 | - run: npx snyk monitor --org=customer-products 141 | --project-name=Financial-Times/n-express-monitor 142 | - run: 143 | name: shared-helper / npm-version-and-publish-public 144 | command: .circleci/shared-helpers/helper-npm-version-and-publish-public 145 | 146 | workflows: 147 | 148 | version: 2 149 | 150 | build-test: 151 | jobs: 152 | - build: 153 | filters: 154 | <<: *filters_ignore_tags 155 | name: build-v<< matrix.node-version >> 156 | matrix: 157 | parameters: 158 | node-version: [ "16.14", "14.19" ] 159 | - test: 160 | requires: 161 | - build-v<< matrix.node-version >> 162 | name: test-v<< matrix.node-version >> 163 | matrix: 164 | parameters: 165 | node-version: [ "16.14", "14.19" ] 166 | 167 | build-test-publish: 168 | jobs: 169 | - build: 170 | filters: 171 | <<: *filters_version_tag 172 | name: build-v<< matrix.node-version >> 173 | matrix: 174 | parameters: 175 | node-version: [ "16.14", "14.19" ] 176 | - test: 177 | filters: 178 | <<: *filters_version_tag 179 | requires: 180 | - build-v<< matrix.node-version >> 181 | name: test-v<< matrix.node-version >> 182 | matrix: 183 | parameters: 184 | node-version: [ "16.14", "14.19" ] 185 | - publish: 186 | context: npm-publish-token 187 | filters: 188 | <<: *filters_version_tag 189 | requires: 190 | - test-v16.14 191 | 192 | nightly: 193 | triggers: 194 | - schedule: 195 | cron: "0 0 * * *" 196 | filters: 197 | <<: *filters_only_main 198 | jobs: 199 | - build: 200 | context: next-nightly-build 201 | name: build-v<< matrix.node-version >> 202 | matrix: 203 | parameters: 204 | node-version: [ "16.14", "14.19" ] 205 | - test: 206 | requires: 207 | - build-v<< matrix.node-version >> 208 | context: next-nightly-build 209 | name: test-v<< matrix.node-version >> 210 | matrix: 211 | parameters: 212 | node-version: [ "16.14", "14.19" ] 213 | 214 | notify: 215 | webhooks: 216 | - url: https://ft-next-webhooks.herokuapp.com/circleci2-workflow 217 | -------------------------------------------------------------------------------- /secret-squirrel.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | files: { 3 | allow: [ 4 | '.snyk', 5 | 'src/__tests__/__snapshots__/action.js.snap', 6 | 'src/__tests__/__snapshots__/operation.js.snap' 7 | ], 8 | allowOverrides: [] 9 | }, 10 | strings: { 11 | deny: [], 12 | denyOverrides: [ 13 | '1o/jo7D\\+kC9ZjHX5v\\+EHrdjl3PhxMrLSOTGsOdHJ', // package-lock.json:58|15222 14 | '8KWJPIb8c2VvY8AJrydh6', // package-lock.json:111|343|457|15262|15438|15525 15 | '/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/', // package-lock.json:149|15289 16 | '\\+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT\\+', // package-lock.json:164|381|495|3936|8366|12976|15298|15465|15552|18315|22084|25678 17 | '/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/', // package-lock.json:170|239|15304|15356 18 | 'RV9/\\+RlWiAIKmjRPQF\\+xbGM9Kklj5bZQFa2s/38A', // package-lock.json:210|1180|15337|16099 19 | 'MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr', // package-lock.json:233|1203|15350|16112 20 | 'flc\\+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/', // package-lock.json:274|15384 21 | 'jzFEnxf5uTEGp\\+3bzAbNOxU1paTgYS4ECU/IgfDw', // package-lock.json:411|15491 22 | '\\+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw', // package-lock.json:429|15503 23 | 'OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK', // package-lock.json:602|6174|6299|13326|15642|20227|20342|25960 24 | '87EwtdMvmUPJSwykpovFB', // package-lock.json:611|15653 25 | '/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA', // package-lock.json:884|15868 26 | '1D\\+\\+VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm', // package-lock.json:908|15892 27 | 'cBhpre4ma\\+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr', // package-lock.json:953|15927 28 | '9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA', // package-lock.json:1158|16084 29 | 'c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ', // package-lock.json:1217|16127 30 | 'slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc', // package-lock.json:1474|16368 31 | '/4qLSKoHGY/xvkZdVBGlKM/HuxxS3\\+sC66HhTNR7', // package-lock.json:1505|16396 32 | '3MYsjlwGTi0tjQ9ANXZu4', // package-lock.json:1935|16828 33 | 'v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV', // package-lock.json:1991|1991|16879|16879 34 | '2eis5WqQGV7peooDyLmNEPUrps9\\+SXX5c9pL3xEB', // package-lock.json:2057|8623|9083|10735|11722|12061|13775|14133|16933|22288|22681|23977|24741|25016|26333|26604 35 | 't44WVXaUjTBRcrpFeFlC8WCruUR456hw', // package-lock.json:2080|4752|8658|9053|9074|9097|9197|10348|10794|11781|12084|12878|13834|14168|14637|16950|19105|22314|22656|22673|22692|22770|23661|24023|24787|25033|25607|26379|26630|27003 36 | 'TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl', // package-lock.json:2123|2123|16989|16989 37 | '/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/', // package-lock.json:2218|17068 38 | 'w3wtFKZIfpC2pnRmFAhAC', // package-lock.json:2532|17323 39 | '/9jIB6UyevL\\+tXuOqrng8j/cxKTWyWUwvSTriiZz', // package-lock.json:2556|2738|13182|14997|15155|17339|17486|25820|27282|27397 40 | 'WclKAEzWH47lCdplFocUM', // package-lock.json:2682|17442 41 | 'Eb283TKP20Fs2MqoPsr9SwA595rRCA\\+QMzYc9nBP', // package-lock.json:2709|17463 42 | 'f8FqlyAdouralcFWFQWF2', // package-lock.json:2870|5156|5443|17589|19415|19634 43 | '\\+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw', // package-lock.json:2970|17684 44 | 'eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd\\+', // package-lock.json:3100|17791 45 | 'JcjK111nNScGob5MNtsntNM1aCNUDipB', // package-lock.json:3125|17810 46 | 'hYD5i0aPN5QwZisEbDStI', // package-lock.json:3164|17840 47 | 'E7AdgFBVeAPVMNcKGsHMA', // package-lock.json:3200|8899|17870|22524 48 | 'pWmJj7c3GTyPhzyvbUXJQ', // package-lock.json:3276|17930 49 | 'WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55', // package-lock.json:3348|3348|17981|17981 50 | '/RPgnr1/GGt\\+ic3iJTzQ8Eu3TdM14SawnVUmGE6A', // package-lock.json:3485|18107 51 | '\\+cnt4CzD5rW\\+NC1wjOUSTOs\\+Te7FOv7AhN7vS9x/', // package-lock.json:3499|18121 52 | '6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG', // package-lock.json:3578|3578|18188|18188 53 | '/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g', // package-lock.json:3613|4867|7056|7400|10137|18207|19192|21013|21308|23369 54 | 'v4ssH/3W9LM2Q7h5qBcy5m0ehCrBDU2YF8q6OY8w', // package-lock.json:3691|18358 55 | 'AY6sXByma2Wr0TXvXJ4nA', // package-lock.json:3729|18387 56 | 'T9SmE/g6UV1uZo1oHAqOvL86XWl7Pl2EpRpnLI8g', // package-lock.json:3790|18433 57 | '/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC', // package-lock.json:3907|18295 58 | 'XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE\\+KMR', // package-lock.json:3989|18491 59 | 'xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw', // package-lock.json:4010|18506 60 | '\\+A9DVCndXfkeFUd3byderg\\+EbDkfnevfCwynWaNA', // package-lock.json:4023|18512 61 | 'A9DVCndXfkeFUd3byderg', // package-lock.json:4023|18512 62 | 'SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb', // package-lock.json:4023|18512 63 | 'FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl', // package-lock.json:4065|4065|18542|18542 64 | 'nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw', // package-lock.json:4074|18551 65 | '2rn2NcvCWcSCovm6BLeuB', // package-lock.json:4137|18599 66 | 'hMQ4CX1p1izmuLYyZqLMO', // package-lock.json:4180|18638 67 | 'xlpDCIgDaitVs03ATR84Q', // package-lock.json:4238|18681 68 | 'gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI', // package-lock.json:4238|18681 69 | 'eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy', // package-lock.json:4352|18776 70 | 'bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/', // package-lock.json:4392|18813 71 | 'wcS5vuPglyXdsQa3XB4wH', // package-lock.json:4411|18829 72 | '\\+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i\\+', // package-lock.json:4472|18878 73 | 'YEwgwAXU9cI67NIda0kJk', // package-lock.json:4519|18918 74 | 'Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp', // package-lock.json:4532|4532|18928|18928 75 | 'JAzQV4WpoY5WHcG0S0HHY', // package-lock.json:4791|19134 76 | 'FuZJMVWyNaIDr4RGmaSYw', // package-lock.json:5053|19337 77 | 'ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz', // package-lock.json:5240|13051|19475|25732 78 | '/x54COGeIv9eb\\+6CkDSQoNTt4XyWoIJvuPsXizxu', // package-lock.json:5249|19481 79 | 'YdcWFniJhzI42\\+AzS\\+gNwmUzOSFcRCQYwySuBBBy', // package-lock.json:5293|19515 80 | '/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT', // package-lock.json:5330|19543 81 | 'PaQqUC9SRmAiSA9CCCYd4', // package-lock.json:5386|19586 82 | 'yugV2uzW5iMRSiZAyDtQd', // package-lock.json:5392|19592 83 | 'rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg', // package-lock.json:5422|5422|13435|13435|19619|19619|26041|26041 84 | '1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg', // package-lock.json:5422|13435|19619|26041 85 | '\\+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J\\+', // package-lock.json:5457|13464|19645|26064 86 | 'ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow', // package-lock.json:5469|5469|9212|9212|19654|19654|22784|22784 87 | 'phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns', // package-lock.json:5481|19665 88 | 'nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/', // package-lock.json:5499|19680 89 | 'Cbt33SmUnSol\\+pnXFvLxSHNq2CemUXNdaXV6Flg7', // package-lock.json:5520|19695 90 | 'uA1FvQXsAnYIl1T3B2X5E', // package-lock.json:5620|19779 91 | '/\\+\\+YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw', // package-lock.json:5640|8958|10656|11643|13683|19796|22571|23912|24676|26258 92 | 'wOcfdRHaZ7VWtqCztfHri', // package-lock.json:5640|8958|10656|11643|13683|19796|22571|23912|24676|26258 93 | 'rEaBd8SUNAWgkvyPKXYMb', // package-lock.json:5688|19831 94 | 'Df2Y9akRFxbdU13aZJL2e', // package-lock.json:5745|19873 95 | 'TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ', // package-lock.json:5999|5999|20062|20062 96 | '\\+HSiqeuxyhG\\+EnZds6iO3Y3ZEnMrfZq/OTGvF/C\\+', // package-lock.json:6317|20362 97 | 'z\\+IT0jT3h3Ql61\\+dklcG7bJJitIWEMB4Sp1piHmA', // package-lock.json:6333|20372 98 | '\\+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE', // package-lock.json:6436|7151|20457|21092 99 | 'zOmdprZcouPYtQgvNU35i', // package-lock.json:6543|7267|20545|21186 100 | '\\+lyT\\+Slx/cbP5sZJ0\\+NDuLcmBE5hXAoK0aUp7vI\\+', // package-lock.json:6572|20576 101 | 'NDuLcmBE5hXAoK0aUp7vI', // package-lock.json:6572|20576 102 | 'uyNhMyl6dr6HaXGHp8VF7cK6KpC6G9z9LiMNsst\\+', // package-lock.json:6763|20746 103 | 'xvVXtCCZ3wQ99IgQxftCg', // package-lock.json:6846|20824 104 | 'Acz9Qvjz2mSsOnPSH7skBmDYCHXVZqkA', // package-lock.json:6967|20931 105 | '/7rQGavedeB8aK\\+Zkyq6upMFVL/9AW6vOYzfRyLg', // package-lock.json:7935|21756 106 | '/52OVTRKb4zP5onXwVF3zVmmToNcOfGC\\+CRDpfK/', // package-lock.json:7982|12672|14976|21785|25461|27267 107 | 'qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/\\+', // package-lock.json:8003|13220|21800|25849 108 | 'LW51bHtLh6HTjx84LA5W4', // package-lock.json:8015|21811 109 | 'sWZlbEP2OsHNkXrMl5GYk', // package-lock.json:8059|21842 110 | 'zUFawoUuixuOv33eZ61Iw', // package-lock.json:8069|21852 111 | 'gARop7aGUbl11pnDfW6xg', // package-lock.json:8147|21914 112 | '6cP692WwGIs9XXdOO4\\+\\+N\\+7qjqv0rqxxVvJ3VHPh', // package-lock.json:8416|22124 113 | '/UODVpGsr5OhXhhXg6f\\+qtJ8uiZ\\+PUxkDWcgIXLw', // package-lock.json:8457|22153 114 | '/6WlbVge9bhM74OpNPQPMGUToDtz\\+KXa1PneJxOA', // package-lock.json:8489|8637|9106|10749|10972|11736|12233|13789|14147|22179|22299|22700|23988|24175|24752|25157|26344|26615 115 | 'R0Ra7wvhV\\+wFW63FaSOFPwRahvea0gMUcGUhVeAg', // package-lock.json:8501|22190 116 | 'qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4', // package-lock.json:8673|8673|22328|22328 117 | '\\+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA', // package-lock.json:8679|22334 118 | '/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/', // package-lock.json:8685|22340 119 | '/xBl6bR8ZYifj\\+dFcFrKI21huSQgJZ6ZtL3B4HfQ', // package-lock.json:8721|22375 120 | 'ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5', // package-lock.json:8734|22388 121 | '\\+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL', // package-lock.json:9252|22814 122 | 'ZDE4sZlJr5ED4YW0yqmkK', // package-lock.json:9252|22814 123 | '/52Z\\+S265okfFj8Kt2cC2MKY\\+xNi3kFs\\+XGI7WXu', // package-lock.json:9518|23024 124 | 'Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F', // package-lock.json:9579|23070 125 | 'GQ2EWRpQV8/o\\+Aw8YqtfZZPfNRWZYkbidE9k5rpl', // package-lock.json:9627|23106 126 | 'VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4', // package-lock.json:9639|9639|23115|23115 127 | '2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw', // package-lock.json:9748|9748|23201|23201 128 | 'gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql\\+', // package-lock.json:9921|23338 129 | '\\+YeaDxkw5Dd8NPN\\+GD6bjnYm2VuPuCXmpuYvmCXQ', // package-lock.json:10009|11204|23417|24328 130 | 'hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A', // package-lock.json:10029|23423 131 | 'oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g', // package-lock.json:10062|23455 132 | 'zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy', // package-lock.json:10074|23464 133 | '\\+89j56EFqaNRdUKqXyRp6kvTcSXnmgEjaVowCXH\\+', // package-lock.json:10109|23493 134 | '/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w', // package-lock.json:10122|23503 135 | 'jQ1YpY9000OaVXlAQj6Zp', // package-lock.json:10122|23503 136 | '7Bttv5axS5IiHoLaVNHeQ', // package-lock.json:10131|23510 137 | 'rwJoB\\+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw', // package-lock.json:10185|23540 138 | '/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/', // package-lock.json:10216|23567 139 | 'pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq', // package-lock.json:10259|23601 140 | '\\+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V\\+', // package-lock.json:10270|23612 141 | 'wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91', // package-lock.json:10288|10288|23627|23627 142 | 'M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A', // package-lock.json:10296|10296|23632|23632 143 | '/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV', // package-lock.json:10462|13033|23693|25720 144 | 'SVIOsh\\+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO', // package-lock.json:10498|23786 145 | 'lv0M6\\+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/', // package-lock.json:10984|24186 146 | '\\+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA', // package-lock.json:11053|24245 147 | 'Xf0nWe6RseziFMu\\+Ap9biiUbmplq6S9/p\\+7w7YXP', // package-lock.json:11232|24371 148 | 'TTlYpa\\+OL\\+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G', // package-lock.json:11348|24458 149 | 'wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg', // package-lock.json:11348|24458 150 | 'izWPrzEJDcSqBa0OZQriA', // package-lock.json:11367|24470 151 | 'YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2', // package-lock.json:11388|11388|24485|24485 152 | 'cBBzwBcNDWoFxt2XEFIpQ', // package-lock.json:11894|24883 153 | 'mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe\\+', // package-lock.json:12093|25041 154 | 'Zn99hVEYcMvKPct1IqNe7', // package-lock.json:12093|25041 155 | '/fxS2pBo2jbfcFRVuFZ/oFC\\+vZz0MNNk0h80iMn5', // package-lock.json:12154|25090 156 | '/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e\\+', // package-lock.json:12170|25106 157 | '\\+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA', // package-lock.json:12180|25116 158 | 'NrQHlS/V/qgv763EYudVwEcMQNxd2lh\\+0VrUByXN', // package-lock.json:12208|25138 159 | 'ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ', // package-lock.json:12455|12455|25324|25324 160 | 'fCp99esgGDWiHENrKxaCENuCxpoMCmAt', // package-lock.json:12554|25380 161 | 'Wr1D9BlriLQyL0E\\+jbkuePVZXYFj47QM/v093wHg', // package-lock.json:12779|25536 162 | 'eWScsOP9c112ZyLdWHi0FxHjI\\+4uVhKYp/gcdRmQ', // package-lock.json:12820|25565 163 | 'Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi\\+', // package-lock.json:12899|25622 164 | '\\+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ', // package-lock.json:12923|25637 165 | '/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg', // package-lock.json:13078|25750 166 | 'JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/', // package-lock.json:13126|25785 167 | '/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN\\+', // package-lock.json:13141|25794 168 | 'z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ', // package-lock.json:13141|25794 169 | 'laJTa3Jb\\+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/', // package-lock.json:13208|25840 170 | 'w9F2EyxfXGo4UyJc4pFL\\+\\+FMjnq0HJS69T3M7d//', // package-lock.json:13248|25871 171 | 'PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw', // package-lock.json:13257|25877 172 | '/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN\\+ZDS', // package-lock.json:13269|25883 173 | 'AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8', // package-lock.json:13269|25883 174 | '9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA', // package-lock.json:14177|26655 175 | '9serjQBIztjRz6FkJez9D', // package-lock.json:14177|26655 176 | '7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg', // package-lock.json:14234|14234|26700|26700 177 | 'Zim1F8Kq4cBnikNhlCMlg', // package-lock.json:14269|26729 178 | '\\+XYOXAVPtWui0ly0NtohUscw\\+UmaHiAWT8hrV1rr', // package-lock.json:14285|26742 179 | 'MNoJdEDLaf8OxYyoWgorQ', // package-lock.json:14332|26779 180 | '/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g', // package-lock.json:14432|26843 181 | 'YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4', // package-lock.json:14598|26971 182 | 'evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq', // package-lock.json:14625|26994 183 | 'LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA', // package-lock.json:14674|27033 184 | 'jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/', // package-lock.json:14817|27137 185 | 'oTZqweIP51xaGPI4uPa56', // package-lock.json:14826|27146 186 | 'eEQ97k66aiEVpNnapVj90', // package-lock.json:14835|27154 187 | 'FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC', // package-lock.json:14840|27159 188 | 'OAvrKmLulotDAiO1Q6ixchPFaHYsis2zZBZSJTR0', // package-lock.json:14846|14846|27165|27165 189 | '2e8H3Od7mQK4o6j6SzHD4', // package-lock.json:14869|27185 190 | 'SmlNwCqdpaPg3ev1HKkMBsIiXeSUwpbA', // package-lock.json:14887|27200 191 | 'Q21c5zPuZ1pl\\+NfxVdxPtdHvmNVOQ6XSYG4AUtyt', // package-lock.json:14935|27241 192 | '/pIz4fMc4Ef\\+5HSQqTEug2S9JZIWDR47duDSLfaA', // package-lock.json:15052|27330 193 | '/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw', // package-lock.json:15071|27346 194 | 'DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg' // package-lock.json:15092|15092|27364|27364 195 | ] 196 | } 197 | }; 198 | --------------------------------------------------------------------------------