Commit c8d32d1d authored by Eirik Alvær's avatar Eirik Alvær

Merge branch 'express-logger' into 'master'

Express logger

See merge request javascript/log-schema-node!2
parents 6f6100fb 9da74271
Pipeline #17163 passed with stages
in 30 seconds
......@@ -7,51 +7,19 @@ variables:
test:
stage: test
except:
- tags
image: node:7.8-alpine
script:
- echo $NEXUS_NPM_TOKEN > ~/.npmrc
- echo 'always-auth=true' >> ~/.npmrc
- npm set registry https://nexus.nsd.no/repository/npm-group/
- npm install
- npm run compile
artifacts:
expire_in: 1 week
paths:
- lib/index.d.ts
- lib/index.js
- log-schema/log-schema.json
- README.md
- LICENSE
- package.json
test-release:
stage: test
only:
- tags
image: node:7.8-alpine
image: node:8-alpine
script:
- echo $NEXUS_NPM_TOKEN > ~/.npmrc
- echo 'always-auth=true' >> ~/.npmrc
- npm set registry https://nexus.nsd.no/repository/npm-group/
- npm set @nsd:registry https://nexus.nsd.no/repository/npm-group/
- npm install
- npm run compile
artifacts:
expire_in: 1 week
paths:
- lib/index.d.ts
- lib/index.js
- log-schema/log-schema.json
- README.md
- LICENSE
- package.json
publish:
stage: publish
only:
- tags
image: node:7.8-alpine
image: node:8-alpine
script:
- echo $NEXUS_NPM_PUB_TOKEN > ~/.npmrc
- echo 'always-auth=true' >> ~/.npmrc
......
lib/test/
log-schema/
!log-schema/log-schema.json
\ No newline at end of file
*
!log-schema/log-schema.json
!lib/*.js
!lib/*.d.ts
!package.json
\ No newline at end of file
import { LogSchema, schema } from "./index";
import Express = require("express");
import Http = require("http");
import JsonSchema = require("jsonschema");
import _ = require("lodash");
const serviceName = process.env.npm_package_name;
const serviceVersion = process.env.npm_package_version;
const maxStringSize = 1000;
export interface LogInput {
message: string;
data?: any;
error?: {
type: string;
code: number;
serviceName?: string;
stack?: string;
data?: any;
originalErrorObject?: any;
};
file?: string;
logRequestPayload?: boolean;
responseTime?: number;
response?: {
body?: string;
headers?: Http.OutgoingHttpHeaders
};
req?: Express.Request;
tags?: string[];
statusCode?: number;
serviceName?: string; // Defaults to name from package.json file when program is started using npm
serviceVersion?: string; // Defaults to version from package.json file when program is started using npm
maxStringSize?: number; // Defaults to 1000
}
export interface FullLogInput extends LogInput {
levelName: LogSchema["levelName"];
}
function limitStringSize(str: string, limit?: number) {
if (!limit) {
limit = maxStringSize;
}
if (str.length > limit) {
return str.substr(0, limit) + "..";
} else {
return str;
}
}
function isDev() {
return process.env.NODE_ENV !== "production";
}
function stringify(data, limit?: number) {
try {
let str = "";
if (isDev()) {
str = JSON.stringify(data, null, 2);
} else {
str = JSON.stringify(data);
}
return limitStringSize(str, limit);
} catch (err) {
return "Unable to stringify.";
}
}
function extractHeaders(headers: Http.IncomingHttpHeaders | Http.OutgoingHttpHeaders, limit?: number): { [k: string]: string } {
let res = {};
_.each(headers, (value, key) => {
if (key === "authorization") {
return;
}
if (key === "cookie") {
return;
}
res[key] = limitStringSize(value.toString(), limit);
});
return res;
}
function getLogData(input: FullLogInput): LogSchema {
const res: LogSchema = {
"@timestamp": new Date().toISOString(),
file: input.file,
levelName: input.levelName,
loggerName: "ExpressLogger",
message: input.message,
responseTime: input.responseTime,
schemaVersion: "v3",
serviceName: input.serviceName || serviceName || "unknown",
serviceVersion: input.serviceVersion || serviceVersion || "unknown",
tags: input.tags,
statusCode: input.statusCode
};
const req = input.req;
if (req) {
res.host = req.hostname;
res.method = req.method;
res.port = req.connection.localPort;
res.request = {
headers: extractHeaders(req.headers, input.maxStringSize),
query: req.query,
path: req.params
}
if (input.logRequestPayload) {
res.request.payload = stringify(req.body, input.maxStringSize);
}
res.url = req.url;
res.xRequestId = _.get(req, "id");
}
const response = input.response;
if (response) {
res.response = {
headers: extractHeaders(response.headers),
body: limitStringSize(response.body, input.maxStringSize)
};
}
if (input.data) {
res.data = stringify(input.data, input.maxStringSize);
}
const error = input.error;
if (error) {
res.error = {
type: error.type,
code: error.code + "",
serviceName: error.serviceName,
stack: error.stack,
data: stringify(error.data, input.maxStringSize),
originalErrorObject: stringify(error.originalErrorObject, input.maxStringSize)
}
}
return res;
}
function log(levelName: LogSchema["levelName"], input: LogInput) {
if (isDev()) {
console.log("----------------------------------------------------------------------------------");
}
try {
const fullLogInput = _.merge({ levelName}, input);
const data = getLogData(fullLogInput);
const str = stringify(data, Number.MAX_SAFE_INTEGER);
console.log(str);
if (isDev()) {
JsonSchema.validate(data, schema, { throwError: true });
}
} catch (err) {
const errData = getLogData({
levelName: "ERROR",
message: "Invalid log format!",
data: {
error: err
},
req: input.req
});
const str = stringify(errData, Number.MAX_SAFE_INTEGER);
console.error(str);
}
}
export function debug(input: LogInput) {
log ("DEBUG", input);
}
export function info(input: LogInput) {
log ("INFO", input);
}
export function warn(input: LogInput) {
log ("WARN", input);
}
export function error(input: LogInput) {
log ("ERROR", input);
}
export function middleware(req: Express.Request, res: Express.Response, next: Express.NextFunction) {
const startTime = Date.now();
const reqInfoString = (req: Express.Request) => req.method.toUpperCase() + " " + req.path;
const message = reqInfoString(req) + " request received."
const file = "ExpressLogger.ts";
info({ message, file, req, logRequestPayload: true })
const send = res.send;
let responseLogged = false;
res.send = function (data) {
if (!responseLogged) {
responseLogged = true;
const responseTime = Date.now() - startTime;
const message = reqInfoString(req) + " " + res.statusCode + " took " + responseTime + " ms.";
const body = _.isString(data)? data : JSON.stringify(data);
const response = {
headers: res.getHeaders(),
body
}
info({ responseTime, message, file, req, response });
}
send.apply(res, arguments);
return res;
}
next();
}
\ No newline at end of file
......@@ -44,5 +44,39 @@ const logEvent: LogSchema = {
console.log(js.validate(logEvent, schema));
```
Currently this package only exposes the schema and the Typescript interface. In
the future it might also provide facilities for validation, etc.
Example using the ExpressLogger:
```typescript
import Express = require("express");
import {ExpressLogger as Logger} from "@nsd/log-schema-node";
const app = Express();
app.use(Logger.middleware);
app.get("/hello", (req, res) => {
Logger.info({
message: "Hello, Logger!",
req
});
res.send("Hello, World!");
});
const port = 1337;
const server = app.listen(port, () => {
Logger.info({ message: "Server started on port " + port});
});
server.on("error", (err) => {
Logger.error({
message: "Server has encountered an error.",
error: {
code: 100,
type: "SERVER_ERROR",
originalErrorObject: err
}
});
});
```
By using the ExpressLogger middleware basic info about each request and response will be logged.
\ No newline at end of file
import * as jsonSchema from "../log-schema/log-schema.json";
const schema = require("../log-schema/log-schema.json");
import ExpressLogger = require("./ExpressLogger");
export { schema, ExpressLogger };
export interface LogSchema {
/**
......@@ -148,8 +152,4 @@ export interface LogSchema {
*/
body?: string;
};
}
const schema: LogSchema = <LogSchema> jsonSchema;
export { schema };
\ No newline at end of file
}
\ No newline at end of file
{
"name": "@nsd/log-schema-node",
"version": "3.0.0",
"version": "3.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/body-parser": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz",
"integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/connect": {
"version": "3.4.32",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz",
"integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/express": {
"version": "4.16.1",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.1.tgz",
"integrity": "sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==",
"dev": true,
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.16.1",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.1.tgz",
"integrity": "sha512-QgbIMRU1EVRry5cIu1ORCQP4flSYqLM1lS5LYyGWfKnFT3E58f0gKto7BR13clBFVrVZ0G0rbLZ1hUpSkgQQOA==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/range-parser": "*"
}
},
"@types/lodash": {
"version": "4.14.121",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.121.tgz",
"integrity": "sha512-ORj7IBWj13iYufXt/VXrCNMbUuCTJfhzme5kx9U/UtcIPdJYuvPDUAlHlbNhz/8lKCLy9XGIZnGrqXOtQbPGoQ==",
"dev": true
},
"@types/mime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
"integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==",
"dev": true
},
"@types/node": {
"version": "11.9.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.5.tgz",
"integrity": "sha512-vVjM0SVzgaOUpflq4GYBvCpozes8OgIIS5gVXVka+OfK3hvnkC1i93U8WiY2OtNE4XUWyyy/86Kf6e0IHTQw1Q==",
"dev": true
},
"@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
"dev": true
},
"@types/serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz",
"integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==",
"dev": true,
"requires": {
"@types/express-serve-static-core": "*",
"@types/mime": "*"
}
},
"accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
"requires": {
"mime-types": "~2.1.18",
"negotiator": "0.6.1"
}
},
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
......@@ -31,6 +114,11 @@
"sprintf-js": "~1.0.2"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
......@@ -63,6 +151,38 @@
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
"integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
"requires": {
"bytes": "3.0.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "~1.6.3",
"iconv-lite": "0.4.23",
"on-finished": "~2.3.0",
"qs": "6.5.2",
"raw-body": "2.3.3",
"type-is": "~1.6.16"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
......@@ -79,6 +199,11 @@
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
"dev": true
},
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
},
"call-me-maybe": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
......@@ -157,6 +282,26 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"content-disposition": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"core-js": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.3.tgz",
......@@ -181,12 +326,37 @@
"ms": "^2.1.1"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"dev": true
},
"dotenv": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz",
"integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w=="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"es5-ext": {
"version": "0.10.47",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.47.tgz",
......@@ -237,6 +407,11 @@
"es6-symbol": "^3.1.1"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
......@@ -255,6 +430,11 @@
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
"dev": true
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"event-emitter": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
......@@ -265,12 +445,103 @@
"es5-ext": "~0.10.14"
}
},
"express": {
"version": "4.16.4",
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
"integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
"requires": {
"accepts": "~1.3.5",
"array-flatten": "1.1.1",
"body-parser": "1.18.3",
"content-disposition": "0.5.2",
"content-type": "~1.0.4",
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.1.1",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.4",
"qs": "6.5.2",
"range-parser": "~1.2.0",
"safe-buffer": "5.1.2",
"send": "0.16.2",
"serve-static": "1.13.2",
"setprototypeof": "1.1.0",
"statuses": "~1.4.0",
"type-is": "~1.6.16",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"finalhandler": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"statuses": "~1.4.0",
"unpipe": "~1.0.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"format-util": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.3.tgz",
"integrity": "sha1-Ay3KShFiYqEsQ/TD7IVmQWxbLZU=",
"dev": true
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
......@@ -306,6 +577,25 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
}
},
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
......@@ -319,8 +609,12 @@
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
"integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
},
"is-promise": {