axe 9.0.0 → 10.0.1

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.
Files changed (5) hide show
  1. package/README.md +724 -179
  2. package/dist/axe.js +1153 -7063
  3. package/dist/axe.min.js +1 -1
  4. package/lib/index.js +261 -166
  5. package/package.json +21 -16
package/lib/index.js CHANGED
@@ -2,219 +2,252 @@
2
2
 
3
3
  // eslint-disable-next-line import/no-unassigned-import
4
4
  require('console-polyfill');
5
-
6
- const cuid = require('cuid');
7
-
5
+ const combine = require('maybe-combine-errors');
8
6
  const format = require('@ladjs/format-util');
9
-
10
7
  const formatSpecifiers = require('format-specifiers');
11
-
8
+ const get = require('@strikeentco/get');
12
9
  const isError = require('iserror');
13
-
14
- const omit = require('lodash.omit');
15
-
10
+ const mergeOptions = require('merge-options');
11
+ const pMapSeries = require('p-map-series');
16
12
  const parseAppInfo = require('parse-app-info');
17
-
18
13
  const parseErr = require('parse-err');
19
-
20
- const safeStringify = require('fast-safe-stringify');
21
-
22
- const superagent = require('superagent');
23
-
14
+ const pickDeep = require('pick-deep');
15
+ const set = require('@strikeentco/set');
16
+ const unset = require('unset-value');
24
17
  const {
25
18
  boolean
26
19
  } = require('boolean');
27
-
28
20
  const pkg = require('../package.json');
29
-
30
21
  const omittedLoggerKeys = new Set(['config', 'log']);
31
22
  const levels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
32
23
  const aliases = {
33
24
  warning: 'warn',
34
25
  err: 'error'
35
26
  };
36
- const endpoint = 'https://api.cabinjs.com';
37
- const levelError = "`level` invalid, must be: ".concat(levels.join(', ')); // <https://stackoverflow.com/a/43233163>
27
+ const levelError = `\`level\` invalid, must be: ${levels.join(', ')}`;
38
28
 
29
+ // <https://github.com/sindresorhus/is-plain-obj/blob/main/index.js>
30
+ function isPlainObject(value) {
31
+ if (typeof value !== 'object' || value === null) {
32
+ return false;
33
+ }
34
+ const prototype = Object.getPrototypeOf(value);
35
+ return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
36
+ }
37
+
38
+ // <https://github.com/GeenenTijd/dotify/blob/master/dotify.js>
39
+ function dotifyToArray(obj) {
40
+ const res = [];
41
+ function recurse(obj, current) {
42
+ for (const key of Object.keys(obj)) {
43
+ const value = obj[key];
44
+ const newKey = current ? current + '.' + key : key; // joined key with dot
45
+ // if (value && typeof value === 'object' && !(value instanceof Date) && !ObjectID.isValid(value)) {
46
+ if (isPlainObject(value)) {
47
+ recurse(value, newKey); // it's a nested object, so do it again
48
+ } else {
49
+ res.push(newKey);
50
+ }
51
+ }
52
+ }
53
+ recurse(obj);
54
+ return res;
55
+ }
56
+
57
+ // <https://stackoverflow.com/a/43233163>
39
58
  function isEmpty(value) {
40
59
  return value === undefined || value === null || typeof value === 'object' && Object.keys(value).length === 0 || typeof value === 'string' && value.trim().length === 0;
41
60
  }
42
-
43
61
  function isNull(value) {
44
62
  return value === null;
45
63
  }
46
-
47
64
  function isUndefined(value) {
48
65
  return typeof value === 'undefined';
49
66
  }
50
-
51
67
  function isObject(value) {
52
68
  return typeof value === 'object' && value !== null && !Array.isArray(value);
53
69
  }
54
-
55
70
  function isString(value) {
56
71
  return typeof value === 'string';
57
72
  }
58
-
59
73
  function isFunction(value) {
60
74
  return typeof value === 'function';
61
75
  }
