@mimik/sumologic-winston-logger 1.6.14 → 1.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,10 @@
1
1
  /* eslint no-process-env: "off" */
2
- const _ = require('lodash');
3
2
  const fs = require('fs');
3
+ const isUndefined = require('lodash.isundefined');
4
+ const isNil = require('lodash.isnil');
5
+ const trim = require('lodash.trim');
6
+ const split = require('lodash.split');
7
+ const difference = require('lodash.difference');
4
8
 
5
9
  /**
6
10
  *
@@ -91,7 +95,7 @@ const checkConfig = (config) => {
91
95
  if (typeof node[prop] === 'object' && node[prop]) {
92
96
  traverseNodeSync(node[prop], `${path}.${prop}`);
93
97
  }
94
- else if (_.isUndefined(node[prop])) errs.push(`${path}.${prop}`);
98
+ else if (isUndefined(node[prop])) errs.push(`${path}.${prop}`);
95
99
  });
96
100
  }
97
101
 
@@ -105,9 +109,9 @@ const checkMode = (mode) => {
105
109
  let logMode = null;
106
110
 
107
111
  if (mode) {
108
- logMode = _.split(_.trim(mode), /\s*,\s*/);
112
+ logMode = split(trim(mode), /\s*,\s*/);
109
113
  if (logMode.length === 0) throw new Error('Invalid LOG_MODE: cannot be an empty array');
110
- if (_.difference(logMode, ALL_MODES).length !== 0) throw new Error(`Invalid items in LOG_MODE: ${mode}`);
114
+ if (difference(logMode, ALL_MODES).length !== 0) throw new Error(`Invalid items in LOG_MODE: ${mode}`);
111
115
  if (logMode.includes(NONE) && logMode.length !== 1) throw new Error(`Cannot have multiple modes when ${NONE} is selected`);
112
116
  if (logMode.includes(ALL)) logMode = [SUMOLOGIC, AWS_S3]; // legacy support
113
117
  }
@@ -149,8 +153,8 @@ if (configuration.mode.includes(AWS_KINESIS)) {
149
153
  maxEvents: parseInt(process.env.KINESIS_AWS_MAX_EVENTS, 10) || DEFAULT_KINESIS_MAX_EVENTS,
150
154
  };
151
155
 
152
- if (!_.isNil(process.env.KINESIS_AWS_ACCESS_KEY_ID)) configuration[AWS_KINESIS].accessKeyId = process.env.KINESIS_AWS_ACCESS_KEY_ID;
153
- if (!_.isNil(process.env.KINESIS_AWS_SECRET_ACCESS_KEY)) configuration[AWS_KINESIS].secretAccessKey = process.env.KINESIS_AWS_SECRET_ACCESS_KEY;
156
+ if (!isNil(process.env.KINESIS_AWS_ACCESS_KEY_ID)) configuration[AWS_KINESIS].accessKeyId = process.env.KINESIS_AWS_ACCESS_KEY_ID;
157
+ if (!isNil(process.env.KINESIS_AWS_SECRET_ACCESS_KEY)) configuration[AWS_KINESIS].secretAccessKey = process.env.KINESIS_AWS_SECRET_ACCESS_KEY;
154
158
  }
