├── .eslintrc.yml
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── example
└── server.js
├── package-lock.json
├── package.json
├── src
├── index.js
├── reporters
│ ├── PrometheusReporter.e2e.spec.js
│ ├── PrometheusReporter.js
│ ├── PrometheusReporter.spec.js
│ └── index.js
└── tracer
│ ├── Reference.js
│ ├── Reference.spec.js
│ ├── Span.js
│ ├── Span.spec.js
│ ├── SpanContext.js
│ ├── SpanContext.spec.js
│ ├── Tracer.js
│ ├── Tracer.spec.js
│ └── index.js
└── test
└── setup.js
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | ---
2 | extends: airbnb-base
3 | env:
4 | node: true
5 | mocha: true
6 | es6: true
7 | parserOptions:
8 | sourceType: strict
9 | rules:
10 | generator-star-spacing:
11 | - 2
12 | - before: true
13 | after: true
14 | no-shadow: 0
15 | require-yield: 0
16 | no-param-reassign: 0
17 | comma-dangle:
18 | - error
19 | - never
20 | no-underscore-dangle: 0
21 | import/no-extraneous-dependencies:
22 | - 2
23 | - devDependencies: true
24 | import/order:
25 | - error
26 | func-names: 0
27 | no-unused-expressions: 0
28 | prefer-arrow-callback: 1
29 | no-use-before-define:
30 | - 2
31 | - functions: false
32 | space-before-function-paren:
33 | - 2
34 | - always
35 | max-len:
36 | - 2
37 | - 120
38 | - 2
39 | semi:
40 | - 2
41 | - never
42 | strict:
43 | - 2
44 | - global
45 | arrow-parens:
46 | - 2
47 | - always
48 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | .nyc_output
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | cache:
3 | directories:
4 | - node_modules
5 | notifications:
6 | email: true
7 | node_js:
8 | - 'stable'
9 | - '8'
10 | branches:
11 | except:
12 | - /^v\d+\.\d+\.\d+$/
13 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | ## 2.1.0 (2017-09-29)
3 |
4 |
5 | #### Features
6 |
7 | * **tracer:** add ignoreTags option ([acd7c58a](git+https://github.com/RisingStack/opentracing-metrics-tracer.git/commit/acd7c58a))
8 |
9 |
10 |
11 | ### 2.0.1 (2017-09-04)
12 |
13 |
14 | #### Bug Fixes
15 |
16 | * **reporter:** report empty parent service key as "unknown" ([22a89a58](git+https://github.com/RisingStack/opentracing-metrics-tracer.git/commit/22a89a58))
17 |
18 |
19 |
20 | ## 2.0.0 (2017-09-04)
21 |
22 |
23 | #### Bug Fixes
24 |
25 | * **reporter:** rename http_request_duration_seconds_bucket to http_request_handler_duration_sec ([b07b5212](git+https://github.com/RisingStack/opentracing-metrics-tracer.git/commit/b07b5212))
26 |
27 |
28 | #### Breaking Changes
29 |
30 | * metrics renamed http_request_duration_seconds_bucket to http_request_handler_duration_seconds_bucket
31 |
32 | ([b07b5212](git+https://github.com/RisingStack/opentracing-metrics-tracer.git/commit/b07b5212))
33 |
34 |
35 |
36 | ## 1.3.0 (2017-09-04)
37 |
38 |
39 | #### Features
40 |
41 | * **example:** add child operation ([7a260dfa](git+https://github.com/RisingStack/opentracing-metrics-tracer.git/commit/7a260dfa))
42 | * **reporter:** report parent_service with http_request_duration_seconds ([a06e2bc0](git+https://github.com/RisingStack/opentracing-metrics-tracer.git/commit/a06e2bc0))
43 |
44 |
45 |
46 | ## 1.2.0 (2017-09-03)
47 |
48 |
49 |
50 | ## 1.1.0 (2017-09-03)
51 |
52 |
53 | #### Features
54 |
55 | * **tracer:** public getters for Span and SpanContext ([a5b54c15](git+https://github.com/RisingStack/opentracing-metrics-tracer.git/commit/a5b54c15))
56 |
57 |
58 |
59 | ## 1.0.0 (2017-09-02)
60 |
61 |
62 | #### Features
63 |
64 | * **reporter:** add Prometheus reporter ([74c2b189](git+https://github.com/RisingStack/opentracing-metrics-tracer.git/commit/74c2b189))
65 | * **tracer:** add Reference, Span, SpanContext and Tracer models ([e87df8e5](git+https://github.com/RisingStack/opentracing-metrics-tracer.git/commit/e87df8e5))
66 |
67 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 RisingStack, Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # opentracing-metrics-tracer
2 |
3 | [](https://travis-ci.org/RisingStack/opentracing-metrics-tracer)
4 |
5 | Exports cross-process metrics via OpenTracing instrumentation to reporters: Prometheus.
6 | It's capable to measure operation characteristics in a distributed system like microservices.
7 |
8 | It also makes possible to reverse engineer the infrastructure topology as we know the initiators
9 |
10 | ## Available Reporters
11 |
12 | - [Prometheus](https://prometheus.io/) via [prom-client](https://github.com/siimon/prom-client)
13 |
14 | ## Getting started
15 |
16 | ```js
17 | const MetricsTracer = require('@risingstack/opentracing-metrics-tracer')
18 | const prometheusReporter = new MetricsTracer.PrometheusReporter()
19 | const metricsTracer = new MetricsTracer('my-service', [prometheusReporter])
20 |
21 | // Instrument
22 | const span = metricsTracer.startSpan('my-operation')
23 | span.finish()
24 | ...
25 |
26 | app.get('/metrics', (req, res) => {
27 | res.set('Content-Type', MetricsTracer.PrometheusReporter.Prometheus.register.contentType)
28 | res.end(prometheusReporter.metrics())
29 | })
30 | ```
31 |
32 | ### With auto instrumentation and multiple tracers
33 |
34 | Check out: https://github.com/RisingStack/opentracing-auto
35 |
36 | ```js
37 | // Prometheus metrics tracer
38 | const MetricsTracer = require('@risingstack/opentracing-metrics-tracer')
39 | const prometheusReporter = new MetricsTracer.PrometheusReporter()
40 | const metricsTracer = new MetricsTracer('my-service', [prometheusReporter])
41 |
42 | // Jaeger tracer (classic distributed tracing)
43 | const jaeger = require('jaeger-client')
44 | const UDPSender = require('jaeger-client/dist/src/reporters/udp_sender').default
45 | const sampler = new jaeger.RateLimitingSampler(1)
46 | const reporter = new jaeger.RemoteReporter(new UDPSender())
47 | const jaegerTracer = new jaeger.Tracer('my-server-pg', reporter, sampler)
48 |
49 | // Auto instrumentation
50 | const Instrument = require('@risingstack/opentracing-auto')
51 | const instrument = new Instrument({
52 | tracers: [metricsTracer, jaegerTracer]
53 | })
54 |
55 | // Rest of your code
56 | const express = require('express')
57 | const app = express()
58 |
59 | app.get('/metrics', (req, res) => {
60 | res.set('Content-Type', MetricsTracer.PrometheusReporter.Prometheus.register.contentType)
61 | res.end(prometheusReporter.metrics())
62 | })
63 | ```
64 |
65 | ### Example
66 |
67 | See [example server](/example/server.js).
68 |
69 | ```sh
70 | node example/server
71 | curl http://localhost:3000
72 | curl http://localhost:3000/metrics
73 | ```
74 |
75 | ## API
76 |
77 | `const Tracer = require('@risingstack/opentracing-metrics-tracer')`
78 |
79 | ### new Tracer(serviceKey, [reporter1, reporter2, ...])
80 |
81 | - **serviceKey** *String*, *required*, unique key that identifies a specific type of service *(for example: my-frontend-api)*
82 | - **reporters** *Array of reporters*, *optional*, *default:* []
83 |
84 | [OpenTracing](https://github.com/opentracing/opentracing-javascript) compatible tracer, for the complete API check out the official [documentation](https://opentracing-javascript.surge.sh/).
85 |
86 | ### new Tracer.PrometheusReporter([opts])
87 |
88 | - **opts** *Object*, *optional*
89 | - **opts.ignoreTags** *Object*, *optional*
90 | - Example: `{ ignoreTags: { [Tags.HTTP_URL]: /\/metrics$/ } }` to ignore Prometheus scraper
91 |
92 | Creates a new Prometheus reporter.
93 |
94 | ### Tracer.PrometheusReporter.Prometheus
95 |
96 | Exposed [prom-client](https://github.com/siimon/prom-client).
97 |
98 | ## Reporters
99 |
100 | ### Prometheus Reporter
101 |
102 | Exposes metrics in Prometheus format via [prom-client](https://github.com/siimon/prom-client)
103 |
104 | #### Metrics
105 |
106 | - [operation_duration_seconds](#operation_duration_seconds)
107 | - [http_request_duration_seconds](#http_request_duration_seconds)
108 |
109 | ##### operation_duration_seconds
110 |
111 | Always measured.
112 | Sample output: Two distributed services communicate over the network.
113 |
114 | ```
115 | # HELP operation_duration_seconds Duration of operations in second
116 | # TYPE operation_duration_seconds histogram
117 | operation_duration_seconds_bucket{le="0.005",parent_service="my-parent-service",name="my-operation" 0
118 | operation_duration_seconds_bucket{le="0.01",parent_service="my-parent-service",name="my-operation" 0
119 | operation_duration_seconds_bucket{le="0.025",parent_service="my-parent-service",name="my-operation" 0
120 | operation_duration_seconds_bucket{le="0.05",parent_service="my-parent-service",name="my-operation" 0
121 | operation_duration_seconds_bucket{le="0.1",parent_service="my-parent-service",name="my-operation" 1
122 | operation_duration_seconds_bucket{le="0.25",parent_service="my-parent-service",name="my-operation" 1
123 | operation_duration_seconds_bucket{le="0.5",parent_service="my-parent-service",name="my-operation" 2
124 | operation_duration_seconds_bucket{le="1",parent_service="my-parent-service",name="my-operation" 2
125 | operation_duration_seconds_bucket{le="2.5",parent_service="my-parent-service",name="my-operation" 2
126 | operation_duration_seconds_bucket{le="5",parent_service="my-parent-service",name="my-operation" 2
127 | operation_duration_seconds_bucket{le="10",parent_service="my-parent-service",name="my-operation" 2
128 | operation_duration_seconds_bucket{le="+Inf",parent_service="my-parent-service",name="my-operation" 2
129 | operation_duration_seconds_sum{parent_service="my-parent-service",name="my-operation" 0.4
130 | operation_duration_seconds_count{parent_service="my-parent-service",name="my-operation" 2
131 | ```
132 |
133 | ##### http_request_duration_seconds
134 |
135 | Measured only when the span is tagged with `SPAN_KIND_RPC_SERVER` and any of `HTTP_URL`, `HTTP_METHOD` or `HTTP_STATUS_CODE`.
136 | Sample output:
137 | ```
138 | # HELP http_request_handler_duration_seconds Duration of HTTP requests in second
139 | # TYPE http_request_handler_duration_seconds histogram
140 | http_request_handler_duration_seconds_bucket{le="0.005",parent_service="my-parent-service",method="GET",code="200",name="http_request" 0
141 | http_request_handler_duration_seconds_bucket{le="0.01",parent_service="my-parent-service",method="GET",code="200",name="http_request" 0
142 | http_request_handler_duration_seconds_bucket{le="0.025",parent_service="my-parent-service",method="GET",code="200",name="http_request" 0
143 | http_request_handler_duration_seconds_bucket{le="0.05",parent_service="my-parent-service",method="GET",code="200",name="http_request" 0
144 | http_request_handler_duration_seconds_bucket{le="0.1",parent_service="my-parent-service",method="GET",code="200",name="http_request" 1
145 | http_request_handler_duration_seconds_bucket{le="0.25",parent_service="my-parent-service",method="GET",code="200",name="http_request" 1
146 | http_request_handler_duration_seconds_bucket{le="0.5",parent_service="my-parent-service",method="GET",code="200",name="http_request" 2
147 | http_request_handler_duration_seconds_bucket{le="1",parent_service="my-parent-service",method="GET",code="200",name="http_request" 2
148 | http_request_handler_duration_seconds_bucket{le="2.5",parent_service="my-parent-service",method="GET",code="200",name="http_request" 2
149 | http_request_handler_duration_seconds_bucket{le="5",parent_service="my-parent-service",method="GET",code="200",name="http_request" 2
150 | http_request_handler_duration_seconds_bucket{le="10",parent_service="my-parent-service",method="GET",code="200",name="http_request" 2
151 | http_request_handler_duration_seconds_bucket{le="+Inf",parent_service="my-parent-service",method="GET",code="200",name="http_request" 2
152 | http_request_handler_duration_seconds_sum{parent_service="my-parent-service",method="GET",code="200",name="http_request" 0.4
153 | http_request_handler_duration_seconds_count{parent_service="my-parent-service",method="GET",code="200",name="http_request" 2
154 | ```
155 |
156 | ## Future and ideas
157 |
158 | This library is new, in the future we could measure much more useful and specific metrics with it.
159 | Please share your ideas in a form of issues or pull-requests.
160 |
--------------------------------------------------------------------------------
/example/server.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const http = require('http')
4 | const { Tags, FORMAT_HTTP_HEADERS } = require('opentracing')
5 | const MetricsTracer = require('../src')
6 |
7 | const prometheusReporter = new MetricsTracer.PrometheusReporter()
8 | const metricsTracer = new MetricsTracer('my-server', [prometheusReporter])
9 | const PORT = process.env.PORT || 3000
10 |
11 | const server = http.createServer((req, res) => {
12 | // Instrumentation
13 | const requestSpan = metricsTracer.startSpan('http_request', {
14 | childOf: metricsTracer.extract(FORMAT_HTTP_HEADERS, req.headers)
15 | })
16 | const headers = {}
17 |
18 | metricsTracer.inject(requestSpan, FORMAT_HTTP_HEADERS, headers)
19 |
20 | requestSpan.setTag(Tags.HTTP_URL, req.url)
21 | requestSpan.setTag(Tags.HTTP_METHOD, req.method || 'GET')
22 | requestSpan.setTag(Tags.HTTP_STATUS_CODE, 200)
23 | requestSpan.setTag(Tags.SPAN_KIND_RPC_CLIENT, true)
24 |
25 | // Dummy router: GET /metrics
26 | if (req.url === '/metrics') {
27 | requestSpan.finish()
28 |
29 | res.writeHead(200, {
30 | 'Content-Type': MetricsTracer.PrometheusReporter.Prometheus.register.contentType
31 | })
32 | res.end(prometheusReporter.metrics())
33 | return
34 | }
35 |
36 | // My child operation like DB access
37 | const childOperationSpan = metricsTracer.startSpan('my_operation', {
38 | childOf: requestSpan
39 | })
40 |
41 | setTimeout(() => {
42 | childOperationSpan.finish()
43 |
44 | res.writeHead(200, headers)
45 | res.end('Ok')
46 |
47 | requestSpan.finish()
48 | }, 30)
49 | })
50 |
51 | server.listen(PORT, (err) => {
52 | // eslint-disable-next-line
53 | console.log(err || `Server is listening on ${PORT}`)
54 | })
55 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@risingstack/opentracing-metrics-tracer",
3 | "version": "2.0.1",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "acorn": {
8 | "version": "5.1.2",
9 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz",
10 | "integrity": "sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA==",
11 | "dev": true
12 | },
13 | "acorn-jsx": {
14 | "version": "3.0.1",
15 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
16 | "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
17 | "dev": true,
18 | "requires": {
19 | "acorn": "3.3.0"
20 | },
21 | "dependencies": {
22 | "acorn": {
23 | "version": "3.3.0",
24 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
25 | "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
26 | "dev": true
27 | }
28 | }
29 | },
30 | "ajv": {
31 | "version": "5.2.3",
32 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz",
33 | "integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=",
34 | "dev": true,
35 | "requires": {
36 | "co": "4.6.0",
37 | "fast-deep-equal": "1.0.0",
38 | "json-schema-traverse": "0.3.1",
39 | "json-stable-stringify": "1.0.1"
40 | }
41 | },
42 | "ajv-keywords": {
43 | "version": "2.1.0",
44 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.0.tgz",
45 | "integrity": "sha1-opbhf3v658HOT34N5T0pyzIWLfA=",
46 | "dev": true
47 | },
48 | "ansi-escapes": {
49 | "version": "3.0.0",
50 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz",
51 | "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==",
52 | "dev": true
53 | },
54 | "ansi-regex": {
55 | "version": "2.1.1",
56 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
57 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
58 | "dev": true
59 | },
60 | "ansi-styles": {
61 | "version": "2.2.1",
62 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
63 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
64 | "dev": true
65 | },
66 | "argparse": {
67 | "version": "1.0.9",
68 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
69 | "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
70 | "dev": true,
71 | "requires": {
72 | "sprintf-js": "1.0.3"
73 | }
74 | },
75 | "array-union": {
76 | "version": "1.0.2",
77 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
78 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
79 | "dev": true,
80 | "requires": {
81 | "array-uniq": "1.0.3"
82 | }
83 | },
84 | "array-uniq": {
85 | "version": "1.0.3",
86 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
87 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
88 | "dev": true
89 | },
90 | "arrify": {
91 | "version": "1.0.1",
92 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
93 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
94 | "dev": true
95 | },
96 | "assertion-error": {
97 | "version": "1.0.2",
98 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz",
99 | "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=",
100 | "dev": true
101 | },
102 | "babel-code-frame": {
103 | "version": "6.26.0",
104 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
105 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
106 | "dev": true,
107 | "requires": {
108 | "chalk": "1.1.3",
109 | "esutils": "2.0.2",
110 | "js-tokens": "3.0.2"
111 | },
112 | "dependencies": {
113 | "chalk": {
114 | "version": "1.1.3",
115 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
116 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
117 | "dev": true,
118 | "requires": {
119 | "ansi-styles": "2.2.1",
120 | "escape-string-regexp": "1.0.5",
121 | "has-ansi": "2.0.0",
122 | "strip-ansi": "3.0.1",
123 | "supports-color": "2.0.0"
124 | }
125 | },
126 | "strip-ansi": {
127 | "version": "3.0.1",
128 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
129 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
130 | "dev": true,
131 | "requires": {
132 | "ansi-regex": "2.1.1"
133 | }
134 | }
135 | }
136 | },
137 | "balanced-match": {
138 | "version": "1.0.0",
139 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
140 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
141 | "dev": true
142 | },
143 | "bintrees": {
144 | "version": "1.0.1",
145 | "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz",
146 | "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ="
147 | },
148 | "brace-expansion": {
149 | "version": "1.1.8",
150 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
151 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
152 | "dev": true,
153 | "requires": {
154 | "balanced-match": "1.0.0",
155 | "concat-map": "0.0.1"
156 | }
157 | },
158 | "browser-stdout": {
159 | "version": "1.3.0",
160 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz",
161 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
162 | "dev": true
163 | },
164 | "builtin-modules": {
165 | "version": "1.1.1",
166 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
167 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
168 | "dev": true
169 | },
170 | "caller-path": {
171 | "version": "0.1.0",
172 | "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
173 | "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
174 | "dev": true,
175 | "requires": {
176 | "callsites": "0.2.0"
177 | }
178 | },
179 | "callsites": {
180 | "version": "0.2.0",
181 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
182 | "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
183 | "dev": true
184 | },
185 | "chai": {
186 | "version": "4.1.2",
187 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz",
188 | "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=",
189 | "dev": true,
190 | "requires": {
191 | "assertion-error": "1.0.2",
192 | "check-error": "1.0.2",
193 | "deep-eql": "3.0.0",
194 | "get-func-name": "2.0.0",
195 | "pathval": "1.1.0",
196 | "type-detect": "4.0.3"
197 | }
198 | },
199 | "chalk": {
200 | "version": "2.1.0",
201 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
202 | "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
203 | "dev": true,
204 | "requires": {
205 | "ansi-styles": "3.2.0",
206 | "escape-string-regexp": "1.0.5",
207 | "supports-color": "4.4.0"
208 | },
209 | "dependencies": {
210 | "ansi-styles": {
211 | "version": "3.2.0",
212 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
213 | "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
214 | "dev": true,
215 | "requires": {
216 | "color-convert": "1.9.0"
217 | }
218 | },
219 | "supports-color": {
220 | "version": "4.4.0",
221 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
222 | "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
223 | "dev": true,
224 | "requires": {
225 | "has-flag": "2.0.0"
226 | }
227 | }
228 | }
229 | },
230 | "check-error": {
231 | "version": "1.0.2",
232 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
233 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
234 | "dev": true
235 | },
236 | "circular-json": {
237 | "version": "0.3.3",
238 | "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
239 | "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
240 | "dev": true
241 | },
242 | "cli-cursor": {
243 | "version": "2.1.0",
244 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
245 | "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
246 | "dev": true,
247 | "requires": {
248 | "restore-cursor": "2.0.0"
249 | }
250 | },
251 | "cli-width": {
252 | "version": "2.2.0",
253 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
254 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
255 | "dev": true
256 | },
257 | "co": {
258 | "version": "4.6.0",
259 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
260 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
261 | "dev": true
262 | },
263 | "color-convert": {
264 | "version": "1.9.0",
265 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
266 | "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
267 | "dev": true,
268 | "requires": {
269 | "color-name": "1.1.3"
270 | }
271 | },
272 | "color-name": {
273 | "version": "1.1.3",
274 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
275 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
276 | "dev": true
277 | },
278 | "commander": {
279 | "version": "2.9.0",
280 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
281 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
282 | "dev": true,
283 | "requires": {
284 | "graceful-readlink": "1.0.1"
285 | }
286 | },
287 | "concat-map": {
288 | "version": "0.0.1",
289 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
290 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
291 | "dev": true
292 | },
293 | "concat-stream": {
294 | "version": "1.6.0",
295 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
296 | "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
297 | "dev": true,
298 | "requires": {
299 | "inherits": "2.0.3",
300 | "readable-stream": "2.3.3",
301 | "typedarray": "0.0.6"
302 | }
303 | },
304 | "contains-path": {
305 | "version": "0.1.0",
306 | "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
307 | "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
308 | "dev": true
309 | },
310 | "core-util-is": {
311 | "version": "1.0.2",
312 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
313 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
314 | "dev": true
315 | },
316 | "cross-spawn": {
317 | "version": "5.1.0",
318 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
319 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
320 | "dev": true,
321 | "requires": {
322 | "lru-cache": "4.1.1",
323 | "shebang-command": "1.2.0",
324 | "which": "1.3.0"
325 | }
326 | },
327 | "debug": {
328 | "version": "2.6.8",
329 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
330 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
331 | "dev": true,
332 | "requires": {
333 | "ms": "2.0.0"
334 | }
335 | },
336 | "dedent": {
337 | "version": "0.7.0",
338 | "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
339 | "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
340 | "dev": true
341 | },
342 | "deep-eql": {
343 | "version": "3.0.0",
344 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.0.tgz",
345 | "integrity": "sha512-9zef2MtjASSE1Pts2Nm6Yh5MTVdVh+s4Qt/e+jPV6qTBhqTc0WOEaWnLvLKGxky0gwZGmcY6TnUqyCD6fNs5Lg==",
346 | "dev": true,
347 | "requires": {
348 | "type-detect": "4.0.3"
349 | }
350 | },
351 | "deep-is": {
352 | "version": "0.1.3",
353 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
354 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
355 | "dev": true
356 | },
357 | "del": {
358 | "version": "2.2.2",
359 | "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
360 | "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
361 | "dev": true,
362 | "requires": {
363 | "globby": "5.0.0",
364 | "is-path-cwd": "1.0.0",
365 | "is-path-in-cwd": "1.0.0",
366 | "object-assign": "4.1.1",
367 | "pify": "2.3.0",
368 | "pinkie-promise": "2.0.1",
369 | "rimraf": "2.6.2"
370 | }
371 | },
372 | "diff": {
373 | "version": "3.2.0",
374 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz",
375 | "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=",
376 | "dev": true
377 | },
378 | "doctrine": {
379 | "version": "2.0.0",
380 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
381 | "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=",
382 | "dev": true,
383 | "requires": {
384 | "esutils": "2.0.2",
385 | "isarray": "1.0.0"
386 | }
387 | },
388 | "error-ex": {
389 | "version": "1.3.1",
390 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
391 | "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
392 | "dev": true,
393 | "requires": {
394 | "is-arrayish": "0.2.1"
395 | }
396 | },
397 | "escape-string-regexp": {
398 | "version": "1.0.5",
399 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
400 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
401 | "dev": true
402 | },
403 | "eslint": {
404 | "version": "4.7.2",
405 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.7.2.tgz",
406 | "integrity": "sha1-/29fUZOEiifum2J74+c/ucteZi4=",
407 | "dev": true,
408 | "requires": {
409 | "ajv": "5.2.3",
410 | "babel-code-frame": "6.26.0",
411 | "chalk": "2.1.0",
412 | "concat-stream": "1.6.0",
413 | "cross-spawn": "5.1.0",
414 | "debug": "3.1.0",
415 | "doctrine": "2.0.0",
416 | "eslint-scope": "3.7.1",
417 | "espree": "3.5.1",
418 | "esquery": "1.0.0",
419 | "estraverse": "4.2.0",
420 | "esutils": "2.0.2",
421 | "file-entry-cache": "2.0.0",
422 | "functional-red-black-tree": "1.0.1",
423 | "glob": "7.1.2",
424 | "globals": "9.18.0",
425 | "ignore": "3.3.5",
426 | "imurmurhash": "0.1.4",
427 | "inquirer": "3.3.0",
428 | "is-resolvable": "1.0.0",
429 | "js-yaml": "3.10.0",
430 | "json-stable-stringify": "1.0.1",
431 | "levn": "0.3.0",
432 | "lodash": "4.17.4",
433 | "minimatch": "3.0.4",
434 | "mkdirp": "0.5.1",
435 | "natural-compare": "1.4.0",
436 | "optionator": "0.8.2",
437 | "path-is-inside": "1.0.2",
438 | "pluralize": "7.0.0",
439 | "progress": "2.0.0",
440 | "require-uncached": "1.0.3",
441 | "semver": "5.4.1",
442 | "strip-ansi": "4.0.0",
443 | "strip-json-comments": "2.0.1",
444 | "table": "4.0.2",
445 | "text-table": "0.2.0"
446 | },
447 | "dependencies": {
448 | "debug": {
449 | "version": "3.1.0",
450 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
451 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
452 | "dev": true,
453 | "requires": {
454 | "ms": "2.0.0"
455 | }
456 | }
457 | }
458 | },
459 | "eslint-config-airbnb-base": {
460 | "version": "11.0.0",
461 | "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.0.0.tgz",
462 | "integrity": "sha1-o+3cHkn9ZJJQnVgjQP1vWHhWI70=",
463 | "dev": true
464 | },
465 | "eslint-import-resolver-node": {
466 | "version": "0.3.1",
467 | "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz",
468 | "integrity": "sha512-yUtXS15gIcij68NmXmP9Ni77AQuCN0itXbCc/jWd8C6/yKZaSNXicpC8cgvjnxVdmfsosIXrjpzFq7GcDryb6A==",
469 | "dev": true,
470 | "requires": {
471 | "debug": "2.6.8",
472 | "resolve": "1.4.0"
473 | }
474 | },
475 | "eslint-module-utils": {
476 | "version": "2.1.1",
477 | "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz",
478 | "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==",
479 | "dev": true,
480 | "requires": {
481 | "debug": "2.6.8",
482 | "pkg-dir": "1.0.0"
483 | }
484 | },
485 | "eslint-plugin-import": {
486 | "version": "2.7.0",
487 | "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.7.0.tgz",
488 | "integrity": "sha512-HGYmpU9f/zJaQiKNQOVfHUh2oLWW3STBrCgH0sHTX1xtsxYlH1zjLh8FlQGEIdZSdTbUMaV36WaZ6ImXkenGxQ==",
489 | "dev": true,
490 | "requires": {
491 | "builtin-modules": "1.1.1",
492 | "contains-path": "0.1.0",
493 | "debug": "2.6.8",
494 | "doctrine": "1.5.0",
495 | "eslint-import-resolver-node": "0.3.1",
496 | "eslint-module-utils": "2.1.1",
497 | "has": "1.0.1",
498 | "lodash.cond": "4.5.2",
499 | "minimatch": "3.0.4",
500 | "read-pkg-up": "2.0.0"
501 | },
502 | "dependencies": {
503 | "doctrine": {
504 | "version": "1.5.0",
505 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
506 | "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
507 | "dev": true,
508 | "requires": {
509 | "esutils": "2.0.2",
510 | "isarray": "1.0.0"
511 | }
512 | }
513 | }
514 | },
515 | "eslint-plugin-promise": {
516 | "version": "3.5.0",
517 | "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.5.0.tgz",
518 | "integrity": "sha1-ePu2/+BHIBYnVp6FpsU3OvKmj8o=",
519 | "dev": true
520 | },
521 | "eslint-scope": {
522 | "version": "3.7.1",
523 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
524 | "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
525 | "dev": true,
526 | "requires": {
527 | "esrecurse": "4.2.0",
528 | "estraverse": "4.2.0"
529 | }
530 | },
531 | "espree": {
532 | "version": "3.5.1",
533 | "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.1.tgz",
534 | "integrity": "sha1-DJiLirRttTEAoZVK5LqZXd0n2H4=",
535 | "dev": true,
536 | "requires": {
537 | "acorn": "5.1.2",
538 | "acorn-jsx": "3.0.1"
539 | }
540 | },
541 | "esprima": {
542 | "version": "4.0.0",
543 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
544 | "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
545 | "dev": true
546 | },
547 | "esquery": {
548 | "version": "1.0.0",
549 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz",
550 | "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=",
551 | "dev": true,
552 | "requires": {
553 | "estraverse": "4.2.0"
554 | }
555 | },
556 | "esrecurse": {
557 | "version": "4.2.0",
558 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz",
559 | "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=",
560 | "dev": true,
561 | "requires": {
562 | "estraverse": "4.2.0",
563 | "object-assign": "4.1.1"
564 | }
565 | },
566 | "estraverse": {
567 | "version": "4.2.0",
568 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
569 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
570 | "dev": true
571 | },
572 | "esutils": {
573 | "version": "2.0.2",
574 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
575 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
576 | "dev": true
577 | },
578 | "external-editor": {
579 | "version": "2.0.5",
580 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.5.tgz",
581 | "integrity": "sha512-Msjo64WT5W+NhOpQXh0nOHm+n0RfU1QUwDnKYvJ8dEJ8zlwLrqXNTv5mSUTJpepf41PDJGyhueTw2vNZW+Fr/w==",
582 | "dev": true,
583 | "requires": {
584 | "iconv-lite": "0.4.19",
585 | "jschardet": "1.5.1",
586 | "tmp": "0.0.33"
587 | }
588 | },
589 | "fast-deep-equal": {
590 | "version": "1.0.0",
591 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
592 | "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
593 | "dev": true
594 | },
595 | "fast-levenshtein": {
596 | "version": "2.0.6",
597 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
598 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
599 | "dev": true
600 | },
601 | "figures": {
602 | "version": "2.0.0",
603 | "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
604 | "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
605 | "dev": true,
606 | "requires": {
607 | "escape-string-regexp": "1.0.5"
608 | }
609 | },
610 | "file-entry-cache": {
611 | "version": "2.0.0",
612 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
613 | "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
614 | "dev": true,
615 | "requires": {
616 | "flat-cache": "1.3.0",
617 | "object-assign": "4.1.1"
618 | }
619 | },
620 | "find-up": {
621 | "version": "1.1.2",
622 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
623 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
624 | "dev": true,
625 | "requires": {
626 | "path-exists": "2.1.0",
627 | "pinkie-promise": "2.0.1"
628 | }
629 | },
630 | "flat-cache": {
631 | "version": "1.3.0",
632 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
633 | "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=",
634 | "dev": true,
635 | "requires": {
636 | "circular-json": "0.3.3",
637 | "del": "2.2.2",
638 | "graceful-fs": "4.1.11",
639 | "write": "0.2.1"
640 | }
641 | },
642 | "formatio": {
643 | "version": "1.2.0",
644 | "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz",
645 | "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=",
646 | "dev": true,
647 | "requires": {
648 | "samsam": "1.2.1"
649 | }
650 | },
651 | "fs.realpath": {
652 | "version": "1.0.0",
653 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
654 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
655 | "dev": true
656 | },
657 | "function-bind": {
658 | "version": "1.1.1",
659 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
660 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
661 | "dev": true
662 | },
663 | "functional-red-black-tree": {
664 | "version": "1.0.1",
665 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
666 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
667 | "dev": true
668 | },
669 | "get-func-name": {
670 | "version": "2.0.0",
671 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
672 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
673 | "dev": true
674 | },
675 | "glob": {
676 | "version": "7.1.2",
677 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
678 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
679 | "dev": true,
680 | "requires": {
681 | "fs.realpath": "1.0.0",
682 | "inflight": "1.0.6",
683 | "inherits": "2.0.3",
684 | "minimatch": "3.0.4",
685 | "once": "1.4.0",
686 | "path-is-absolute": "1.0.1"
687 | }
688 | },
689 | "globals": {
690 | "version": "9.18.0",
691 | "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
692 | "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
693 | "dev": true
694 | },
695 | "globby": {
696 | "version": "5.0.0",
697 | "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
698 | "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
699 | "dev": true,
700 | "requires": {
701 | "array-union": "1.0.2",
702 | "arrify": "1.0.1",
703 | "glob": "7.1.2",
704 | "object-assign": "4.1.1",
705 | "pify": "2.3.0",
706 | "pinkie-promise": "2.0.1"
707 | }
708 | },
709 | "graceful-fs": {
710 | "version": "4.1.11",
711 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
712 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
713 | "dev": true
714 | },
715 | "graceful-readlink": {
716 | "version": "1.0.1",
717 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
718 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
719 | "dev": true
720 | },
721 | "growl": {
722 | "version": "1.9.2",
723 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
724 | "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=",
725 | "dev": true
726 | },
727 | "has": {
728 | "version": "1.0.1",
729 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
730 | "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
731 | "dev": true,
732 | "requires": {
733 | "function-bind": "1.1.1"
734 | }
735 | },
736 | "has-ansi": {
737 | "version": "2.0.0",
738 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
739 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
740 | "dev": true,
741 | "requires": {
742 | "ansi-regex": "2.1.1"
743 | }
744 | },
745 | "has-flag": {
746 | "version": "2.0.0",
747 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
748 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
749 | "dev": true
750 | },
751 | "he": {
752 | "version": "1.1.1",
753 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
754 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
755 | "dev": true
756 | },
757 | "hosted-git-info": {
758 | "version": "2.5.0",
759 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
760 | "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==",
761 | "dev": true
762 | },
763 | "iconv-lite": {
764 | "version": "0.4.19",
765 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
766 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
767 | "dev": true
768 | },
769 | "ignore": {
770 | "version": "3.3.5",
771 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz",
772 | "integrity": "sha512-JLH93mL8amZQhh/p6mfQgVBH3M6epNq3DfsXsTSuSrInVjwyYlFE1nv2AgfRCC8PoOhM0jwQ5v8s9LgbK7yGDw==",
773 | "dev": true
774 | },
775 | "imurmurhash": {
776 | "version": "0.1.4",
777 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
778 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
779 | "dev": true
780 | },
781 | "inflight": {
782 | "version": "1.0.6",
783 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
784 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
785 | "dev": true,
786 | "requires": {
787 | "once": "1.4.0",
788 | "wrappy": "1.0.2"
789 | }
790 | },
791 | "inherits": {
792 | "version": "2.0.3",
793 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
794 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
795 | "dev": true
796 | },
797 | "inquirer": {
798 | "version": "3.3.0",
799 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
800 | "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
801 | "dev": true,
802 | "requires": {
803 | "ansi-escapes": "3.0.0",
804 | "chalk": "2.1.0",
805 | "cli-cursor": "2.1.0",
806 | "cli-width": "2.2.0",
807 | "external-editor": "2.0.5",
808 | "figures": "2.0.0",
809 | "lodash": "4.17.4",
810 | "mute-stream": "0.0.7",
811 | "run-async": "2.3.0",
812 | "rx-lite": "4.0.8",
813 | "rx-lite-aggregates": "4.0.8",
814 | "string-width": "2.1.1",
815 | "strip-ansi": "4.0.0",
816 | "through": "2.3.8"
817 | }
818 | },
819 | "is-arrayish": {
820 | "version": "0.2.1",
821 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
822 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
823 | "dev": true
824 | },
825 | "is-builtin-module": {
826 | "version": "1.0.0",
827 | "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
828 | "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
829 | "dev": true,
830 | "requires": {
831 | "builtin-modules": "1.1.1"
832 | }
833 | },
834 | "is-fullwidth-code-point": {
835 | "version": "2.0.0",
836 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
837 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
838 | "dev": true
839 | },
840 | "is-path-cwd": {
841 | "version": "1.0.0",
842 | "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
843 | "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
844 | "dev": true
845 | },
846 | "is-path-in-cwd": {
847 | "version": "1.0.0",
848 | "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz",
849 | "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=",
850 | "dev": true,
851 | "requires": {
852 | "is-path-inside": "1.0.0"
853 | }
854 | },
855 | "is-path-inside": {
856 | "version": "1.0.0",
857 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz",
858 | "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=",
859 | "dev": true,
860 | "requires": {
861 | "path-is-inside": "1.0.2"
862 | }
863 | },
864 | "is-promise": {
865 | "version": "2.1.0",
866 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
867 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
868 | "dev": true
869 | },
870 | "is-resolvable": {
871 | "version": "1.0.0",
872 | "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz",
873 | "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=",
874 | "dev": true,
875 | "requires": {
876 | "tryit": "1.0.3"
877 | }
878 | },
879 | "isarray": {
880 | "version": "1.0.0",
881 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
882 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
883 | "dev": true
884 | },
885 | "isexe": {
886 | "version": "2.0.0",
887 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
888 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
889 | "dev": true
890 | },
891 | "js-tokens": {
892 | "version": "3.0.2",
893 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
894 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
895 | "dev": true
896 | },
897 | "js-yaml": {
898 | "version": "3.10.0",
899 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
900 | "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
901 | "dev": true,
902 | "requires": {
903 | "argparse": "1.0.9",
904 | "esprima": "4.0.0"
905 | }
906 | },
907 | "jschardet": {
908 | "version": "1.5.1",
909 | "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.5.1.tgz",
910 | "integrity": "sha512-vE2hT1D0HLZCLLclfBSfkfTTedhVj0fubHpJBHKwwUWX0nSbhPAfk+SG9rTX95BYNmau8rGFfCeaT6T5OW1C2A==",
911 | "dev": true
912 | },
913 | "json-schema-traverse": {
914 | "version": "0.3.1",
915 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
916 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
917 | "dev": true
918 | },
919 | "json-stable-stringify": {
920 | "version": "1.0.1",
921 | "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
922 | "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
923 | "dev": true,
924 | "requires": {
925 | "jsonify": "0.0.0"
926 | }
927 | },
928 | "json3": {
929 | "version": "3.3.2",
930 | "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz",
931 | "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=",
932 | "dev": true
933 | },
934 | "jsonify": {
935 | "version": "0.0.0",
936 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
937 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
938 | "dev": true
939 | },
940 | "just-extend": {
941 | "version": "1.1.22",
942 | "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.22.tgz",
943 | "integrity": "sha1-MzCvdWyralQnAMZLLk5KoGLVL/8=",
944 | "dev": true
945 | },
946 | "levn": {
947 | "version": "0.3.0",
948 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
949 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
950 | "dev": true,
951 | "requires": {
952 | "prelude-ls": "1.1.2",
953 | "type-check": "0.3.2"
954 | }
955 | },
956 | "load-json-file": {
957 | "version": "2.0.0",
958 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
959 | "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
960 | "dev": true,
961 | "requires": {
962 | "graceful-fs": "4.1.11",
963 | "parse-json": "2.2.0",
964 | "pify": "2.3.0",
965 | "strip-bom": "3.0.0"
966 | }
967 | },
968 | "locate-path": {
969 | "version": "2.0.0",
970 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
971 | "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
972 | "dev": true,
973 | "requires": {
974 | "p-locate": "2.0.0",
975 | "path-exists": "3.0.0"
976 | },
977 | "dependencies": {
978 | "path-exists": {
979 | "version": "3.0.0",
980 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
981 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
982 | "dev": true
983 | }
984 | }
985 | },
986 | "lodash": {
987 | "version": "4.17.4",
988 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
989 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
990 | "dev": true
991 | },
992 | "lodash._baseassign": {
993 | "version": "3.2.0",
994 | "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz",
995 | "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=",
996 | "dev": true,
997 | "requires": {
998 | "lodash._basecopy": "3.0.1",
999 | "lodash.keys": "3.1.2"
1000 | }
1001 | },
1002 | "lodash._basecopy": {
1003 | "version": "3.0.1",
1004 | "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
1005 | "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=",
1006 | "dev": true
1007 | },
1008 | "lodash._basecreate": {
1009 | "version": "3.0.3",
1010 | "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz",
1011 | "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=",
1012 | "dev": true
1013 | },
1014 | "lodash._getnative": {
1015 | "version": "3.9.1",
1016 | "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
1017 | "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
1018 | "dev": true
1019 | },
1020 | "lodash._isiterateecall": {
1021 | "version": "3.0.9",
1022 | "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
1023 | "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
1024 | "dev": true
1025 | },
1026 | "lodash.cond": {
1027 | "version": "4.5.2",
1028 | "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
1029 | "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=",
1030 | "dev": true
1031 | },
1032 | "lodash.create": {
1033 | "version": "3.1.1",
1034 | "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz",
1035 | "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=",
1036 | "dev": true,
1037 | "requires": {
1038 | "lodash._baseassign": "3.2.0",
1039 | "lodash._basecreate": "3.0.3",
1040 | "lodash._isiterateecall": "3.0.9"
1041 | }
1042 | },
1043 | "lodash.isarguments": {
1044 | "version": "3.1.0",
1045 | "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
1046 | "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
1047 | "dev": true
1048 | },
1049 | "lodash.isarray": {
1050 | "version": "3.0.4",
1051 | "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
1052 | "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
1053 | "dev": true
1054 | },
1055 | "lodash.keys": {
1056 | "version": "3.1.2",
1057 | "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
1058 | "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
1059 | "dev": true,
1060 | "requires": {
1061 | "lodash._getnative": "3.9.1",
1062 | "lodash.isarguments": "3.1.0",
1063 | "lodash.isarray": "3.0.4"
1064 | }
1065 | },
1066 | "lolex": {
1067 | "version": "2.1.2",
1068 | "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.1.2.tgz",
1069 | "integrity": "sha1-JpS5U8nqTQE+W4v7qJHJkQJbJik=",
1070 | "dev": true
1071 | },
1072 | "lru-cache": {
1073 | "version": "4.1.1",
1074 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
1075 | "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
1076 | "dev": true,
1077 | "requires": {
1078 | "pseudomap": "1.0.2",
1079 | "yallist": "2.1.2"
1080 | }
1081 | },
1082 | "mimic-fn": {
1083 | "version": "1.1.0",
1084 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz",
1085 | "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=",
1086 | "dev": true
1087 | },
1088 | "minimatch": {
1089 | "version": "3.0.4",
1090 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
1091 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
1092 | "dev": true,
1093 | "requires": {
1094 | "brace-expansion": "1.1.8"
1095 | }
1096 | },
1097 | "minimist": {
1098 | "version": "0.0.8",
1099 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
1100 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
1101 | "dev": true
1102 | },
1103 | "mkdirp": {
1104 | "version": "0.5.1",
1105 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
1106 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
1107 | "dev": true,
1108 | "requires": {
1109 | "minimist": "0.0.8"
1110 | }
1111 | },
1112 | "mocha": {
1113 | "version": "3.5.3",
1114 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz",
1115 | "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==",
1116 | "dev": true,
1117 | "requires": {
1118 | "browser-stdout": "1.3.0",
1119 | "commander": "2.9.0",
1120 | "debug": "2.6.8",
1121 | "diff": "3.2.0",
1122 | "escape-string-regexp": "1.0.5",
1123 | "glob": "7.1.1",
1124 | "growl": "1.9.2",
1125 | "he": "1.1.1",
1126 | "json3": "3.3.2",
1127 | "lodash.create": "3.1.1",
1128 | "mkdirp": "0.5.1",
1129 | "supports-color": "3.1.2"
1130 | },
1131 | "dependencies": {
1132 | "glob": {
1133 | "version": "7.1.1",
1134 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
1135 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
1136 | "dev": true,
1137 | "requires": {
1138 | "fs.realpath": "1.0.0",
1139 | "inflight": "1.0.6",
1140 | "inherits": "2.0.3",
1141 | "minimatch": "3.0.4",
1142 | "once": "1.4.0",
1143 | "path-is-absolute": "1.0.1"
1144 | }
1145 | },
1146 | "has-flag": {
1147 | "version": "1.0.0",
1148 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
1149 | "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
1150 | "dev": true
1151 | },
1152 | "supports-color": {
1153 | "version": "3.1.2",
1154 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz",
1155 | "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=",
1156 | "dev": true,
1157 | "requires": {
1158 | "has-flag": "1.0.0"
1159 | }
1160 | }
1161 | }
1162 | },
1163 | "ms": {
1164 | "version": "2.0.0",
1165 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1166 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
1167 | "dev": true
1168 | },
1169 | "mute-stream": {
1170 | "version": "0.0.7",
1171 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
1172 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
1173 | "dev": true
1174 | },
1175 | "native-promise-only": {
1176 | "version": "0.8.1",
1177 | "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
1178 | "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=",
1179 | "dev": true
1180 | },
1181 | "natural-compare": {
1182 | "version": "1.4.0",
1183 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
1184 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
1185 | "dev": true
1186 | },
1187 | "nise": {
1188 | "version": "1.0.1",
1189 | "resolved": "https://registry.npmjs.org/nise/-/nise-1.0.1.tgz",
1190 | "integrity": "sha1-DakrEKhU6XwPSW9sKEWjASgLPu8=",
1191 | "dev": true,
1192 | "requires": {
1193 | "formatio": "1.2.0",
1194 | "just-extend": "1.1.22",
1195 | "lolex": "1.6.0",
1196 | "path-to-regexp": "1.7.0"
1197 | },
1198 | "dependencies": {
1199 | "lolex": {
1200 | "version": "1.6.0",
1201 | "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz",
1202 | "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=",
1203 | "dev": true
1204 | }
1205 | }
1206 | },
1207 | "normalize-package-data": {
1208 | "version": "2.4.0",
1209 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
1210 | "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
1211 | "dev": true,
1212 | "requires": {
1213 | "hosted-git-info": "2.5.0",
1214 | "is-builtin-module": "1.0.0",
1215 | "semver": "5.4.1",
1216 | "validate-npm-package-license": "3.0.1"
1217 | }
1218 | },
1219 | "nyc": {
1220 | "version": "11.2.1",
1221 | "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.2.1.tgz",
1222 | "integrity": "sha1-rYUK/p261/SXByi0suR/7Rw4chw=",
1223 | "dev": true,
1224 | "requires": {
1225 | "archy": "1.0.0",
1226 | "arrify": "1.0.1",
1227 | "caching-transform": "1.0.1",
1228 | "convert-source-map": "1.5.0",
1229 | "debug-log": "1.0.1",
1230 | "default-require-extensions": "1.0.0",
1231 | "find-cache-dir": "0.1.1",
1232 | "find-up": "2.1.0",
1233 | "foreground-child": "1.5.6",
1234 | "glob": "7.1.2",
1235 | "istanbul-lib-coverage": "1.1.1",
1236 | "istanbul-lib-hook": "1.0.7",
1237 | "istanbul-lib-instrument": "1.8.0",
1238 | "istanbul-lib-report": "1.1.1",
1239 | "istanbul-lib-source-maps": "1.2.1",
1240 | "istanbul-reports": "1.1.2",
1241 | "md5-hex": "1.3.0",
1242 | "merge-source-map": "1.0.4",
1243 | "micromatch": "2.3.11",
1244 | "mkdirp": "0.5.1",
1245 | "resolve-from": "2.0.0",
1246 | "rimraf": "2.6.1",
1247 | "signal-exit": "3.0.2",
1248 | "spawn-wrap": "1.3.8",
1249 | "test-exclude": "4.1.1",
1250 | "yargs": "8.0.2",
1251 | "yargs-parser": "5.0.0"
1252 | },
1253 | "dependencies": {
1254 | "align-text": {
1255 | "version": "0.1.4",
1256 | "bundled": true,
1257 | "dev": true,
1258 | "requires": {
1259 | "kind-of": "3.2.2",
1260 | "longest": "1.0.1",
1261 | "repeat-string": "1.6.1"
1262 | }
1263 | },
1264 | "amdefine": {
1265 | "version": "1.0.1",
1266 | "bundled": true,
1267 | "dev": true
1268 | },
1269 | "ansi-regex": {
1270 | "version": "2.1.1",
1271 | "bundled": true,
1272 | "dev": true
1273 | },
1274 | "ansi-styles": {
1275 | "version": "2.2.1",
1276 | "bundled": true,
1277 | "dev": true
1278 | },
1279 | "append-transform": {
1280 | "version": "0.4.0",
1281 | "bundled": true,
1282 | "dev": true,
1283 | "requires": {
1284 | "default-require-extensions": "1.0.0"
1285 | }
1286 | },
1287 | "archy": {
1288 | "version": "1.0.0",
1289 | "bundled": true,
1290 | "dev": true
1291 | },
1292 | "arr-diff": {
1293 | "version": "2.0.0",
1294 | "bundled": true,
1295 | "dev": true,
1296 | "requires": {
1297 | "arr-flatten": "1.1.0"
1298 | }
1299 | },
1300 | "arr-flatten": {
1301 | "version": "1.1.0",
1302 | "bundled": true,
1303 | "dev": true
1304 | },
1305 | "array-unique": {
1306 | "version": "0.2.1",
1307 | "bundled": true,
1308 | "dev": true
1309 | },
1310 | "arrify": {
1311 | "version": "1.0.1",
1312 | "bundled": true,
1313 | "dev": true
1314 | },
1315 | "async": {
1316 | "version": "1.5.2",
1317 | "bundled": true,
1318 | "dev": true
1319 | },
1320 | "babel-code-frame": {
1321 | "version": "6.26.0",
1322 | "bundled": true,
1323 | "dev": true,
1324 | "requires": {
1325 | "chalk": "1.1.3",
1326 | "esutils": "2.0.2",
1327 | "js-tokens": "3.0.2"
1328 | }
1329 | },
1330 | "babel-generator": {
1331 | "version": "6.26.0",
1332 | "bundled": true,
1333 | "dev": true,
1334 | "requires": {
1335 | "babel-messages": "6.23.0",
1336 | "babel-runtime": "6.26.0",
1337 | "babel-types": "6.26.0",
1338 | "detect-indent": "4.0.0",
1339 | "jsesc": "1.3.0",
1340 | "lodash": "4.17.4",
1341 | "source-map": "0.5.7",
1342 | "trim-right": "1.0.1"
1343 | }
1344 | },
1345 | "babel-messages": {
1346 | "version": "6.23.0",
1347 | "bundled": true,
1348 | "dev": true,
1349 | "requires": {
1350 | "babel-runtime": "6.26.0"
1351 | }
1352 | },
1353 | "babel-runtime": {
1354 | "version": "6.26.0",
1355 | "bundled": true,
1356 | "dev": true,
1357 | "requires": {
1358 | "core-js": "2.5.1",
1359 | "regenerator-runtime": "0.11.0"
1360 | }
1361 | },
1362 | "babel-template": {
1363 | "version": "6.26.0",
1364 | "bundled": true,
1365 | "dev": true,
1366 | "requires": {
1367 | "babel-runtime": "6.26.0",
1368 | "babel-traverse": "6.26.0",
1369 | "babel-types": "6.26.0",
1370 | "babylon": "6.18.0",
1371 | "lodash": "4.17.4"
1372 | }
1373 | },
1374 | "babel-traverse": {
1375 | "version": "6.26.0",
1376 | "bundled": true,
1377 | "dev": true,
1378 | "requires": {
1379 | "babel-code-frame": "6.26.0",
1380 | "babel-messages": "6.23.0",
1381 | "babel-runtime": "6.26.0",
1382 | "babel-types": "6.26.0",
1383 | "babylon": "6.18.0",
1384 | "debug": "2.6.8",
1385 | "globals": "9.18.0",
1386 | "invariant": "2.2.2",
1387 | "lodash": "4.17.4"
1388 | }
1389 | },
1390 | "babel-types": {
1391 | "version": "6.26.0",
1392 | "bundled": true,
1393 | "dev": true,
1394 | "requires": {
1395 | "babel-runtime": "6.26.0",
1396 | "esutils": "2.0.2",
1397 | "lodash": "4.17.4",
1398 | "to-fast-properties": "1.0.3"
1399 | }
1400 | },
1401 | "babylon": {
1402 | "version": "6.18.0",
1403 | "bundled": true,
1404 | "dev": true
1405 | },
1406 | "balanced-match": {
1407 | "version": "1.0.0",
1408 | "bundled": true,
1409 | "dev": true
1410 | },
1411 | "brace-expansion": {
1412 | "version": "1.1.8",
1413 | "bundled": true,
1414 | "dev": true,
1415 | "requires": {
1416 | "balanced-match": "1.0.0",
1417 | "concat-map": "0.0.1"
1418 | }
1419 | },
1420 | "braces": {
1421 | "version": "1.8.5",
1422 | "bundled": true,
1423 | "dev": true,
1424 | "requires": {
1425 | "expand-range": "1.8.2",
1426 | "preserve": "0.2.0",
1427 | "repeat-element": "1.1.2"
1428 | }
1429 | },
1430 | "builtin-modules": {
1431 | "version": "1.1.1",
1432 | "bundled": true,
1433 | "dev": true
1434 | },
1435 | "caching-transform": {
1436 | "version": "1.0.1",
1437 | "bundled": true,
1438 | "dev": true,
1439 | "requires": {
1440 | "md5-hex": "1.3.0",
1441 | "mkdirp": "0.5.1",
1442 | "write-file-atomic": "1.3.4"
1443 | }
1444 | },
1445 | "camelcase": {
1446 | "version": "1.2.1",
1447 | "bundled": true,
1448 | "dev": true,
1449 | "optional": true
1450 | },
1451 | "center-align": {
1452 | "version": "0.1.3",
1453 | "bundled": true,
1454 | "dev": true,
1455 | "optional": true,
1456 | "requires": {
1457 | "align-text": "0.1.4",
1458 | "lazy-cache": "1.0.4"
1459 | }
1460 | },
1461 | "chalk": {
1462 | "version": "1.1.3",
1463 | "bundled": true,
1464 | "dev": true,
1465 | "requires": {
1466 | "ansi-styles": "2.2.1",
1467 | "escape-string-regexp": "1.0.5",
1468 | "has-ansi": "2.0.0",
1469 | "strip-ansi": "3.0.1",
1470 | "supports-color": "2.0.0"
1471 | }
1472 | },
1473 | "cliui": {
1474 | "version": "2.1.0",
1475 | "bundled": true,
1476 | "dev": true,
1477 | "optional": true,
1478 | "requires": {
1479 | "center-align": "0.1.3",
1480 | "right-align": "0.1.3",
1481 | "wordwrap": "0.0.2"
1482 | },
1483 | "dependencies": {
1484 | "wordwrap": {
1485 | "version": "0.0.2",
1486 | "bundled": true,
1487 | "dev": true,
1488 | "optional": true
1489 | }
1490 | }
1491 | },
1492 | "code-point-at": {
1493 | "version": "1.1.0",
1494 | "bundled": true,
1495 | "dev": true
1496 | },
1497 | "commondir": {
1498 | "version": "1.0.1",
1499 | "bundled": true,
1500 | "dev": true
1501 | },
1502 | "concat-map": {
1503 | "version": "0.0.1",
1504 | "bundled": true,
1505 | "dev": true
1506 | },
1507 | "convert-source-map": {
1508 | "version": "1.5.0",
1509 | "bundled": true,
1510 | "dev": true
1511 | },
1512 | "core-js": {
1513 | "version": "2.5.1",
1514 | "bundled": true,
1515 | "dev": true
1516 | },
1517 | "cross-spawn": {
1518 | "version": "4.0.2",
1519 | "bundled": true,
1520 | "dev": true,
1521 | "requires": {
1522 | "lru-cache": "4.1.1",
1523 | "which": "1.3.0"
1524 | }
1525 | },
1526 | "debug": {
1527 | "version": "2.6.8",
1528 | "bundled": true,
1529 | "dev": true,
1530 | "requires": {
1531 | "ms": "2.0.0"
1532 | }
1533 | },
1534 | "debug-log": {
1535 | "version": "1.0.1",
1536 | "bundled": true,
1537 | "dev": true
1538 | },
1539 | "decamelize": {
1540 | "version": "1.2.0",
1541 | "bundled": true,
1542 | "dev": true
1543 | },
1544 | "default-require-extensions": {
1545 | "version": "1.0.0",
1546 | "bundled": true,
1547 | "dev": true,
1548 | "requires": {
1549 | "strip-bom": "2.0.0"
1550 | }
1551 | },
1552 | "detect-indent": {
1553 | "version": "4.0.0",
1554 | "bundled": true,
1555 | "dev": true,
1556 | "requires": {
1557 | "repeating": "2.0.1"
1558 | }
1559 | },
1560 | "error-ex": {
1561 | "version": "1.3.1",
1562 | "bundled": true,
1563 | "dev": true,
1564 | "requires": {
1565 | "is-arrayish": "0.2.1"
1566 | }
1567 | },
1568 | "escape-string-regexp": {
1569 | "version": "1.0.5",
1570 | "bundled": true,
1571 | "dev": true
1572 | },
1573 | "esutils": {
1574 | "version": "2.0.2",
1575 | "bundled": true,
1576 | "dev": true
1577 | },
1578 | "execa": {
1579 | "version": "0.7.0",
1580 | "bundled": true,
1581 | "dev": true,
1582 | "requires": {
1583 | "cross-spawn": "5.1.0",
1584 | "get-stream": "3.0.0",
1585 | "is-stream": "1.1.0",
1586 | "npm-run-path": "2.0.2",
1587 | "p-finally": "1.0.0",
1588 | "signal-exit": "3.0.2",
1589 | "strip-eof": "1.0.0"
1590 | },
1591 | "dependencies": {
1592 | "cross-spawn": {
1593 | "version": "5.1.0",
1594 | "bundled": true,
1595 | "dev": true,
1596 | "requires": {
1597 | "lru-cache": "4.1.1",
1598 | "shebang-command": "1.2.0",
1599 | "which": "1.3.0"
1600 | }
1601 | }
1602 | }
1603 | },
1604 | "expand-brackets": {
1605 | "version": "0.1.5",
1606 | "bundled": true,
1607 | "dev": true,
1608 | "requires": {
1609 | "is-posix-bracket": "0.1.1"
1610 | }
1611 | },
1612 | "expand-range": {
1613 | "version": "1.8.2",
1614 | "bundled": true,
1615 | "dev": true,
1616 | "requires": {
1617 | "fill-range": "2.2.3"
1618 | }
1619 | },
1620 | "extglob": {
1621 | "version": "0.3.2",
1622 | "bundled": true,
1623 | "dev": true,
1624 | "requires": {
1625 | "is-extglob": "1.0.0"
1626 | }
1627 | },
1628 | "filename-regex": {
1629 | "version": "2.0.1",
1630 | "bundled": true,
1631 | "dev": true
1632 | },
1633 | "fill-range": {
1634 | "version": "2.2.3",
1635 | "bundled": true,
1636 | "dev": true,
1637 | "requires": {
1638 | "is-number": "2.1.0",
1639 | "isobject": "2.1.0",
1640 | "randomatic": "1.1.7",
1641 | "repeat-element": "1.1.2",
1642 | "repeat-string": "1.6.1"
1643 | }
1644 | },
1645 | "find-cache-dir": {
1646 | "version": "0.1.1",
1647 | "bundled": true,
1648 | "dev": true,
1649 | "requires": {
1650 | "commondir": "1.0.1",
1651 | "mkdirp": "0.5.1",
1652 | "pkg-dir": "1.0.0"
1653 | }
1654 | },
1655 | "find-up": {
1656 | "version": "2.1.0",
1657 | "bundled": true,
1658 | "dev": true,
1659 | "requires": {
1660 | "locate-path": "2.0.0"
1661 | }
1662 | },
1663 | "for-in": {
1664 | "version": "1.0.2",
1665 | "bundled": true,
1666 | "dev": true
1667 | },
1668 | "for-own": {
1669 | "version": "0.1.5",
1670 | "bundled": true,
1671 | "dev": true,
1672 | "requires": {
1673 | "for-in": "1.0.2"
1674 | }
1675 | },
1676 | "foreground-child": {
1677 | "version": "1.5.6",
1678 | "bundled": true,
1679 | "dev": true,
1680 | "requires": {
1681 | "cross-spawn": "4.0.2",
1682 | "signal-exit": "3.0.2"
1683 | }
1684 | },
1685 | "fs.realpath": {
1686 | "version": "1.0.0",
1687 | "bundled": true,
1688 | "dev": true
1689 | },
1690 | "get-caller-file": {
1691 | "version": "1.0.2",
1692 | "bundled": true,
1693 | "dev": true
1694 | },
1695 | "get-stream": {
1696 | "version": "3.0.0",
1697 | "bundled": true,
1698 | "dev": true
1699 | },
1700 | "glob": {
1701 | "version": "7.1.2",
1702 | "bundled": true,
1703 | "dev": true,
1704 | "requires": {
1705 | "fs.realpath": "1.0.0",
1706 | "inflight": "1.0.6",
1707 | "inherits": "2.0.3",
1708 | "minimatch": "3.0.4",
1709 | "once": "1.4.0",
1710 | "path-is-absolute": "1.0.1"
1711 | }
1712 | },
1713 | "glob-base": {
1714 | "version": "0.3.0",
1715 | "bundled": true,
1716 | "dev": true,
1717 | "requires": {
1718 | "glob-parent": "2.0.0",
1719 | "is-glob": "2.0.1"
1720 | }
1721 | },
1722 | "glob-parent": {
1723 | "version": "2.0.0",
1724 | "bundled": true,
1725 | "dev": true,
1726 | "requires": {
1727 | "is-glob": "2.0.1"
1728 | }
1729 | },
1730 | "globals": {
1731 | "version": "9.18.0",
1732 | "bundled": true,
1733 | "dev": true
1734 | },
1735 | "graceful-fs": {
1736 | "version": "4.1.11",
1737 | "bundled": true,
1738 | "dev": true
1739 | },
1740 | "handlebars": {
1741 | "version": "4.0.10",
1742 | "bundled": true,
1743 | "dev": true,
1744 | "requires": {
1745 | "async": "1.5.2",
1746 | "optimist": "0.6.1",
1747 | "source-map": "0.4.4",
1748 | "uglify-js": "2.8.29"
1749 | },
1750 | "dependencies": {
1751 | "source-map": {
1752 | "version": "0.4.4",
1753 | "bundled": true,
1754 | "dev": true,
1755 | "requires": {
1756 | "amdefine": "1.0.1"
1757 | }
1758 | }
1759 | }
1760 | },
1761 | "has-ansi": {
1762 | "version": "2.0.0",
1763 | "bundled": true,
1764 | "dev": true,
1765 | "requires": {
1766 | "ansi-regex": "2.1.1"
1767 | }
1768 | },
1769 | "has-flag": {
1770 | "version": "1.0.0",
1771 | "bundled": true,
1772 | "dev": true
1773 | },
1774 | "hosted-git-info": {
1775 | "version": "2.5.0",
1776 | "bundled": true,
1777 | "dev": true
1778 | },
1779 | "imurmurhash": {
1780 | "version": "0.1.4",
1781 | "bundled": true,
1782 | "dev": true
1783 | },
1784 | "inflight": {
1785 | "version": "1.0.6",
1786 | "bundled": true,
1787 | "dev": true,
1788 | "requires": {
1789 | "once": "1.4.0",
1790 | "wrappy": "1.0.2"
1791 | }
1792 | },
1793 | "inherits": {
1794 | "version": "2.0.3",
1795 | "bundled": true,
1796 | "dev": true
1797 | },
1798 | "invariant": {
1799 | "version": "2.2.2",
1800 | "bundled": true,
1801 | "dev": true,
1802 | "requires": {
1803 | "loose-envify": "1.3.1"
1804 | }
1805 | },
1806 | "invert-kv": {
1807 | "version": "1.0.0",
1808 | "bundled": true,
1809 | "dev": true
1810 | },
1811 | "is-arrayish": {
1812 | "version": "0.2.1",
1813 | "bundled": true,
1814 | "dev": true
1815 | },
1816 | "is-buffer": {
1817 | "version": "1.1.5",
1818 | "bundled": true,
1819 | "dev": true
1820 | },
1821 | "is-builtin-module": {
1822 | "version": "1.0.0",
1823 | "bundled": true,
1824 | "dev": true,
1825 | "requires": {
1826 | "builtin-modules": "1.1.1"
1827 | }
1828 | },
1829 | "is-dotfile": {
1830 | "version": "1.0.3",
1831 | "bundled": true,
1832 | "dev": true
1833 | },
1834 | "is-equal-shallow": {
1835 | "version": "0.1.3",
1836 | "bundled": true,
1837 | "dev": true,
1838 | "requires": {
1839 | "is-primitive": "2.0.0"
1840 | }
1841 | },
1842 | "is-extendable": {
1843 | "version": "0.1.1",
1844 | "bundled": true,
1845 | "dev": true
1846 | },
1847 | "is-extglob": {
1848 | "version": "1.0.0",
1849 | "bundled": true,
1850 | "dev": true
1851 | },
1852 | "is-finite": {
1853 | "version": "1.0.2",
1854 | "bundled": true,
1855 | "dev": true,
1856 | "requires": {
1857 | "number-is-nan": "1.0.1"
1858 | }
1859 | },
1860 | "is-fullwidth-code-point": {
1861 | "version": "1.0.0",
1862 | "bundled": true,
1863 | "dev": true,
1864 | "requires": {
1865 | "number-is-nan": "1.0.1"
1866 | }
1867 | },
1868 | "is-glob": {
1869 | "version": "2.0.1",
1870 | "bundled": true,
1871 | "dev": true,
1872 | "requires": {
1873 | "is-extglob": "1.0.0"
1874 | }
1875 | },
1876 | "is-number": {
1877 | "version": "2.1.0",
1878 | "bundled": true,
1879 | "dev": true,
1880 | "requires": {
1881 | "kind-of": "3.2.2"
1882 | }
1883 | },
1884 | "is-posix-bracket": {
1885 | "version": "0.1.1",
1886 | "bundled": true,
1887 | "dev": true
1888 | },
1889 | "is-primitive": {
1890 | "version": "2.0.0",
1891 | "bundled": true,
1892 | "dev": true
1893 | },
1894 | "is-stream": {
1895 | "version": "1.1.0",
1896 | "bundled": true,
1897 | "dev": true
1898 | },
1899 | "is-utf8": {
1900 | "version": "0.2.1",
1901 | "bundled": true,
1902 | "dev": true
1903 | },
1904 | "isarray": {
1905 | "version": "1.0.0",
1906 | "bundled": true,
1907 | "dev": true
1908 | },
1909 | "isexe": {
1910 | "version": "2.0.0",
1911 | "bundled": true,
1912 | "dev": true
1913 | },
1914 | "isobject": {
1915 | "version": "2.1.0",
1916 | "bundled": true,
1917 | "dev": true,
1918 | "requires": {
1919 | "isarray": "1.0.0"
1920 | }
1921 | },
1922 | "istanbul-lib-coverage": {
1923 | "version": "1.1.1",
1924 | "bundled": true,
1925 | "dev": true
1926 | },
1927 | "istanbul-lib-hook": {
1928 | "version": "1.0.7",
1929 | "bundled": true,
1930 | "dev": true,
1931 | "requires": {
1932 | "append-transform": "0.4.0"
1933 | }
1934 | },
1935 | "istanbul-lib-instrument": {
1936 | "version": "1.8.0",
1937 | "bundled": true,
1938 | "dev": true,
1939 | "requires": {
1940 | "babel-generator": "6.26.0",
1941 | "babel-template": "6.26.0",
1942 | "babel-traverse": "6.26.0",
1943 | "babel-types": "6.26.0",
1944 | "babylon": "6.18.0",
1945 | "istanbul-lib-coverage": "1.1.1",
1946 | "semver": "5.4.1"
1947 | }
1948 | },
1949 | "istanbul-lib-report": {
1950 | "version": "1.1.1",
1951 | "bundled": true,
1952 | "dev": true,
1953 | "requires": {
1954 | "istanbul-lib-coverage": "1.1.1",
1955 | "mkdirp": "0.5.1",
1956 | "path-parse": "1.0.5",
1957 | "supports-color": "3.2.3"
1958 | },
1959 | "dependencies": {
1960 | "supports-color": {
1961 | "version": "3.2.3",
1962 | "bundled": true,
1963 | "dev": true,
1964 | "requires": {
1965 | "has-flag": "1.0.0"
1966 | }
1967 | }
1968 | }
1969 | },
1970 | "istanbul-lib-source-maps": {
1971 | "version": "1.2.1",
1972 | "bundled": true,
1973 | "dev": true,
1974 | "requires": {
1975 | "debug": "2.6.8",
1976 | "istanbul-lib-coverage": "1.1.1",
1977 | "mkdirp": "0.5.1",
1978 | "rimraf": "2.6.1",
1979 | "source-map": "0.5.7"
1980 | }
1981 | },
1982 | "istanbul-reports": {
1983 | "version": "1.1.2",
1984 | "bundled": true,
1985 | "dev": true,
1986 | "requires": {
1987 | "handlebars": "4.0.10"
1988 | }
1989 | },
1990 | "js-tokens": {
1991 | "version": "3.0.2",
1992 | "bundled": true,
1993 | "dev": true
1994 | },
1995 | "jsesc": {
1996 | "version": "1.3.0",
1997 | "bundled": true,
1998 | "dev": true
1999 | },
2000 | "kind-of": {
2001 | "version": "3.2.2",
2002 | "bundled": true,
2003 | "dev": true,
2004 | "requires": {
2005 | "is-buffer": "1.1.5"
2006 | }
2007 | },
2008 | "lazy-cache": {
2009 | "version": "1.0.4",
2010 | "bundled": true,
2011 | "dev": true,
2012 | "optional": true
2013 | },
2014 | "lcid": {
2015 | "version": "1.0.0",
2016 | "bundled": true,
2017 | "dev": true,
2018 | "requires": {
2019 | "invert-kv": "1.0.0"
2020 | }
2021 | },
2022 | "load-json-file": {
2023 | "version": "1.1.0",
2024 | "bundled": true,
2025 | "dev": true,
2026 | "requires": {
2027 | "graceful-fs": "4.1.11",
2028 | "parse-json": "2.2.0",
2029 | "pify": "2.3.0",
2030 | "pinkie-promise": "2.0.1",
2031 | "strip-bom": "2.0.0"
2032 | }
2033 | },
2034 | "locate-path": {
2035 | "version": "2.0.0",
2036 | "bundled": true,
2037 | "dev": true,
2038 | "requires": {
2039 | "p-locate": "2.0.0",
2040 | "path-exists": "3.0.0"
2041 | },
2042 | "dependencies": {
2043 | "path-exists": {
2044 | "version": "3.0.0",
2045 | "bundled": true,
2046 | "dev": true
2047 | }
2048 | }
2049 | },
2050 | "lodash": {
2051 | "version": "4.17.4",
2052 | "bundled": true,
2053 | "dev": true
2054 | },
2055 | "longest": {
2056 | "version": "1.0.1",
2057 | "bundled": true,
2058 | "dev": true
2059 | },
2060 | "loose-envify": {
2061 | "version": "1.3.1",
2062 | "bundled": true,
2063 | "dev": true,
2064 | "requires": {
2065 | "js-tokens": "3.0.2"
2066 | }
2067 | },
2068 | "lru-cache": {
2069 | "version": "4.1.1",
2070 | "bundled": true,
2071 | "dev": true,
2072 | "requires": {
2073 | "pseudomap": "1.0.2",
2074 | "yallist": "2.1.2"
2075 | }
2076 | },
2077 | "md5-hex": {
2078 | "version": "1.3.0",
2079 | "bundled": true,
2080 | "dev": true,
2081 | "requires": {
2082 | "md5-o-matic": "0.1.1"
2083 | }
2084 | },
2085 | "md5-o-matic": {
2086 | "version": "0.1.1",
2087 | "bundled": true,
2088 | "dev": true
2089 | },
2090 | "mem": {
2091 | "version": "1.1.0",
2092 | "bundled": true,
2093 | "dev": true,
2094 | "requires": {
2095 | "mimic-fn": "1.1.0"
2096 | }
2097 | },
2098 | "merge-source-map": {
2099 | "version": "1.0.4",
2100 | "bundled": true,
2101 | "dev": true,
2102 | "requires": {
2103 | "source-map": "0.5.7"
2104 | }
2105 | },
2106 | "micromatch": {
2107 | "version": "2.3.11",
2108 | "bundled": true,
2109 | "dev": true,
2110 | "requires": {
2111 | "arr-diff": "2.0.0",
2112 | "array-unique": "0.2.1",
2113 | "braces": "1.8.5",
2114 | "expand-brackets": "0.1.5",
2115 | "extglob": "0.3.2",
2116 | "filename-regex": "2.0.1",
2117 | "is-extglob": "1.0.0",
2118 | "is-glob": "2.0.1",
2119 | "kind-of": "3.2.2",
2120 | "normalize-path": "2.1.1",
2121 | "object.omit": "2.0.1",
2122 | "parse-glob": "3.0.4",
2123 | "regex-cache": "0.4.4"
2124 | }
2125 | },
2126 | "mimic-fn": {
2127 | "version": "1.1.0",
2128 | "bundled": true,
2129 | "dev": true
2130 | },
2131 | "minimatch": {
2132 | "version": "3.0.4",
2133 | "bundled": true,
2134 | "dev": true,
2135 | "requires": {
2136 | "brace-expansion": "1.1.8"
2137 | }
2138 | },
2139 | "minimist": {
2140 | "version": "0.0.8",
2141 | "bundled": true,
2142 | "dev": true
2143 | },
2144 | "mkdirp": {
2145 | "version": "0.5.1",
2146 | "bundled": true,
2147 | "dev": true,
2148 | "requires": {
2149 | "minimist": "0.0.8"
2150 | }
2151 | },
2152 | "ms": {
2153 | "version": "2.0.0",
2154 | "bundled": true,
2155 | "dev": true
2156 | },
2157 | "normalize-package-data": {
2158 | "version": "2.4.0",
2159 | "bundled": true,
2160 | "dev": true,
2161 | "requires": {
2162 | "hosted-git-info": "2.5.0",
2163 | "is-builtin-module": "1.0.0",
2164 | "semver": "5.4.1",
2165 | "validate-npm-package-license": "3.0.1"
2166 | }
2167 | },
2168 | "normalize-path": {
2169 | "version": "2.1.1",
2170 | "bundled": true,
2171 | "dev": true,
2172 | "requires": {
2173 | "remove-trailing-separator": "1.1.0"
2174 | }
2175 | },
2176 | "npm-run-path": {
2177 | "version": "2.0.2",
2178 | "bundled": true,
2179 | "dev": true,
2180 | "requires": {
2181 | "path-key": "2.0.1"
2182 | }
2183 | },
2184 | "number-is-nan": {
2185 | "version": "1.0.1",
2186 | "bundled": true,
2187 | "dev": true
2188 | },
2189 | "object-assign": {
2190 | "version": "4.1.1",
2191 | "bundled": true,
2192 | "dev": true
2193 | },
2194 | "object.omit": {
2195 | "version": "2.0.1",
2196 | "bundled": true,
2197 | "dev": true,
2198 | "requires": {
2199 | "for-own": "0.1.5",
2200 | "is-extendable": "0.1.1"
2201 | }
2202 | },
2203 | "once": {
2204 | "version": "1.4.0",
2205 | "bundled": true,
2206 | "dev": true,
2207 | "requires": {
2208 | "wrappy": "1.0.2"
2209 | }
2210 | },
2211 | "optimist": {
2212 | "version": "0.6.1",
2213 | "bundled": true,
2214 | "dev": true,
2215 | "requires": {
2216 | "minimist": "0.0.8",
2217 | "wordwrap": "0.0.3"
2218 | }
2219 | },
2220 | "os-homedir": {
2221 | "version": "1.0.2",
2222 | "bundled": true,
2223 | "dev": true
2224 | },
2225 | "os-locale": {
2226 | "version": "2.1.0",
2227 | "bundled": true,
2228 | "dev": true,
2229 | "requires": {
2230 | "execa": "0.7.0",
2231 | "lcid": "1.0.0",
2232 | "mem": "1.1.0"
2233 | }
2234 | },
2235 | "p-finally": {
2236 | "version": "1.0.0",
2237 | "bundled": true,
2238 | "dev": true
2239 | },
2240 | "p-limit": {
2241 | "version": "1.1.0",
2242 | "bundled": true,
2243 | "dev": true
2244 | },
2245 | "p-locate": {
2246 | "version": "2.0.0",
2247 | "bundled": true,
2248 | "dev": true,
2249 | "requires": {
2250 | "p-limit": "1.1.0"
2251 | }
2252 | },
2253 | "parse-glob": {
2254 | "version": "3.0.4",
2255 | "bundled": true,
2256 | "dev": true,
2257 | "requires": {
2258 | "glob-base": "0.3.0",
2259 | "is-dotfile": "1.0.3",
2260 | "is-extglob": "1.0.0",
2261 | "is-glob": "2.0.1"
2262 | }
2263 | },
2264 | "parse-json": {
2265 | "version": "2.2.0",
2266 | "bundled": true,
2267 | "dev": true,
2268 | "requires": {
2269 | "error-ex": "1.3.1"
2270 | }
2271 | },
2272 | "path-exists": {
2273 | "version": "2.1.0",
2274 | "bundled": true,
2275 | "dev": true,
2276 | "requires": {
2277 | "pinkie-promise": "2.0.1"
2278 | }
2279 | },
2280 | "path-is-absolute": {
2281 | "version": "1.0.1",
2282 | "bundled": true,
2283 | "dev": true
2284 | },
2285 | "path-key": {
2286 | "version": "2.0.1",
2287 | "bundled": true,
2288 | "dev": true
2289 | },
2290 | "path-parse": {
2291 | "version": "1.0.5",
2292 | "bundled": true,
2293 | "dev": true
2294 | },
2295 | "path-type": {
2296 | "version": "1.1.0",
2297 | "bundled": true,
2298 | "dev": true,
2299 | "requires": {
2300 | "graceful-fs": "4.1.11",
2301 | "pify": "2.3.0",
2302 | "pinkie-promise": "2.0.1"
2303 | }
2304 | },
2305 | "pify": {
2306 | "version": "2.3.0",
2307 | "bundled": true,
2308 | "dev": true
2309 | },
2310 | "pinkie": {
2311 | "version": "2.0.4",
2312 | "bundled": true,
2313 | "dev": true
2314 | },
2315 | "pinkie-promise": {
2316 | "version": "2.0.1",
2317 | "bundled": true,
2318 | "dev": true,
2319 | "requires": {
2320 | "pinkie": "2.0.4"
2321 | }
2322 | },
2323 | "pkg-dir": {
2324 | "version": "1.0.0",
2325 | "bundled": true,
2326 | "dev": true,
2327 | "requires": {
2328 | "find-up": "1.1.2"
2329 | },
2330 | "dependencies": {
2331 | "find-up": {
2332 | "version": "1.1.2",
2333 | "bundled": true,
2334 | "dev": true,
2335 | "requires": {
2336 | "path-exists": "2.1.0",
2337 | "pinkie-promise": "2.0.1"
2338 | }
2339 | }
2340 | }
2341 | },
2342 | "preserve": {
2343 | "version": "0.2.0",
2344 | "bundled": true,
2345 | "dev": true
2346 | },
2347 | "pseudomap": {
2348 | "version": "1.0.2",
2349 | "bundled": true,
2350 | "dev": true
2351 | },
2352 | "randomatic": {
2353 | "version": "1.1.7",
2354 | "bundled": true,
2355 | "dev": true,
2356 | "requires": {
2357 | "is-number": "3.0.0",
2358 | "kind-of": "4.0.0"
2359 | },
2360 | "dependencies": {
2361 | "is-number": {
2362 | "version": "3.0.0",
2363 | "bundled": true,
2364 | "dev": true,
2365 | "requires": {
2366 | "kind-of": "3.2.2"
2367 | },
2368 | "dependencies": {
2369 | "kind-of": {
2370 | "version": "3.2.2",
2371 | "bundled": true,
2372 | "dev": true,
2373 | "requires": {
2374 | "is-buffer": "1.1.5"
2375 | }
2376 | }
2377 | }
2378 | },
2379 | "kind-of": {
2380 | "version": "4.0.0",
2381 | "bundled": true,
2382 | "dev": true,
2383 | "requires": {
2384 | "is-buffer": "1.1.5"
2385 | }
2386 | }
2387 | }
2388 | },
2389 | "read-pkg": {
2390 | "version": "1.1.0",
2391 | "bundled": true,
2392 | "dev": true,
2393 | "requires": {
2394 | "load-json-file": "1.1.0",
2395 | "normalize-package-data": "2.4.0",
2396 | "path-type": "1.1.0"
2397 | }
2398 | },
2399 | "read-pkg-up": {
2400 | "version": "1.0.1",
2401 | "bundled": true,
2402 | "dev": true,
2403 | "requires": {
2404 | "find-up": "1.1.2",
2405 | "read-pkg": "1.1.0"
2406 | },
2407 | "dependencies": {
2408 | "find-up": {
2409 | "version": "1.1.2",
2410 | "bundled": true,
2411 | "dev": true,
2412 | "requires": {
2413 | "path-exists": "2.1.0",
2414 | "pinkie-promise": "2.0.1"
2415 | }
2416 | }
2417 | }
2418 | },
2419 | "regenerator-runtime": {
2420 | "version": "0.11.0",
2421 | "bundled": true,
2422 | "dev": true
2423 | },
2424 | "regex-cache": {
2425 | "version": "0.4.4",
2426 | "bundled": true,
2427 | "dev": true,
2428 | "requires": {
2429 | "is-equal-shallow": "0.1.3"
2430 | }
2431 | },
2432 | "remove-trailing-separator": {
2433 | "version": "1.1.0",
2434 | "bundled": true,
2435 | "dev": true
2436 | },
2437 | "repeat-element": {
2438 | "version": "1.1.2",
2439 | "bundled": true,
2440 | "dev": true
2441 | },
2442 | "repeat-string": {
2443 | "version": "1.6.1",
2444 | "bundled": true,
2445 | "dev": true
2446 | },
2447 | "repeating": {
2448 | "version": "2.0.1",
2449 | "bundled": true,
2450 | "dev": true,
2451 | "requires": {
2452 | "is-finite": "1.0.2"
2453 | }
2454 | },
2455 | "require-directory": {
2456 | "version": "2.1.1",
2457 | "bundled": true,
2458 | "dev": true
2459 | },
2460 | "require-main-filename": {
2461 | "version": "1.0.1",
2462 | "bundled": true,
2463 | "dev": true
2464 | },
2465 | "resolve-from": {
2466 | "version": "2.0.0",
2467 | "bundled": true,
2468 | "dev": true
2469 | },
2470 | "right-align": {
2471 | "version": "0.1.3",
2472 | "bundled": true,
2473 | "dev": true,
2474 | "optional": true,
2475 | "requires": {
2476 | "align-text": "0.1.4"
2477 | }
2478 | },
2479 | "rimraf": {
2480 | "version": "2.6.1",
2481 | "bundled": true,
2482 | "dev": true,
2483 | "requires": {
2484 | "glob": "7.1.2"
2485 | }
2486 | },
2487 | "semver": {
2488 | "version": "5.4.1",
2489 | "bundled": true,
2490 | "dev": true
2491 | },
2492 | "set-blocking": {
2493 | "version": "2.0.0",
2494 | "bundled": true,
2495 | "dev": true
2496 | },
2497 | "shebang-command": {
2498 | "version": "1.2.0",
2499 | "bundled": true,
2500 | "dev": true,
2501 | "requires": {
2502 | "shebang-regex": "1.0.0"
2503 | }
2504 | },
2505 | "shebang-regex": {
2506 | "version": "1.0.0",
2507 | "bundled": true,
2508 | "dev": true
2509 | },
2510 | "signal-exit": {
2511 | "version": "3.0.2",
2512 | "bundled": true,
2513 | "dev": true
2514 | },
2515 | "slide": {
2516 | "version": "1.1.6",
2517 | "bundled": true,
2518 | "dev": true
2519 | },
2520 | "source-map": {
2521 | "version": "0.5.7",
2522 | "bundled": true,
2523 | "dev": true
2524 | },
2525 | "spawn-wrap": {
2526 | "version": "1.3.8",
2527 | "bundled": true,
2528 | "dev": true,
2529 | "requires": {
2530 | "foreground-child": "1.5.6",
2531 | "mkdirp": "0.5.1",
2532 | "os-homedir": "1.0.2",
2533 | "rimraf": "2.6.1",
2534 | "signal-exit": "3.0.2",
2535 | "which": "1.3.0"
2536 | }
2537 | },
2538 | "spdx-correct": {
2539 | "version": "1.0.2",
2540 | "bundled": true,
2541 | "dev": true,
2542 | "requires": {
2543 | "spdx-license-ids": "1.2.2"
2544 | }
2545 | },
2546 | "spdx-expression-parse": {
2547 | "version": "1.0.4",
2548 | "bundled": true,
2549 | "dev": true
2550 | },
2551 | "spdx-license-ids": {
2552 | "version": "1.2.2",
2553 | "bundled": true,
2554 | "dev": true
2555 | },
2556 | "string-width": {
2557 | "version": "2.1.1",
2558 | "bundled": true,
2559 | "dev": true,
2560 | "requires": {
2561 | "is-fullwidth-code-point": "2.0.0",
2562 | "strip-ansi": "4.0.0"
2563 | },
2564 | "dependencies": {
2565 | "ansi-regex": {
2566 | "version": "3.0.0",
2567 | "bundled": true,
2568 | "dev": true
2569 | },
2570 | "is-fullwidth-code-point": {
2571 | "version": "2.0.0",
2572 | "bundled": true,
2573 | "dev": true
2574 | },
2575 | "strip-ansi": {
2576 | "version": "4.0.0",
2577 | "bundled": true,
2578 | "dev": true,
2579 | "requires": {
2580 | "ansi-regex": "3.0.0"
2581 | }
2582 | }
2583 | }
2584 | },
2585 | "strip-ansi": {
2586 | "version": "3.0.1",
2587 | "bundled": true,
2588 | "dev": true,
2589 | "requires": {
2590 | "ansi-regex": "2.1.1"
2591 | }
2592 | },
2593 | "strip-bom": {
2594 | "version": "2.0.0",
2595 | "bundled": true,
2596 | "dev": true,
2597 | "requires": {
2598 | "is-utf8": "0.2.1"
2599 | }
2600 | },
2601 | "strip-eof": {
2602 | "version": "1.0.0",
2603 | "bundled": true,
2604 | "dev": true
2605 | },
2606 | "supports-color": {
2607 | "version": "2.0.0",
2608 | "bundled": true,
2609 | "dev": true
2610 | },
2611 | "test-exclude": {
2612 | "version": "4.1.1",
2613 | "bundled": true,
2614 | "dev": true,
2615 | "requires": {
2616 | "arrify": "1.0.1",
2617 | "micromatch": "2.3.11",
2618 | "object-assign": "4.1.1",
2619 | "read-pkg-up": "1.0.1",
2620 | "require-main-filename": "1.0.1"
2621 | }
2622 | },
2623 | "to-fast-properties": {
2624 | "version": "1.0.3",
2625 | "bundled": true,
2626 | "dev": true
2627 | },
2628 | "trim-right": {
2629 | "version": "1.0.1",
2630 | "bundled": true,
2631 | "dev": true
2632 | },
2633 | "uglify-js": {
2634 | "version": "2.8.29",
2635 | "bundled": true,
2636 | "dev": true,
2637 | "optional": true,
2638 | "requires": {
2639 | "source-map": "0.5.7",
2640 | "uglify-to-browserify": "1.0.2",
2641 | "yargs": "3.10.0"
2642 | },
2643 | "dependencies": {
2644 | "yargs": {
2645 | "version": "3.10.0",
2646 | "bundled": true,
2647 | "dev": true,
2648 | "optional": true,
2649 | "requires": {
2650 | "camelcase": "1.2.1",
2651 | "cliui": "2.1.0",
2652 | "decamelize": "1.2.0",
2653 | "window-size": "0.1.0"
2654 | }
2655 | }
2656 | }
2657 | },
2658 | "uglify-to-browserify": {
2659 | "version": "1.0.2",
2660 | "bundled": true,
2661 | "dev": true,
2662 | "optional": true
2663 | },
2664 | "validate-npm-package-license": {
2665 | "version": "3.0.1",
2666 | "bundled": true,
2667 | "dev": true,
2668 | "requires": {
2669 | "spdx-correct": "1.0.2",
2670 | "spdx-expression-parse": "1.0.4"
2671 | }
2672 | },
2673 | "which": {
2674 | "version": "1.3.0",
2675 | "bundled": true,
2676 | "dev": true,
2677 | "requires": {
2678 | "isexe": "2.0.0"
2679 | }
2680 | },
2681 | "which-module": {
2682 | "version": "2.0.0",
2683 | "bundled": true,
2684 | "dev": true
2685 | },
2686 | "window-size": {
2687 | "version": "0.1.0",
2688 | "bundled": true,
2689 | "dev": true,
2690 | "optional": true
2691 | },
2692 | "wordwrap": {
2693 | "version": "0.0.3",
2694 | "bundled": true,
2695 | "dev": true
2696 | },
2697 | "wrap-ansi": {
2698 | "version": "2.1.0",
2699 | "bundled": true,
2700 | "dev": true,
2701 | "requires": {
2702 | "string-width": "1.0.2",
2703 | "strip-ansi": "3.0.1"
2704 | },
2705 | "dependencies": {
2706 | "string-width": {
2707 | "version": "1.0.2",
2708 | "bundled": true,
2709 | "dev": true,
2710 | "requires": {
2711 | "code-point-at": "1.1.0",
2712 | "is-fullwidth-code-point": "1.0.0",
2713 | "strip-ansi": "3.0.1"
2714 | }
2715 | }
2716 | }
2717 | },
2718 | "wrappy": {
2719 | "version": "1.0.2",
2720 | "bundled": true,
2721 | "dev": true
2722 | },
2723 | "write-file-atomic": {
2724 | "version": "1.3.4",
2725 | "bundled": true,
2726 | "dev": true,
2727 | "requires": {
2728 | "graceful-fs": "4.1.11",
2729 | "imurmurhash": "0.1.4",
2730 | "slide": "1.1.6"
2731 | }
2732 | },
2733 | "y18n": {
2734 | "version": "3.2.1",
2735 | "bundled": true,
2736 | "dev": true
2737 | },
2738 | "yallist": {
2739 | "version": "2.1.2",
2740 | "bundled": true,
2741 | "dev": true
2742 | },
2743 | "yargs": {
2744 | "version": "8.0.2",
2745 | "bundled": true,
2746 | "dev": true,
2747 | "requires": {
2748 | "camelcase": "4.1.0",
2749 | "cliui": "3.2.0",
2750 | "decamelize": "1.2.0",
2751 | "get-caller-file": "1.0.2",
2752 | "os-locale": "2.1.0",
2753 | "read-pkg-up": "2.0.0",
2754 | "require-directory": "2.1.1",
2755 | "require-main-filename": "1.0.1",
2756 | "set-blocking": "2.0.0",
2757 | "string-width": "2.1.1",
2758 | "which-module": "2.0.0",
2759 | "y18n": "3.2.1",
2760 | "yargs-parser": "7.0.0"
2761 | },
2762 | "dependencies": {
2763 | "camelcase": {
2764 | "version": "4.1.0",
2765 | "bundled": true,
2766 | "dev": true
2767 | },
2768 | "cliui": {
2769 | "version": "3.2.0",
2770 | "bundled": true,
2771 | "dev": true,
2772 | "requires": {
2773 | "string-width": "1.0.2",
2774 | "strip-ansi": "3.0.1",
2775 | "wrap-ansi": "2.1.0"
2776 | },
2777 | "dependencies": {
2778 | "string-width": {
2779 | "version": "1.0.2",
2780 | "bundled": true,
2781 | "dev": true,
2782 | "requires": {
2783 | "code-point-at": "1.1.0",
2784 | "is-fullwidth-code-point": "1.0.0",
2785 | "strip-ansi": "3.0.1"
2786 | }
2787 | }
2788 | }
2789 | },
2790 | "load-json-file": {
2791 | "version": "2.0.0",
2792 | "bundled": true,
2793 | "dev": true,
2794 | "requires": {
2795 | "graceful-fs": "4.1.11",
2796 | "parse-json": "2.2.0",
2797 | "pify": "2.3.0",
2798 | "strip-bom": "3.0.0"
2799 | }
2800 | },
2801 | "path-type": {
2802 | "version": "2.0.0",
2803 | "bundled": true,
2804 | "dev": true,
2805 | "requires": {
2806 | "pify": "2.3.0"
2807 | }
2808 | },
2809 | "read-pkg": {
2810 | "version": "2.0.0",
2811 | "bundled": true,
2812 | "dev": true,
2813 | "requires": {
2814 | "load-json-file": "2.0.0",
2815 | "normalize-package-data": "2.4.0",
2816 | "path-type": "2.0.0"
2817 | }
2818 | },
2819 | "read-pkg-up": {
2820 | "version": "2.0.0",
2821 | "bundled": true,
2822 | "dev": true,
2823 | "requires": {
2824 | "find-up": "2.1.0",
2825 | "read-pkg": "2.0.0"
2826 | }
2827 | },
2828 | "strip-bom": {
2829 | "version": "3.0.0",
2830 | "bundled": true,
2831 | "dev": true
2832 | },
2833 | "yargs-parser": {
2834 | "version": "7.0.0",
2835 | "bundled": true,
2836 | "dev": true,
2837 | "requires": {
2838 | "camelcase": "4.1.0"
2839 | }
2840 | }
2841 | }
2842 | },
2843 | "yargs-parser": {
2844 | "version": "5.0.0",
2845 | "bundled": true,
2846 | "dev": true,
2847 | "requires": {
2848 | "camelcase": "3.0.0"
2849 | },
2850 | "dependencies": {
2851 | "camelcase": {
2852 | "version": "3.0.0",
2853 | "bundled": true,
2854 | "dev": true
2855 | }
2856 | }
2857 | }
2858 | }
2859 | },
2860 | "object-assign": {
2861 | "version": "4.1.1",
2862 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
2863 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
2864 | "dev": true
2865 | },
2866 | "once": {
2867 | "version": "1.4.0",
2868 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
2869 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
2870 | "dev": true,
2871 | "requires": {
2872 | "wrappy": "1.0.2"
2873 | }
2874 | },
2875 | "onetime": {
2876 | "version": "2.0.1",
2877 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
2878 | "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
2879 | "dev": true,
2880 | "requires": {
2881 | "mimic-fn": "1.1.0"
2882 | }
2883 | },
2884 | "opentracing": {
2885 | "version": "0.14.1",
2886 | "resolved": "https://registry.npmjs.org/opentracing/-/opentracing-0.14.1.tgz",
2887 | "integrity": "sha1-QNJ4vupBdmCjXdnT7nZRH/qRHc0="
2888 | },
2889 | "optionator": {
2890 | "version": "0.8.2",
2891 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
2892 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
2893 | "dev": true,
2894 | "requires": {
2895 | "deep-is": "0.1.3",
2896 | "fast-levenshtein": "2.0.6",
2897 | "levn": "0.3.0",
2898 | "prelude-ls": "1.1.2",
2899 | "type-check": "0.3.2",
2900 | "wordwrap": "1.0.0"
2901 | }
2902 | },
2903 | "os-tmpdir": {
2904 | "version": "1.0.2",
2905 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
2906 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
2907 | "dev": true
2908 | },
2909 | "p-limit": {
2910 | "version": "1.1.0",
2911 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz",
2912 | "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=",
2913 | "dev": true
2914 | },
2915 | "p-locate": {
2916 | "version": "2.0.0",
2917 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
2918 | "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
2919 | "dev": true,
2920 | "requires": {
2921 | "p-limit": "1.1.0"
2922 | }
2923 | },
2924 | "parse-json": {
2925 | "version": "2.2.0",
2926 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
2927 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
2928 | "dev": true,
2929 | "requires": {
2930 | "error-ex": "1.3.1"
2931 | }
2932 | },
2933 | "path-exists": {
2934 | "version": "2.1.0",
2935 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
2936 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
2937 | "dev": true,
2938 | "requires": {
2939 | "pinkie-promise": "2.0.1"
2940 | }
2941 | },
2942 | "path-is-absolute": {
2943 | "version": "1.0.1",
2944 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
2945 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
2946 | "dev": true
2947 | },
2948 | "path-is-inside": {
2949 | "version": "1.0.2",
2950 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
2951 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
2952 | "dev": true
2953 | },
2954 | "path-parse": {
2955 | "version": "1.0.5",
2956 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
2957 | "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
2958 | "dev": true
2959 | },
2960 | "path-to-regexp": {
2961 | "version": "1.7.0",
2962 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
2963 | "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
2964 | "dev": true,
2965 | "requires": {
2966 | "isarray": "0.0.1"
2967 | },
2968 | "dependencies": {
2969 | "isarray": {
2970 | "version": "0.0.1",
2971 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
2972 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
2973 | "dev": true
2974 | }
2975 | }
2976 | },
2977 | "path-type": {
2978 | "version": "2.0.0",
2979 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
2980 | "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
2981 | "dev": true,
2982 | "requires": {
2983 | "pify": "2.3.0"
2984 | }
2985 | },
2986 | "pathval": {
2987 | "version": "1.1.0",
2988 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
2989 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
2990 | "dev": true
2991 | },
2992 | "pify": {
2993 | "version": "2.3.0",
2994 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
2995 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
2996 | "dev": true
2997 | },
2998 | "pinkie": {
2999 | "version": "2.0.4",
3000 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
3001 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
3002 | "dev": true
3003 | },
3004 | "pinkie-promise": {
3005 | "version": "2.0.1",
3006 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
3007 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
3008 | "dev": true,
3009 | "requires": {
3010 | "pinkie": "2.0.4"
3011 | }
3012 | },
3013 | "pkg-dir": {
3014 | "version": "1.0.0",
3015 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
3016 | "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
3017 | "dev": true,
3018 | "requires": {
3019 | "find-up": "1.1.2"
3020 | }
3021 | },
3022 | "pluralize": {
3023 | "version": "7.0.0",
3024 | "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
3025 | "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
3026 | "dev": true
3027 | },
3028 | "prelude-ls": {
3029 | "version": "1.1.2",
3030 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
3031 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
3032 | "dev": true
3033 | },
3034 | "process-nextick-args": {
3035 | "version": "1.0.7",
3036 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
3037 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
3038 | "dev": true
3039 | },
3040 | "progress": {
3041 | "version": "2.0.0",
3042 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
3043 | "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
3044 | "dev": true
3045 | },
3046 | "prom-client": {
3047 | "version": "10.1.1",
3048 | "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-10.1.1.tgz",
3049 | "integrity": "sha512-g+W8Y3XMLjyYuawRYW0HBX32sO6H+E9SidUQBhG/DSiVix8Hdkjpy2xUZZx/cPWbMJ17etWCrMucVv5oNuhDNA==",
3050 | "requires": {
3051 | "tdigest": "0.1.1"
3052 | }
3053 | },
3054 | "pseudomap": {
3055 | "version": "1.0.2",
3056 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
3057 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
3058 | "dev": true
3059 | },
3060 | "read-pkg": {
3061 | "version": "2.0.0",
3062 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
3063 | "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
3064 | "dev": true,
3065 | "requires": {
3066 | "load-json-file": "2.0.0",
3067 | "normalize-package-data": "2.4.0",
3068 | "path-type": "2.0.0"
3069 | }
3070 | },
3071 | "read-pkg-up": {
3072 | "version": "2.0.0",
3073 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
3074 | "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
3075 | "dev": true,
3076 | "requires": {
3077 | "find-up": "2.1.0",
3078 | "read-pkg": "2.0.0"
3079 | },
3080 | "dependencies": {
3081 | "find-up": {
3082 | "version": "2.1.0",
3083 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
3084 | "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
3085 | "dev": true,
3086 | "requires": {
3087 | "locate-path": "2.0.0"
3088 | }
3089 | }
3090 | }
3091 | },
3092 | "readable-stream": {
3093 | "version": "2.3.3",
3094 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
3095 | "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
3096 | "dev": true,
3097 | "requires": {
3098 | "core-util-is": "1.0.2",
3099 | "inherits": "2.0.3",
3100 | "isarray": "1.0.0",
3101 | "process-nextick-args": "1.0.7",
3102 | "safe-buffer": "5.1.1",
3103 | "string_decoder": "1.0.3",
3104 | "util-deprecate": "1.0.2"
3105 | }
3106 | },
3107 | "require-uncached": {
3108 | "version": "1.0.3",
3109 | "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
3110 | "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
3111 | "dev": true,
3112 | "requires": {
3113 | "caller-path": "0.1.0",
3114 | "resolve-from": "1.0.1"
3115 | }
3116 | },
3117 | "resolve": {
3118 | "version": "1.4.0",
3119 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz",
3120 | "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==",
3121 | "dev": true,
3122 | "requires": {
3123 | "path-parse": "1.0.5"
3124 | }
3125 | },
3126 | "resolve-from": {
3127 | "version": "1.0.1",
3128 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
3129 | "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
3130 | "dev": true
3131 | },
3132 | "restore-cursor": {
3133 | "version": "2.0.0",
3134 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
3135 | "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
3136 | "dev": true,
3137 | "requires": {
3138 | "onetime": "2.0.1",
3139 | "signal-exit": "3.0.2"
3140 | }
3141 | },
3142 | "rimraf": {
3143 | "version": "2.6.2",
3144 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
3145 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
3146 | "dev": true,
3147 | "requires": {
3148 | "glob": "7.1.2"
3149 | }
3150 | },
3151 | "run-async": {
3152 | "version": "2.3.0",
3153 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
3154 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
3155 | "dev": true,
3156 | "requires": {
3157 | "is-promise": "2.1.0"
3158 | }
3159 | },
3160 | "rx-lite": {
3161 | "version": "4.0.8",
3162 | "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
3163 | "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
3164 | "dev": true
3165 | },
3166 | "rx-lite-aggregates": {
3167 | "version": "4.0.8",
3168 | "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
3169 | "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
3170 | "dev": true,
3171 | "requires": {
3172 | "rx-lite": "4.0.8"
3173 | }
3174 | },
3175 | "safe-buffer": {
3176 | "version": "5.1.1",
3177 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
3178 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
3179 | "dev": true
3180 | },
3181 | "samsam": {
3182 | "version": "1.2.1",
3183 | "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.2.1.tgz",
3184 | "integrity": "sha1-7dOQk6MYQ3DLhZJDsr3yVefY6mc=",
3185 | "dev": true
3186 | },
3187 | "semver": {
3188 | "version": "5.4.1",
3189 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
3190 | "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==",
3191 | "dev": true
3192 | },
3193 | "shebang-command": {
3194 | "version": "1.2.0",
3195 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
3196 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
3197 | "dev": true,
3198 | "requires": {
3199 | "shebang-regex": "1.0.0"
3200 | }
3201 | },
3202 | "shebang-regex": {
3203 | "version": "1.0.0",
3204 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
3205 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
3206 | "dev": true
3207 | },
3208 | "signal-exit": {
3209 | "version": "3.0.2",
3210 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
3211 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
3212 | "dev": true
3213 | },
3214 | "sinon": {
3215 | "version": "3.2.1",
3216 | "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz",
3217 | "integrity": "sha512-KY3OLOWpek/I4NGAMHetuutVgS2aRgMR5g5/1LSYvPJ3qo2BopIvk3esFztPxF40RWf/NNNJzdFPriSkXUVK3A==",
3218 | "dev": true,
3219 | "requires": {
3220 | "diff": "3.2.0",
3221 | "formatio": "1.2.0",
3222 | "lolex": "2.1.2",
3223 | "native-promise-only": "0.8.1",
3224 | "nise": "1.0.1",
3225 | "path-to-regexp": "1.7.0",
3226 | "samsam": "1.2.1",
3227 | "text-encoding": "0.6.4",
3228 | "type-detect": "4.0.3"
3229 | }
3230 | },
3231 | "sinon-chai": {
3232 | "version": "2.14.0",
3233 | "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-2.14.0.tgz",
3234 | "integrity": "sha512-9stIF1utB0ywNHNT7RgiXbdmen8QDCRsrTjw+G9TgKt1Yexjiv8TOWZ6WHsTPz57Yky3DIswZvEqX8fpuHNDtQ==",
3235 | "dev": true
3236 | },
3237 | "slice-ansi": {
3238 | "version": "1.0.0",
3239 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
3240 | "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
3241 | "dev": true,
3242 | "requires": {
3243 | "is-fullwidth-code-point": "2.0.0"
3244 | }
3245 | },
3246 | "spdx-correct": {
3247 | "version": "1.0.2",
3248 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
3249 | "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
3250 | "dev": true,
3251 | "requires": {
3252 | "spdx-license-ids": "1.2.2"
3253 | }
3254 | },
3255 | "spdx-expression-parse": {
3256 | "version": "1.0.4",
3257 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
3258 | "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=",
3259 | "dev": true
3260 | },
3261 | "spdx-license-ids": {
3262 | "version": "1.2.2",
3263 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
3264 | "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=",
3265 | "dev": true
3266 | },
3267 | "sprintf-js": {
3268 | "version": "1.0.3",
3269 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
3270 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
3271 | "dev": true
3272 | },
3273 | "string_decoder": {
3274 | "version": "1.0.3",
3275 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
3276 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
3277 | "dev": true,
3278 | "requires": {
3279 | "safe-buffer": "5.1.1"
3280 | }
3281 | },
3282 | "string-width": {
3283 | "version": "2.1.1",
3284 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
3285 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
3286 | "dev": true,
3287 | "requires": {
3288 | "is-fullwidth-code-point": "2.0.0",
3289 | "strip-ansi": "4.0.0"
3290 | }
3291 | },
3292 | "strip-ansi": {
3293 | "version": "4.0.0",
3294 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
3295 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
3296 | "dev": true,
3297 | "requires": {
3298 | "ansi-regex": "3.0.0"
3299 | },
3300 | "dependencies": {
3301 | "ansi-regex": {
3302 | "version": "3.0.0",
3303 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
3304 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
3305 | "dev": true
3306 | }
3307 | }
3308 | },
3309 | "strip-bom": {
3310 | "version": "3.0.0",
3311 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
3312 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
3313 | "dev": true
3314 | },
3315 | "strip-json-comments": {
3316 | "version": "2.0.1",
3317 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
3318 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
3319 | "dev": true
3320 | },
3321 | "supports-color": {
3322 | "version": "2.0.0",
3323 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
3324 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
3325 | "dev": true
3326 | },
3327 | "table": {
3328 | "version": "4.0.2",
3329 | "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
3330 | "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
3331 | "dev": true,
3332 | "requires": {
3333 | "ajv": "5.2.3",
3334 | "ajv-keywords": "2.1.0",
3335 | "chalk": "2.1.0",
3336 | "lodash": "4.17.4",
3337 | "slice-ansi": "1.0.0",
3338 | "string-width": "2.1.1"
3339 | }
3340 | },
3341 | "tdigest": {
3342 | "version": "0.1.1",
3343 | "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz",
3344 | "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=",
3345 | "requires": {
3346 | "bintrees": "1.0.1"
3347 | }
3348 | },
3349 | "text-encoding": {
3350 | "version": "0.6.4",
3351 | "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
3352 | "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
3353 | "dev": true
3354 | },
3355 | "text-table": {
3356 | "version": "0.2.0",
3357 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
3358 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
3359 | "dev": true
3360 | },
3361 | "through": {
3362 | "version": "2.3.8",
3363 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
3364 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
3365 | "dev": true
3366 | },
3367 | "tmp": {
3368 | "version": "0.0.33",
3369 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
3370 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
3371 | "dev": true,
3372 | "requires": {
3373 | "os-tmpdir": "1.0.2"
3374 | }
3375 | },
3376 | "tryit": {
3377 | "version": "1.0.3",
3378 | "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz",
3379 | "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=",
3380 | "dev": true
3381 | },
3382 | "type-check": {
3383 | "version": "0.3.2",
3384 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
3385 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
3386 | "dev": true,
3387 | "requires": {
3388 | "prelude-ls": "1.1.2"
3389 | }
3390 | },
3391 | "type-detect": {
3392 | "version": "4.0.3",
3393 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz",
3394 | "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=",
3395 | "dev": true
3396 | },
3397 | "typedarray": {
3398 | "version": "0.0.6",
3399 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
3400 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
3401 | "dev": true
3402 | },
3403 | "util-deprecate": {
3404 | "version": "1.0.2",
3405 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
3406 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
3407 | "dev": true
3408 | },
3409 | "uuid": {
3410 | "version": "3.1.0",
3411 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
3412 | "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
3413 | },
3414 | "validate-npm-package-license": {
3415 | "version": "3.0.1",
3416 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
3417 | "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
3418 | "dev": true,
3419 | "requires": {
3420 | "spdx-correct": "1.0.2",
3421 | "spdx-expression-parse": "1.0.4"
3422 | }
3423 | },
3424 | "which": {
3425 | "version": "1.3.0",
3426 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
3427 | "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
3428 | "dev": true,
3429 | "requires": {
3430 | "isexe": "2.0.0"
3431 | }
3432 | },
3433 | "wordwrap": {
3434 | "version": "1.0.0",
3435 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
3436 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
3437 | "dev": true
3438 | },
3439 | "wrappy": {
3440 | "version": "1.0.2",
3441 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
3442 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
3443 | "dev": true
3444 | },
3445 | "write": {
3446 | "version": "0.2.1",
3447 | "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
3448 | "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
3449 | "dev": true,
3450 | "requires": {
3451 | "mkdirp": "0.5.1"
3452 | }
3453 | },
3454 | "yallist": {
3455 | "version": "2.1.2",
3456 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
3457 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
3458 | "dev": true
3459 | }
3460 | }
3461 | }
3462 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@risingstack/opentracing-metrics-tracer",
3 | "version": "2.1.0",
4 | "description": "Exports metrics via OpenTracing instrumentation and Prometheus reporter",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "test": "mocha test/setup.js 'src/**/*.spec.js'",
8 | "coverage": "nyc --reporter=html --reporter=text npm run test",
9 | "lint": "eslint test src"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/RisingStack/opentracing-metrics-tracer.git"
14 | },
15 | "author": "Peter Marton",
16 | "license": "MIT",
17 | "bugs": {
18 | "url": "https://github.com/RisingStack/opentracing-metrics-tracer/issues"
19 | },
20 | "homepage": "https://github.com/RisingStack/opentracing-metrics-tracer#readme",
21 | "dependencies": {
22 | "opentracing": "0.14.1",
23 | "prom-client": "10.1.1",
24 | "uuid": "3.1.0"
25 | },
26 | "devDependencies": {
27 | "chai": "4.1.2",
28 | "dedent": "0.7.0",
29 | "eslint": "4.7.2",
30 | "eslint-config-airbnb-base": "11.0.0",
31 | "eslint-plugin-import": "2.7.0",
32 | "eslint-plugin-promise": "3.5.0",
33 | "mocha": "3.5.3",
34 | "nyc": "11.2.1",
35 | "sinon": "3.2.1",
36 | "sinon-chai": "2.14.0"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { Tracer } = require('./tracer')
4 | const { PrometheusReporter } = require('./reporters')
5 |
6 | module.exports = Object.assign(Tracer, {
7 | PrometheusReporter
8 | })
9 |
--------------------------------------------------------------------------------
/src/reporters/PrometheusReporter.e2e.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const sinon = require('sinon')
4 | const dedent = require('dedent')
5 | const { expect } = require('chai')
6 | const { Tags } = require('opentracing')
7 | const { Tracer } = require('../tracer')
8 | const PrometheusReporter = require('./PrometheusReporter')
9 |
10 | describe('e2e: PrometheusReporter', () => {
11 | let clock
12 |
13 | beforeEach(() => {
14 | clock = sinon.useFakeTimers()
15 | })
16 |
17 | afterEach(() => {
18 | clock.restore()
19 | })
20 |
21 | describe('operation metrics', () => {
22 | it('should have operation metrics initialized', () => {
23 | const reporter = new PrometheusReporter()
24 |
25 | expect(reporter.metrics()).to.be.equal(dedent`
26 | # HELP operation_duration_seconds Duration of operations in second
27 | # TYPE operation_duration_seconds histogram\n
28 | `)
29 | })
30 |
31 | it('should have operation metrics', () => {
32 | const reporter = new PrometheusReporter()
33 | const tracer = new Tracer('my-service', [reporter])
34 |
35 | const span1 = tracer.startSpan('my-operation')
36 | clock.tick(100)
37 | span1.finish()
38 |
39 | const span2 = tracer.startSpan('my-operation')
40 | clock.tick(300)
41 | span2.finish()
42 |
43 | const labelStr = `parent_service="${PrometheusReporter.LABEL_PARENT_SERVICE_UNKNOWN}",name="my-operation"`
44 |
45 | expect(reporter.metrics()).to.be.equal(dedent`
46 | # HELP operation_duration_seconds Duration of operations in second
47 | # TYPE operation_duration_seconds histogram
48 | operation_duration_seconds_bucket{le="0.005",${labelStr}} 0
49 | operation_duration_seconds_bucket{le="0.01",${labelStr}} 0
50 | operation_duration_seconds_bucket{le="0.025",${labelStr}} 0
51 | operation_duration_seconds_bucket{le="0.05",${labelStr}} 0
52 | operation_duration_seconds_bucket{le="0.1",${labelStr}} 1
53 | operation_duration_seconds_bucket{le="0.25",${labelStr}} 1
54 | operation_duration_seconds_bucket{le="0.5",${labelStr}} 2
55 | operation_duration_seconds_bucket{le="1",${labelStr}} 2
56 | operation_duration_seconds_bucket{le="2.5",${labelStr}} 2
57 | operation_duration_seconds_bucket{le="5",${labelStr}} 2
58 | operation_duration_seconds_bucket{le="10",${labelStr}} 2
59 | operation_duration_seconds_bucket{le="+Inf",${labelStr}} 2
60 | operation_duration_seconds_sum{${labelStr}} 0.4
61 | operation_duration_seconds_count{${labelStr}} 2\n
62 | `)
63 | })
64 |
65 | it('should have operation metrics with parent', () => {
66 | const reporter = new PrometheusReporter()
67 | const parentTracer = new Tracer('parent-service')
68 | const tracer = new Tracer('service', [reporter])
69 |
70 | const parentSpan1 = parentTracer.startSpan('parent-operation')
71 | const span1 = tracer.startSpan('my-operation', { childOf: parentSpan1 })
72 | clock.tick(100)
73 | span1.finish()
74 |
75 | const parentSpan2 = parentTracer.startSpan('parent-operation')
76 | const span2 = tracer.startSpan('my-operation', { childOf: parentSpan2 })
77 | clock.tick(300)
78 | span2.finish()
79 |
80 | const labelStr = 'parent_service="parent-service",name="my-operation"'
81 |
82 | expect(reporter.metrics()).to.be.equal(dedent`
83 | # HELP operation_duration_seconds Duration of operations in second
84 | # TYPE operation_duration_seconds histogram
85 | operation_duration_seconds_bucket{le="0.005",${labelStr}} 0
86 | operation_duration_seconds_bucket{le="0.01",${labelStr}} 0
87 | operation_duration_seconds_bucket{le="0.025",${labelStr}} 0
88 | operation_duration_seconds_bucket{le="0.05",${labelStr}} 0
89 | operation_duration_seconds_bucket{le="0.1",${labelStr}} 1
90 | operation_duration_seconds_bucket{le="0.25",${labelStr}} 1
91 | operation_duration_seconds_bucket{le="0.5",${labelStr}} 2
92 | operation_duration_seconds_bucket{le="1",${labelStr}} 2
93 | operation_duration_seconds_bucket{le="2.5",${labelStr}} 2
94 | operation_duration_seconds_bucket{le="5",${labelStr}} 2
95 | operation_duration_seconds_bucket{le="10",${labelStr}} 2
96 | operation_duration_seconds_bucket{le="+Inf",${labelStr}} 2
97 | operation_duration_seconds_sum{${labelStr}} 0.4
98 | operation_duration_seconds_count{${labelStr}} 2\n
99 | `)
100 | })
101 | })
102 |
103 | describe('http_request_handler', () => {
104 | it('should have http_request_handler metrics', () => {
105 | const reporter = new PrometheusReporter({
106 | ignoreTags: {
107 | [Tags.HTTP_URL]: /bar/
108 | }
109 | })
110 | const tracer = new Tracer('my-service', [reporter])
111 |
112 | const span1 = tracer.startSpan('http_request')
113 | span1.setTag(Tags.HTTP_URL, 'http://127.0.0.1/foo')
114 | span1.setTag(Tags.HTTP_METHOD, 'GET')
115 | span1.setTag(Tags.HTTP_STATUS_CODE, 200)
116 | span1.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_RPC_SERVER)
117 | clock.tick(100)
118 | span1.finish()
119 |
120 | // will be ignored
121 | const span2 = tracer.startSpan('http_request')
122 | span2.setTag(Tags.HTTP_URL, 'http://127.0.0.1/bar')
123 | span2.setTag(Tags.HTTP_METHOD, 'GET')
124 | span2.setTag(Tags.HTTP_STATUS_CODE, 200)
125 | span2.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_RPC_SERVER)
126 | clock.tick(300)
127 | span2.finish()
128 |
129 | const labelStr1 = `parent_service="${PrometheusReporter.LABEL_PARENT_SERVICE_UNKNOWN}",name="http_request"`
130 | const labelStr2 = `parent_service="${PrometheusReporter.LABEL_PARENT_SERVICE_UNKNOWN}",method="GET",code="200"`
131 |
132 | expect(reporter.metrics()).to.be.equal(dedent`
133 | # HELP operation_duration_seconds Duration of operations in second
134 | # TYPE operation_duration_seconds histogram
135 | operation_duration_seconds_bucket{le="0.005",${labelStr1}} 0
136 | operation_duration_seconds_bucket{le="0.01",${labelStr1}} 0
137 | operation_duration_seconds_bucket{le="0.025",${labelStr1}} 0
138 | operation_duration_seconds_bucket{le="0.05",${labelStr1}} 0
139 | operation_duration_seconds_bucket{le="0.1",${labelStr1}} 1
140 | operation_duration_seconds_bucket{le="0.25",${labelStr1}} 1
141 | operation_duration_seconds_bucket{le="0.5",${labelStr1}} 1
142 | operation_duration_seconds_bucket{le="1",${labelStr1}} 1
143 | operation_duration_seconds_bucket{le="2.5",${labelStr1}} 1
144 | operation_duration_seconds_bucket{le="5",${labelStr1}} 1
145 | operation_duration_seconds_bucket{le="10",${labelStr1}} 1
146 | operation_duration_seconds_bucket{le="+Inf",${labelStr1}} 1
147 | operation_duration_seconds_sum{${labelStr1}} 0.1
148 | operation_duration_seconds_count{${labelStr1}} 1
149 |
150 | # HELP http_request_handler_duration_seconds Duration of HTTP requests in second
151 | # TYPE http_request_handler_duration_seconds histogram
152 | http_request_handler_duration_seconds_bucket{le="0.005",${labelStr2}} 0
153 | http_request_handler_duration_seconds_bucket{le="0.01",${labelStr2}} 0
154 | http_request_handler_duration_seconds_bucket{le="0.025",${labelStr2}} 0
155 | http_request_handler_duration_seconds_bucket{le="0.05",${labelStr2}} 0
156 | http_request_handler_duration_seconds_bucket{le="0.1",${labelStr2}} 1
157 | http_request_handler_duration_seconds_bucket{le="0.25",${labelStr2}} 1
158 | http_request_handler_duration_seconds_bucket{le="0.5",${labelStr2}} 1
159 | http_request_handler_duration_seconds_bucket{le="1",${labelStr2}} 1
160 | http_request_handler_duration_seconds_bucket{le="2.5",${labelStr2}} 1
161 | http_request_handler_duration_seconds_bucket{le="5",${labelStr2}} 1
162 | http_request_handler_duration_seconds_bucket{le="10",${labelStr2}} 1
163 | http_request_handler_duration_seconds_bucket{le="+Inf",${labelStr2}} 1
164 | http_request_handler_duration_seconds_sum{${labelStr2}} 0.1
165 | http_request_handler_duration_seconds_count{${labelStr2}} 1\n
166 | `)
167 | })
168 | })
169 | })
170 |
--------------------------------------------------------------------------------
/src/reporters/PrometheusReporter.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const assert = require('assert')
4 | const Prometheus = require('prom-client')
5 | const { Tags } = require('opentracing')
6 | const Span = require('../tracer/Span')
7 |
8 | const DURATION_HISTOGRAM_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
9 | const METRICS_NAME_OPERATION_DURATION_SECONDS = 'operation_duration_seconds'
10 | const METRICS_NAME_HTTP_REQUEST_HANDLER_DURATION_SECONDS = 'http_request_handler_duration_seconds'
11 | const LABEL_PARENT_SERVICE_UNKNOWN = 'unknown'
12 |
13 | /**
14 | * Observe span events and expose them in Prometheus metrics format
15 | * @class PrometheusReporter
16 | */
17 | class PrometheusReporter {
18 | /**
19 | * @static getParentServiceKey
20 | * @param {Span} span
21 | * @return {String} parentService
22 | */
23 | static getParentService (span) {
24 | const spanContext = span.context()
25 | const parentService = spanContext.parentServiceKey() || LABEL_PARENT_SERVICE_UNKNOWN
26 |
27 | return parentService
28 | }
29 |
30 | /**
31 | * @constructor
32 | * @param {Object} [options={}]
33 | * @param {Object} [options.ignoreTags={}]
34 | * @returns {PrometheusReporter}
35 | */
36 | constructor ({ ignoreTags = {} } = {}) {
37 | this._registry = new Prometheus.Registry()
38 | this._options = {
39 | ignoreTags
40 | }
41 |
42 | // Initialize metrics
43 | this._metricsOperationDurationSeconds()
44 | }
45 |
46 | /**
47 | * Returns with the reporter's metrics in Prometheus format
48 | * @method metrics
49 | * @returns {Object} metrics
50 | */
51 | metrics () {
52 | return this._registry.metrics()
53 | }
54 |
55 | /**
56 | * Called by Tracer when a span is finished
57 | * @method reportFinish
58 | * @param {Span} span
59 | */
60 | reportFinish (span) {
61 | assert(span instanceof Span, 'span is required')
62 |
63 | // Ignore by tag value
64 | const isIgnored = Object.entries(this._options.ignoreTags).some(([tagKey, regexp]) => {
65 | const tagValue = span.getTag(tagKey)
66 | return tagValue && tagValue.match(regexp)
67 | })
68 |
69 | if (isIgnored) {
70 | return
71 | }
72 |
73 | // Operation metrics
74 | this._reportOperationFinish(span)
75 |
76 | // HTTP Request
77 | if (span.getTag(Tags.SPAN_KIND) === Tags.SPAN_KIND_RPC_SERVER &&
78 | (span.getTag(Tags.HTTP_URL) || span.getTag(Tags.HTTP_METHOD) || span.getTag(Tags.HTTP_STATUS_CODE))) {
79 | this._reportHttpRequestFinish(span)
80 | }
81 | }
82 |
83 | /**
84 | * Observe operation metrics
85 | * @method _reportOperationFinish
86 | * @private
87 | * @param {Span} span
88 | */
89 | _reportOperationFinish (span) {
90 | assert(span instanceof Span, 'span is required')
91 |
92 | this._metricsOperationDurationSeconds()
93 | .labels(PrometheusReporter.getParentService(span), span.operationName())
94 | .observe(span.duration() / 1000)
95 | }
96 |
97 | /**
98 | * Observe HTTP request metrics
99 | * @method _reportHttpRequestFinish
100 | * @private
101 | * @param {Span} span
102 | */
103 | _reportHttpRequestFinish (span) {
104 | assert(span instanceof Span, 'span is required')
105 |
106 | this._metricshttpRequestDurationSeconds()
107 | .labels(
108 | PrometheusReporter.getParentService(span),
109 | span.getTag(Tags.HTTP_METHOD),
110 | span.getTag(Tags.HTTP_STATUS_CODE)
111 | )
112 | .observe(span.duration() / 1000)
113 | }
114 |
115 | /**
116 | * Singleton to get operation duration metrics
117 | * @method _metricsOperationDurationSeconds
118 | * @private
119 | * @return {Prometheus.Histogram} operationDurationSeconds
120 | */
121 | _metricsOperationDurationSeconds () {
122 | let operationDurationSeconds = this._registry.getSingleMetric(METRICS_NAME_OPERATION_DURATION_SECONDS)
123 |
124 | if (!operationDurationSeconds) {
125 | operationDurationSeconds = new Prometheus.Histogram({
126 | name: METRICS_NAME_OPERATION_DURATION_SECONDS,
127 | help: 'Duration of operations in second',
128 | labelNames: ['parent_service', 'name'],
129 | buckets: DURATION_HISTOGRAM_BUCKETS,
130 | registers: [this._registry]
131 | })
132 | }
133 |
134 | return operationDurationSeconds
135 | }
136 |
137 | /**
138 | * Singleton to get HTTP request duration metrics
139 | * @method _metricshttpRequestDurationSeconds
140 | * @private
141 | * @return {Prometheus.Histogram} httpRequestDurationSeconds
142 | */
143 | _metricshttpRequestDurationSeconds () {
144 | let httpRequestDurationSeconds = this._registry.getSingleMetric(METRICS_NAME_HTTP_REQUEST_HANDLER_DURATION_SECONDS)
145 |
146 | if (!httpRequestDurationSeconds) {
147 | httpRequestDurationSeconds = new Prometheus.Histogram({
148 | name: METRICS_NAME_HTTP_REQUEST_HANDLER_DURATION_SECONDS,
149 | help: 'Duration of HTTP requests in second',
150 | labelNames: ['parent_service', 'method', 'code'],
151 | buckets: DURATION_HISTOGRAM_BUCKETS,
152 | registers: [this._registry]
153 | })
154 | }
155 |
156 | return httpRequestDurationSeconds
157 | }
158 | }
159 |
160 | PrometheusReporter.Prometheus = Prometheus
161 | PrometheusReporter.LABEL_PARENT_SERVICE_UNKNOWN = LABEL_PARENT_SERVICE_UNKNOWN
162 |
163 | module.exports = PrometheusReporter
164 |
--------------------------------------------------------------------------------
/src/reporters/PrometheusReporter.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const sinon = require('sinon')
4 | const { expect } = require('chai')
5 | const dedent = require('dedent')
6 | const { Tags } = require('opentracing')
7 | const { Tracer } = require('../tracer')
8 | const PrometheusReporter = require('./PrometheusReporter')
9 |
10 | describe('reporter/PrometheusReporter', () => {
11 | let clock
12 |
13 | beforeEach(() => {
14 | clock = sinon.useFakeTimers()
15 | })
16 |
17 | afterEach(() => {
18 | clock.restore()
19 | })
20 |
21 | describe('#constructor', () => {
22 | it('should create a PrometheusReporter', () => {
23 | const prometheusReporter = new PrometheusReporter()
24 |
25 | expect(prometheusReporter).to.have.property('_registry')
26 | })
27 | })
28 |
29 | describe('#reportFinish', () => {
30 | it('should skip operation metrics by tag value', function () {
31 | // init
32 | const prometheusReporter = new PrometheusReporter({
33 | ignoreTags: {
34 | [Tags.HTTP_URL]: /foo/
35 | }
36 | })
37 | const metricsOperationDurationSeconds = prometheusReporter._metricsOperationDurationSeconds()
38 |
39 | const metricsStub = {
40 | observe: this.sandbox.spy()
41 | }
42 |
43 | this.sandbox.stub(metricsOperationDurationSeconds, 'labels').callsFake(() => metricsStub)
44 |
45 | // generate data
46 | const tracer = new Tracer('service')
47 |
48 | const span = tracer.startSpan('my-operation')
49 | span.setTag(Tags.HTTP_URL, 'http://127.0.0.1/foo')
50 | clock.tick(100)
51 | span.finish()
52 |
53 | prometheusReporter.reportFinish(span)
54 |
55 | // assert
56 | expect(metricsOperationDurationSeconds.labels).to.have.callCount(0)
57 | expect(metricsStub.observe).to.have.callCount(0)
58 | })
59 |
60 | it('should observe operation metrics without parent', function () {
61 | // init
62 | const prometheusReporter = new PrometheusReporter()
63 | const metricsOperationDurationSeconds = prometheusReporter._metricsOperationDurationSeconds()
64 |
65 | const metricsStub = {
66 | observe: this.sandbox.spy()
67 | }
68 |
69 | this.sandbox.stub(metricsOperationDurationSeconds, 'labels').callsFake(() => metricsStub)
70 |
71 | // generate data
72 | const tracer = new Tracer('service')
73 |
74 | const span = tracer.startSpan('my-operation')
75 | clock.tick(100)
76 | span.finish()
77 |
78 | prometheusReporter.reportFinish(span)
79 |
80 | // assert
81 | expect(metricsOperationDurationSeconds.labels).to.have.callCount(1)
82 | expect(metricsOperationDurationSeconds.labels).to.be.calledWith(PrometheusReporter.LABEL_PARENT_SERVICE_UNKNOWN)
83 |
84 | expect(metricsStub.observe).to.have.callCount(1)
85 | expect(metricsStub.observe).to.be.calledWith(0.1)
86 | })
87 |
88 | it('should observe operation metrics with parent', function () {
89 | // init
90 | const prometheusReporter = new PrometheusReporter()
91 | const metricsOperationDurationSeconds = prometheusReporter._metricsOperationDurationSeconds()
92 |
93 | const metricsStub = {
94 | observe: this.sandbox.spy()
95 | }
96 |
97 | this.sandbox.stub(metricsOperationDurationSeconds, 'labels').callsFake(() => metricsStub)
98 |
99 | // generate data
100 | const parentTracer = new Tracer('parent-service')
101 | const tracer = new Tracer('service')
102 |
103 | const parentSpan1 = parentTracer.startSpan('parent-operation')
104 | const span1 = tracer.startSpan('my-operation', { childOf: parentSpan1 })
105 | clock.tick(100)
106 | span1.finish()
107 |
108 | const parentSpan2 = parentTracer.startSpan('parent-operation')
109 | const span2 = tracer.startSpan('my-operation', { childOf: parentSpan2 })
110 | clock.tick(300)
111 | span2.finish()
112 |
113 | prometheusReporter.reportFinish(span1)
114 | prometheusReporter.reportFinish(span2)
115 |
116 | // assert
117 | expect(metricsOperationDurationSeconds.labels).to.have.callCount(2)
118 | expect(metricsOperationDurationSeconds.labels).to.be.calledWith('parent-service')
119 |
120 | expect(metricsStub.observe).to.have.callCount(2)
121 | expect(metricsStub.observe).to.be.calledWith(0.1)
122 | expect(metricsStub.observe).to.be.calledWith(0.3)
123 | })
124 |
125 | it('should observe HTTP request metrics without parent', function () {
126 | // init
127 | const prometheusReporter = new PrometheusReporter()
128 | const httpRequestDurationSeconds = prometheusReporter._metricshttpRequestDurationSeconds()
129 |
130 | const metricsStub = {
131 | observe: this.sandbox.spy()
132 | }
133 |
134 | this.sandbox.stub(httpRequestDurationSeconds, 'labels').callsFake(() => metricsStub)
135 |
136 | // generate data
137 | const tracer = new Tracer('service')
138 |
139 | const span = tracer.startSpan('http_request')
140 | span.setTag(Tags.HTTP_METHOD, 'GET')
141 | span.setTag(Tags.HTTP_STATUS_CODE, 200)
142 | span.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_RPC_SERVER)
143 | clock.tick(100)
144 | span.finish()
145 |
146 | prometheusReporter.reportFinish(span)
147 |
148 | // assert
149 | expect(httpRequestDurationSeconds.labels).to.have.callCount(1)
150 | expect(httpRequestDurationSeconds.labels)
151 | .to.be.calledWith(PrometheusReporter.LABEL_PARENT_SERVICE_UNKNOWN, 'GET', 200)
152 |
153 | expect(metricsStub.observe).to.have.callCount(1)
154 | expect(metricsStub.observe).to.be.calledWith(0.1)
155 | })
156 |
157 | it('should observe HTTP request metrics with parent', function () {
158 | // init
159 | const prometheusReporter = new PrometheusReporter()
160 | const httpRequestDurationSeconds = prometheusReporter._metricshttpRequestDurationSeconds()
161 |
162 | const metricsStub = {
163 | observe: this.sandbox.spy()
164 | }
165 |
166 | this.sandbox.stub(httpRequestDurationSeconds, 'labels').callsFake(() => metricsStub)
167 |
168 | // generate data
169 | const parentTracer = new Tracer('parent-service')
170 | const tracer = new Tracer('service')
171 |
172 | const parentSpan1 = parentTracer.startSpan('parent-operation')
173 | const span1 = tracer.startSpan('http_request', { childOf: parentSpan1 })
174 | span1.setTag(Tags.HTTP_METHOD, 'GET')
175 | span1.setTag(Tags.HTTP_STATUS_CODE, 200)
176 | span1.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_RPC_SERVER)
177 | clock.tick(100)
178 | span1.finish()
179 |
180 | const parentSpan2 = parentTracer.startSpan('parent-operation')
181 | const span2 = tracer.startSpan('http_request', { childOf: parentSpan2 })
182 | span2.setTag(Tags.HTTP_METHOD, 'POST')
183 | span2.setTag(Tags.HTTP_STATUS_CODE, 201)
184 | span2.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_RPC_SERVER)
185 | clock.tick(300)
186 | span2.finish()
187 |
188 | prometheusReporter.reportFinish(span1)
189 | prometheusReporter.reportFinish(span2)
190 |
191 | // assert
192 | expect(httpRequestDurationSeconds.labels).to.have.callCount(2)
193 | expect(httpRequestDurationSeconds.labels).to.be.calledWith('parent-service', 'POST', 201)
194 |
195 | expect(metricsStub.observe).to.have.callCount(2)
196 | expect(metricsStub.observe).to.be.calledWith(0.1)
197 | expect(metricsStub.observe).to.be.calledWith(0.3)
198 | })
199 |
200 | it('should skip client HTTP requests', function () {
201 | // init
202 | const prometheusReporter = new PrometheusReporter()
203 | const httpRequestDurationSeconds = prometheusReporter._metricshttpRequestDurationSeconds()
204 |
205 | const metricsStub = {
206 | observe: this.sandbox.spy()
207 | }
208 |
209 | this.sandbox.stub(httpRequestDurationSeconds, 'labels').callsFake(() => metricsStub)
210 |
211 | // generate data
212 | const tracer = new Tracer('service')
213 |
214 | const span = tracer.startSpan('http_request')
215 | span.setTag(Tags.HTTP_METHOD, 'GET')
216 | span.setTag(Tags.HTTP_STATUS_CODE, 200)
217 | span.setTag(Tags.SPAN_KIND_RPC_SERVER, false) // or not set
218 | clock.tick(100)
219 | span.finish()
220 |
221 | prometheusReporter.reportFinish(span)
222 |
223 | // assert
224 | expect(httpRequestDurationSeconds.labels).to.have.callCount(0)
225 | expect(metricsStub.observe).to.have.callCount(0)
226 | })
227 | })
228 |
229 | describe('#metrics', () => {
230 | it('should have operation metrics initialized', () => {
231 | const reporter = new PrometheusReporter()
232 |
233 | expect(reporter.metrics()).to.be.equal(dedent`
234 | # HELP operation_duration_seconds Duration of operations in second
235 | # TYPE operation_duration_seconds histogram\n
236 | `)
237 | })
238 | })
239 | })
240 |
--------------------------------------------------------------------------------
/src/reporters/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const PrometheusReporter = require('./PrometheusReporter')
4 |
5 | module.exports = {
6 | PrometheusReporter
7 | }
8 |
--------------------------------------------------------------------------------
/src/tracer/Reference.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const assert = require('assert')
4 | const { REFERENCE_CHILD_OF, REFERENCE_FOLLOWS_FROM } = require('opentracing')
5 | const Span = require('./Span')
6 | const SpanContext = require('./SpanContext')
7 |
8 | /**
9 | * Reference pairs a reference type constant (e.g., REFERENCE_CHILD_OF or REFERENCE_FOLLOWS_FROM)
10 | * with the SpanContext it points to
11 | * Follows the original opentracing API
12 | * @class Reference
13 | */
14 | class Reference {
15 | /**
16 | * @constructor
17 | * @param {String} type - the Reference type constant (e.g., REFERENCE_CHILD_OF or REFERENCE_FOLLOWS_FROM
18 | * @param {SpanContext|Span} referencedContext - the SpanContext being referred to.
19 | * As a convenience, a Span instance may be passed in instead (in which case its .context() is used here)
20 | * @returns {Reference}
21 | */
22 | constructor (type, referencedContext) {
23 | assert([REFERENCE_CHILD_OF, REFERENCE_FOLLOWS_FROM].includes(type), 'Invalid type')
24 | assert(
25 | referencedContext instanceof Span || referencedContext instanceof SpanContext,
26 | 'referencedContext must have a type Span or SpanContext'
27 | )
28 |
29 | this._type = type
30 | this._referencedContext = referencedContext instanceof Span ?
31 | referencedContext.context() :
32 | referencedContext
33 | }
34 |
35 | /**
36 | * @method referencedContext
37 | * @return {SpanContext} referencedContext
38 | */
39 | referencedContext () {
40 | return this._referencedContext
41 | }
42 |
43 | /**
44 | * @method type
45 | * @return {String} type
46 | */
47 | type () {
48 | return this._type
49 | }
50 | }
51 |
52 | module.exports = Reference
53 |
--------------------------------------------------------------------------------
/src/tracer/Reference.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { expect } = require('chai')
4 | const { REFERENCE_CHILD_OF, REFERENCE_FOLLOWS_FROM } = require('opentracing')
5 | const Reference = require('./Reference')
6 | const Span = require('./Span')
7 | const SpanContext = require('./SpanContext')
8 | const Tracer = require('./Tracer')
9 |
10 | describe('tracer/Reference', () => {
11 | describe('#constructor', () => {
12 | it('should create Reference with SpanContext', () => {
13 | const spanContext = new SpanContext('service-1')
14 | const reference = new Reference(REFERENCE_CHILD_OF, spanContext)
15 |
16 | expect(reference._referencedContext).to.be.eql(spanContext)
17 | })
18 |
19 | it('should create Reference with Span', () => {
20 | const tracer = new Tracer('service-1')
21 | const spanContext = new SpanContext('service-1')
22 | const span = new Span(tracer, 'operation', spanContext)
23 | const reference = new Reference(REFERENCE_CHILD_OF, span)
24 |
25 | expect(reference._referencedContext).to.be.eql(spanContext)
26 | })
27 |
28 | it('should create Reference with type REFERENCE_CHILD_OF', () => {
29 | const spanContext = new SpanContext('service-1')
30 | const reference = new Reference(REFERENCE_CHILD_OF, spanContext)
31 |
32 | expect(reference._type).to.be.equal(REFERENCE_CHILD_OF)
33 | })
34 |
35 | it('should create Reference with type REFERENCE_FOLLOWS_FROM', () => {
36 | const spanContext = new SpanContext('service-1')
37 | const reference = new Reference(REFERENCE_FOLLOWS_FROM, spanContext)
38 |
39 | expect(reference._type).to.be.equal(REFERENCE_FOLLOWS_FROM)
40 | })
41 |
42 | it('should reject with invalid type', () => {
43 | const spanContext = new SpanContext('service-1')
44 |
45 | expect(() => {
46 | // eslint-disable-next-line
47 | new Reference('invalid', spanContext)
48 | }).to.throw('Invalid type')
49 | })
50 | })
51 |
52 | describe('#referencedContext', () => {
53 | it('should get referencedContext', () => {
54 | const spanContext = new SpanContext('service-1')
55 | const reference = new Reference(REFERENCE_CHILD_OF, spanContext)
56 |
57 | expect(reference.referencedContext()).to.be.equal(spanContext)
58 | })
59 | })
60 |
61 | describe('#type', () => {
62 | it('should get type', () => {
63 | const spanContext = new SpanContext('service-1')
64 | const reference = new Reference(REFERENCE_CHILD_OF, spanContext)
65 |
66 | expect(reference.type()).to.be.equal(REFERENCE_CHILD_OF)
67 | })
68 | })
69 | })
70 |
--------------------------------------------------------------------------------
/src/tracer/Span.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const assert = require('assert')
4 | const SpanContext = require('./SpanContext')
5 |
6 | /**
7 | * Follows the original opentracing API
8 | * @class Span
9 | */
10 | class Span {
11 | /**
12 | * @constructor
13 | * @param {Tracer} tracer
14 | * @param {String} operationName
15 | * @param {SpanContext} spanContext
16 | * @param {Object} tags
17 | * @param {Number} startTime
18 | * @param {Array} references - Array of Reference
19 | * @returns {Span}
20 | */
21 | constructor (
22 | tracer,
23 | operationName,
24 | spanContext,
25 | tags = {},
26 | startTime = Date.now(),
27 | references = []
28 | ) {
29 | assert(tracer, 'tracer is required')
30 | assert(spanContext instanceof SpanContext, 'spanContext is required')
31 | assert(typeof operationName === 'string', 'operationName is required')
32 |
33 | this._tracer = tracer
34 | this._operationName = operationName
35 | this._spanContext = spanContext
36 | this._startTime = startTime
37 | this._references = references
38 | this._logs = []
39 | this._tags = tags
40 | this._duration = undefined
41 | }
42 |
43 | /**
44 | * Adds the given key value pairs to the set of span tags
45 | * @method addTags
46 | * @param {Object} keyValueMap - [key: string]: any
47 | * @returns {Span}
48 | */
49 | addTags (keyValueMap) {
50 | assert(typeof keyValueMap === 'object', 'keyValueMap is required')
51 |
52 | this._tags = Object.assign(this._tags, keyValueMap)
53 |
54 | return this
55 | }
56 |
57 | /**
58 | * Returns the SpanContext object associated with this Span
59 | * @method context
60 | * @returns {SpanContext}
61 | */
62 | context () {
63 | return this._spanContext
64 | }
65 |
66 | /**
67 | * Sets the end timestamp and finalizes Span state
68 | * @method finishTime
69 | * @param {Number} [finishTime] - Optional finish time in milliseconds as a Unix timestamp
70 | */
71 | finish (finishTime) {
72 | assert(finishTime === undefined || typeof finishTime === 'number', 'finishTime is required')
73 |
74 | this._duration = (finishTime || Date.now()) - this._startTime
75 | }
76 |
77 | /**
78 | * Returns the value for a baggage item given its key
79 | * @method getBaggageItem
80 | * @param {String} key - The key for the given trace attribute
81 | * @returns {String|undefined} value - String value for the given key
82 | * or undefined if the key does not correspond to a set trace attribute
83 | */
84 | getBaggageItem (key) {
85 | assert(typeof key === 'string', 'key is required')
86 |
87 | return this._spanContext.getBaggageItem(key)
88 | }
89 |
90 | /**
91 | * Add a log record to this Span, optionally at a user-provided timestamp
92 | * @method log
93 | * @param {Object} keyValuePairs - An object mapping string keys to arbitrary value types
94 | * @param {Number} [timestamp] - An optional parameter specifying the timestamp in milliseconds
95 | * since the Unix epoch
96 | * @returns {Span}
97 | */
98 | log (keyValuePairs, timestamp) {
99 | assert(typeof keyValuePairs === 'object', 'keyValuePairs is required')
100 | assert(timestamp === undefined || typeof timestamp === 'number', 'timestamp is required')
101 |
102 | this._logs.push({
103 | time: timestamp || Date.now(),
104 | data: keyValuePairs
105 | })
106 |
107 | return this
108 | }
109 |
110 | /**
111 | * @method logEvent
112 | * @deprecated
113 | */
114 | // eslint-disable-next-line
115 | logEvent () {}
116 |
117 | /**
118 | * Sets a key:value pair on this Span that also propagates to future children of the associated Span
119 | * @method setBaggageItem
120 | * @param {String} key
121 | * @param {String} value
122 | * @returns {Span}
123 | */
124 | setBaggageItem (key, value) {
125 | assert(typeof key === 'string', 'key is required')
126 | assert(typeof value === 'string', 'value is required')
127 |
128 | this._spanContext.setBaggageItem(key, value)
129 | return this
130 | }
131 |
132 | /**
133 | * Sets the string name for the logical operation this span represents
134 | * @method setOperationName
135 | * @param {String} operationName
136 | * @returns {Span}
137 | */
138 | setOperationName (operationName) {
139 | assert(typeof operationName === 'string', 'operationName is required')
140 |
141 | this._operationName = operationName
142 | return this
143 | }
144 |
145 | /**
146 | * Adds a single tag to the span. See addTags() for details
147 | * @method setTag
148 | * @param {String} key
149 | * @param {*} value
150 | * @returns {Span}
151 | */
152 | setTag (key, value) {
153 | assert(typeof key === 'string', 'key is required')
154 | assert(value !== undefined, 'value is required')
155 |
156 | this._tags[key] = value
157 | return this
158 | }
159 |
160 | /**
161 | * Returns the Tracer object used to create this Span
162 | * @method tracer
163 | * @returns {Tracer}
164 | */
165 | tracer () {
166 | return this._tracer
167 | }
168 | }
169 |
170 | /**
171 | * Extends the original opentracing API with getters for reporting
172 | * @class MetricsSpan
173 | * @extends Span
174 | */
175 | class MetricsSpan extends Span {
176 | /**
177 | * Get operation name
178 | * @method operationName
179 | * @returns {String} operationName
180 | */
181 | operationName () {
182 | return this._operationName
183 | }
184 |
185 | /**
186 | * Get duration
187 | * @method duration
188 | * @returns {Number|Undefined} duration
189 | */
190 | duration () {
191 | return this._duration
192 | }
193 |
194 | /**
195 | * Get a single tag
196 | * @method getTag
197 | * @param {String} key
198 | * @returns {String} value
199 | */
200 | getTag (key) {
201 | assert(typeof key === 'string', 'key is required')
202 |
203 | return this._tags[key]
204 | }
205 |
206 | /**
207 | * Sets the end timestamp and finalizes Span state
208 | * @method finishTime
209 | * @param {Number} [finishTime] - Optional finish time in milliseconds as a Unix timestamp
210 | */
211 | finish (finishTime) {
212 | super.finish(finishTime)
213 |
214 | this._tracer.reportFinish(this)
215 | }
216 | }
217 |
218 | module.exports = MetricsSpan
219 |
--------------------------------------------------------------------------------
/src/tracer/Span.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const sinon = require('sinon')
4 | const { expect } = require('chai')
5 | const { Tags } = require('opentracing')
6 | const Span = require('./Span')
7 | const SpanContext = require('./SpanContext')
8 | const Tracer = require('./Tracer')
9 |
10 | describe('tracer/Span', () => {
11 | let clock
12 |
13 | beforeEach(() => {
14 | clock = sinon.useFakeTimers()
15 | })
16 |
17 | afterEach(() => {
18 | clock.restore()
19 | })
20 |
21 | describe('#constructor', () => {
22 | it('should create a Span', () => {
23 | const tracer = new Tracer('service-1')
24 | const spanContext = new SpanContext('service-1')
25 | const span = new Span(tracer, 'operation', spanContext)
26 |
27 | expect(span._startTime).to.be.lte(0)
28 | })
29 | })
30 |
31 | describe('#addTags', () => {
32 | it('should add a tag', () => {
33 | const tracer = new Tracer('service-1')
34 | const spanContext = new SpanContext('service-1')
35 | const span = new Span(tracer, 'operation', spanContext)
36 | const tags = {
37 | [Tags.HTTP_METHOD]: 'GET',
38 | [Tags.SPAN_KIND_RPC_CLIENT]: true
39 | }
40 |
41 | span.addTags(tags)
42 |
43 | expect(span._tags).to.be.eql(tags)
44 | })
45 | })
46 |
47 | describe('#context', () => {
48 | it('should get context', () => {
49 | const tracer = new Tracer('service-1')
50 | const spanContext = new SpanContext('service-1')
51 | const span = new Span(tracer, 'operation', spanContext)
52 |
53 | expect(span.context()).to.be.eql(spanContext)
54 | })
55 | })
56 |
57 | describe('#finish', () => {
58 | it('should calculate duration in milliseconds', function () {
59 | const tracer = new Tracer('service-1')
60 | const spanContext = new SpanContext('service-1')
61 | const span = new Span(tracer, 'operation', spanContext)
62 |
63 | this.sandbox.spy(tracer, 'reportFinish')
64 |
65 | clock.tick(150)
66 |
67 | span.finish()
68 |
69 | expect(span._duration).to.be.equal(150)
70 | expect(tracer.reportFinish).to.be.calledWith(span)
71 | })
72 |
73 | it('should use passed finishTime', () => {
74 | const tracer = new Tracer('service-1')
75 | const spanContext = new SpanContext('service-1')
76 | const span = new Span(tracer, 'operation', spanContext)
77 |
78 | clock.tick(150)
79 |
80 | span.finish(110)
81 |
82 | expect(span._duration).to.be.equal(110)
83 | })
84 | })
85 |
86 | describe('#getBaggageItem', () => {
87 | it('should get baggage item', () => {
88 | const tracer = new Tracer('service-1')
89 | const spanContext = new SpanContext('service-1')
90 | const span = new Span(tracer, 'operation', spanContext)
91 |
92 | spanContext.setBaggageItem('key1', 'value1')
93 | spanContext.setBaggageItem('key2', 'value2')
94 |
95 | expect(span.getBaggageItem('key1')).to.be.equal('value1')
96 | expect(span.getBaggageItem('key2')).to.be.equal('value2')
97 | })
98 | })
99 |
100 | describe('#operationName', () => {
101 | it('should get operation name', () => {
102 | const tracer = new Tracer('service-1')
103 | const spanContext = new SpanContext('service-1')
104 | const span = new Span(tracer, 'operation', spanContext)
105 |
106 | expect(span.operationName()).to.be.equal('operation')
107 | })
108 | })
109 |
110 | describe('#duration', () => {
111 | it('should get duration', () => {
112 | const tracer = new Tracer('service-1')
113 | const spanContext = new SpanContext('service-1')
114 | const span = new Span(tracer, 'operation', spanContext)
115 | clock.tick(100)
116 | span.finish()
117 |
118 | expect(span.duration()).to.be.equal(100)
119 | })
120 | })
121 |
122 | describe('#getTag', () => {
123 | it('should get a tag value', () => {
124 | const tracer = new Tracer('service-1')
125 | const spanContext = new SpanContext('service-1')
126 | const span = new Span(tracer, 'operation', spanContext)
127 | span.setTag(Tags.HTTP_METHOD, 'GET')
128 |
129 | expect(span.getTag(Tags.HTTP_METHOD)).to.be.equal('GET')
130 | })
131 | })
132 |
133 | describe('#log', () => {
134 | it('should log', () => {
135 | const tracer = new Tracer('service-1')
136 | const spanContext = new SpanContext('service-1')
137 | const span = new Span(tracer, 'operation', spanContext)
138 |
139 | clock.tick(10)
140 | span.log({ foo: 'bar' })
141 |
142 | clock.tick(10)
143 | span.log({ so: 'such' })
144 |
145 | expect(span._logs).to.be.eql([
146 | {
147 | time: 10,
148 | data: { foo: 'bar' }
149 | },
150 | {
151 | time: 20,
152 | data: { so: 'such' }
153 | }
154 | ])
155 | })
156 |
157 | it('should log with timestamp', () => {
158 | const tracer = new Tracer('service-1')
159 | const spanContext = new SpanContext('service-1')
160 | const span = new Span(tracer, 'operation', spanContext)
161 |
162 | clock.tick(10)
163 | span.log({ foo: 'bar' }, 5)
164 |
165 | clock.tick(10)
166 | span.log({ so: 'such' }, 15)
167 |
168 | expect(span._logs).to.be.eql([
169 | {
170 | time: 5,
171 | data: { foo: 'bar' }
172 | },
173 | {
174 | time: 15,
175 | data: { so: 'such' }
176 | }
177 | ])
178 | })
179 | })
180 |
181 | describe('#logEvent', () => {
182 | it('should do nothing as it\'s deprectaed', () => {
183 | const tracer = new Tracer('service-1')
184 | const spanContext = new SpanContext('service-1')
185 | const span = new Span(tracer, 'operation', spanContext)
186 |
187 | span.logEvent()
188 | })
189 | })
190 |
191 | describe('#setBaggageItem', () => {
192 | it('should set baggage item', () => {
193 | const tracer = new Tracer('service-1')
194 | const spanContext = new SpanContext('service-1')
195 | const span = new Span(tracer, 'operation', spanContext)
196 |
197 | span.setBaggageItem('key1', 'value1')
198 | span.setBaggageItem('key2', 'value2')
199 |
200 | expect(span.getBaggageItem('key1')).to.be.equal('value1')
201 | expect(span.getBaggageItem('key2')).to.be.equal('value2')
202 | })
203 | })
204 |
205 | describe('#setOperationName', () => {
206 | it('should set baggage item', () => {
207 | const tracer = new Tracer('service-1')
208 | const spanContext = new SpanContext('service-1')
209 | const span = new Span(tracer, 'operation', spanContext)
210 |
211 | expect(span._operationName).to.be.equal('operation')
212 |
213 | span.setOperationName('operation2')
214 |
215 | expect(span._operationName).to.be.equal('operation2')
216 | })
217 | })
218 |
219 | describe('#setTag', () => {
220 | it('should set a tag', () => {
221 | const tracer = new Tracer('service-1')
222 | const spanContext = new SpanContext('service-1')
223 | const span = new Span(tracer, 'operation', spanContext)
224 |
225 | span.setTag(Tags.HTTP_METHOD, 'GET')
226 | span.setTag(Tags.SPAN_KIND_RPC_CLIENT, true)
227 |
228 | expect(span._tags).to.be.eql({
229 | [Tags.HTTP_METHOD]: 'GET',
230 | [Tags.SPAN_KIND_RPC_CLIENT]: true
231 | })
232 | })
233 | })
234 |
235 | describe('#tracer', () => {
236 | it('should get tracer', () => {
237 | const tracer = new Tracer('service-1')
238 | const spanContext = new SpanContext('service-1')
239 | const span = new Span(tracer, 'operation', spanContext)
240 |
241 | expect(span.tracer()).to.be.eql(tracer)
242 | })
243 | })
244 | })
245 |
--------------------------------------------------------------------------------
/src/tracer/SpanContext.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const assert = require('assert')
4 | const uuidV1 = require('uuid/v1')
5 | const uuidV4 = require('uuid/v4')
6 |
7 | /**
8 | * @class SpanContext
9 | */
10 | class SpanContext {
11 | /**
12 | * @constructor
13 | * @param {String} serviceKey
14 | * @param {String} [parentServiceKey]
15 | * @param {String} [traceId]
16 | * @param {String} [spanId]
17 | * @param {String} [parentSpanId]
18 | * @returns {SpanContext}
19 | */
20 | constructor (
21 | serviceKey,
22 | parentServiceKey,
23 | traceId = `${uuidV1()}-${uuidV4()}`,
24 | spanId = uuidV4(),
25 | parentSpanId
26 | ) {
27 | assert(typeof serviceKey === 'string', 'serviceKey is required')
28 |
29 | this._serviceKey = serviceKey
30 | this._parentServiceKey = parentServiceKey
31 | this._traceId = traceId
32 | this._spanId = spanId
33 | this._parentSpanId = parentSpanId
34 | this._baggage = {}
35 | }
36 |
37 | /**
38 | * Returns the value for a baggage item given its key
39 | * @method getBaggageItem
40 | * @param {String} key - The key for the given trace attribute
41 | * @returns {String|undefined} value - String value for the given key
42 | * or undefined if the key does not correspond to a set trace attribute
43 | */
44 | getBaggageItem (key) {
45 | assert(typeof key === 'string', 'key is required')
46 |
47 | return this._baggage[key]
48 | }
49 |
50 | /**
51 | * Sets a key:value pair on this Span that also propagates to future children of the associated Span
52 | * @method setBaggageItem
53 | * @param {String} key
54 | * @param {String} value
55 | * @returns {Span}
56 | */
57 | setBaggageItem (key, value) {
58 | assert(typeof key === 'string', 'key is required')
59 | assert(typeof value === 'string', 'value is required')
60 |
61 | this._baggage[key] = value
62 | return this
63 | }
64 |
65 | /**
66 | * Returns the parentServiceKey
67 | * @method parentServiceKey
68 | * @returns {String|Undefined}
69 | */
70 | parentServiceKey () {
71 | return this._parentServiceKey
72 | }
73 | }
74 |
75 | module.exports = SpanContext
76 |
--------------------------------------------------------------------------------
/src/tracer/SpanContext.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { expect } = require('chai')
4 | const SpanContext = require('./SpanContext')
5 |
6 | describe('tracer/SpanContext', () => {
7 | describe('#constructor', () => {
8 | it('should create spanContext with parameters', () => {
9 | const spanContext = new SpanContext('service-2', 'service-1', 'trace-id', 'span-id', 'parent-span-id')
10 |
11 | expect(spanContext._serviceKey).to.be.equal('service-2')
12 | expect(spanContext._parentServiceKey).to.be.equal('service-1')
13 | expect(spanContext._traceId).to.be.equal('trace-id')
14 | expect(spanContext._spanId).to.be.equal('span-id')
15 | expect(spanContext._parentSpanId).to.be.equal('parent-span-id')
16 | })
17 |
18 | it('should generate traceId and spanId', () => {
19 | const spanContext = new SpanContext('service-2', 'service-1')
20 |
21 | expect(spanContext._traceId.length).to.be.equal(73)
22 | expect(spanContext._spanId.length).to.be.equal(36)
23 | })
24 | })
25 |
26 | describe('#setBaggageItem', () => {
27 | it('should set baggage item', () => {
28 | const spanContext = new SpanContext('service-1')
29 | spanContext.setBaggageItem('key1', 'value1')
30 | spanContext.setBaggageItem('key2', 'value2')
31 |
32 | expect(spanContext._baggage).to.be.eql({
33 | key1: 'value1',
34 | key2: 'value2'
35 | })
36 | })
37 | })
38 |
39 | describe('#getBaggageItem', () => {
40 | it('should get baggage item', () => {
41 | const spanContext = new SpanContext('service-1')
42 | spanContext.setBaggageItem('key1', 'value1')
43 | spanContext.setBaggageItem('key2', 'value2')
44 |
45 | expect(spanContext.getBaggageItem('key1')).to.be.equal('value1')
46 | expect(spanContext.getBaggageItem('key2')).to.be.equal('value2')
47 | })
48 | })
49 |
50 | describe('#parentServiceKey', () => {
51 | it('should return with the parentServiceKey', () => {
52 | const spanContext = new SpanContext('service-2', 'service-1', 'trace-id', 'span-id', 'parent-span-id')
53 | expect(spanContext.parentServiceKey()).to.be.equal('service-1')
54 | })
55 | })
56 | })
57 |
--------------------------------------------------------------------------------
/src/tracer/Tracer.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const assert = require('assert')
4 | const { FORMAT_BINARY, FORMAT_TEXT_MAP, FORMAT_HTTP_HEADERS, REFERENCE_CHILD_OF } = require('opentracing')
5 | const Span = require('./Span')
6 | const SpanContext = require('./SpanContext')
7 | const Reference = require('./Reference')
8 |
9 | const CARRIER_KEY_SERVICE_KEYS = 'metrics-tracer-service-key'
10 | const CARRIER_KEY_TRACE_ID = 'metrics-tracer-trace-id'
11 | const CARRIER_KEY_SPAN_IDS = 'metrics-tracer-span-id'
12 |
13 | /**
14 | * Tracer is the entry-point between the instrumentation API and the tracing implementation
15 | * Follows the original opentracing API
16 | * @class Tracer
17 | */
18 | class Tracer {
19 | /**
20 | * @constructor
21 | * @param {String} serviceKey
22 | * @param {Array} reporters
23 | * @returns {Tracer}
24 | */
25 | constructor (serviceKey, reporters = []) {
26 | this._serviceKey = serviceKey
27 | this._reporters = reporters
28 | }
29 |
30 | /**
31 | * @method extract
32 | * @param {String} operationName - the name of the operation
33 | * @param {Object} options
34 | * @param {SpanContext} [options.childOf] - a parent SpanContext (or Span,
35 | * for convenience) that the newly-started span will be the child of
36 | * (per REFERENCE_CHILD_OF). If specified, `fields.references` must
37 | * be unspecified.
38 | * @param {array} [options.references] - an array of Reference instances,
39 | * each pointing to a causal parent SpanContext. If specified,
40 | * `fields.childOf` must be unspecified.
41 | * @param {object} [options.tags] - set of key-value pairs which will be set
42 | * as tags on the newly created Span. Ownership of the object is
43 | * passed to the created span for efficiency reasons (the caller
44 | * should not modify this object after calling startSpan).
45 | * @param {number} [options.startTime] - a manually specified start time for
46 | * the created Span object. The time should be specified in
47 | * milliseconds as Unix timestamp. Decimal value are supported
48 | * to represent time values with sub-millisecond accuracy.
49 | * @returns {Span} span - a new Span object
50 | */
51 | startSpan (operationName, options = {}) {
52 | assert(typeof operationName === 'string', 'operationName is required')
53 |
54 | let spanContext
55 |
56 | // Handle options.childOf
57 | if (options.childOf) {
58 | const childOf = new Reference(REFERENCE_CHILD_OF, options.childOf)
59 |
60 | if (options.references) {
61 | options.references.push(childOf)
62 | } else {
63 | options.references = [childOf]
64 | }
65 |
66 | const parentSpanContext = childOf.referencedContext()
67 | const serviceKey = this._serviceKey
68 | const parentServiceKey = parentSpanContext._serviceKey
69 | const traceId = parentSpanContext._traceId
70 | const spanId = undefined
71 | const parentSpanId = parentSpanContext._spanId
72 |
73 | spanContext = new SpanContext(
74 | serviceKey,
75 | parentServiceKey,
76 | traceId,
77 | spanId,
78 | parentSpanId
79 | )
80 | }
81 |
82 | spanContext = spanContext || new SpanContext(this._serviceKey)
83 |
84 | return new Span(
85 | this,
86 | operationName,
87 | spanContext,
88 | options.tags,
89 | options.startTime,
90 | options.references
91 | )
92 | }
93 |
94 | /**
95 | * @method extract
96 | * @param {String} format - the format of the carrier
97 | * @param {*} carrier - the type of the carrier object is determined by the format
98 | * @returns {SpanContext|null} - The extracted SpanContext, or null if no such SpanContext could
99 | * be found in carrier
100 | */
101 | // eslint-disable-next-line class-methods-use-this
102 | extract (format, carrier) {
103 | assert(format, [FORMAT_BINARY, FORMAT_TEXT_MAP, FORMAT_HTTP_HEADERS].includes(format), 'Invalid type')
104 | assert(typeof carrier === 'object', 'carrier is required')
105 |
106 | if (format === FORMAT_BINARY) {
107 | // TODO: log
108 | } else {
109 | const tmpServiceKeys = (carrier[Tracer.CARRIER_KEY_SERVICE_KEYS] || '').split(':')
110 | const tmpSpanKeys = (carrier[Tracer.CARRIER_KEY_SPAN_IDS] || '').split(':')
111 |
112 | const serviceKey = tmpServiceKeys.shift()
113 | const parentServiceKey = tmpServiceKeys.shift() || undefined
114 | const traceId = carrier[Tracer.CARRIER_KEY_TRACE_ID]
115 | const spanId = tmpSpanKeys.shift()
116 | const parentSpanId = tmpSpanKeys.shift() || undefined
117 |
118 | if (!serviceKey || !traceId || !spanId) {
119 | return null
120 | }
121 |
122 | return new SpanContext(
123 | serviceKey,
124 | parentServiceKey,
125 | traceId,
126 | spanId,
127 | parentSpanId
128 | )
129 | }
130 |
131 | return null
132 | }
133 |
134 | /**
135 | * @method inject
136 | * @param {SpanContext|Span} spanContext - he SpanContext to inject into the carrier object.
137 | * As a convenience, a Span instance may be passed in instead
138 | * (in which case its .context() is used for the inject())
139 | * @param {String} format - the format of the carrier
140 | * @param {*} carrier - the type of the carrier object is determined by the format
141 | */
142 | // eslint-disable-next-line class-methods-use-this
143 | inject (spanContext, format, carrier) {
144 | assert(spanContext, 'spanContext is required')
145 | assert(format, [FORMAT_BINARY, FORMAT_TEXT_MAP, FORMAT_HTTP_HEADERS].includes(format), 'Invalid type')
146 | assert(typeof carrier === 'object', 'carrier is required')
147 |
148 | const injectedContext = spanContext instanceof Span ?
149 | spanContext.context() : spanContext
150 |
151 | if (format === FORMAT_BINARY) {
152 | // TODO: log not implemented
153 | } else {
154 | let serviceKeysStr = injectedContext._serviceKey
155 | if (injectedContext.parentServiceKey()) {
156 | serviceKeysStr += `:${injectedContext.parentServiceKey()}`
157 | }
158 |
159 | let spanIdsStr = injectedContext._spanId
160 | if (injectedContext._parentSpanId) {
161 | spanIdsStr += `:${injectedContext._parentSpanId}`
162 | }
163 |
164 | carrier[Tracer.CARRIER_KEY_SERVICE_KEYS] = serviceKeysStr
165 | carrier[Tracer.CARRIER_KEY_TRACE_ID] = injectedContext._traceId
166 | carrier[Tracer.CARRIER_KEY_SPAN_IDS] = spanIdsStr
167 | }
168 | }
169 | }
170 |
171 | Tracer.CARRIER_KEY_SERVICE_KEYS = CARRIER_KEY_SERVICE_KEYS
172 | Tracer.CARRIER_KEY_TRACE_ID = CARRIER_KEY_TRACE_ID
173 | Tracer.CARRIER_KEY_SPAN_IDS = CARRIER_KEY_SPAN_IDS
174 |
175 | /**
176 | * Extends the original opentracing API
177 | * @class MetricsTracer
178 | * @extends Tracer
179 | */
180 | class MetricsTracer extends Tracer {
181 | /**
182 | * @method reportFinish
183 | * @param {Span} span
184 | */
185 | reportFinish (span) {
186 | this._reporters.forEach((reporter) => reporter.reportFinish(span))
187 | }
188 | }
189 |
190 | module.exports = MetricsTracer
191 |
--------------------------------------------------------------------------------
/src/tracer/Tracer.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { expect } = require('chai')
4 | const { FORMAT_HTTP_HEADERS, FORMAT_BINARY } = require('opentracing')
5 | const Span = require('./Span')
6 | const SpanContext = require('./SpanContext')
7 | const Tracer = require('./Tracer')
8 |
9 | describe('tracer/Tracer', () => {
10 | describe('#constructor', () => {
11 | it('should create a tracer', () => {
12 | const tracer = new Tracer('service-key', ['reporter'])
13 |
14 | expect(tracer._serviceKey).to.be.equal('service-key')
15 | expect(tracer._reporters).to.be.eql(['reporter'])
16 | })
17 | })
18 |
19 | describe('#startSpan', () => {
20 | it('should start a span', () => {
21 | const tracer = new Tracer('service-key')
22 | const span = tracer.startSpan('my-operation')
23 |
24 | expect(span).to.be.instanceof(Span)
25 | })
26 |
27 | it('should start a span with options.childOf', () => {
28 | const tracer1 = new Tracer('service-1')
29 | const parentSpanContext = new SpanContext('service-1')
30 | const parentSpan = new Span(tracer1, 'operation', parentSpanContext)
31 |
32 | const tracer2 = new Tracer('service-2')
33 | const span = tracer2.startSpan('my-operation', {
34 | childOf: parentSpan
35 | })
36 | const spanContext = span.context()
37 |
38 | expect(spanContext._serviceKey).to.be.equal('service-2')
39 | expect(spanContext._parentServiceKey).to.be.equal(parentSpanContext._serviceKey)
40 | expect(spanContext._traceId).to.be.equal(parentSpanContext._traceId)
41 | expect(spanContext._spanId).to.be.not.equal(parentSpanContext._spanId)
42 | expect(spanContext._parentSpanId).to.be.equal(parentSpanContext._spanId)
43 | })
44 |
45 | it('should start a span with options.references and options.childOf', () => {
46 | const tracer1 = new Tracer('service-1')
47 | const parentSpanContext = new SpanContext('service-1')
48 | const parentSpan = new Span(tracer1, 'operation', parentSpanContext)
49 |
50 | const tracer2 = new Tracer('service-2')
51 | const span = tracer2.startSpan('my-operation', {
52 | childOf: parentSpan,
53 | references: []
54 | })
55 | const spanContext = span.context()
56 |
57 | expect(spanContext._serviceKey).to.be.equal('service-2')
58 | expect(spanContext._parentServiceKey).to.be.equal(parentSpanContext._serviceKey)
59 | expect(spanContext._traceId).to.be.equal(parentSpanContext._traceId)
60 | expect(spanContext._spanId).to.be.not.equal(parentSpanContext._spanId)
61 | expect(spanContext._parentSpanId).to.be.equal(parentSpanContext._spanId)
62 | })
63 | })
64 |
65 | describe('#inject', () => {
66 | it('should inject Span', () => {
67 | const tracer = new Tracer('service-key')
68 | const span = tracer.startSpan('my-operation')
69 | const spanContext = span.context()
70 | const carrier = {}
71 |
72 | tracer.inject(span, FORMAT_HTTP_HEADERS, carrier)
73 |
74 | expect(carrier).to.be.eql({
75 | [Tracer.CARRIER_KEY_SERVICE_KEYS]: spanContext._serviceKey,
76 | [Tracer.CARRIER_KEY_TRACE_ID]: spanContext._traceId,
77 | [Tracer.CARRIER_KEY_SPAN_IDS]: spanContext._spanId
78 | })
79 | })
80 |
81 | it('should inject SpanContext', () => {
82 | const tracer = new Tracer('service-key')
83 | const span = tracer.startSpan('my-operation')
84 | const spanContext = span.context()
85 | const carrier = {}
86 |
87 | tracer.inject(spanContext, FORMAT_HTTP_HEADERS, carrier)
88 |
89 | expect(carrier).to.be.eql({
90 | [Tracer.CARRIER_KEY_SERVICE_KEYS]: spanContext._serviceKey,
91 | [Tracer.CARRIER_KEY_TRACE_ID]: spanContext._traceId,
92 | [Tracer.CARRIER_KEY_SPAN_IDS]: spanContext._spanId
93 | })
94 | })
95 |
96 | it('should inject SpanContext with parent', () => {
97 | const tracer1 = new Tracer('service-1')
98 | const parentSpanContext = new SpanContext('service-1')
99 | const parentSpan = new Span(tracer1, 'operation', parentSpanContext)
100 |
101 | const tracer2 = new Tracer('service-2')
102 | const span = tracer2.startSpan('my-operation', {
103 | childOf: parentSpan
104 | })
105 | const spanContext = span.context()
106 | const carrier = {}
107 |
108 | tracer2.inject(spanContext, FORMAT_HTTP_HEADERS, carrier)
109 |
110 | expect(carrier).to.be.eql({
111 | [Tracer.CARRIER_KEY_SERVICE_KEYS]: `${spanContext._serviceKey}:${spanContext._parentServiceKey}`,
112 | [Tracer.CARRIER_KEY_TRACE_ID]: spanContext._traceId,
113 | [Tracer.CARRIER_KEY_SPAN_IDS]: `${spanContext._spanId}:${spanContext._parentSpanId}`
114 | })
115 | })
116 |
117 | it('should not inject with unsupported format', () => {
118 | const tracer = new Tracer('service-key')
119 | const span = tracer.startSpan('my-operation')
120 | const spanContext = span.context()
121 | const carrier = {}
122 |
123 | tracer.inject(spanContext, FORMAT_BINARY, carrier)
124 |
125 | expect(carrier).to.be.eql({})
126 | })
127 | })
128 |
129 | describe('#extract', () => {
130 | it('should extract SpanContext', () => {
131 | const tracer = new Tracer('service-key')
132 | const span = tracer.startSpan('my-operation')
133 | const spanContext = span.context()
134 | const carrier = {}
135 |
136 | tracer.inject(span, FORMAT_HTTP_HEADERS, carrier)
137 | const spanContextExtracted = tracer.extract(FORMAT_HTTP_HEADERS, carrier)
138 |
139 | expect(spanContextExtracted).to.be.eql(spanContext)
140 | })
141 |
142 | it('should return null with invalid carrier', () => {
143 | const tracer = new Tracer('service-key')
144 | const spanContextExtracted = tracer.extract(FORMAT_HTTP_HEADERS, {})
145 |
146 | expect(spanContextExtracted).to.be.eql(null)
147 | })
148 |
149 | it('should return null with unsupported format', () => {
150 | const tracer = new Tracer('service-key')
151 | const spanContextExtracted = tracer.extract(FORMAT_BINARY, {})
152 |
153 | expect(spanContextExtracted).to.be.eql(null)
154 | })
155 | })
156 |
157 | describe('#reportFinish', () => {
158 | it('should call reporters', function () {
159 | const reporter1 = {
160 | reportFinish: this.sandbox.spy()
161 | }
162 | const reporter2 = {
163 | reportFinish: this.sandbox.spy()
164 | }
165 | const tracer = new Tracer('service-key', [reporter1, reporter2])
166 | const span = tracer.startSpan('my-operation')
167 |
168 | tracer.reportFinish(span)
169 |
170 | expect(reporter1.reportFinish).to.be.calledWith(span)
171 | expect(reporter2.reportFinish).to.be.calledWith(span)
172 | })
173 | })
174 | })
175 |
--------------------------------------------------------------------------------
/src/tracer/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Reference = require('./Reference')
4 | const Span = require('./Span')
5 | const SpanContext = require('./SpanContext')
6 | const Tracer = require('./Tracer')
7 |
8 | module.exports = {
9 | Reference,
10 | Span,
11 | SpanContext,
12 | Tracer
13 | }
14 |
--------------------------------------------------------------------------------
/test/setup.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const sinon = require('sinon')
4 | const chai = require('chai')
5 | const sinonChai = require('sinon-chai')
6 |
7 | before(() => {
8 | chai.use(sinonChai)
9 | })
10 |
11 | beforeEach(function beforeEach () {
12 | this.sandbox = sinon.sandbox.create()
13 | })
14 |
15 | afterEach(function afterEach () {
16 | this.sandbox.restore()
17 | })
18 |
--------------------------------------------------------------------------------