62
-
63
- function isBoolean(value) {
64
- return typeof value === 'boolean';
65
- }
66
-
67
76
  class Axe {
68
77
  constructor() {
69
78
  var _this = this;
70
-
71
79
  let config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
72
- this.config = Object.assign({
73
- key: '',
74
- endpoint,
75
- headers: {},
76
- timeout: 5000,
77
- retry: 3,
78
- showStack: process.env.SHOW_STACK ? boolean(process.env.SHOW_STACK) : true,
79
- meta: {
80
- show: process.env.SHOW_META ? boolean(process.env.SHOW_META) : true,
81
- showApp: process.env.SHOW_META_APP ? boolean(process.env.SHOW_META_APP) : false,
82
- omittedFields: process.env.OMIT_META_FIELDS ? process.env.OMIT_META_FIELDS.split(',').map(s => s.trim()) : []
83
- },
80
+ const remappedFields = {};
81
+ if (process.env.AXE_REMAPPED_META_FIELDS) {
82
+ const arr = process.env.AXE_REMAPPED_META_FIELDS.split(',').map(v => v.split(':'));
83
+ for (const [prop, value] of arr) {
84
+ remappedFields[prop] = value;
85
+ }
86
+ }
87
+ this.config = mergeOptions({
88
+ showStack: process.env.AXE_SHOW_STACK ? boolean(process.env.AXE_SHOW_STACK) : true,
89
+ meta: Object.assign({
90
+ show: process.env.AXE_SHOW_META ? boolean(process.env.AXE_SHOW_META) : true,
91
+ remappedFields,
92
+ omittedFields: process.env.AXE_OMIT_META_FIELDS ? process.env.AXE_OMIT_META_FIELDS.split(',').map(s => s.trim()) : ['level', 'err', 'app', 'args'],
93
+ pickedFields: process.env.AXE_PICK_META_FIELDS ? process.env.AXE_PICK_META_FIELDS.split(',').map(s => s.trim()) : [],
94
+ cleanupRemapping: true
95
+ }, typeof config.meta === 'object' ? config.meta : {}),
96
+ version: pkg.version,
84
97
  silent: false,
85
98
  logger: console,
86
99
  name: false,
87
100
  level: 'info',
88
101
  levels: ['info', 'warn', 'error', 'fatal'],
89
- // TODO: if user specifies `key` and it is `process.platform === 'browser' || process.browser || env === 'production'` then set `capture` to `true`
90
- capture: false,
91
- callback: false,
92
- appInfo: process.env.APP_INFO ? boolean(process.env.APP_INFO) : true
93
- }, config); // For backwards compatability
94
-
95
- if (this.config.showMeta) {
96
- this.config.meta.show = this.config.showMeta;
97
- delete this.config.showMeta;
98
- }
99
-
102
+ appInfo: process.env.AXE_APP_INFO ? boolean(process.env.AXE_APP_INFO) : true,
103
+ hooks: Object.assign({
104
+ pre: [],
105
+ post: []
106
+ }, typeof config.hooks === 'object' ? config.hooks : {})
107
+ }, config);
100
108
  this.appInfo = this.config.appInfo ? isFunction(parseAppInfo) ? parseAppInfo() : false : false;
101
- this.log = this.log.bind(this); // Inherit methods from parent logger
109
+ this.log = this.log.bind(this);
102
110
 
111
+ // Inherit methods from parent logger
103
112
  const methods = Object.keys(this.config.logger).filter(key => !omittedLoggerKeys.has(key));
104
-
105
113
  for (const element of methods) {
106
114
  this[element] = this.config.logger[element];
107
- } // Bind helper functions for each log level
108
-
115
+ }
109
116
 
117
+ // Bind helper functions for each log level
110
118
  for (const element of levels) {
119
+ // Ensure function exists in logger passed
120
+ if (typeof this.config.logger[element] !== 'function') {
121
+ if (element === 'fatal') {
122
+ this.config.logger.fatal = this.config.logger.error || this.config.logger.info || this.config.logger.log;
123
+ } else {
124
+ this.config.logger[element] = this.config.logger.info || this.config.logger.log;
125
+ }
126
+ }
127
+
128
+ // Bind log handler which normalizes args and populates meta
111
129
  this[element] = function () {
112
130
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
113
131
  args[_key] = arguments[_key];
114
132
  }
115
-
116
133
  return _this.log(element, ...Array.prototype.slice.call(args));
117
134
  };
118
- } // We could have used `auto-bind` but it's not compiled for browser
119
-
120
-
135
+ }
121
136
  this.setLevel = this.setLevel.bind(this);
122
137
  this.getNormalizedLevel = this.getNormalizedLevel.bind(this);
123
138
  this.setName = this.setName.bind(this);
124
- this.setCallback = this.setCallback.bind(this); // Set the logger name
125
139
 
