kayvee 3.17.0 → 3.18.0

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.
@@ -2,13 +2,6 @@
2
2
  * Module dependencies.
3
3
  * @private
4
4
  */
5
- var __spreadArrays = (this && this.__spreadArrays) || function () {
6
- for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
7
- for (var r = Array(s), k = 0, i = 0; i < il; i++)
8
- for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
9
- r[k] = a[j];
10
- return r;
11
- };
12
5
  var fs = require("fs");
13
6
  var path = require("path");
14
7
  var kayvee = require("../lib/kayvee");
@@ -18,11 +11,10 @@ var _ = require("underscore");
18
11
  /**
19
12
  * all relative files path in a directory
20
13
  */
21
- function walkDirSync(dir, files) {
22
- if (files === void 0) { files = []; }
23
- var list = fs.readdirSync(dir);
24
- list.forEach(function (file) {
25
- var f = path.join(dir, file);
14
+ function walkDirSync(dir, files = []) {
15
+ const list = fs.readdirSync(dir);
16
+ list.forEach((file) => {
17
+ const f = path.join(dir, file);
26
18
  if (fs.statSync(path.join(dir, file)).isDirectory()) {
27
19
  walkDirSync(f, files);
28
20
  }
@@ -30,7 +22,7 @@ function walkDirSync(dir, files) {
30
22
  files.push(f);
31
23
  }
32
24
  });
33
- return files.map(function (f) { return path.relative(dir, f); });
25
+ return files.map((f) => path.relative(dir, f));
34
26
  }
35
27
  /**
36
28
  * returns a middleware function that checks if path exists in dir.
@@ -38,12 +30,11 @@ function walkDirSync(dir, files) {
38
30
  * Files in the directory are prefixed by base_path and compared to
39
31
  * req.path
40
32
  */
41
- function skip_path(dir, base_path) {
42
- if (base_path === void 0) { base_path = "/"; }
43
- var files = walkDirSync(dir);
44
- files = files.map(function (file) { return path.join(base_path, file); });
45
- console.error("KayveeMiddleware: Skipping successful requests for files in " + dir + " at " + base_path);
46
- return function (req, res) { return _(files).contains(req.path) && res.statusCode < 400; };
33
+ function skip_path(dir, base_path = "/") {
34
+ let files = walkDirSync(dir);
35
+ files = files.map((file) => path.join(base_path, file));
36
+ console.error(`KayveeMiddleware: Skipping successful requests for files in ${dir} at ${base_path}`);
37
+ return (req, res) => _(files).contains(req.path) && res.statusCode < 400;
47
38
  }
48
39
  /**
49
40
  * request path
@@ -63,7 +54,7 @@ function getQueryParams(req) {
63
54
  allowPrototypes: false,
64
55
  ignoreQueryPrefix: true,
65
56
  });
66
- return "?" + require("qs").stringify(parsedQueryString);
57
+ return `?${require("qs").stringify(parsedQueryString)}`;
67
58
  }
68
59
  /**
69
60
  * response size
@@ -104,8 +95,8 @@ function getIp(req) {
104
95
  * Log level
105
96
  */
106
97
  function getLogLevel(req, res) {
107
- var statusCode = res.statusCode;
108
- var result;
98
+ const statusCode = res.statusCode;
99
+ let result;
109
100
  if (statusCode >= 499) {
110
101
  result = KayveeLogger.Error;
111
102
  }
@@ -119,53 +110,45 @@ function getLogLevel(req, res) {
119
110
  */
120
111
  var defaultHandlers = [
121
112
  // Request method
122
- function (req) { return ({ method: req.method }); },
113
+ (req) => ({ method: req.method }),
123
114
  // Path (URL without query params)
124
- function (req) { return ({ path: getBaseUrl(req) }); },
115
+ (req) => ({ path: getBaseUrl(req) }),
125
116
  // Query params
126
- function (req) { return ({ params: getQueryParams(req) }); },
117
+ (req) => ({ params: getQueryParams(req) }),
127
118
  // Response size
128
- function (req, res) { return ({ "response-size": getResponseSize(res) }); },
119
+ (req, res) => ({ "response-size": getResponseSize(res) }),
129
120
  // Response time (ns)
130
- function (req, res) { return ({ "response-time": getResponseTimeNs(req, res) }); },
121
+ (req, res) => ({ "response-time": getResponseTimeNs(req, res) }),
131
122
  // Status code
132
- function (req, res) { return ({ "status-code": res.statusCode }); },
123
+ (req, res) => ({ "status-code": res.statusCode }),
133
124
  // Ip address
134
- function (req) { return ({ ip: getIp(req) }); },
125
+ (req) => ({ ip: getIp(req) }),
135
126
  // Via -- what library/code produced this log?
136
- function () { return ({ via: "kayvee-middleware" }); },
127
+ () => ({ via: "kayvee-middleware" }),
137
128
  // Kayvee's reserved fields
138
129
  // Log level
139
- function (req, res) { return ({ level: getLogLevel(req, res) }); },
130
+ (req, res) => ({ level: getLogLevel(req, res) }),
140
131
  // Source -- which app emitted this log?
141
132
  // -> Gets passed in among `options` during library initialization
142
133
  // Title
143
- function () { return ({ title: "request-finished" }); },
134
+ () => ({ title: "request-finished" }),
144
135
  ];
145
- var defaultContextHandlers = [];
146
- function handlerData(handlers) {
147
- var args = [];
148
- for (var _i = 1; _i < arguments.length; _i++) {
149
- args[_i - 1] = arguments[_i];
150
- }
151
- var data = {};
152
- handlers.forEach(function (h) {
136
+ const defaultContextHandlers = [];
137
+ function handlerData(handlers, ...args) {
138
+ const data = {};
139
+ handlers.forEach((h) => {
153
140
  try {
154
- var handler_data = h.apply(void 0, args);
141
+ const handler_data = h(...args);
155
142
  _.extend(data, handler_data);
156
143
  }
157
144
  catch (e) {
158
- // ignore invalid handler
145
+ // swallow invalid handler
159
146
  }
160
147
  });
161
148
  return data;
162
149
  }
163
- var ContextLogger = /** @class */ (function () {
164
- function ContextLogger(logger, handlers) {
165
- var args = [];
166
- for (var _i = 2; _i < arguments.length; _i++) {
167
- args[_i - 2] = arguments[_i];
168
- }
150
+ class ContextLogger {
151
+ constructor(logger, handlers, ...args) {
169
152
  this.logger = null;
170
153
  this.handlers = [];
171
154
  this.args = [];
@@ -173,34 +156,25 @@ var ContextLogger = /** @class */ (function () {
173
156
  this.handlers = handlers;
174
157
  this.args = args;
175
158
  }
176
- ContextLogger.prototype._contextualData = function (data) {
177
- return _.extend(handlerData.apply(void 0, __spreadArrays([this.handlers], this.args)), data);
178
- };
179
- return ContextLogger;
180
- }());
181
- var _loop_1 = function (func) {
159
+ _contextualData(data) {
160
+ return _.extend(handlerData(this.handlers, ...this.args), data);
161
+ }
162
+ }
163
+ for (const func of KayveeLogger.LEVELS) {
182
164
  ContextLogger.prototype[func] = function (title) {
183
- this[func + "D"](title, {});
165
+ this[`${func}D`](title, {});
184
166
  };
185
- ContextLogger.prototype[func + "D"] = function (title, data) {
186
- this.logger[func + "D"](title, this._contextualData(data));
167
+ ContextLogger.prototype[`${func}D`] = function (title, data) {
168
+ this.logger[`${func}D`](title, this._contextualData(data));
187
169
  };
188
- };
189
- for (var _i = 0, _a = KayveeLogger.LEVELS; _i < _a.length; _i++) {
190
- var func = _a[_i];
191
- _loop_1(func);
192
170
  }
193
- var _loop_2 = function (func) {
171
+ for (const func of KayveeLogger.METRICS) {
194
172
  ContextLogger.prototype[func] = function (title, value) {
195
- this[func + "D"](title, value, {});
173
+ this[`${func}D`](title, value, {});
196
174
  };
197
- ContextLogger.prototype[func + "D"] = function (title, value, data) {
198
- this.logger[func + "D"](title, value, this._contextualData(data));
175
+ ContextLogger.prototype[`${func}D`] = function (title, value, data) {
176
+ this.logger[`${func}D`](title, value, this._contextualData(data));
199
177
  };
200
- };
201
- for (var _b = 0, _c = KayveeLogger.METRICS; _b < _c.length; _b++) {
202
- var func = _c[_b];
203
- _loop_2(func);
204
178
  }
205
179
  /*
206
180
  * User configuration is passed via an `options` object.
@@ -225,20 +199,20 @@ for (var _b = 0, _c = KayveeLogger.METRICS; _b < _c.length; _b++) {
225
199
  * base_handlers: e.g. [function(request, response) { return {"key":"val"}]
226
200
  *
227
201
  */
228
- var formatLine = function (options_arg) {
202
+ var formatLine = (options_arg) => {
229
203
  var options = options_arg || {};
230
204
  // `source` is the one required field
231
205
  if (!options.source) {
232
206
  throw Error("Missing required config for 'source' in Kayvee middleware 'options'");
233
207
  }
234
- var router = KayveeLogger.getGlobalRouter();
235
- return function (tokens, req, res) {
208
+ const router = KayveeLogger.getGlobalRouter();
209
+ return (tokens, req, res) => {
236
210
  // Build a dict of data to log
237
211
  var data = { _kvmeta: undefined }; // Adding _kvmeta here to make typescript compile happy
238
212
  // Add user-configured request headers
239
213
  var custom_headers = options.headers || [];
240
214
  var header_data = {};
241
- custom_headers.forEach(function (h) {
215
+ custom_headers.forEach((h) => {
242
216
  // Header field names are case insensitive, so let's be consistent
243
217
  var lc = h.toLowerCase();
244
218
  header_data[lc] = req.headers[lc];
@@ -248,9 +222,9 @@ var formatLine = function (options_arg) {
248
222
  var custom_handlers = options.handlers || [];
249
223
  // Allow user to override `base_handlers`; provide sane default set of handlers
250
224
  var base_handlers = options.base_handlers || defaultHandlers;
251
- base_handlers = base_handlers.concat([function () { return ({ source: options.source }); }]);
225
+ base_handlers = base_handlers.concat([() => ({ source: options.source })]);
252
226
  // Execute custom-handlers THEN base-handlers
253
- var all_handlers = custom_handlers.concat(base_handlers);
227
+ const all_handlers = custom_handlers.concat(base_handlers);
254
228
  _.extend(data, handlerData(all_handlers, req, res));
255
229
  if (router) {
256
230
  data._kvmeta = router.route(data);
@@ -258,7 +232,7 @@ var formatLine = function (options_arg) {
258
232
  return kayvee.format(data);
259
233
  };
260
234
  };
261
- var defaultContextLoggerOpts = {
235
+ const defaultContextLoggerOpts = {
262
236
  enabled: true,
263
237
  handlers: defaultContextHandlers,
264
238
  };
@@ -267,8 +241,7 @@ var defaultContextLoggerOpts = {
267
241
  * @public
268
242
  */
269
243
  if (process.env.NODE_ENV === "test") {
270
- module.exports = function (clever_options, morgan_options) {
271
- if (morgan_options === void 0) { morgan_options = { skip: null }; }
244
+ module.exports = (clever_options, morgan_options = { skip: null }) => {
272
245
  if (clever_options.ignore_dir) {
273
246
  morgan_options.skip = skip_path(clever_options.ignore_dir.directory, clever_options.ignore_dir.path);
274
247
  }
@@ -277,22 +250,21 @@ if (process.env.NODE_ENV === "test") {
277
250
  module.exports.ContextLogger = ContextLogger;
278
251
  }
279
252
  else {
280
- module.exports = function (clever_options, context_logger_options) {
281
- if (context_logger_options === void 0) { context_logger_options = defaultContextLoggerOpts; }
253
+ module.exports = (clever_options, context_logger_options = defaultContextLoggerOpts) => {
282
254
  // `source` is the one required field
283
255
  if (!clever_options.source) {
284
256
  throw new Error("Missing required config for 'source' in Kayvee middleware 'options'");
285
257
  }
286
- var logger = new KayveeLogger(clever_options.source);
287
- var morgan_options = {
258
+ const logger = new KayveeLogger(clever_options.source);
259
+ const morgan_options = {
288
260
  stream: process.stderr,
289
261
  skip: null,
290
262
  };
291
263
  if (clever_options.ignore_dir) {
292
264
  morgan_options.skip = skip_path(clever_options.ignore_dir.directory, clever_options.ignore_dir.path);
293
265
  }
294
- var morgan_logger = morgan(formatLine(clever_options), morgan_options);
295
- return function (req, res, next) {
266
+ const morgan_logger = morgan(formatLine(clever_options), morgan_options);
267
+ return (req, res, next) => {
296
268
  if (context_logger_options.enabled) {
297
269
  req.log = new ContextLogger(logger, context_logger_options.handlers, req);
298
270
  }
@@ -4,20 +4,20 @@ var schema = require("./schema_definitions");
4
4
  var yaml = require("js-yaml");
5
5
  var _ = require("underscore");
6
6
  var packageJson = require("../../package.json");
7
- var kvVersion = packageJson.version;
8
- var teamName = process.env._TEAM_OWNER || "UNSET";
9
- var reEnvvarTokens = new RegExp("\\$\\{(.+?)\\}", "g");
10
- var reFieldTokens = new RegExp("%\\{(.+?)\\}", "g");
7
+ const kvVersion = packageJson.version;
8
+ const teamName = process.env._TEAM_OWNER || "UNSET";
9
+ const reEnvvarTokens = new RegExp("\\$\\{(.+?)\\}", "g");
10
+ const reFieldTokens = new RegExp("%\\{(.+?)\\}", "g");
11
11
  // For performance reason this code is intentionally redundant and not-inlined.
12
12
  // Removing redundancy and inlining this function some how makes performance worst.
13
13
  function substituteEnvVars(obj, subber) {
14
- var rtn = {};
15
- var replacer = function (s) { return s.replace(reEnvvarTokens, function (__, p1) { return subber(p1); }); };
16
- for (var key in obj) {
17
- var val = obj[key];
14
+ const rtn = {};
15
+ const replacer = (s) => s.replace(reEnvvarTokens, (__, p1) => subber(p1));
16
+ for (const key in obj) {
17
+ const val = obj[key];
18
18
  if (Array.isArray(val)) {
19
- var updatedVals = Array(val.length);
20
- for (var i = 0; i < val.length; i++) {
19
+ const updatedVals = Array(val.length);
20
+ for (let i = 0; i < val.length; i++) {
21
21
  updatedVals[i] = replacer(val[i]);
22
22
  }
23
23
  rtn[key] = updatedVals;
@@ -29,51 +29,51 @@ function substituteEnvVars(obj, subber) {
29
29
  return rtn;
30
30
  }
31
31
  function deepKey(obj, key) {
32
- var path = key.split(".");
33
- var idx = 0;
34
- var val = obj;
32
+ const path = key.split(".");
33
+ let idx = 0;
34
+ let val = obj;
35
35
  do {
36
36
  val = val[path[idx++]];
37
37
  } while (val && idx < path.length);
38
38
  return val;
39
39
  }
40
40
  function fieldMatches(obj, field, values) {
41
- var val = obj[field] || deepKey(obj, field);
41
+ const val = obj[field] || deepKey(obj, field);
42
42
  if (val == null || val === "") {
43
43
  return false;
44
44
  }
45
45
  if (values[0] === "*") {
46
46
  return true;
47
47
  }
48
- for (var i = 0; i < values.length; i++) {
48
+ for (let i = 0; i < values.length; i++) {
49
49
  if (values[i] === val) {
50
50
  return true;
51
51
  }
52
52
  }
53
53
  return false;
54
54
  }
55
- var Rule = /** @class */ (function () {
56
- function Rule(name, matchers, output) {
55
+ class Rule {
56
+ constructor(name, matchers, output) {
57
57
  this.name = null;
58
58
  this.matchers = null;
59
59
  this.output = null;
60
60
  this.name = name;
61
61
  this.matchers = matchers;
62
- var envMissing = [];
63
- this.output = substituteEnvVars(output, function (k) {
64
- var val = process.env[k];
62
+ const envMissing = [];
63
+ this.output = substituteEnvVars(output, (k) => {
64
+ const val = process.env[k];
65
65
  if (val == null) {
66
66
  envMissing.push(k);
67
67
  }
68
68
  return val;
69
69
  });
70
70
  if (envMissing.length > 0) {
71
- throw new Error("Missing env var(s): " + envMissing.join(", "));
71
+ throw new Error(`Missing env var(s): ${envMissing.join(", ")}`);
72
72
  }
73
- Object.keys(matchers).forEach(function (field) {
74
- var fieldVals = matchers[field];
73
+ Object.keys(matchers).forEach((field) => {
74
+ const fieldVals = matchers[field];
75
75
  if (fieldVals.indexOf("*") !== -1 && fieldVals.length > 1) {
76
- throw new Error("Invalid matcher values in " + name + "." + field + ".\n" +
76
+ throw new Error(`Invalid matcher values in ${name}.${field}.\n` +
77
77
  "Wildcard matcher can't co-exist with other matchers.");
78
78
  }
79
79
  });
@@ -83,24 +83,24 @@ var Rule = /** @class */ (function () {
83
83
  this.output.rule = this.name;
84
84
  }
85
85
  // matches returns true if `msg` matches against this rule
86
- Rule.prototype.matches = function (msg) {
87
- for (var field in this.matchers) {
86
+ matches(msg) {
87
+ for (const field in this.matchers) {
88
88
  if (!fieldMatches(msg, field, this.matchers[field])) {
89
89
  return false;
90
90
  }
91
91
  }
92
92
  return true;
93
- };
93
+ }
94
94
  // returns the output with kv substitutions performed
95
- Rule.prototype.outputFor = function (msg) {
96
- var rtn = {};
97
- var subst = function (__, k) { return msg[k] || deepKey(msg, k) || "KEY_NOT_FOUND"; };
98
- var replacer = function (s) { return s.replace(reFieldTokens, subst); };
99
- for (var key in this.output) {
100
- var val = this.output[key];
95
+ outputFor(msg) {
96
+ const rtn = {};
97
+ const subst = (__, k) => msg[k] || deepKey(msg, k) || "KEY_NOT_FOUND";
98
+ const replacer = (s) => s.replace(reFieldTokens, subst);
99
+ for (const key in this.output) {
100
+ const val = this.output[key];
101
101
  if (Array.isArray(val)) {
102
- var updatedVals = Array(val.length);
103
- for (var i = 0; i < val.length; i++) {
102
+ const updatedVals = Array(val.length);
103
+ for (let i = 0; i < val.length; i++) {
104
104
  updatedVals[i] = replacer(val[i]);
105
105
  }
106
106
  rtn[key] = updatedVals;
@@ -110,9 +110,8 @@ var Rule = /** @class */ (function () {
110
110
  }
111
111
  }
112
112
  return rtn;
113
- };
114
- return Rule;
115
- }());
113
+ }
114
+ }
116
115
  // validateKVConfig ensures that `routes` matches the config schema. We have this
117
116
  // function instead of just doing a plain jsonschema.validate in order to get
118
117
  // better error messages for the "output" object (by default jsonschema would
@@ -120,11 +119,11 @@ var Rule = /** @class */ (function () {
120
119
  // formats, but won't tell you what's wrong because it doesn't let you
121
120
  // condition on the output.type property).
122
121
  function validateKVConfig(config) {
123
- var validator = new jsonschema.Validator();
124
- var results = validator.validate(config, schema);
122
+ const validator = new jsonschema.Validator();
123
+ const results = validator.validate(config, schema);
125
124
  return {
126
125
  valid: results.valid,
127
- errors: results.errors.map(function (err) { return err.stack; }),
126
+ errors: results.errors.map((err) => err.stack),
128
127
  };
129
128
  }
130
129
  // parseConfig parses and validates the configuration passed as a string. It
@@ -132,53 +131,53 @@ function validateKVConfig(config) {
132
131
  // it was successfully parsed, rules is an array of rules, and errors is an
133
132
  // array of errors.
134
133
  function parseConfig(fileString) {
135
- var config;
134
+ let config;
136
135
  try {
137
136
  config = yaml.safeLoad(fileString);
138
137
  }
139
138
  catch (e) {
140
139
  return { valid: false, rules: [], errors: [e] };
141
140
  }
142
- var validateRes = validateKVConfig(config);
141
+ const validateRes = validateKVConfig(config);
143
142
  if (!validateRes.valid) {
144
143
  return _.assign(validateRes, { rules: [] });
145
144
  }
146
145
  try {
147
- var rulesObj = _.mapObject(config.routes, function (elem, name) { return new Rule(name, elem.matchers, elem.output); });
148
- var rules = _.values(rulesObj);
149
- return { valid: true, rules: rules, errors: [] };
146
+ const rulesObj = _.mapObject(config.routes, (elem, name) => new Rule(name, elem.matchers, elem.output));
147
+ const rules = _.values(rulesObj);
148
+ return { valid: true, rules, errors: [] };
150
149
  }
151
150
  catch (e) {
152
151
  return { valid: false, rules: [], errors: [e] };
153
152
  }
154
153
  }
155
- var Router = /** @class */ (function () {
156
- function Router(rules) {
154
+ class Router {
155
+ constructor(rules) {
157
156
  this.rules = null;
158
157
  this.rules = rules || [];
159
158
  }
160
159
  // loadConfig reads in the config located at `filename` and sets the routing
161
160
  // rules to what it finds there. It should be a YAML-formatted file with
162
161
  // routing rules placed under the `routes` key on the root object.
163
- Router.prototype.loadConfig = function (filename) {
164
- var data = fs.readFileSync(filename, "utf8");
162
+ loadConfig(filename) {
163
+ const data = fs.readFileSync(filename, "utf8");
165
164
  this._loadConfigString(data);
166
- };
167
- Router.prototype._loadConfigString = function (configStr) {
168
- var parsedRules = parseConfig(configStr);
165
+ }
166
+ _loadConfigString(configStr) {
167
+ const parsedRules = parseConfig(configStr);
169
168
  if (!parsedRules.valid) {
170
169
  throw new Error(parsedRules.errors);
171
170
  }
172
171
  this.rules = parsedRules.rules;
173
- };
172
+ }
174
173
  // route matches the log line `msg` against all loaded rules and returns a
175
174
  // metadata object describing the outputs it should be sent to based on that
176
175
  // matching. logger.ts will attach this to log lines under the `_kvmeta`
177
176
  // property.
178
- Router.prototype.route = function (msg) {
179
- var outputs = [];
180
- for (var i = 0; i < this.rules.length; i++) {
181
- var rule = this.rules[i];
177
+ route(msg) {
178
+ const outputs = [];
179
+ for (let i = 0; i < this.rules.length; i++) {
180
+ const rule = this.rules[i];
182
181
  if (rule.matches(msg)) {
183
182
  outputs.push(rule.outputFor(msg));
184
183
  }
@@ -189,10 +188,9 @@ var Router = /** @class */ (function () {
189
188
  kv_language: "js",
190
189
  routes: outputs,
191
190
  };
192
- };
193
- return Router;
194
- }());
191
+ }
192
+ }
195
193
  module.exports = {
196
- Router: Router,
197
- Rule: Rule,
194
+ Router,
195
+ Rule,
198
196
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kayvee",
3
3
  "description": "Write data to key=val pairs, for human and machine readability",
4
- "version": "3.17.0",
4
+ "version": "3.18.0",
5
5
  "main": "index.js",
6
6
  "repository": {
7
7
  "type": "git",
@@ -19,20 +19,24 @@
19
19
  "@clever/prettier-config": "1.0.0",
20
20
  "@types/mocha": "^8.0.3",
21
21
  "@types/node": "^12.12.68",
22
+ "@typescript-eslint/eslint-plugin": "^5.2.0",
23
+ "@typescript-eslint/parser": "^5.2.0",
22
24
  "babel-eslint": "^6.0.2",
23
25
  "benchmark": "^2.1.1",
24
- "eslint": "^2.7.0",
26
+ "eslint": "^7.11.0",
25
27
  "eslint-config-airbnb": "^7.0.0",
26
- "eslint-plugin-jsx-a11y": "^0.6.2",
27
- "eslint-plugin-react": "^4.3.0",
28
+ "eslint-config-prettier": "^6.12.0",
29
+ "eslint-formatter-summary": "^1.1.0",
30
+ "eslint-plugin-jsx-a11y": "^6.3.1",
31
+ "eslint-plugin-react": "^7.21.5",
32
+ "eslint-plugin-react-hooks": "^4.1.2",
28
33
  "express": "^4.13.4",
29
34
  "mocha": "^3.5.3",
30
35
  "prettier": "2.1.2",
31
36
  "sinon": "^1.17.4",
32
37
  "supertest": "^1.2.0",
33
38
  "ts-node": "^9.0.0",
34
- "tslint": "^3.7.4",
35
- "typescript": "^4.0.3"
39
+ "typescript": "^3.9.10"
36
40
  },
37
41
  "scripts": {
38
42
  "test": "make -Br test",