├── .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 | [](https://badge.fury.io/js/%40financial-times%2Fn-express-monitor)
6 | [](https://circleci.com/gh/Financial-Times/n-express-monitor)
7 | [](https://coveralls.io/github/Financial-Times/n-express-monitor?branch=main)
8 | [](https://gitter.im/Financial-Times/n-express-monitor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
9 | [](https://app.fossa.io/projects/git%2Bgithub.com%2FFinancial-Times%2Fn-express-monitor?ref=badge_shield)
10 |
11 | 
12 | [](https://snyk.io/test/github/Financial-Times/n-express-monitor)
13 | [](https://scrutinizer-ci.com/g/Financial-Times/n-express-monitor/?branch=main)
14 | [](https://david-dm.org/Financial-Times/n-express-monitor)
15 | [](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 |
--------------------------------------------------------------------------------