126
- if (this.config.name) this.setName(this.config.name); // Set the logger level
140
+ // Set the logger name
141
+ if (this.config.name) this.setName(this.config.name);
127
142
 
128
- this.setLevel(this.config.level); // Aliases
143
+ // Set the logger level
144
+ this.setLevel(this.config.level);
129
145
 
146
+ // Aliases
130
147
  this.err = this.error;
131
148
  this.warning = this.warn;
132
- }
133
149
 
134
- setCallback(callback) {
135
- this.config.callback = callback;
150
+ // Pre and Post Hooks
151
+ this.pre = function (level, fn) {
152
+ this.config.hooks.pre.push(function (_level) {
153
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
154
+ args[_key2 - 1] = arguments[_key2];
155
+ }
156
+ if (level !== _level) return [...args];
157
+ return fn(...args);
158
+ });
159
+ };
160
+ this.post = function (level, fn) {
161
+ this.config.hooks.post.push(function (_level) {
162
+ for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
163
+ args[_key3 - 1] = arguments[_key3];
164
+ }
165
+ if (level !== _level) return [...args];
166
+ return fn(...args);
167
+ });
168
+ };
136
169
  }
137
-
138
170
  setLevel(level) {
139
- if (!isString(level) || !levels.includes(level)) throw new Error(levelError); // Support signale logger and other loggers that use `logLevel`
140
-
141
- if (isString(this.config.logger.logLevel)) this.config.logger.logLevel = level;else this.config.logger.level = level; // Adjusts `this.config.levels` array
171
+ if (!isString(level) || levels.indexOf(level) === -1) throw new Error(levelError);
172
+ // Support signale logger and other loggers that use `logLevel`
173
+ if (isString(this.config.logger.logLevel)) this.config.logger.logLevel = level;else this.config.logger.level = level;
174
+ // Adjusts `this.config.levels` array
142
175
  // so that it has all proceeding (inclusive)
143
-
144
176
  this.config.levels = levels.slice(levels.indexOf(level));
145
177
  }
146
-
147
178
  getNormalizedLevel(level) {
148
179
  if (!isString(level)) return 'info';
149
180
  if (isString(aliases[level])) return aliases[level];
150
- if (!levels.includes(level)) return 'info';
181
+ if (levels.indexOf(level) === -1) return 'info';
151
182
  return level;
152
183
  }
153
-
154
184
  setName(name) {
155
- if (!isString(name)) throw new Error('`name` must be a String'); // Support signale logger and other loggers that use `scope`
156
-
185
+ if (!isString(name)) throw new Error('`name` must be a String');
186
+ // Support signale logger and other loggers that use `scope`
157
187
  if (isString(this.config.logger.scope)) this.config.logger.scope = name;else this.config.logger.name = name;
158
- } // eslint-disable-next-line complexity
159
-
188
+ }
160
189
 
190
+ // eslint-disable-next-line complexity
161
191
  log(level, message, meta) {
162
192
  const originalArgs = [];
193
+ const errors = [];
163
194
  if (!isUndefined(level)) originalArgs.push(level);
164
195
  if (!isUndefined(message)) originalArgs.push(message);
165
196
  if (!isUndefined(meta)) originalArgs.push(meta);
166
-
167
- for (var _len2 = arguments.length, args = new Array(_len2 > 3 ? _len2 - 3 : 0), _key2 = 3; _key2 < _len2; _key2++) {
168
- args[_key2 - 3] = arguments[_key2];
197
+ for (var _len4 = arguments.length, args = new Array(_len4 > 3 ? _len4 - 3 : 0), _key4 = 3; _key4 < _len4; _key4++) {
198
+ args[_key4 - 3] = arguments[_key4];
169
199
  }
170
-
171
200
  for (const arg of Array.prototype.slice.call(args)) {
172
201
  originalArgs.push(arg);
173
202
  }
174
-
175
203
  const {
176
204
  config
177
205
  } = this;
178
206
  let modifier = 0;
179
-
180
207
  if (isString(level) && isString(aliases[level])) {
181
208
  level = aliases[level];
182
209
  } else if (isError(level)) {
183
210
  meta = message;
184
211
  message = level;
185
212
  level = 'error';
186
- } else if (!isString(level) || !levels.includes(level)) {
213
+ } else if (!isString(level) || levels.indexOf(level) === -1) {
187
214
  meta = message;
188
215
  message = level;
189
216
  level = this.getNormalizedLevel(level);
190
217
  modifier = -1;
191
- } // Bunyan support (meta, message, ...args)
218
+ }
192
219
 
220
+ // Return early if it is not a valid logging level
221
+ if (config.levels.indexOf(level) === -1) return;
193
222
 
223
+ // Bunyan support (meta, message, ...args)
194
224
  let isBunyan = false;
195
-
196
225
  if ((isObject(message) || Array.isArray(message)) && isString(meta)) {
197
226
  isBunyan = true;
198
227
  const _meta = meta;
199
228
  meta = message;
200
229
  message = isString(_meta) && originalArgs.length >= 3 + modifier ? format(...originalArgs.slice(2 + modifier)) : _meta;
201
- } // If message was undefined then set it to level
230
+ }
202
231
 