155
159
  if (configuration.mode.includes(AWS_S3)) {
156
160
  configuration[AWS_S3] = {
@@ -161,8 +165,8 @@ if (configuration.mode.includes(AWS_S3)) {
161
165
  maxEvents: parseInt(process.env.S3_AWS_MAX_EVENTS, 10) || DEFAULT_S3_MAX_EVENTS,
162
166
  };
163
167
 
164
- if (!_.isNil(process.env.S3_AWS_ACCESS_KEY_ID)) configuration[AWS_S3].accessKeyId = process.env.S3_AWS_ACCESS_KEY_ID;
165
- if (!_.isNil(process.env.S3_AWS_SECRET_ACCESS_KEY)) configuration[AWS_S3].secretAccessKey = process.env.S3_AWS_SECRET_ACCESS_KEY;
168
+ if (!isNil(process.env.S3_AWS_ACCESS_KEY_ID)) configuration[AWS_S3].accessKeyId = process.env.S3_AWS_ACCESS_KEY_ID;
169
+ if (!isNil(process.env.S3_AWS_SECRET_ACCESS_KEY)) configuration[AWS_S3].secretAccessKey = process.env.S3_AWS_SECRET_ACCESS_KEY;
166
170
  }
167
171
  const { filter } = configuration;
168
172
  let filterConfig = [];
package/index.js CHANGED
@@ -18,6 +18,7 @@ const {
18
18
  WARN,
19
19
  // LOG,
20
20
  FLUSH,
21
+ FLUSH_EXIT,
21
22
  } = require('./lib/common');
22
23
 
23
24
  let sumo;
@@ -108,17 +109,54 @@ logger.flushAndExit = (code) => {
108
109
 
109
110
  if (config.mode.includes(NONE)) return process.exit(code);
110
111
  if (awsS3) {
111
- awsS3.flush();
112
- awsS3.on(FLUSH, () => {
112
+ awsS3.flush(FLUSH_EXIT);
113
+ awsS3.on(FLUSH_EXIT, () => {
113
114
  if ((!sumo || sumoDone) && (!awsKinesis || awsKinesisDone)) return process.exit(code);
114
115
  awsS3Done = true;
115
116
  return null;
116
117
  });
117
118
  }
119
+ if (sumo) {
120
+ sumo.flush(FLUSH_EXIT);
121
+ sumo.on(FLUSH_EXIT, () => {
122
+ if ((!awsS3 || awsS3Done) && (!awsKinesis || awsKinesisDone)) return process.exit(code);
123
+ sumoDone = true;
124
+ return null;
125
+ });
126
+ }
127
+ if (awsKinesis) {
128
+ awsKinesis.flush(FLUSH_EXIT);
129
+ awsKinesis.on(FLUSH_EXIT, () => {
130
+ if ((!awsS3 || awsS3Done) && (!sumo || sumoDone)) return process.exit(code);
131
+ awsKinesisDone = true;
132
+ return null;
133
+ });
134
+ }
135
+ return null;
136
+ };
137
+
138
+ setTimeout(flushing, config.exitDelay);
139
+ };
140
+
141
+ logger.flush = () => {
142
+ const flushing = () => {
143
+ let sumoDone = false;
144
+ let awsS3Done = false;
145
+ let awsKinesisDone = false;
146
+
147
+ if (config.mode.includes(NONE)) return null;
148
+ if (awsS3) {
149
+ awsS3.flush(FLUSH);
150
+ awsS3.on(FLUSH, () => {
151
+ if ((!sumo || sumoDone) && (!awsKinesis || awsKinesisDone)) return null;
152
+ awsS3Done = true;
153
+ return null;
154
+ });
155
+ }
118
156
  if (sumo) {
119
157
  sumo.flush();
120
158
  sumo.on(FLUSH, () => {
121
- if ((!awsS3 || awsS3Done) && (!awsKinesis || awsKinesisDone)) return process.exit(code);
159
+ if ((!awsS3 || awsS3Done) && (!awsKinesis || awsKinesisDone)) return null;
122
160
  sumoDone = true;
123
161
  return null;
124
162
  });
@@ -126,7 +164,7 @@ logger.flushAndExit = (code) => {
126
164
  if (awsKinesis) {
127
165
  awsKinesis.flush();
128
166
  awsKinesis.on(FLUSH, () => {
129
- if ((!awsS3 || awsS3Done) && (!sumo || sumoDone)) return process.exit(code);
167
+ if ((!awsS3 || awsS3Done) && (!sumo || sumoDone)) return null;
130
168
  awsKinesisDone = true;
131
169
  return null;
132
170
  });
@@ -1,15 +1,11 @@
1
1
  const Transport = require('winston-transport');
2
2
  const Promise = require('bluebird');
3
- const AWS = require('aws-sdk');
4
-
5
- AWS.config.setPromisesDependency(Promise);
3
+ const { KinesisClient, PutRecordsCommand } = require('@aws-sdk/client-kinesis');
6
4
 
7
5
  const {
8
- KINESIS_AWS_API_VERSION,
9
6
  AWS_KINESIS,
10
7
  LOG,
11
8
  WARN,
12
- FLUSH,
13
9
  MESSAGE,
14
10
  UNKNOWN_TYPE,
15
11
  UNKNOWN_ID,
@@ -40,11 +36,12 @@ module.exports = class AwsKinesis extends Transport {
40
36
  this.streamNameInfo = options.streamNameInfo;
41
37
  this.streamNameError = options.streamNameError;
42
38
  this.streamNameOther = options.streamNameOther;
43
- this.kinesis = new AWS.Kinesis({
44
- apiVersion: KINESIS_AWS_API_VERSION,
39
+ this.kinesis = new KinesisClient({
45
40
  region: options.region,
46
- accessKeyId: options.accessKeyId,
47
- secretAccessKey: options.secretAccessKey,
41
+ credentials: {
42
+ accessKeyId: options.accessKeyId,
43
+ secretAccessKey: options.secretAccessKey,
44
+ },
48
45
  });
49
46
  this.time = setInterval(() => {
50
47
  Object.keys(events).forEach((level) => {
@@ -63,10 +60,12 @@ module.exports = class AwsKinesis extends Transport {
63
60
  if (lvl === INFO) StreamName = this.streamNameInfo;
64
61
  else if (lvl === ERROR) StreamName = this.streamNameError;
65
62
  else StreamName = this.streamNameOther;
66
- return this.kinesis.putRecords({
63
+ const command = new PutRecordsCommand({
67
64
  StreamName,
68
65
  Records,
69
- }).promise();
66
+ });
67
+
68
+ return this.kinesis.send(command);
70
69
  }
71
70
 
72
71
  send(Records, lvl) {
@@ -133,8 +132,8 @@ module.exports = class AwsKinesis extends Transport {
133
132
  }
134
133
  }
135
134
 
136
- flush() {
135
+ flush(type) {
137
136
  return this.flushEvent()
138
- .then(() => this.emit(FLUSH, { message: `logs flushed to ${AWS_KINESIS}` }));
137
+ .then(() => this.emit(type, { message: `logs ${type} to ${AWS_KINESIS}` }));
139
138
  }
140
139
  };
@@ -1,15 +1,11 @@
1
1
  const Transport = require('winston-transport');
2
2
  const Promise = require('bluebird');
3
- const AWS = require('aws-sdk');
4
-
5
- AWS.config.setPromisesDependency(Promise);
3
+ const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
6
4
 
7
5
  const {
8
- S3_AWS_API_VERSION,
9
6
  AWS_S3,
10
7
  LOG,
11
8
  WARN,
12
- FLUSH,
13
9
  MESSAGE,
14
10
  UNKNOWN_TYPE,
15
11
  UNKNOWN_ID,
@@ -30,11 +26,12 @@ module.exports = class AwsS3 extends Transport {
30
26
  this.maxEvents = options.maxEvents;
31
27
  this.timeInterval = 1000 * 60 * options.timeout; // in minutes
32
28
  this.bucketname = options.bucketname;
33
- this.s3 = new AWS.S3({
34
- apiVersion: S3_AWS_API_VERSION,
29
+ this.s3 = new S3Client({
35
30
  region: options.region,
36
- accessKeyId: options.accessKeyId,
37
- secretAccessKey: options.secretAccessKey,
31
+ credentials: {
32
+ accessKeyId: options.accessKeyId,
33
+ secretAccessKey: options.secretAccessKey,
34
+ },
38
35
  });
39
36
  this.time = setInterval(() => {
40
37
  Object.keys(events).forEach((level) => {
@@ -56,11 +53,13 @@ module.exports = class AwsS3 extends Transport {
56
53
  }
57
54
 
58
55
  put(data, lvl, date) {
59
- return this.s3.putObject({
56
+ const command = new PutObjectCommand({
60
57
  Bucket: this.bucketname,
61
58
  Key: `${lvl}/${this.server}/${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}/${date.toISOString()}.json`,
62
59
  Body: JSON.stringify(data),
63
- }).promise();
60
+ });
61
+
62
+ return this.s3.send(command);
64
63
  }
65
64
 
66
65
  send(data, lvl, date) {
@@ -77,18 +76,22 @@ module.exports = class AwsS3 extends Transport {
77
76
  const errors = [];
78
77
  let count = 0;
79
78
 
80
- return Promise.map(Object.keys(data), (sType) => Promise.each(Object.keys(data[sType]), (sId) => this.s3.putObject({
81
- Bucket: this.bucketname,
82
- Key: `${lvl}/${sType}/${sId}/${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}/${date.toISOString()}.json`,
83
- Body: JSON.stringify(data[sType][sId]),
84
- }).promise()
85
- .then(() => {
86
- count += 1;
87
- })
88
- .catch((err) => errors.push({
89
- data: data[sType][sId],
90
- error: { message: err.message, statusCode: err.statusCode || 500, details: err },
91
- }))))
79
+ return Promise.map(Object.keys(data), (sType) => Promise.each(Object.keys(data[sType]), (sId) => {
80
+ const command = new PutObjectCommand({
81
+ Bucket: this.bucketname,
82
+ Key: `${lvl}/${sType}/${sId}/${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}/${date.toISOString()}.json`,
83
+ Body: JSON.stringify(data[sType][sId]),
84
+ });
85
+
86
+ return this.s3.send(command)
87
+ .then(() => {
88
+ count += 1;
89
+ })
90
+ .catch((err) => errors.push({
91
+ data: data[sType][sId],
92
+ error: { message: err.message, statusCode: err.statusCode || 500, details: err },
93
+ }));
94
+ }))
92
95
  .then(() => ({ count, errors }));
93
96
  }
94
97
 
@@ -224,9 +227,9 @@ module.exports = class AwsS3 extends Transport {
224
227
  }
225
228
  }
226
229
 
227
- flush() {
230
+ flush(type) {
228
231
  return this.flushEvent()
229
232
  .then(() => this.flushTypeEvent())
230
- .then(() => this.emit(FLUSH, { message: `logs flushed to ${AWS_S3}` }));
233
+ .then(() => this.emit(type, { message: `logs ${type} to ${AWS_S3}` }));
231
234
  }
232
235
  };
package/lib/common.js CHANGED
@@ -5,16 +5,14 @@ const DEFAULT_LEVEL = 'debug';
5
5
  const DEFAULT_ENV = ENV_LOCAL;
6
6
  const DEFAULT_FILTER_FILE = null;
7
7
 
8
+ const ALL = 'all'; // legacy support
9
+ const NONE = 'none';
8
10
  const AWS_S3 = 'awsS3';
9
11
  const SUMOLOGIC = 'sumologic';
10
12
  const AWS_KINESIS = 'awsKinesis';
11
- const DEFAULT_MODE = [SUMOLOGIC];
12
- const ALL = 'all'; // legacy support
13
- const NONE = 'none';
13
+ const DEFAULT_MODE = [NONE];
14
14
  const ALL_MODES = [AWS_S3, SUMOLOGIC, AWS_KINESIS, ALL, NONE];
15
15
 
16
- const S3_AWS_API_VERSION = '2006-03-01';
17
- const KINESIS_AWS_API_VERSION = '2013-12-02';
18
16
  const DEFAULT_S3_MAX_SIZE = 5; // max size of the data before sending to S3, in mB
19
17
  const DEFAULT_S3_MAX_EVENTS = 1000; // max number of events before sending to S3
20
18
  const DEFAULT_S3_TIMEOUT = 5; // max time before sending events to S3, in minutes
@@ -23,7 +21,7 @@ const DEFAULT_KINESIS_MAX_EVENTS = 1000; // max number of events before sending
23
21
  const DEFAULT_KINESIS_TIMEOUT = 1000; // max time before sending events to Kinesis, in ms
24
22
 
25
23
  const DEFAULT_EXIT_DELAY = 2000; // delay for flushing the transports and exiting, in ms
26
- const DEFAULT_NO_STACK = 'no';
24
+ const DEFAULT_NO_STACK = 'yes';
27
25
 
28
26
  const SPLAT = Symbol.for('splat');
29
27
  const LEVEL = Symbol.for('level');
@@ -32,6 +30,7 @@ const MESSAGE = Symbol.for('message');
32
30
  const WARN = 'warn';
33
31
  const LOG = 'logged';
34
32
  const FLUSH = 'flushed';
33
+ const FLUSH_EXIT = 'flushed and exit';
35
34
 
36
35
  const UNKNOWN_TYPE = 'unknownServerType';
37
36
  const UNKNOWN_ID = 'unknownServerId';
@@ -55,8 +54,6 @@ module.exports = {
55
54
  ALL_MODES,
56
55
  ENV_DEV,
57
56
  ENV_LOCAL,
58
- S3_AWS_API_VERSION,
59
- KINESIS_AWS_API_VERSION,
60
57
  DEFAULT_S3_MAX_SIZE,
61
58
  DEFAULT_S3_MAX_EVENTS,
62
59
  DEFAULT_S3_TIMEOUT,
@@ -71,6 +68,7 @@ module.exports = {
71
68
  WARN,
72
69
  LOG,
73
70
  FLUSH,
71
+ FLUSH_EXIT,
74
72
  UNKNOWN_TYPE,
75
73
  UNKNOWN_ID,
76
74
  CLIENTS,
package/lib/formatLib.js CHANGED
@@ -1,5 +1,8 @@
1
1
  const { format } = require('winston');
2
- const _ = require('lodash');
2
+ const forEach = require('lodash.foreach');
3
+ const isObject = require('lodash.isobject');
4
+ const isString = require('lodash.isstring');
5
+ const isNil = require('lodash.isnil');
3
6
 
4
7
  const { logs, isProd } = require('@mimik/lib-filters');
5
8
 
@@ -41,7 +44,7 @@ const correlationId = format((origInfo) => {
41
44
  const info = origInfo;
42
45
  const meta = info[SPLAT];
43
46
 
44
- if (meta && _.isString(meta[meta.length - 1])) {
47
+ if (meta && isString(meta[meta.length - 1])) {
45
48
  const results = meta[meta.length - 1].split('/');
46
49
 
47
50
  ([info.correlationId, info.requestedAt] = results);
@@ -63,14 +66,14 @@ const errorStack = format((origInfo) => {
63
66
  let newValue;
64
67
  let prop;
65
68
 
66
- _.forEach(meta, (item) => {
67
- if (_.isObject(item)) {
69
+ forEach(meta, (item) => {
70
+ if (isObject(item)) {
68
71
  target = item;
69
72
  return false;
70
73
  }
71
74
  return true;
72
75
  });
73
- _.forEach(target, (origValue, key) => {
76
+ forEach(target, (origValue, key) => {
74
77
  let value = origValue;
75
78
 
76
79
  if (value instanceof Error) {
@@ -96,8 +99,8 @@ const filterMeta = format((origInfo, opts) => {
96
99
  let target;
97
100
  let index;
98
101
 
99
- _.forEach(meta, (origItem, i) => {
100
- if (!_.isObject(origItem)) return true;
102
+ forEach(meta, (origItem, i) => {
103
+ if (!isObject(origItem)) return true;
101
104
  let item = origItem;
102
105
 
103
106
  // in order to vaoid having the do it for in the rest of the code for mongoose objects
@@ -115,9 +118,9 @@ const filterMeta = format((origInfo, opts) => {
115
118
  index = i;
116
119
  return false;
117
120
  });
118
- if (_.isNil(index)) return info;
121
+ if (isNil(index)) return info;
119
122
  meta[index] = target;
120
- _.forEach(target, (value, key) => {
123
+ forEach(target, (value, key) => {
121
124
  if (info[key] && !isReserved(key)) {
122
125
  info[key] = value;
123
126
  }
package/lib/stackLib.js CHANGED
@@ -1,6 +1,7 @@
1
- const _ = require('lodash');
2
1
  const crypto = require('crypto');
3
2
  const path = require('path');
3
+ const reject = require('lodash.reject');
4
+ const trimStart = require('lodash.trimstart');
4
5
 
5
6
  // Stack trace format :
6
7
  // https://github.com/v8/v8/wiki/Stack%20Trace%20API
@@ -16,7 +17,7 @@ const parseStack = (newError) => {
16
17
  const stackList = newError.stack.split('\n').slice(1);
17
18
  // remove all lines that refer to this file.
18
19
  // start the stack trace from the code that called this module.
19
- const truncatedList = _.reject(stackList, (line) => line.includes(__filename) || line.includes('winston') || line.includes('logform') || line.includes('formatLib'));
20
+ const truncatedList = reject(stackList, (line) => line.includes(__filename) || line.includes('winston') || line.includes('logform') || line.includes('formatLib'));
20
21
 
21
22
  if (truncatedList.length === 0) return null;
22
23
  const firstLine = truncatedList[0];
@@ -29,7 +30,7 @@ const parseStack = (newError) => {
29
30
  line: stackParts[3],
30
31
  pos: stackParts[4],
31
32
  file: path.basename(stackParts[2]),
32
- stack: ` ${_.trimStart(truncatedList.join('\n'))}`,
33
+ stack: ` ${trimStart(truncatedList.join('\n'))}`,
33
34
  };
34
35
  stackInfo.hash = crypto.createHash('sha256', SECRET)
35
36
  .update(stackInfo.stack)
@@ -5,7 +5,6 @@ const {
5
5
  SUMOLOGIC,
6
6
  LOG,
7
7
  WARN,
8
- FLUSH,
9
8
  UNKNOWN_TYPE,
10
9
  UNKNOWN_ID,
11
10
  CLIENTS,
@@ -67,7 +66,7 @@ module.exports = class Sumo extends Transport {
67
66
  if (callback) setImmediate(callback);
68
67
  }
69
68
 
70
- flush() {
71
- return Promise.resolve().then(() => this.emit(FLUSH, { message: `logs flushed to ${SUMOLOGIC}` }));
69
+ flush(type) {
70
+ return Promise.resolve().then(() => this.emit(type, { message: `logs ${type} to ${SUMOLOGIC}` }));
72
71
  }
73
72
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mimik/sumologic-winston-logger",
3
- "version": "1.6.14",
3
+ "version": "1.6.16",
4
4
  "description": "Log wrapper for sumo, s3, kinesis and winston",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -33,33 +33,44 @@
33
33
  "url": "https://bitbucket.org/mimiktech/sumologic-winston-logger"
34
34
  },
35
35
  "dependencies": {
36
- "@mimik/lib-filters": "^1.4.6",
37
- "aws-sdk": "2.1282.0",
38
- "axios": "1.2.1",
36
+ "@mimik/lib-filters": "^1.4.8",
37
+ "@aws-sdk/client-s3": "3.388.0",
38
+ "@aws-sdk/client-kinesis": "3.388.0",
39
+ "axios": "1.4.0",
39
40
  "bluebird": "3.7.2",
40
- "lodash": "4.17.21",
41
- "winston": "3.8.2",
41
+ "lodash.difference": "4.5.0",
42
+ "lodash.foreach": "4.5.0",
43
+ "lodash.isnil": "4.0.0",
44
+ "lodash.isobject": "3.0.2",
45
+ "lodash.isstring": "4.0.1",
46
+ "lodash.isundefined": "",
47
+ "lodash.reject": "4.6.0",
48
+ "lodash.split": "4.4.2",
49
+ "lodash.trim": "4.5.1",
50
+ "lodash.trimstart": "4.5.1",
51
+ "winston": "3.10.0",
42
52
  "winston-transport": "4.5.0"
43
53
  },
44
54
  "devDependencies": {
45
55
  "@mimik/eslint-plugin-dependencies": "^2.4.5",
46
56
  "@mimik/eslint-plugin-document-env": "^1.0.5",
47
57
  "@mimik/request-helper": "^1.7.8",
48
- "body-parser": "1.20.1",
58
+ "body-parser": "1.20.2",
49
59
  "chai": "4.3.7",
50
- "eslint": "8.30.0",
60
+ "eslint": "8.46.0",
51
61
  "eslint-config-airbnb": "19.0.4",
52
- "eslint-plugin-import": "2.26.0",
53
- "eslint-plugin-jsx-a11y": "6.6.1",
54
- "eslint-plugin-react": "7.31.11",
62
+ "eslint-plugin-import": "2.28.0",
63
+ "eslint-plugin-jsx-a11y": "6.7.1",
64
+ "eslint-plugin-react": "7.33.1",
55
65
  "eslint-plugin-react-hooks": "4.6.0",
56
66
  "express": "4.18.2",
57
- "husky": "8.0.2",
67
+ "husky": "8.0.3",
58
68
  "jsdoc-to-markdown": "8.0.0",
69
+ "lodash": "4.17.21",
59
70
  "mocha": "10.2.0",
60
71
  "mochawesome": "7.1.3",
61
72
  "nyc": "15.1.0",
62
- "sinon": "15.0.1",
73
+ "sinon": "15.2.0",
63
74
  "supertest": "6.3.3"
64
75
  }
65
76
  }