232
+ // If message was undefined then set it to level
233
+ if (isUndefined(message)) message = level;
203
234
 
204
- if (isUndefined(message)) message = level; // If only `message` was passed then if it was an Object
235
+ // If only `message` was passed then if it was an Object
205
236
  // preserve it as an Object by setting it as meta
206
-
207
237
  if (originalArgs.slice(1 + modifier).length === 1 && !isString(message) && !isError(message)) {
208
238
  meta = {
209
239
  message
210
240
  };
211
241
  message = level;
212
242
  } else if (!isBunyan && originalArgs.length >= 4 + modifier) {
213
- // If there are four or more args
214
- // then infer to use util.format on everything
215
- message = format(...originalArgs.slice(1 + modifier));
243
+ message = undefined;
216
244
  meta = {};
217
- } else if (!isBunyan && originalArgs.length === 3 + modifier && isString(message) && formatSpecifiers.some(t => message.includes(t))) {
245
+ const messages = [];
246
+ for (const arg of originalArgs) {
247
+ if (isError(arg)) errors.push(arg);else if (isString(arg)) messages.push(arg);
248
+ }
249
+ if (errors.length === 0 && messages.length > 0) message = format(...messages);else if (errors.length > 0 && level === 'log') level = 'error';
250
+ } else if (!isBunyan && originalArgs.length === 3 + modifier && isString(message) && formatSpecifiers.some(t => message.indexOf(t) !== -1)) {
218
251
  // Otherwise if there are three args and if the `message` contains
219
252
  // a placeholder token (e.g. '%s' or '%d' - see above `formatSpecifiers` variable)
220
253
  // then we can infer that the `meta` arg passed is used for formatting
@@ -222,9 +255,8 @@ class Axe {
222
255
  meta = {};
223
256
  } else if (!isError(message)) {
224
257
  if (isError(meta)) {
225
- meta = {
226
- err: parseErr(meta)
227
- }; // } else if (!isPlainObject(meta) && !isUndefined(meta) && !isNull(meta)) {
258
+ errors.push(meta);
259
+ meta = {};
228
260
  } else if (!isObject(meta) && !isUndefined(meta) && !isNull(meta)) {
229
261
  // If the `meta` variable passed was not an Object then convert it
230
262
  message = format(message, meta);
@@ -235,65 +267,49 @@ class Axe {
235
267
  // (as opposed to using something like fast-json-stringify)
236
268
  message = format(message);
237
269
  }
238
- } // If (!isPlainObject(meta)) meta = {};
239
-
240
-
270
+ } else if (isError(meta)) {
271
+ errors.push(meta);
272
+ // handle additional args
273
+ for (const arg of originalArgs.slice(2 + modifier)) {
274
+ // should skip this better with slice and modifier adjustment
275
+ if (meta === arg) continue;
276
+ if (isError(arg)) errors.push(arg);
277
+ }
278
+ meta = {};
279
+ }
241
280
  if (!isUndefined(meta) && !isObject(meta)) meta = {
242
- meta
281
+ original_meta: meta
243
282
  };else if (!isObject(meta)) meta = {};
244
- const hadErrorInMeta = isObject(meta.err);
245
- let error;
246
-
247
283
  if (isError(message)) {
248
- error = message;
249
- if (!hadErrorInMeta) meta.err = parseErr(error);
250
- ({
251
- message
252
- } = message);
253
- } else if (isError(meta.err)) {
254
- error = meta.err;
255
- } // Omit `callback` from `meta` if it was passed
256
-
257
-
258
- const callback = isFunction(config.callback) && (!isBoolean(meta.callback) || meta.callback);
259
- meta = omit(meta, ['callback']); // Set default level on meta
260
-
261
- meta.level = level; // Add `app` object to metadata
262
-
263
- if (this.appInfo) meta.app = this.appInfo; // Set the body used for returning with and sending logs
264
- // (and also remove circular references)
265
-
266
- const body = safeStringify({
267
- message,
268
- meta
269
- }); // Send to Cabin or other logging service here the `message` and `meta`
270
-
271
- if (config.capture && config.levels.includes(level) && (!isError(error) || !error._captureFailed)) {
272
- // If the user didn't specify a key
273
- // and they are using the default endpoint
274
- // then we should throw an error to them
275
- if (config.endpoint === endpoint && !config.key) throw new Error("Cabin API key required (e.g. `{ key: 'YOUR-CABIN-API-KEY' })`)\n<https://cabinjs.com>"); // Capture the log over HTTP
276
-
277
- const request = superagent.post(config.endpoint).set('X-Request-Id', cuid()).timeout(config.timeout);
278
- if (!process.browser) request.set('User-Agent', "axe/".concat(pkg.version)); // Basic auth (e.g. Cabin API key)
279
-
280
- if (config.key) request.auth(config.key); // Set headers if any
284
+ errors.unshift(message);
285
+ message = undefined;
286
+ }
281
287
 
282
- if (!isEmpty(config.headers)) request.set(config.headers);
283
- request.type('application/json').send(body).retry(config.retry).end(error_ => {
284
- if (error_) {
285
- error_._captureFailed = true;
286
- this.config.logger.error(error_);
287
- }
288
- });
289
- } // Custom callback function (e.g. Slack message)
288
+ //
289
+ // rewrite `meta.err` to `meta.original_err` for consistency
290
+ // (in case someone has an object with `.err` property on it with an error)
291
+ //
292
+ if (isObject(meta.err)) {
293
+ if (isError(meta.err)) errors.push(meta.err);
294
+ meta.original_err = isError(meta.err) ? parseErr(meta.err) : meta.err;
295
+ }
296
+ let err;
297
+ if (errors.length > 0) {
298
+ err = combine(errors);
299
+ meta.err = parseErr(err);
300
+ if (!isString(message)) message = err.message;
301
+ }
290
302
 
303
+ // Set `args` prop with original arguments passed
304
+ meta.args = originalArgs;
291
305
 
292
- if (callback) config.callback(level, message, meta); // Suppress logs if it was silent
306
+ // Set default level on meta
307
+ meta.level = level;
293
308
 
294
- if (config.silent) return body; // Return early if it is not a valid logging level
309
+ // Add `app` object to metadata
310
+ if (this.appInfo) meta.app = this.appInfo;
295
311
 
296
- if (!config.levels.includes(level)) return body; //
312
+ //
297
313
  // determine log method to use
298
314
  //
299
315
  // if we didn't pass a level as a method
@@ -302,30 +318,109 @@ class Axe {
302
318
  //
303
319
  // and fatal should use error (e.g. in browser)
304
320
  //
321
+ const method = modifier === -1 ? 'log' : level;
305
322
 
306
- let method = level;
307
- if (modifier === -1) method = 'log';else if (level === 'fatal') method = 'error'; // If there was meta information then output it
308
- // setup ommitted fields
323
+ //
324
+ // NOTE: using lodash _.omit and _.pick would have been _very slow_
325
+ //
326
+ // const omittedAndPickedFields = {
327
+ // ..._.omit(meta, this.config.meta.omittedFields),
328
+ // ..._.pick(meta, this.config.meta.pickedFields)
329
+ // };
330
+ //
331
+ // also we don't want to mutate anything in `meta`
332
+ // and ideally we only want to pick exactly what we need
333
+ // (and not have two operations, one for omit, and one for pick)
334
+ //
309
335
 
310
- const omittedFields = [...this.config.meta.omittedFields];
311
- omittedFields.push('level');
312
- if (!hadErrorInMeta) omittedFields.push('err'); // Omit app is configured
336
+ if (!isEmpty(this.config.meta.remappedFields)) {
337
+ for (const key of Object.keys(this.config.meta.remappedFields)) {
338
+ set(meta, this.config.meta.remappedFields[key], get(meta, key));
339
+ unset(meta, key);
340
+ // cleanup empty objects after remapping
341
+ if (this.config.meta.cleanupRemapping) {
342
+ const index = key.lastIndexOf('.');
343
+ if (index === -1) continue;
344
+ const parentKey = key.slice(0, index);
345
+ if (isEmpty(get(meta, parentKey))) unset(meta, parentKey);
346
+ }
347
+ }
348
+ }
349
+ if (!isEmpty(this.config.meta.omittedFields) || !isEmpty(this.config.meta.pickedFields)) {
350
+ const dotified = dotifyToArray(meta);
351
+ // dotified = [
352
+ // 'err.name',
353
+ // 'err.message',
354
+ // 'err.stack',
355
+ // 'level',
356
+ // 'app.name',
357
+ // 'app.version',
358
+ // 'app.node',
359
+ // 'app.hash',
360
+ // // ...
361
+ // ]
362
+
363
+ if (!isEmpty(this.config.meta.omittedFields)) {
364
+ for (const prop of this.config.meta.omittedFields) {
365
+ // <https://stackoverflow.com/a/9882349>
366
+ let i = dotified.length;
367
+ while (i--) {
368
+ if (dotified[i] === prop || dotified[i].indexOf(`${prop}.`) === 0) dotified.splice(i, 1);
369
+ }
370
+ }
371
+ }
372
+ if (!isEmpty(this.config.meta.pickedFields)) {
373
+ for (const prop of this.config.meta.pickedFields) {
374
+ // response.headers.boop
375
+ // response.headers.beep
376
+ // response.body
377
+ // response.text
378
+ // response
379
+ //
380
+ // so we need to split by the first period and omit any keys from dotified starting with it
381
+ const index = prop.indexOf('.');
382
+ const key = prop.slice(0, index + 1);
383
+ if (index !== -1) {
384
+ let i = dotified.length;
385
+ while (i--) {
386
+ if (dotified[i].indexOf(key) === 0) dotified.splice(i, 1);
387
+ }
388
+ }
389
+
390
+ // finally add it if it did not already exist
391
+ if (dotified.indexOf(prop) === -1) dotified.push(prop);
392
+ }
393
+ }
313
394
 
314
- if (!this.config.meta.showApp) omittedFields.push('app');
315
- const omitted = omit(meta, omittedFields); // Show stack trace if necessary (along with any metadata)
395
+ // now we call pick-deep using the final array
396
+ meta = pickDeep(meta, dotified);
397
+ }
316
398
 
317
- if (isError(error) && config.showStack) {
318
- if (!config.meta.show || isEmpty(omitted)) this.config.logger[method](error);else this.config.logger[method](error, omitted);
319
- } else if (!config.meta.show || isEmpty(omitted)) {
320
- this.config.logger[method](message);
321
- } else {
322
- this.config.logger[method](message, omitted);
323
- } // Return the parsed body in case we need it
399
+ // pre-hooks
400
+ for (const hook of this.config.hooks.pre) {
401
+ [err, message, meta] = hook(method, err, message, meta);
402
+ }
324
403
 
404
+ // only invoke logger methods if it was not silent
405
+ if (!config.silent) {
406
+ // Show stack trace if necessary (along with any metadata)
407
+ if (isError(err) && config.showStack) {
408
+ if (!config.meta.show || isEmpty(meta)) {
409
+ this.config.logger[method](err);
410
+ } else {
411
+ this.config.logger[method](err, meta);
412
+ }
413
+ } else if (!config.meta.show || isEmpty(meta)) {
414
+ this.config.logger[method](message);
415
+ } else {
416
+ this.config.logger[method](message, meta);
417
+ }
418
+ }
325
419
 
326
- return body;
420
+ // post-hooks
421
+ pMapSeries(this.config.hooks.post, hook => hook(method, err, message, meta)).then().catch(err => {
422
+ this.config.logger.error(err);
423
+ });
327
424
  }
328
-
329
425
  }
330
-
331
426
  module.exports = Axe;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "axe",
3
- "description": "Logging add-on to send logs over HTTP to your server in Node and Browser environments. Works with any logger! Chop up your logs consistently! Made for Cabin and Lad.",
4
- "version": "9.0.0",
3
+ "description": "Axe is a logger-agnostic wrapper that normalizes logs regardless of argument style. Great for large development teams, old and new projects, and works with Pino, Bunyan, Winston, console, and more. It is lightweight, performant, highly-configurable, and automatically adds OS, CPU, and Git information to your logs. It supports hooks (useful for masking sensitive data) and dot-notation remapping, omitting, and picking of log metadata properties. Made for Forward Email, Lad, and Cabin.",
4
+ "version": "10.0.1",
5
5
  "author": "Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com)",
6
6
  "browser": {
7
7
  "parse-app-info": false
@@ -11,20 +11,25 @@
11
11
  },
12
12
  "contributors": [
13
13
  "Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com)",
14
- "Alexis Tyler <xo@wvvw.me> (https://wvvw.me/)"
14
+ "Alexis Tyler <xo@wvvw.me> (https://wvvw.me/)",
15
+ "shadowgate15 (https://github.com/shadowgate15)",
16
+ "Spencer Snyder <sasnyde2@gmail.com> (https://spencersnyder.io)"
15
17
  ],
16
18
  "dependencies": {
17
19
  "@ladjs/format-util": "^1.0.4",
18
- "boolean": "^3.2.0",
19
- "console-polyfill": "^0.3.0",
20
- "cuid": "^2.1.8",
21
- "fast-safe-stringify": "^2.1.1",
20
+ "@strikeentco/get": "1.0.1",
21
+ "@strikeentco/set": "1.0.2",
22
+ "boolean": "3.2.0",
23
+ "console-polyfill": "0.3.0",
22
24
  "format-specifiers": "^1.0.0",
23
- "iserror": "^0.0.2",
24
- "lodash.omit": "^4.5.0",
25
+ "iserror": "0.0.2",
26
+ "maybe-combine-errors": "1.0.0",
27
+ "merge-options": "3.0.4",
28
+ "p-map-series": "2",
25
29
  "parse-app-info": "^4.0.3",
26
30
  "parse-err": "^0.0.12",
27
- "superagent": "^8.0.0"
31
+ "pick-deep": "1.0.0",
32
+ "unset-value": "2.0.1"
28
33
  },
29
34
  "devDependencies": {
30
35
  "@babel/cli": "^7.17.10",
@@ -55,7 +60,7 @@
55
60
  "rimraf": "^3.0.2",
56
61
  "signale": "^1.4.0",
57
62
  "sinon": "^14.0.0",
58
- "tinyify": "^3.0.0",
63
+ "tinyify": "3.0.0",
59
64
  "xo": "^0.50.0"
60
65
  },
61
66
  "engines": {
@@ -111,16 +116,16 @@
111
116
  },
112
117
  "scripts": {
113
118
  "ava": "cross-env NODE_ENV=test ava",
114
- "browserify": "browserify src/index.js -o dist/axe.js -s Axe -g [ babelify --configFile ./.dist.babelrc ]",
119
+ "browserify": "browserify src/index.js -o dist/axe.js -s Axe -g [ babelify --configFile ./.dist.babelrc.json ]",
115
120
  "build": "npm run build:clean && npm run build:lib && npm run build:dist",
116
121
  "build:clean": "rimraf lib dist",
117
122
  "build:dist": "npm run browserify && npm run minify",
118
- "build:lib": "babel --config-file ./.lib.babelrc src --out-dir lib",
123
+ "build:lib": "babel --config-file ./.lib.babelrc.json src --out-dir lib",
119
124
  "lint": "xo --fix && remark . -qfo && fixpack",
120
125
  "lint-build": "npm run lint-lib && npm run lint-dist",
121
- "lint-dist": "eslint --no-inline-config -c .dist.eslintrc dist",
122
- "lint-lib": "eslint --no-inline-config -c .lib.eslintrc lib",
123
- "minify": "cross-env NODE_ENV=production browserify src/index.js -o dist/axe.min.js -s Axe -g [ babelify --configFile ./.dist.babelrc ] -p tinyify",
126
+ "lint-dist": "eslint --no-inline-config -c .dist.eslintrc.json dist",
127
+ "lint-lib": "eslint --no-inline-config -c .lib.eslintrc.json lib",
128
+ "minify": "cross-env NODE_ENV=production browserify src/index.js -o dist/axe.min.js -s Axe -g [ babelify --configFile ./.dist.babelrc.json ] -p tinyify",
124
129
  "nyc": "cross-env NODE_ENV=test nyc ava",
125
130
  "prepare": "husky install",
126
131
  "pretest": "npm run lint",