From 68d3556aab8c76fa9a54ec5f44644c134154654e Mon Sep 17 00:00:00 2001 From: Arnold Trakhtenberg Date: Wed, 11 Jul 2018 10:55:23 -0700 Subject: [PATCH 001/330] Make things prettier, add RedisFeatureStore to toplevel ts declarations, fix LDFeatureStore.close return value --- index.d.ts | 58 ++++++++++++++++++++++++++++++++++------------- package-lock.json | 23 +++++++++++++++++-- package.json | 1 + 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/index.d.ts b/index.d.ts index e37f8e4..ae0704a 100644 --- a/index.d.ts +++ b/index.d.ts @@ -6,13 +6,17 @@ * Documentation: http://docs.launchdarkly.com/docs/node-sdk-reference */ -declare module "ldclient-node" { +declare module 'ldclient-node' { import { EventEmitter } from 'events'; - + import { ClientOpts } from 'redis'; + namespace errors { export const LDPollingError: ErrorConstructor; export const LDStreamingError: ErrorConstructor; export const LDClientError: ErrorConstructor; + export const LDUnexpectedResponseError: ErrorConstructor; + export const LDInvalidSDKKeyError: ErrorConstructor; + export function isHttpErrorRecoverable(status: number): boolean; } /** @@ -20,6 +24,16 @@ declare module "ldclient-node" { */ export function init(key: string, options?: LDOptions): LDClient; + /** + * Create a feature flag store backed by a Redis instance + */ + export function RedisFeatureStore( + redisOpts: ClientOpts, + cacheTTL: number, + prefix: string, + logger: LDLogger | object + ): LDFeatureStore; + /** * The types of values a feature flag can have. * @@ -31,7 +45,7 @@ declare module "ldclient-node" { * A map of feature flags from their keys to their values. */ export type LDFlagSet = { - [key: string]: LDFlagValue, + [key: string]: LDFlagValue; }; /** @@ -79,7 +93,6 @@ declare module "ldclient-node" { */ logger?: LDLogger | object; - /** * Feature store used by the LaunchDarkly client, defaults to in memory storage. * @@ -131,7 +144,7 @@ declare module "ldclient-node" { /** * Whether to send events back to LaunchDarkly - */ + */ sendEvents?: boolean; /** @@ -165,7 +178,6 @@ declare module "ldclient-node" { * Defaults to 300. */ userKeysFlushInterval?: number; - } /** @@ -230,7 +242,11 @@ declare module "ldclient-node" { * Any additional attributes associated with the user. */ custom?: { - [key: string]: string | boolean | number | Array, + [key: string]: + | string + | boolean + | number + | Array; }; } @@ -358,10 +374,8 @@ declare module "ldclient-node" { /** * Close the feature store. * - * @returns - * The store instance. */ - close: () => LDFeatureStore; + close: () => void; } /** @@ -412,12 +426,22 @@ declare module "ldclient-node" { * * @param callback * The callback to receive the variation result. - * + * * @returns a Promise containing the flag value */ - variation: (key: string, user: LDUser, defaultValue: LDFlagValue, callback?: (err: any, res: LDFlagValue) => void) => Promise; + variation: ( + key: string, + user: LDUser, + defaultValue: LDFlagValue, + callback?: (err: any, res: LDFlagValue) => void + ) => Promise; - toggle: (key: string, user: LDUser, defaultValue: LDFlagValue, callback?: (err: any, res: LDFlagValue) => void) => Promise; + toggle: ( + key: string, + user: LDUser, + defaultValue: LDFlagValue, + callback?: (err: any, res: LDFlagValue) => void + ) => Promise; /** * Retrieves the set of all flag values for a user. @@ -429,7 +453,10 @@ declare module "ldclient-node" { * The node style callback to receive the variation result. * @returns a Promise containing the set of all flag values for a user */ - allFlags: (user: LDUser, callback?: (err: any, res: LDFlagSet) => void) => Promise; + allFlags: ( + user: LDUser, + callback?: (err: any, res: LDFlagSet) => void + ) => Promise; /** * @@ -449,7 +476,6 @@ declare module "ldclient-node" { */ close: () => void; - /** * * @returns Whether the client is configured in offline mode. @@ -491,7 +517,7 @@ declare module "ldclient-node" { * Internally, the LaunchDarkly SDK keeps an event queue for track and identify calls. * These are flushed periodically (see configuration option: flushInterval) * and when the queue size limit (see configuration option: capacity) is reached. - * + * * @returns a Promise which resolves once flushing is finished */ flush: (callback?: (err: any, res: boolean) => void) => Promise; diff --git a/package-lock.json b/package-lock.json index d29d597..3d9a264 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ldclient-node", - "version": "5.0.1", + "version": "5.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -24,6 +24,25 @@ "js-tokens": "3.0.2" } }, + "@types/events": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" + }, + "@types/node": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.2.tgz", + "integrity": "sha512-m9zXmifkZsMHZBOyxZWilMwmTlpC8x5Ty360JKTiXvlXZfBWYpsg9ZZvP/Ye+iZUh+Q+MxDLjItVTWIsfwz+8Q==" + }, + "@types/redis": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.6.tgz", + "integrity": "sha512-kaSI4XQwCfJtPiuyCXvLxCaw2N0fMZesdob3Jh01W20vNFct+3lfvJ/4yCJxbSopXOBOzpg+pGxkW6uWZrPZHA==", + "requires": { + "@types/events": "1.2.0", + "@types/node": "10.5.2" + } + }, "abab": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", @@ -5708,7 +5727,7 @@ }, "tunnel": { "version": "https://github.com/launchdarkly/node-tunnel/tarball/d860e57650cce1ea655d00854c81babe6b47e02c", - "integrity": "sha512-prl+yIntUTIhkHoz2YtT7xtcAoMEgfsm+RL2bUGFI6e229NTICfo+jFKj1UFCDqc1wm/SQK7TM2U06sgeoO9jQ==" + "integrity": "sha1-DxkgfzcgRtPUaCGDy+INSgR8zdk=" }, "tunnel-agent": { "version": "0.6.0", diff --git a/package.json b/package.json index 279ccaa..0a582c4 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ }, "homepage": "https://github.com/launchdarkly/node-client", "dependencies": { + "@types/redis": "2.8.6", "async": "2.6.0", "crypto": "0.0.3", "hoek": "4.2.1", From c5c665fc98d1ecb86acc6dde5797c518367c75b6 Mon Sep 17 00:00:00 2001 From: Arnold Trakhtenberg Date: Wed, 11 Jul 2018 10:58:36 -0700 Subject: [PATCH 002/330] Add modules for ldclient StreamProcessor, Requestor, and FeatureStore --- index.d.ts | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/index.d.ts b/index.d.ts index ae0704a..f3e456c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -378,6 +378,32 @@ declare module 'ldclient-node' { close: () => void; } + /** + * The LaunchDarkly client stream processor + * + * The client uses this internally to retrieve updates from LaunchDarkly. + */ + export interface LDStreamProcessor { + start: (fn?: (err?: any) => void) => void; + stop: () => void; + close: () => void; + } + + /** + * The LaunchDarkly client feature flag requetor + * + * The client uses this internally to retrieve feature + * flags from LaunchDarkly. + */ + export interface LDFeatureRequestor { + requestObject: ( + kind: any, + key: string, + cb: (err: any, body: any) => void + ) => void; + requestAllData: (cb: (err: any, body: any) => void) => void; + } + /** * The LaunchDarkly client's instance interface. * @@ -523,3 +549,31 @@ declare module 'ldclient-node' { flush: (callback?: (err: any, res: boolean) => void) => Promise; } } + +declare module 'ldclient-node/streaming' { + import { + LDOptions, + LDFeatureRequestor, + LDStreamProcessor + } from 'ldclient-node'; + + function StreamProcessor( + sdkKey: string, + options: LDOptions, + requestor: LDFeatureRequestor + ): LDStreamProcessor; + export = StreamProcessor; +} +declare module 'ldclient-node/requestor' { + import { LDOptions, LDFeatureRequestor } from 'ldclient-node'; + + function Requestor(sdkKey: string, options: LDOptions): LDFeatureRequestor; + export = Requestor; +} + +declare module 'ldclient-node/feature_store' { + import { LDFeatureStore } from 'ldclient-node'; + + function InMemoryFeatureStore(): LDFeatureStore; + export = InMemoryFeatureStore; +} From 407e6ce8ba5604bc0dc0678a1d5ffe61d90a6115 Mon Sep 17 00:00:00 2001 From: Arnold Trakhtenberg Date: Wed, 11 Jul 2018 13:57:22 -0700 Subject: [PATCH 003/330] address review --- index.d.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index f3e456c..2805344 100644 --- a/index.d.ts +++ b/index.d.ts @@ -16,7 +16,6 @@ declare module 'ldclient-node' { export const LDClientError: ErrorConstructor; export const LDUnexpectedResponseError: ErrorConstructor; export const LDInvalidSDKKeyError: ErrorConstructor; - export function isHttpErrorRecoverable(status: number): boolean; } /** @@ -390,7 +389,7 @@ declare module 'ldclient-node' { } /** - * The LaunchDarkly client feature flag requetor + * The LaunchDarkly client feature flag requestor * * The client uses this internally to retrieve feature * flags from LaunchDarkly. From 762b1c8deb3423d22957fbccc09eece9f412588e Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Thu, 19 Jul 2018 16:03:35 +0000 Subject: [PATCH 004/330] fix: package.json to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/npm:cryptiles:20180710 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0a582c4..278c2ef 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "node-cache": "^3.2.1", "node-sha1": "0.0.1", "redis": "^2.6.0-2", - "request": "2.85.0", + "request": "2.87.0", "request-etag": "^2.0.3", "semver": "5.5.0", "tunnel": "https://github.com/launchdarkly/node-tunnel/tarball/d860e57650cce1ea655d00854c81babe6b47e02c", From c45414efca8bcce4f4982bc2193769b28589aa09 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 26 Jul 2018 14:10:35 -0700 Subject: [PATCH 005/330] remove npm dependency on "crypto", use built-in version instead --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 6be5295..c3d97e9 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "dependencies": { "@types/redis": "2.8.6", "async": "2.6.0", - "crypto": "0.0.3", "hoek": "4.2.1", "lrucache": "^1.0.3", "node-cache": "^3.2.1", From b4fe5f49f5a88fa796d2d489042f21ebbf85b3bc Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 26 Jul 2018 14:15:31 -0700 Subject: [PATCH 006/330] update package-lock --- package-lock.json | 111 ++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 74 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3d9a264..d79cab9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ldclient-node", - "version": "5.1.0", + "version": "5.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -72,14 +72,14 @@ } }, "ajv": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", - "integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "requires": { "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "json-schema-traverse": "0.3.1", - "json-stable-stringify": "1.0.1" + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, "align-text": { @@ -817,14 +817,6 @@ "inherits": "2.0.3" } }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "requires": { - "hoek": "4.2.1" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1191,29 +1183,6 @@ "which": "1.3.0" } }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.2.1" - } - } - } - }, - "crypto": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-0.0.3.tgz", - "integrity": "sha1-RwqBuGvkxe4XrMggeh9TFa4g27A=" - }, "cssom": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", @@ -1584,9 +1553,14 @@ "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" }, "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "fast-levenshtein": { "version": "2.0.6", @@ -1668,13 +1642,23 @@ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", - "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { "asynckit": "0.4.0", - "combined-stream": "1.0.5", + "combined-stream": "1.0.6", "mime-types": "2.1.17" + }, + "dependencies": { + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "1.0.0" + } + } } }, "fragment-cache": { @@ -2098,7 +2082,7 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "requires": { - "ajv": "5.2.3", + "ajv": "5.5.2", "har-schema": "2.0.0" } }, @@ -2193,17 +2177,6 @@ } } }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.1", - "sntp": "2.0.2" - } - }, "hoek": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", @@ -3113,7 +3086,7 @@ "nwmatcher": "1.4.4", "parse5": "4.0.0", "pn": "1.1.0", - "request": "2.85.0", + "request": "2.87.0", "request-promise-native": "1.0.5", "sax": "1.2.4", "symbol-tree": "3.2.2", @@ -4420,9 +4393,9 @@ } }, "request": { - "version": "2.85.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", - "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", "requires": { "aws-sign2": "0.7.0", "aws4": "1.6.0", @@ -4430,9 +4403,8 @@ "combined-stream": "1.0.5", "extend": "3.0.1", "forever-agent": "0.6.1", - "form-data": "2.3.1", + "form-data": "2.3.2", "har-validator": "5.0.3", - "hawk": "6.0.2", "http-signature": "1.2.0", "is-typedarray": "1.0.0", "isstream": "0.1.2", @@ -4442,7 +4414,6 @@ "performance-now": "2.1.0", "qs": "6.5.1", "safe-buffer": "5.1.1", - "stringstream": "0.0.5", "tough-cookie": "2.3.3", "tunnel-agent": "0.6.0", "uuid": "3.1.0" @@ -4456,7 +4427,7 @@ "lodash.assign": "4.2.0", "lodash.clonedeep": "4.5.0", "lru-cache": "4.1.2", - "request": "2.85.0" + "request": "2.87.0" } }, "request-promise-core": { @@ -5040,14 +5011,6 @@ "kind-of": "3.2.2" } }, - "sntp": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", - "integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=", - "requires": { - "hoek": "4.2.1" - } - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -5727,7 +5690,7 @@ }, "tunnel": { "version": "https://github.com/launchdarkly/node-tunnel/tarball/d860e57650cce1ea655d00854c81babe6b47e02c", - "integrity": "sha1-DxkgfzcgRtPUaCGDy+INSgR8zdk=" + "integrity": "sha512-prl+yIntUTIhkHoz2YtT7xtcAoMEgfsm+RL2bUGFI6e229NTICfo+jFKj1UFCDqc1wm/SQK7TM2U06sgeoO9jQ==" }, "tunnel-agent": { "version": "0.6.0", From d4e15b1e7c748cdd0f79c62a7f76d4026c03c6ae Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 1 Aug 2018 12:54:58 -0700 Subject: [PATCH 007/330] treat HTTP 400 as a recoverable error --- errors.js | 2 +- test/event_processor-test.js | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/errors.js b/errors.js index 74b7495..7112be3 100644 --- a/errors.js +++ b/errors.js @@ -20,7 +20,7 @@ exports.LDClientError = createCustomError('LaunchDarklyClientError'); exports.isHttpErrorRecoverable = function(status) { if (status >= 400 && status < 500) { - return status === 408 || status === 429; + return status === 400 || status === 408 || status === 429; } return true; } diff --git a/test/event_processor-test.js b/test/event_processor-test.js index acdfebc..6ae8014 100644 --- a/test/event_processor-test.js +++ b/test/event_processor-test.js @@ -417,8 +417,8 @@ describe('EventProcessor', function() { function verifyRecoverableHttpError(done, status) { ep = EventProcessor(sdkKey, defaultConfig); - var e = { kind: 'identify', creationDate: 1000, user: user }; - ep.sendEvent(e); + var e0 = { kind: 'identify', creationDate: 1000, user: user }; + ep.sendEvent(e0); nock(eventsUri).post('/bulk').reply(status); nock(eventsUri).post('/bulk').reply(status); @@ -427,10 +427,24 @@ describe('EventProcessor', function() { function() {}, function(err) { expect(err.message).toContain('error ' + status); - done(); + + var e1 = { kind: 'identify', creationDate: 1001, user: user }; + ep.sendEvent(e1); + + // this second event should go through + flushAndGetRequest(function(output) { + expect(output.length).toEqual(1); + expect(output[0].creationDate).toEqual(1001); + + done(); + }); }); } + it('retries after a 400 error', function(done) { + verifyRecoverableHttpError(done, 400); + }); + it('stops sending events after a 401 error', function(done) { verifyUnrecoverableHttpError(done, 401); }); From ccbb753e179ef3c89aa2e973d19ab896fe288aab Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 1 Aug 2018 14:40:49 -0700 Subject: [PATCH 008/330] update doc comment for new waitForInitialization behavior --- index.d.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index 2805344..d0906bb 100644 --- a/index.d.ts +++ b/index.d.ts @@ -429,11 +429,12 @@ declare module 'ldclient-node' { /** * Returns a Promise that will be resolved if the client successfully initializes, or * rejected if client initialization has irrevocably failed (for instance, if it detects - * that the SDK key is invalid). The sucess and failure cases can also be detected by listening + * that the SDK key is invalid). The success and failure cases can also be detected by listening * for the events "ready" and "failed". - * @returns a Promise containing the initialization state of the client + * @returns a Promise containing the initialization state of the client; if successful, the resolved + * value is the same client object */ - waitForInitialization: () => Promise; + waitForInitialization: () => Promise; /** * Retrieves a flag's value. From 018c6f218c4402848e305a129777d353ced25dbb Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 1 Aug 2018 15:24:07 -0700 Subject: [PATCH 009/330] fix waitForInitialization to always resolve with a value --- index.js | 2 +- test/LDClient-test.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 3a7589b..f2f9e8b 100644 --- a/index.js +++ b/index.js @@ -144,7 +144,7 @@ var newClient = function(sdkKey, config) { } return new Promise(function(resolve, reject) { - client.once('ready', resolve); + client.once('ready', function() { resolve(client) }); client.once('failed', reject); }); }; diff --git a/test/LDClient-test.js b/test/LDClient-test.js index 22dd209..8f87334 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -325,6 +325,7 @@ describe('LDClient', function() { client.waitForInitialization().then(callback) .then(() => { expect(callback).toHaveBeenCalled(); + expect(callback.mock.calls[0][0]).toBe(client); done(); }).catch(done.error) }); @@ -338,6 +339,7 @@ describe('LDClient', function() { client.waitForInitialization().then(callback) .then(() => { expect(callback).toHaveBeenCalled(); + expect(callback.mock.calls[0][0]).toBe(client); done(); }).catch(done.error) }).catch(done.error) From 95951819c8c46d58849d589113cceead12835d29 Mon Sep 17 00:00:00 2001 From: Arnold Trakhtenberg Date: Fri, 17 Aug 2018 13:44:30 -0700 Subject: [PATCH 010/330] Update feature store type for versioned data kind --- index.d.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index d0906bb..da67fcf 100644 --- a/index.d.ts +++ b/index.d.ts @@ -302,21 +302,27 @@ declare module 'ldclient-node' { /** * Get a flag's value. * + * @param kind + * The type of data to be accessed + * * @param key * The flag key * * @param callback * Will be called with the resulting flag. */ - get: (key: string, callback: (res: LDFlagValue) => void) => void; + get: (kind: object, key: string, callback: (res: LDFlagValue) => void) => void; /** * Get all flags. * + * @param kind + * The type of data to be accessed + * * @param callback * Will be called with the resulting flag set. */ - all: (callback: (res: LDFlagSet) => void) => void; + all: (kind: object, callback: (res: LDFlagSet) => void) => void; /** * Initialize the store. @@ -332,6 +338,9 @@ declare module 'ldclient-node' { /** * Delete a key from the store. * + * @param kind + * The type of data to be accessed + * * @param key * The flag key. * @@ -342,11 +351,14 @@ declare module 'ldclient-node' { * @param callback * Will be called when the delete operation is complete. */ - delete: (key: string, version: string, callback?: () => void) => void; + delete: (kind: object, key: string, version: string, callback?: () => void) => void; /** * Upsert a flag to the store. * + * @param kind + * The type of data to be accessed + * * @param key * The flag key. * @@ -356,7 +368,7 @@ declare module 'ldclient-node' { * @param callback * Will be called after the upsert operation is complete. */ - upsert: (key: string, flag: LDFlagValue, callback?: () => void) => void; + upsert: (kind: object, key: string, flag: LDFlagValue, callback?: () => void) => void; /** * Is the store initialized? From e51d5548f1f11af0bb22325a234b2e5c6cb7782d Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 20 Aug 2018 15:14:07 -0700 Subject: [PATCH 011/330] add new version of allFlags() that captures more metadata --- flags_state.js | 36 +++++++++++++++++++++++++ index.d.ts | 61 +++++++++++++++++++++++++++++++++++++++++-- index.js | 25 ++++++++++++------ test/LDClient-test.js | 47 +++++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 10 deletions(-) create mode 100644 flags_state.js diff --git a/flags_state.js b/flags_state.js new file mode 100644 index 0000000..d7bd8f2 --- /dev/null +++ b/flags_state.js @@ -0,0 +1,36 @@ + +function FlagsStateBuilder(valid) { + var builder = {}; + var flagValues = {}; + var flagMetadata = {}; + + builder.addFlag = function(flag, value, variation) { + flagValues[flag.key] = value; + var meta = { + version: flag.version, + trackEvents: flag.trackEvents + }; + if (variation !== undefined && variation !== null) { + meta.variation = variation; + } + if (flag.debugEventsUntilDate !== undefined && flag.debugEventsUntilDate !== null) { + meta.debugEventsUntilDate = flag.debugEventsUntilDate; + } + flagMetadata[flag.key] = meta; + }; + + builder.build = function() { + return { + valid: valid, + allValues: function() { return flagValues; }, + getFlagValue: function(key) { return flagValues[key]; }, + toJson: function() { + return Object.assign({}, flagValues, { $flagsState: flagMetadata }); + } + }; + } + + return builder; +} + +module.exports = FlagsStateBuilder; diff --git a/index.d.ts b/index.d.ts index da67fcf..a1bdc62 100644 --- a/index.d.ts +++ b/index.d.ts @@ -47,6 +47,45 @@ declare module 'ldclient-node' { [key: string]: LDFlagValue; }; + /** + * An object that contains the state of all feature flags, generated by the client's + * allFlagsState() method. + */ + export interface LDFlagsState = { + /** + * True if this object contains a valid snapshot of feature flag state, or false if the + * state could not be computed (for instance, because the client was offline or there + * was no user). + */ + valid: boolean; + + /** + * Returns the value of an individual feature flag at the time the state was recorded. + * It will be null if the flag returned the default value, or if there was no such flag. + * @param key the flag key + */ + getFlagValue: (key: string) => LDFlagValue; + + /** + * Returns a map of feature flag keys to values. If a flag would have evaluated to the + * default value, its value will be null. + * + * Do not use this method if you are passing data to the front end to "bootstrap" the + * JavaScript client. Instead, use toJson(). + */ + allValues: () => LDFlagSet; + + /** + * Returns a Javascript representation of the entire state map, in the format used by + * the Javascript SDK. Use this method if you are passing data to the front end in + * order to "bootstrap" the JavaScript client. + * + * Do nto rely on the exact shape of this data, as it may change in future to support + * the needs of the JavaScript client. + */ + toJson: () => object; + }; + /** * LaunchDarkly initialization options. */ @@ -484,8 +523,9 @@ declare module 'ldclient-node' { /** * Retrieves the set of all flag values for a user. * - * @param key - * The key of the flag for which to retrieve the corresponding value. + * This method is deprecated; user allFlagsState() instead. Current versions of the client-side + * SDK will not generate analytics events correctly if you pass the result of allFlags(). + * * @param user * @param callback * The node style callback to receive the variation result. @@ -496,6 +536,23 @@ declare module 'ldclient-node' { callback?: (err: any, res: LDFlagSet) => void ) => Promise; + /** + * Builds an object that encapsulates the state of all feature flags for a given user, + * including the flag values and also metadata that can be used on the front end. This + * method does not send analytics events back to LaunchDarkly. + * + * The most common use case for this method is to bootstrap a set of client-side + * feature flags from a back-end service. + * + * @param user The end user requesting the feature flags. + * @param callback The node-style callback to receive the state result. + * @returns a Promise containing the state object + */ + allFlagsState: ( + user: LDUser, + callback?: (err: any, res: LDFlagsState) => void + ) => Promise; + /** * * The secure_mode_hash method computes an HMAC signature of a user signed with the client's SDK key. diff --git a/index.js b/index.js index f2f9e8b..9403363 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ var EventEmitter = require('events').EventEmitter; var EventProcessor = require('./event_processor'); var PollingProcessor = require('./polling'); var StreamingProcessor = require('./streaming'); +var FlagsStateBuilder = require('./flags_state'); var configuration = require('./configuration'); var evaluate = require('./evaluate_flag'); var messages = require('./messages'); @@ -232,29 +233,37 @@ var newClient = function(sdkKey, config) { } client.allFlags = function(user, callback) { + return wrapPromiseCallback( + client.allFlagsState(user).then(function(state) { + return state.allValues(); + }), + callback); + } + + client.allFlagsState = function(user, callback) { return wrapPromiseCallback(new Promise(function(resolve, reject) { sanitizeUser(user); - var results = {}; - + if (this.isOffline()) { - config.logger.info("allFlags() called in offline mode. Returning empty map."); - return resolve({}); + config.logger.info("allFlagsState() called in offline mode. Returning empty state."); + return resolve(FlagsStateBuilder(false).build()); } if (!user) { - config.logger.info("allFlags() called without user. Returning empty map."); - return resolve({}); + config.logger.info("allFlagsState() called without user. Returning empty state."); + return resolve(FlagsStateBuilder(false).build()); } + var builder = FlagsStateBuilder(true); config.featureStore.all(dataKind.features, function(flags) { async.forEachOf(flags, function(flag, key, iterateeCb) { // At the moment, we don't send any events here evaluate.evaluate(flag, user, config.featureStore, function(err, variation, value, events) { - results[key] = value; + builder.addFlag(flag, value, variation); setImmediate(iterateeCb); }) }, function(err) { - return err ? reject(err) : resolve(results); + return err ? reject(err) : resolve(builder.build()); }); }); }.bind(this)), callback); diff --git a/test/LDClient-test.js b/test/LDClient-test.js index 8f87334..a4c16e8 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -83,6 +83,18 @@ describe('LDClient', function() { }); }); + it('returns empty state for allFlagsState in offline mode and logs a message', function(done) { + var client = LDClient.init('secret', {offline: true, logger: logger}); + client.on('ready', function() { + client.allFlagsState({key: 'user'}, function(err, state) { + expect(state.valid).toEqual(false); + expect(state.allValues()).toEqual({}); + expect(logger.info).toHaveBeenCalledTimes(1); + done(); + }); + }); + }); + it('allows deprecated method all_flags', function(done) { var client = LDClient.init('secret', {offline: true, logger: logger}); client.on('ready', function() { @@ -262,6 +274,41 @@ describe('LDClient', function() { }); }); + it('captures flag state with allFlagsState()', function(done) { + var flag = { + key: 'feature', + version: 100, + on: true, + targets: [], + fallthrough: { variation: 1 }, + variations: ['a', 'b'], + trackEvents: true, + debugEventsUntilDate: 1000 + }; + var client = createOnlineClientWithFlags({ feature: flag }); + var user = { key: 'user' }; + client.on('ready', function() { + client.allFlagsState(user, function(err, state) { + expect(err).toBeNull(); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({feature: 'b'}); + expect(state.getFlagValue('feature')).toEqual('b'); + expect(state.toJson()).toEqual({ + feature: 'b', + $flagsState: { + feature: { + version: 100, + variation: 1, + trackEvents: true, + debugEventsUntilDate: 1000 + } + } + }); + done(); + }); + }); + }); + it('should not overflow the call stack when evaluating a huge number of flags', function(done) { var flagCount = 5000; var flags = {}; From 38616c815367dd0cd62aa28862da1603a14844f6 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 20 Aug 2018 15:24:06 -0700 Subject: [PATCH 012/330] add deprecation warning for allFlags --- index.js | 1 + test/LDClient-test.js | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 9403363..07c1aa8 100644 --- a/index.js +++ b/index.js @@ -233,6 +233,7 @@ var newClient = function(sdkKey, config) { } client.allFlags = function(user, callback) { + config.logger.warn("allFlags() is deprecated. Call 'allFlagsState' instead and call toJson() on the result"); return wrapPromiseCallback( client.allFlagsState(user).then(function(state) { return state.allValues(); diff --git a/test/LDClient-test.js b/test/LDClient-test.js index a4c16e8..075b5a7 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -29,8 +29,10 @@ describe('LDClient', function() { }; beforeEach(function() { + logger.debug = jest.fn(); logger.info = jest.fn(); logger.warn = jest.fn(); + logger.error = jest.fn(); eventProcessor.events = []; updateProcessor.error = null; }); @@ -115,7 +117,8 @@ describe('LDClient', function() { return LDClient.init('secret', { featureStore: store, eventProcessor: eventProcessor, - updateProcessor: updateProcessor + updateProcessor: updateProcessor, + logger: logger }); } @@ -269,6 +272,7 @@ describe('LDClient', function() { client.allFlags(user, function(err, results) { expect(err).toBeNull(); expect(results).toEqual({feature: 'b'}); + expect(logger.warn).toHaveBeenCalledTimes(1); // deprecation warning done(); }); }); From 90e1c1cc02cc5b7fa60071b3d772e33c398565c2 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 20 Aug 2018 18:06:53 -0700 Subject: [PATCH 013/330] typo --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index a1bdc62..3e15617 100644 --- a/index.d.ts +++ b/index.d.ts @@ -80,7 +80,7 @@ declare module 'ldclient-node' { * the Javascript SDK. Use this method if you are passing data to the front end in * order to "bootstrap" the JavaScript client. * - * Do nto rely on the exact shape of this data, as it may change in future to support + * Do not rely on the exact shape of this data, as it may change in future to support * the needs of the JavaScript client. */ toJson: () => object; From 2b672fb7aed01c89a1c979fce6e59e43c0473bc9 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 20 Aug 2018 18:07:19 -0700 Subject: [PATCH 014/330] name function toJSON so JSON.stringify will use it --- flags_state.js | 2 +- index.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flags_state.js b/flags_state.js index d7bd8f2..e3e0ffc 100644 --- a/flags_state.js +++ b/flags_state.js @@ -24,7 +24,7 @@ function FlagsStateBuilder(valid) { valid: valid, allValues: function() { return flagValues; }, getFlagValue: function(key) { return flagValues[key]; }, - toJson: function() { + toJSON: function() { return Object.assign({}, flagValues, { $flagsState: flagMetadata }); } }; diff --git a/index.d.ts b/index.d.ts index 3e15617..42eda94 100644 --- a/index.d.ts +++ b/index.d.ts @@ -83,7 +83,7 @@ declare module 'ldclient-node' { * Do not rely on the exact shape of this data, as it may change in future to support * the needs of the JavaScript client. */ - toJson: () => object; + toJSON: () => object; }; /** From 1a35b79e31c14385744f11005e4ea0936cb5bc6b Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 20 Aug 2018 18:09:08 -0700 Subject: [PATCH 015/330] comment edits --- index.d.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index 42eda94..4872297 100644 --- a/index.d.ts +++ b/index.d.ts @@ -523,7 +523,7 @@ declare module 'ldclient-node' { /** * Retrieves the set of all flag values for a user. * - * This method is deprecated; user allFlagsState() instead. Current versions of the client-side + * This method is deprecated; use allFlagsState() instead. Current versions of the client-side * SDK will not generate analytics events correctly if you pass the result of allFlags(). * * @param user @@ -542,7 +542,8 @@ declare module 'ldclient-node' { * method does not send analytics events back to LaunchDarkly. * * The most common use case for this method is to bootstrap a set of client-side - * feature flags from a back-end service. + * feature flags from a back-end service. Call the toJSON() method of the returned object + * to convert it to the data structure used by the client-side SDK. * * @param user The end user requesting the feature flags. * @param callback The node-style callback to receive the state result. From 72e2e24bcca31fd483fb2243100b10f54282cf97 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 20 Aug 2018 18:10:04 -0700 Subject: [PATCH 016/330] fix test --- test/LDClient-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/LDClient-test.js b/test/LDClient-test.js index 075b5a7..762c1fa 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -297,7 +297,7 @@ describe('LDClient', function() { expect(state.valid).toEqual(true); expect(state.allValues()).toEqual({feature: 'b'}); expect(state.getFlagValue('feature')).toEqual('b'); - expect(state.toJson()).toEqual({ + expect(state.toJSON()).toEqual({ feature: 'b', $flagsState: { feature: { From ba0d334aa25e7b4fc0615fc1571979ac1fe54b41 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 20 Aug 2018 20:20:14 -0700 Subject: [PATCH 017/330] add $valid property in case we care about that on the front end --- flags_state.js | 2 +- test/LDClient-test.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/flags_state.js b/flags_state.js index e3e0ffc..10f15c6 100644 --- a/flags_state.js +++ b/flags_state.js @@ -25,7 +25,7 @@ function FlagsStateBuilder(valid) { allValues: function() { return flagValues; }, getFlagValue: function(key) { return flagValues[key]; }, toJSON: function() { - return Object.assign({}, flagValues, { $flagsState: flagMetadata }); + return Object.assign({}, flagValues, { $flagsState: flagMetadata, $valid: valid }); } }; } diff --git a/test/LDClient-test.js b/test/LDClient-test.js index 762c1fa..943ee64 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -306,7 +306,8 @@ describe('LDClient', function() { trackEvents: true, debugEventsUntilDate: 1000 } - } + }, + $valid: true }); done(); }); From a9a5c2f43273f7edd2a8ab57e36e71644c2f99dc Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Tue, 21 Aug 2018 13:50:08 -0700 Subject: [PATCH 018/330] add ability to filter for only client-side flags --- index.d.ts | 13 +++++++++++++ index.js | 16 +++++++++++----- test/LDClient-test.js | 23 +++++++++++++++++++++-- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/index.d.ts b/index.d.ts index 4872297..1d05503 100644 --- a/index.d.ts +++ b/index.d.ts @@ -454,6 +454,16 @@ declare module 'ldclient-node' { requestAllData: (cb: (err: any, body: any) => void) => void; } + /** + * Optional settings that can be passed to LDClient.allFlagsState(). + */ + export type LDFlagsStateOptions = { + /** + * True if the state should include only flags that have been marked for use with the + * client-side SDK. By default, all flags are included. + clientSideOnly?: boolean; + }; + /** * The LaunchDarkly client's instance interface. * @@ -546,11 +556,14 @@ declare module 'ldclient-node' { * to convert it to the data structure used by the client-side SDK. * * @param user The end user requesting the feature flags. + * @param options Optional object with properties that determine how the state is computed; + * set `clientSideOnly: true` to include only client-side-enabled flags * @param callback The node-style callback to receive the state result. * @returns a Promise containing the state object */ allFlagsState: ( user: LDUser, + options?: LDFlagsStateOptions, callback?: (err: any, res: LDFlagsState) => void ) => Promise; diff --git a/index.js b/index.js index 07c1aa8..5f6f1fb 100644 --- a/index.js +++ b/index.js @@ -241,7 +241,8 @@ var newClient = function(sdkKey, config) { callback); } - client.allFlagsState = function(user, callback) { + client.allFlagsState = function(user, options, callback) { + options = options || {}; return wrapPromiseCallback(new Promise(function(resolve, reject) { sanitizeUser(user); @@ -256,13 +257,18 @@ var newClient = function(sdkKey, config) { } var builder = FlagsStateBuilder(true); + var clientOnly = options.clientSideOnly; config.featureStore.all(dataKind.features, function(flags) { async.forEachOf(flags, function(flag, key, iterateeCb) { - // At the moment, we don't send any events here - evaluate.evaluate(flag, user, config.featureStore, function(err, variation, value, events) { - builder.addFlag(flag, value, variation); + if (clientOnly && !flag.clientSide) { setImmediate(iterateeCb); - }) + } else { + // At the moment, we don't send any events here + evaluate.evaluate(flag, user, config.featureStore, function(err, variation, value, events) { + builder.addFlag(flag, value, variation); + setImmediate(iterateeCb); + }); + } }, function(err) { return err ? reject(err) : resolve(builder.build()); }); diff --git a/test/LDClient-test.js b/test/LDClient-test.js index 943ee64..bb132a7 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -88,7 +88,7 @@ describe('LDClient', function() { it('returns empty state for allFlagsState in offline mode and logs a message', function(done) { var client = LDClient.init('secret', {offline: true, logger: logger}); client.on('ready', function() { - client.allFlagsState({key: 'user'}, function(err, state) { + client.allFlagsState({key: 'user'}, {}, function(err, state) { expect(state.valid).toEqual(false); expect(state.allValues()).toEqual({}); expect(logger.info).toHaveBeenCalledTimes(1); @@ -292,7 +292,7 @@ describe('LDClient', function() { var client = createOnlineClientWithFlags({ feature: flag }); var user = { key: 'user' }; client.on('ready', function() { - client.allFlagsState(user, function(err, state) { + client.allFlagsState(user, {}, function(err, state) { expect(err).toBeNull(); expect(state.valid).toEqual(true); expect(state.allValues()).toEqual({feature: 'b'}); @@ -314,6 +314,25 @@ describe('LDClient', function() { }); }); + it('can filter for only client-side flags with allFlagsState()', function(done) { + var flag1 = { key: 'server-side-1', on: false, offVariation: 0, variations: ['a'], clientSide: false }; + var flag2 = { key: 'server-side-2', on: false, offVariation: 0, variations: ['b'], clientSide: false }; + var flag3 = { key: 'client-side-1', on: false, offVariation: 0, variations: ['value1'], clientSide: true }; + var flag4 = { key: 'client-side-2', on: false, offVariation: 0, variations: ['value2'], clientSide: true }; + var client = createOnlineClientWithFlags({ + 'server-side-1': flag1, 'server-side-2': flag2, 'client-side-1': flag3, 'client-side-2': flag4 + }); + var user = { key: 'user' }; + client.on('ready', function() { + client.allFlagsState(user, { clientSideOnly: true }, function(err, state) { + expect(err).toBeNull(); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({ 'client-side-1': 'value1', 'client-side-2': 'value2' }); + done(); + }); + }); + }); + it('should not overflow the call stack when evaluating a huge number of flags', function(done) { var flagCount = 5000; var flags = {}; From 06bdb460183a7a95f4344c66d79eb19dc1502242 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Tue, 21 Aug 2018 17:26:53 -0700 Subject: [PATCH 019/330] fix comment --- index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/index.d.ts b/index.d.ts index 1d05503..b5f5be8 100644 --- a/index.d.ts +++ b/index.d.ts @@ -461,6 +461,7 @@ declare module 'ldclient-node' { /** * True if the state should include only flags that have been marked for use with the * client-side SDK. By default, all flags are included. + */ clientSideOnly?: boolean; }; From aa39f0e346fd6163361322e2b4f874655d129c63 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Aug 2018 16:06:36 -0700 Subject: [PATCH 020/330] add npm audit to build --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9cc27c7..1d0ed68 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,6 +16,7 @@ node-template: &node-template command: npm test environment: JEST_JUNIT_OUTPUT: "reports/junit/js-test-results.xml" + - run: npm audit - store_test_results: path: reports/junit - store_artifacts: From 2dd2edcedb0dc5318da985e311ce820f55d86998 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Aug 2018 16:08:24 -0700 Subject: [PATCH 021/330] run npm audit fix --- package-lock.json | 5240 +++++++++++++++++++++++---------------------- 1 file changed, 2652 insertions(+), 2588 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d9fa69..e5e628e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,23 +5,42 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.42.tgz", - "integrity": "sha512-L8i94FLSyaLQpRfDo/qqSm8Ndb44zMtXParXo0MebJICG1zoCCL4+GkzUOlB4BNTRSXXQdb3feam/qw7bKPipQ==", + "version": "7.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-rc.2.tgz", + "integrity": "sha512-+cVix+HBNakVp7IU1WReJV8dnJl/yaBA5JRXc758BSrvJCH2hKp1Z0xHIiUaOvxMwKXc3EXGIYhlnx5T+6ofGA==", "dev": true, "requires": { - "@babel/highlight": "7.0.0-beta.42" + "@babel/highlight": "7.0.0-rc.2" } }, "@babel/highlight": { - "version": "7.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.42.tgz", - "integrity": "sha512-X3Ur/A/lIbbP8W0pmwgqtDXIxhQmxPaiwY9SKP7kF9wvZfjZRwMvbJE92ozUhF3UDK3DCKaV7oGqmI1rP/zqWA==", + "version": "7.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-rc.2.tgz", + "integrity": "sha512-96V6XHAh9XHzjmucShCP8tULwXsC446doZ6REaLVdZDPNj3NsWbsC7OBeY+u6UWiFxHTTv6YmA4Veh4wXuucYw==", "dev": true, "requires": { - "chalk": "2.3.2", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } } }, "@types/events": { @@ -39,36 +58,29 @@ "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.6.tgz", "integrity": "sha1-NnTQehOtdrzNpMN9w5CeTpV1fn4=", "requires": { - "@types/events": "1.2.0", - "@types/node": "10.5.2" + "@types/events": "*", + "@types/node": "*" } }, "abab": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", - "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", "dev": true }, - "abbrev": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", - "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", - "dev": true, - "optional": true - }, "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", + "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", "dev": true }, "acorn-globals": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", - "integrity": "sha1-q3FgJdvhfFTT74HTLs4rLZn+JTg=", + "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", "dev": true, "requires": { - "acorn": "5.5.3" + "acorn": "^5.0.0" } }, "ajv": { @@ -76,10 +88,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } }, "align-text": { @@ -88,9 +100,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" } }, "amdefine": { @@ -99,34 +111,29 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, - "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha1-9zIHu4EgfXX9bIPxJa8m7qN4yjA=", - "dev": true - }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "3.1.10", - "normalize-path": "2.1.1" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" }, "dependencies": { "arr-diff": { @@ -142,41 +149,30 @@ "dev": true }, "braces": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz", - "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "kind-of": "6.0.2", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -187,13 +183,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -202,7 +198,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -211,24 +207,64 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -236,17 +272,17 @@ "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -255,7 +291,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -264,7 +300,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -275,10 +311,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -287,49 +323,38 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } }, "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } + "kind-of": "^6.0.0" } }, "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "is-number": { @@ -338,7 +363,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -347,7 +372,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -361,66 +386,48 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.1", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } } } }, "append-transform": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", - "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", - "dev": true, - "requires": { - "default-require-extensions": "1.0.0" - } - }, - "aproba": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", - "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=", - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", "dev": true, - "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.5" + "default-require-extensions": "^2.0.0" } }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { - "sprintf-js": "1.0.3" + "sprintf-js": "~1.0.2" } }, "arr-diff": { @@ -429,13 +436,13 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "1.1.0" + "arr-flatten": "^1.0.1" } }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "arr-union": { @@ -463,9 +470,12 @@ "dev": true }, "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } }, "assert-plus": { "version": "1.0.0", @@ -487,7 +497,7 @@ "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha1-bIw/uCfdQ+45GPJ7gngqt2WKb9k=", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, "async": { @@ -495,13 +505,13 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", "integrity": "sha1-YaKau2/MAm/qd+VtHG7FOnlZUfQ=", "requires": { - "lodash": "4.17.4" + "lodash": "^4.14.0" } }, "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", "dev": true }, "asynckit": { @@ -510,9 +520,9 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.0.tgz", - "integrity": "sha512-SuiKH8vbsOyCALjA/+EINmt/Kdl+TQPrtFgW7XZZcwtryFu9e5kQoX3bjCW6mIvGH1fbeAZZuvwGR5IlBRznGw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, "aws-sign2": { @@ -531,88 +541,52 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, "babel-core": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", - "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-generator": "6.26.1", - "babel-helpers": "6.24.1", - "babel-messages": "6.23.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "convert-source-map": "1.5.1", - "debug": "2.6.9", - "json5": "0.5.1", - "lodash": "4.17.4", - "minimatch": "3.0.4", - "path-is-absolute": "1.0.1", - "private": "0.1.8", - "slash": "1.0.0", - "source-map": "0.5.7" + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" } }, "babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha1-GERAjTuPDTWkBOp6wYDwh6YBvZA=", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.4", - "source-map": "0.5.7", - "trim-right": "1.0.1" + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" } }, "babel-helpers": { @@ -621,18 +595,18 @@ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" } }, "babel-jest": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.3.tgz", - "integrity": "sha512-BgSjmtl3mW3i+VeVHEr9d2zFSAT66G++pJcHQiUjd00pkW+voYXFctIm/indcqOWWXw5a1nUpR1XWszD9fJ1qg==", + "version": "22.4.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.4.tgz", + "integrity": "sha512-A9NB6/lZhYyypR9ATryOSDcqBaqNdzq4U+CN+/wcMsLcmKkPxQEoTKLajGfd3IkxNyVBT8NewUK2nWyGbSzHEQ==", "dev": true, "requires": { - "babel-plugin-istanbul": "4.1.5", - "babel-preset-jest": "22.4.3" + "babel-plugin-istanbul": "^4.1.5", + "babel-preset-jest": "^22.4.4" } }, "babel-messages": { @@ -641,24 +615,25 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-plugin-istanbul": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.5.tgz", - "integrity": "sha1-Z2DN2Xf0EdPhdbsGTyvDJ9mbK24=", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", + "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==", "dev": true, "requires": { - "find-up": "2.1.0", - "istanbul-lib-instrument": "1.10.1", - "test-exclude": "4.2.1" + "babel-plugin-syntax-object-rest-spread": "^6.13.0", + "find-up": "^2.1.0", + "istanbul-lib-instrument": "^1.10.1", + "test-exclude": "^4.2.1" } }, "babel-plugin-jest-hoist": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.3.tgz", - "integrity": "sha512-zhvv4f6OTWy2bYevcJftwGCWXMFe7pqoz41IhMi4xna7xNsX5NygdagsrE0y6kkfuXq8UalwvPwKTyAxME2E/g==", + "version": "22.4.4", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz", + "integrity": "sha512-DUvGfYaAIlkdnygVIEl0O4Av69NtuQWcrjMOv6DODPuhuGLDnbsARz3AwiiI/EkIMMlxQDUcrZ9yoyJvTNjcVQ==", "dev": true }, "babel-plugin-syntax-object-rest-spread": { @@ -668,13 +643,13 @@ "dev": true }, "babel-preset-jest": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-22.4.3.tgz", - "integrity": "sha512-a+M3LTEXTq3gxv0uBN9Qm6ahUl7a8pj923nFbCUdqFUSsf3YrX8Uc+C3MEwji5Af3LiQjSC7w4ooYewlz8HRTA==", + "version": "22.4.4", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-22.4.4.tgz", + "integrity": "sha512-+dxMtOFwnSYWfum0NaEc0O03oSdwBsjx4tMSChRDPGwu/4wSY6Q6ANW3wkjKpJzzguaovRs/DODcT4hbSN8yiA==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "22.4.3", - "babel-plugin-syntax-object-rest-spread": "6.13.0" + "babel-plugin-jest-hoist": "^22.4.4", + "babel-plugin-syntax-object-rest-spread": "^6.13.0" } }, "babel-register": { @@ -683,22 +658,22 @@ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { - "babel-core": "6.26.0", - "babel-runtime": "6.26.0", - "core-js": "2.5.4", - "home-or-tmp": "2.0.0", - "lodash": "4.17.4", - "mkdirp": "0.5.1", - "source-map-support": "0.4.18" + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" }, "dependencies": { "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha1-Aoam3ovkJkEzhZTpfM6nXwosWF8=", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "0.5.7" + "source-map": "^0.5.6" } } } @@ -709,8 +684,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.4", - "regenerator-runtime": "0.11.1" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, "babel-template": { @@ -719,11 +694,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.4" + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" } }, "babel-traverse": { @@ -732,15 +707,15 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.4", - "lodash": "4.17.4" + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" } }, "babel-types": { @@ -749,16 +724,16 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.4", - "to-fast-properties": "1.0.3" + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" } }, "babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, "balanced-match": { @@ -770,16 +745,16 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.1", - "pascalcase": "0.1.1" + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" }, "dependencies": { "define-property": { @@ -788,7 +763,36 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "isobject": { @@ -796,34 +800,31 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "optional": true, "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "2.0.3" + "tweetnacl": "^0.14.3" } }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -833,9 +834,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" } }, "browser-process-hrtime": { @@ -845,9 +846,9 @@ "dev": true }, "browser-resolve": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", - "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", "dev": true, "requires": { "resolve": "1.1.7" @@ -859,9 +860,15 @@ "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", "dev": true, "requires": { - "node-int64": "0.4.0" + "node-int64": "^0.4.0" } }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -871,18 +878,18 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", - "dev": true, - "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" }, "dependencies": { "isobject": { @@ -906,6 +913,15 @@ "dev": true, "optional": true }, + "capture-exit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-1.2.0.tgz", + "integrity": "sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28=", + "dev": true, + "requires": { + "rsvp": "^3.3.3" + } + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -918,8 +934,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" } }, "chai": { @@ -928,23 +944,48 @@ "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", "dev": true, "requires": { - "assertion-error": "1.1.0", - "check-error": "1.0.2", - "deep-eql": "3.0.1", - "get-func-name": "2.0.0", - "pathval": "1.1.0", - "type-detect": "4.0.8" + "assertion-error": "^1.0.1", + "check-error": "^1.0.1", + "deep-eql": "^3.0.0", + "get-func-name": "^2.0.0", + "pathval": "^1.0.0", + "type-detect": "^4.0.0" } }, "chalk": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", - "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.3.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, "check-error": { @@ -954,21 +995,21 @@ "dev": true }, "ci-info": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz", - "integrity": "sha1-cQGTJkuwXHe4yQ0C9aryIhamZ7I=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.4.0.tgz", + "integrity": "sha512-Oqmw2pVfCl8sCL+1QgMywPfdxPJPkC51y4usw0iiE2S9qnEOAqXy8bwl1CpMpnoU39g4iKJTz6QZj+28FvOnjQ==", "dev": true }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "dependencies": { "define-property": { @@ -977,58 +1018,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-descriptor": "^0.1.0" } }, "isobject": { @@ -1036,12 +1026,6 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true } } }, @@ -1052,8 +1036,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", + "center-align": "^0.1.1", + "right-align": "^0.1.1", "wordwrap": "0.0.2" }, "dependencies": { @@ -1079,7 +1063,8 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true }, "collection-visit": { "version": "1.0.0", @@ -1087,23 +1072,23 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", + "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "1.1.1" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", "dev": true }, "colors": { @@ -1116,13 +1101,13 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "compare-versions": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.1.0.tgz", - "integrity": "sha512-4hAxDSBypT/yp2ySFD346So6Ragw5xmBn/e/agIGl3bZr6DLUqnoRZPusxKrXdYRZpgexO9daejmIenlq/wrIQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.3.1.tgz", + "integrity": "sha512-GkIcfJ9sDt4+gS+RWH3X+kR7ezuKdu3fg2oA9nRA8HZoqZwAKv3ml3TyfB9OyV2iFXxCw7q5XfV6SyPbSCT2pw==", "dev": true }, "component-emitter": { @@ -1137,18 +1122,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "content-type-parser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz", - "integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ==", - "dev": true - }, "convert-source-map": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", @@ -1162,9 +1135,9 @@ "dev": true }, "core-js": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.4.tgz", - "integrity": "sha1-8si/GB8qgLkvNgEhQpzmOi8K6uA=", + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", "dev": true }, "core-util-is": { @@ -1178,24 +1151,24 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "4.1.2", - "shebang-command": "1.2.0", - "which": "1.3.0" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, "cssom": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", - "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", + "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", "dev": true }, "cssstyle": { - "version": "0.2.37", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", - "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz", + "integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==", "dev": true, "requires": { - "cssom": "0.3.2" + "cssom": "0.3.x" } }, "cycle": { @@ -1208,13 +1181,37 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.1.tgz", + "integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } } }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -1238,7 +1235,7 @@ "integrity": "sha1-38lARACtHI/gI+faHfHBR8S0RN8=", "dev": true, "requires": { - "type-detect": "4.0.8" + "type-detect": "^4.0.0" } }, "deep-equal": { @@ -1247,13 +1244,6 @@ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", "dev": true }, - "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", - "dev": true, - "optional": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1261,38 +1251,72 @@ "dev": true }, "default-require-extensions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", - "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", "dev": true, "requires": { - "strip-bom": "2.0.0" + "strip-bom": "^3.0.0" } }, "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "foreach": "2.0.5", - "object-keys": "1.0.11" + "object-keys": "^1.0.12" } }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } @@ -1302,29 +1326,15 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true - }, "detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" } }, - "detect-libc": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.2.tgz", - "integrity": "sha1-ca1dIEvxempsqPRQxhRUBm70YeE=", - "dev": true, - "optional": true - }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -1334,16 +1344,16 @@ "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha1-k3RCZEymoxJh7zbj7Gd/6AVYLJA=", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", "dev": true, "requires": { - "webidl-conversions": "4.0.2" + "webidl-conversions": "^4.0.2" } }, "double-ended-queue": { @@ -1352,34 +1362,35 @@ "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" }, "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { - "is-arrayish": "0.2.1" + "is-arrayish": "^0.2.1" } }, "es-abstract": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", - "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { - "es-to-primitive": "1.1.1", - "function-bind": "1.1.1", - "has": "1.0.1", - "is-callable": "1.1.3", - "is-regex": "1.0.4" + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" } }, "es-to-primitive": { @@ -1388,9 +1399,9 @@ "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", "dev": true, "requires": { - "is-callable": "1.1.3", - "is-date-object": "1.0.1", - "is-symbol": "1.0.1" + "is-callable": "^1.1.1", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.1" } }, "escape-string-regexp": { @@ -1400,16 +1411,16 @@ "dev": true }, "escodegen": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", - "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", + "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "dev": true, "requires": { - "esprima": "3.1.3", - "estraverse": "4.2.0", - "esutils": "2.0.2", - "optionator": "0.8.2", - "source-map": "0.6.1" + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" }, "dependencies": { "esprima": { @@ -1421,16 +1432,16 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true } } }, "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "estraverse": { @@ -1446,12 +1457,12 @@ "dev": true }, "exec-sh": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.1.tgz", - "integrity": "sha512-aLt95pexaugVtQerpmE51+4QfWrNc304uez7jvj6fWnN8GeEHpttB8F36n8N7uVhUMbH/1enbxQ9HImZ4w/9qg==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", + "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==", "dev": true, "requires": { - "merge": "1.2.0" + "merge": "^1.2.0" } }, "execa": { @@ -1460,28 +1471,22 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" } }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, "expand-brackets": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "0.1.1" + "is-posix-bracket": "^0.1.0" } }, "expand-range": { @@ -1490,21 +1495,21 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "2.2.3" + "fill-range": "^2.1.0" } }, "expect": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/expect/-/expect-22.4.3.tgz", - "integrity": "sha1-1aKdCg4fshU1V8rvJnTUVH6RRnQ=", + "integrity": "sha512-XcNXEPehqn8b/jm8FYotdX0YrXn36qp4HWlrVT4ktwQas1l1LPxiVWncYnnL2eyMtKAmVIaG0XAp0QlrqJaxaA==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "jest-diff": "22.4.3", - "jest-get-type": "22.4.3", - "jest-matcher-utils": "22.4.3", - "jest-message-util": "22.4.3", - "jest-regex-util": "22.4.3" + "ansi-styles": "^3.2.0", + "jest-diff": "^22.4.3", + "jest-get-type": "^22.4.3", + "jest-matcher-utils": "^22.4.3", + "jest-message-util": "^22.4.3", + "jest-regex-util": "^22.4.3" } }, "extend": { @@ -1518,17 +1523,17 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -1539,7 +1544,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "extsprintf": { @@ -1574,7 +1579,7 @@ "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", "dev": true, "requires": { - "bser": "2.0.0" + "bser": "^2.0.0" } }, "filename-regex": { @@ -1589,21 +1594,21 @@ "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", "dev": true, "requires": { - "glob": "7.1.2", - "minimatch": "3.0.4" + "glob": "^7.0.3", + "minimatch": "^3.0.3" } }, "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" } }, "find-up": { @@ -1612,7 +1617,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "for-in": { @@ -1627,15 +1632,9 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "1.0.2" + "for-in": "^1.0.1" } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -1646,9 +1645,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { - "asynckit": "0.4.0", + "asynckit": "^0.4.0", "combined-stream": "1.0.6", - "mime-types": "2.1.17" + "mime-types": "^2.1.12" }, "dependencies": { "combined-stream": { @@ -1656,7 +1655,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } } } @@ -1667,7 +1666,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "0.2.2" + "map-cache": "^0.2.2" } }, "fs.realpath": { @@ -1677,293 +1676,544 @@ "dev": true }, "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", "dev": true, "optional": true, "requires": { - "nan": "2.10.0", - "node-pre-gyp": "0.6.39" + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" }, "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true }, - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, "requires": { - "hoek": "2.16.3" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, "requires": { - "boom": "2.10.1" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "extsprintf": { + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=" + "bundled": true, + "dev": true, + "optional": true }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" + "ms": "2.0.0" } }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" + "minipass": "^2.2.1" } }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "^2.1.0" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" + "minimatch": "^3.0.4" } }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, "requires": { - "number-is-nan": "1.0.1" + "brace-expansion": "^1.1.7" } }, - "mime-db": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=" + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" + } }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" + "minipass": "^2.2.1" } }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, "requires": { - "hoek": "2.16.3" + "minimist": "0.0.8" } }, - "string-width": { + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, "requires": { - "ansi-regex": "2.1.1" + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } } }, - "verror": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, "requires": { - "extsprintf": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } - } - } - }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, "dev": true, - "optional": true, - "requires": { - "number-is-nan": "1.0.1" - } + "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, "dev": true, "optional": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, "dev": true, + "optional": true, "requires": { - "ansi-regex": "2.1.1" + "string-width": "^1.0.2" } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true } } }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, "get-func-name": { @@ -1989,21 +2239,21 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "glob-base": { @@ -2012,8 +2262,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" } }, "glob-parent": { @@ -2022,13 +2272,13 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "^2.0.0" } }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, "graceful-fs": { @@ -2049,10 +2299,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" }, "dependencies": { "async": { @@ -2067,7 +2317,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -2082,17 +2332,17 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" + "ajv": "^5.1.0", + "har-schema": "^2.0.0" } }, "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "1.1.1" + "function-bind": "^1.1.1" } }, "has-ansi": { @@ -2101,7 +2351,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "has-flag": { @@ -2110,22 +2360,15 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" }, "dependencies": { "isobject": { @@ -2142,8 +2385,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "is-number": { @@ -2152,7 +2395,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -2161,7 +2404,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -2172,7 +2415,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -2188,23 +2431,23 @@ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "dev": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" } }, "hosted-git-info": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", - "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", "dev": true }, "html-encoding-sniffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha1-5w2EuU2lOqN14R/jo1G+ZkLKRvg=", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", "dev": true, "requires": { - "whatwg-encoding": "1.0.3" + "whatwg-encoding": "^1.0.1" } }, "http-signature": { @@ -2212,16 +2455,19 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=", - "dev": true + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } }, "import-local": { "version": "1.0.0", @@ -2229,8 +2475,8 @@ "integrity": "sha1-Xk/9wD9P5sAJxnKb6yljHC+CJ7w=", "dev": true, "requires": { - "pkg-dir": "2.0.0", - "resolve-cwd": "2.0.0" + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" } }, "imurmurhash": { @@ -2245,8 +2491,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -2255,20 +2501,13 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "ini": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", - "dev": true, - "optional": true - }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { - "loose-envify": "1.3.1" + "loose-envify": "^1.0.0" } }, "invert-kv": { @@ -2278,20 +2517,12 @@ "dev": true }, "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } + "kind-of": "^3.0.2" } }, "is-arrayish": { @@ -2303,7 +2534,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-builtin-module": { @@ -2312,39 +2543,31 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "1.1.1" + "builtin-modules": "^1.0.0" } }, "is-callable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, "is-ci": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", - "integrity": "sha1-JH5BYueGDOu9rzC3dNawrH3P56U=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.0.tgz", + "integrity": "sha512-plgvKjQtalH2P3Gytb7L61Lmz95g2DlpzFiQyRSFew8WoJKxtKRzrZMeyRN2supblm3Psc8OQGy7Xjb6XG11jw==", "dev": true, "requires": { - "ci-info": "1.1.3" + "ci-info": "^1.3.0" } }, "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } + "kind-of": "^3.0.2" } }, "is-date-object": { @@ -2354,20 +2577,20 @@ "dev": true }, "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "dependencies": { "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -2384,7 +2607,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "2.0.0" + "is-primitive": "^2.0.0" } }, "is-extendable": { @@ -2405,7 +2628,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-fullwidth-code-point": { @@ -2426,7 +2649,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "is-number": { @@ -2435,33 +2658,16 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "3.2.2" - } - }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", - "dev": true, - "requires": { - "is-number": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } + "kind-of": "^3.0.2" } }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" }, "dependencies": { "isobject": { @@ -2490,7 +2696,7 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "1.0.1" + "has": "^1.0.1" } }, "is-stream": { @@ -2519,7 +2725,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isarray": { @@ -2548,90 +2754,46 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, - "istanbul-api": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.1.tgz", - "integrity": "sha1-TDsF0YwAFtECLgebmNyCxA9IiVQ=", - "dev": true, - "requires": { - "async": "2.6.0", - "compare-versions": "3.1.0", - "fileset": "2.0.3", - "istanbul-lib-coverage": "1.2.0", - "istanbul-lib-hook": "1.2.0", - "istanbul-lib-instrument": "1.10.1", - "istanbul-lib-report": "1.1.4", - "istanbul-lib-source-maps": "1.2.4", - "istanbul-reports": "1.3.0", - "js-yaml": "3.11.0", - "mkdirp": "0.5.1", - "once": "1.4.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "istanbul-lib-source-maps": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.4.tgz", - "integrity": "sha512-UzuK0g1wyQijiaYQxj/CdNycFhAd2TLtO2obKQMTZrZ1jzEMRY3rvpASEKkaxbRR6brvdovfA03znPa/pXcejg==", - "dev": true, - "requires": { - "debug": "3.1.0", - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "source-map": "0.5.7" - } - } - } - }, "istanbul-lib-coverage": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz", - "integrity": "sha1-99jy5CuX43/nlhFMsPnWi146Q0E=", + "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==", "dev": true }, "istanbul-lib-hook": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.0.tgz", - "integrity": "sha512-p3En6/oGkFQV55Up8ZPC2oLxvgSxD8CzA0yBrhRZSh3pfv3OFj9aSGVC0yoerAi/O4u7jUVnOGVX1eVFM+0tmQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.1.tgz", + "integrity": "sha512-eLAMkPG9FU0v5L02lIkcj/2/Zlz9OuluaXikdr5iStk8FDbSwAixTK9TkYxbF0eNnzAJTwM2fkV2A1tpsIp4Jg==", "dev": true, "requires": { - "append-transform": "0.4.0" + "append-transform": "^1.0.0" } }, "istanbul-lib-instrument": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz", - "integrity": "sha1-cktLbKzrqGktPx+dByfiecQBr3s=", + "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", "dev": true, "requires": { - "babel-generator": "6.26.1", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "istanbul-lib-coverage": "1.2.0", - "semver": "5.5.0" + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.0", + "semver": "^5.3.0" } }, "istanbul-lib-report": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.4.tgz", - "integrity": "sha1-6IbN9QXE672OCZ5DlqkNCijirLU=", + "integrity": "sha512-Azqvq5tT0U09nrncK3q82e/Zjkxa4tkFZv7E6VcqP0QCPn6oNljDPfrZEC/umNXds2t7b8sRJfs6Kmpzt8m2kA==", "dev": true, "requires": { - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "path-parse": "1.0.5", - "supports-color": "3.2.3" + "istanbul-lib-coverage": "^1.2.0", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" }, "dependencies": { "has-flag": { @@ -2646,206 +2808,587 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "1.0.0" + "has-flag": "^1.0.0" } } } }, - "istanbul-lib-source-maps": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz", - "integrity": "sha1-IPtUsU4Us/tu22rKNXH9IUPbROY=", + "istanbul-reports": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.3.0.tgz", + "integrity": "sha512-y2Z2IMqE1gefWUaVjrBm0mSKvUkaBy9Vqz8iwr/r40Y9hBbIteH5wqHG/9DLTfJ9xUnUT2j7A3+VVJ6EaYBllA==", + "dev": true, + "requires": { + "handlebars": "^4.0.3" + } + }, + "jest": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-22.4.3.tgz", + "integrity": "sha1-ImH0sRfcRtmkoaZz0hUJWN7pLxY=", "dev": true, "requires": { - "debug": "3.1.0", - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "source-map": "0.5.7" + "import-local": "^1.0.0", + "jest-cli": "^22.4.3" }, "dependencies": { + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" } + }, + "jest-cli": { + "version": "22.4.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-22.4.4.tgz", + "integrity": "sha512-I9dsgkeyjVEEZj9wrGrqlH+8OlNob9Iptyl+6L5+ToOLJmHm4JwOPatin1b2Bzp5R5YRQJ+oiedx7o1H7wJzhA==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "import-local": "^1.0.0", + "is-ci": "^1.0.10", + "istanbul-api": "^1.1.14", + "istanbul-lib-coverage": "^1.1.1", + "istanbul-lib-instrument": "^1.8.0", + "istanbul-lib-source-maps": "^1.2.1", + "jest-changed-files": "^22.2.0", + "jest-config": "^22.4.4", + "jest-environment-jsdom": "^22.4.1", + "jest-get-type": "^22.1.0", + "jest-haste-map": "^22.4.2", + "jest-message-util": "^22.4.0", + "jest-regex-util": "^22.1.0", + "jest-resolve-dependencies": "^22.1.0", + "jest-runner": "^22.4.4", + "jest-runtime": "^22.4.4", + "jest-snapshot": "^22.4.0", + "jest-util": "^22.4.1", + "jest-validate": "^22.4.4", + "jest-worker": "^22.2.2", + "micromatch": "^2.3.11", + "node-notifier": "^5.2.1", + "realpath-native": "^1.0.0", + "rimraf": "^2.5.4", + "slash": "^1.0.0", + "string-length": "^2.0.0", + "strip-ansi": "^4.0.0", + "which": "^1.2.12", + "yargs": "^10.0.3" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "is-ci": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.0.tgz", + "integrity": "sha512-plgvKjQtalH2P3Gytb7L61Lmz95g2DlpzFiQyRSFew8WoJKxtKRzrZMeyRN2supblm3Psc8OQGy7Xjb6XG11jw==", + "dev": true, + "requires": { + "ci-info": "^1.3.0" + } + }, + "istanbul-api": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.1.tgz", + "integrity": "sha512-duj6AlLcsWNwUpfyfHt0nWIeRiZpuShnP40YTxOGQgtaN8fd6JYSxsvxUphTDy8V5MfDXo4s/xVCIIvVCO808g==", + "dev": true, + "requires": { + "async": "^2.1.4", + "compare-versions": "^3.1.0", + "fileset": "^2.0.2", + "istanbul-lib-coverage": "^1.2.0", + "istanbul-lib-hook": "^1.2.0", + "istanbul-lib-instrument": "^1.10.1", + "istanbul-lib-report": "^1.1.4", + "istanbul-lib-source-maps": "^1.2.4", + "istanbul-reports": "^1.3.0", + "js-yaml": "^3.7.0", + "mkdirp": "^0.5.1", + "once": "^1.4.0" + }, + "dependencies": { + "istanbul-lib-source-maps": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.5.tgz", + "integrity": "sha512-8O2T/3VhrQHn0XcJbP1/GN7kXMiRAlPi+fj3uEHrjBD8Oz7Py0prSC25C09NuAZS6bgW1NNKAvCSHZXB0irSGA==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "istanbul-lib-coverage": "^1.2.0", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" + } + } + } + }, + "istanbul-lib-coverage": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz", + "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz", + "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", + "dev": true, + "requires": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.0", + "semver": "^5.3.0" + } + }, + "istanbul-lib-source-maps": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz", + "integrity": "sha512-fDa0hwU/5sDXwAklXgAoCJCOsFsBplVQ6WBldz5UwaqOzmDhUK4nfuR7/G//G2lERlblUNJB8P6e8cXq3a7MlA==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "istanbul-lib-coverage": "^1.1.2", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" + } + }, + "jest-changed-files": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-22.4.3.tgz", + "integrity": "sha512-83Dh0w1aSkUNFhy5d2dvqWxi/y6weDwVVLU6vmK0cV9VpRxPzhTeGimbsbRDSnEoszhF937M4sDLLeS7Cu/Tmw==", + "dev": true, + "requires": { + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "22.4.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-22.4.4.tgz", + "integrity": "sha512-9CKfo1GC4zrXSoMLcNeDvQBfgtqGTB1uP8iDIZ97oB26RCUb886KkKWhVcpyxVDOUxbhN+uzcBCeFe7w+Iem4A==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^22.4.1", + "jest-environment-node": "^22.4.1", + "jest-get-type": "^22.1.0", + "jest-jasmine2": "^22.4.4", + "jest-regex-util": "^22.1.0", + "jest-resolve": "^22.4.2", + "jest-util": "^22.4.1", + "jest-validate": "^22.4.4", + "pretty-format": "^22.4.0" + } + }, + "jest-environment-jsdom": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz", + "integrity": "sha512-FviwfR+VyT3Datf13+ULjIMO5CSeajlayhhYQwpzgunswoaLIPutdbrnfUHEMyJCwvqQFaVtTmn9+Y8WCt6n1w==", + "dev": true, + "requires": { + "jest-mock": "^22.4.3", + "jest-util": "^22.4.3", + "jsdom": "^11.5.1" + } + }, + "jest-get-type": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "dev": true + }, + "jest-haste-map": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-22.4.3.tgz", + "integrity": "sha512-4Q9fjzuPVwnaqGKDpIsCSoTSnG3cteyk2oNVjBX12HHOaF1oxql+uUiqZb5Ndu7g/vTZfdNwwy4WwYogLh29DQ==", + "dev": true, + "requires": { + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.1.11", + "jest-docblock": "^22.4.3", + "jest-serializer": "^22.4.3", + "jest-worker": "^22.4.3", + "micromatch": "^2.3.11", + "sane": "^2.0.0" + } + }, + "jest-message-util": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz", + "integrity": "sha512-iAMeKxhB3Se5xkSjU0NndLLCHtP4n+GtCqV0bISKA5dmOXQfEbdEmYiu2qpnWBDCQdEafNDDU6Q+l6oBMd/+BA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0-beta.35", + "chalk": "^2.0.1", + "micromatch": "^2.3.11", + "slash": "^1.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-regex-util": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-22.4.3.tgz", + "integrity": "sha512-LFg1gWr3QinIjb8j833bq7jtQopiwdAs67OGfkPrvy7uNUbVMfTXXcOKXJaeY5GgjobELkKvKENqq1xrUectWg==", + "dev": true + }, + "jest-resolve-dependencies": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-22.4.3.tgz", + "integrity": "sha512-06czCMVToSN8F2U4EvgSB1Bv/56gc7MpCftZ9z9fBgUQM7dzHGCMBsyfVA6dZTx8v0FDcnALf7hupeQxaBCvpA==", + "dev": true, + "requires": { + "jest-regex-util": "^22.4.3" + } + }, + "jest-runner": { + "version": "22.4.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-22.4.4.tgz", + "integrity": "sha512-5S/OpB51igQW9xnkM5Tgd/7ZjiAuIoiJAVtvVTBcEBiXBIFzWM3BAMPBM19FX68gRV0KWyFuGKj0EY3M3aceeQ==", + "dev": true, + "requires": { + "exit": "^0.1.2", + "jest-config": "^22.4.4", + "jest-docblock": "^22.4.0", + "jest-haste-map": "^22.4.2", + "jest-jasmine2": "^22.4.4", + "jest-leak-detector": "^22.4.0", + "jest-message-util": "^22.4.0", + "jest-runtime": "^22.4.4", + "jest-util": "^22.4.1", + "jest-worker": "^22.2.2", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "22.4.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-22.4.4.tgz", + "integrity": "sha512-WRTj9m///npte1YjuphCYX7GRY/c2YvJImU9t7qOwFcqHr4YMzmX6evP/3Sehz5DKW2Vi8ONYPCFWe36JVXxfw==", + "dev": true, + "requires": { + "babel-core": "^6.0.0", + "babel-jest": "^22.4.4", + "babel-plugin-istanbul": "^4.1.5", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "exit": "^0.1.2", + "graceful-fs": "^4.1.11", + "jest-config": "^22.4.4", + "jest-haste-map": "^22.4.2", + "jest-regex-util": "^22.1.0", + "jest-resolve": "^22.4.2", + "jest-util": "^22.4.1", + "jest-validate": "^22.4.4", + "json-stable-stringify": "^1.0.1", + "micromatch": "^2.3.11", + "realpath-native": "^1.0.0", + "slash": "^1.0.0", + "strip-bom": "3.0.0", + "write-file-atomic": "^2.1.0", + "yargs": "^10.0.3" + } + }, + "jest-snapshot": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-22.4.3.tgz", + "integrity": "sha512-JXA0gVs5YL0HtLDCGa9YxcmmV2LZbwJ+0MfyXBBc5qpgkEYITQFJP7XNhcHFbUvRiniRpRbGVfJrOoYhhGE0RQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^22.4.3", + "jest-matcher-utils": "^22.4.3", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^22.4.3" + } + }, + "jest-util": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-22.4.3.tgz", + "integrity": "sha512-rfDfG8wyC5pDPNdcnAlZgwKnzHvZDu8Td2NJI/jAGKEGxJPYiE4F0ss/gSAkG4778Y23Hvbz+0GMrDJTeo7RjQ==", + "dev": true, + "requires": { + "callsites": "^2.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.11", + "is-ci": "^1.0.10", + "jest-message-util": "^22.4.3", + "mkdirp": "^0.5.1", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "jest-validate": { + "version": "22.4.4", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-22.4.4.tgz", + "integrity": "sha512-dmlf4CIZRGvkaVg3fa0uetepcua44DHtktHm6rcoNVtYlpwe6fEJRkMFsaUVcFHLzbuBJ2cPw9Gl9TKfnzMVwg==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-config": "^22.4.4", + "jest-get-type": "^22.1.0", + "leven": "^2.1.0", + "pretty-format": "^22.4.0" + } + }, + "jest-worker": { + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-22.4.3.tgz", + "integrity": "sha512-B1ucW4fI8qVAuZmicFxI1R3kr2fNeYJyvIQ1rKcuLYnenFV5K5aMbxFj6J0i00Ju83S8jP2d7Dz14+AvbIHRYQ==", + "dev": true, + "requires": { + "merge-stream": "^1.0.1" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "node-notifier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.2.1.tgz", + "integrity": "sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "semver": "^5.4.1", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "realpath-native": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.0.1.tgz", + "integrity": "sha512-W14EcXuqUvKP8dkWkD7B95iMy77lpMnlFXbbk409bQtNCbeu0kvRE5reo+yIZ3JXxg6frbGsz2DLQ39lrCB40g==", + "dev": true, + "requires": { + "util.promisify": "^1.0.0" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "dev": true, + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yargs": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", + "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^8.1.0" + } + } + } } } }, - "istanbul-reports": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.3.0.tgz", - "integrity": "sha1-LzIugeHZUgdnWX3KPCCgzOiaNVQ=", - "dev": true, - "requires": { - "handlebars": "4.0.11" - } - }, - "jest": { + "jest-diff": { "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-22.4.3.tgz", - "integrity": "sha1-ImH0sRfcRtmkoaZz0hUJWN7pLxY=", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz", + "integrity": "sha512-/QqGvCDP5oZOF6PebDuLwrB2BMD8ffJv6TAGAdEVuDx1+uEgrHpSFrfrOiMRx2eJ1hgNjlQrOQEHetVwij90KA==", "dev": true, "requires": { - "import-local": "1.0.0", - "jest-cli": "22.4.3" + "chalk": "^2.0.1", + "diff": "^3.2.0", + "jest-get-type": "^22.4.3", + "pretty-format": "^22.4.3" }, "dependencies": { - "jest-cli": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-22.4.3.tgz", - "integrity": "sha512-IiHybF0DJNqZPsbjn4Cy4vcqcmImpoFwNFnkehzVw8lTUSl4axZh5DHewu5bdpZF2Y5gUqFKYzH0FH4Qx2k+UA==", + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-escapes": "3.1.0", - "chalk": "2.3.2", - "exit": "0.1.2", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "import-local": "1.0.0", - "is-ci": "1.1.0", - "istanbul-api": "1.3.1", - "istanbul-lib-coverage": "1.2.0", - "istanbul-lib-instrument": "1.10.1", - "istanbul-lib-source-maps": "1.2.3", - "jest-changed-files": "22.4.3", - "jest-config": "22.4.3", - "jest-environment-jsdom": "22.4.3", - "jest-get-type": "22.4.3", - "jest-haste-map": "22.4.3", - "jest-message-util": "22.4.3", - "jest-regex-util": "22.4.3", - "jest-resolve-dependencies": "22.4.3", - "jest-runner": "22.4.3", - "jest-runtime": "22.4.3", - "jest-snapshot": "22.4.3", - "jest-util": "22.4.3", - "jest-validate": "22.4.3", - "jest-worker": "22.4.3", - "micromatch": "2.3.11", - "node-notifier": "5.2.1", - "realpath-native": "1.0.0", - "rimraf": "2.6.2", - "slash": "1.0.0", - "string-length": "2.0.0", - "strip-ansi": "4.0.0", - "which": "1.3.0", - "yargs": "10.1.2" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } } } }, - "jest-changed-files": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-22.4.3.tgz", - "integrity": "sha1-iIIYHgIsOL1GouTRjUTRnZCpD7I=", - "dev": true, - "requires": { - "throat": "4.1.0" - } - }, - "jest-config": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-22.4.3.tgz", - "integrity": "sha512-KSg3EOToCgkX+lIvenKY7J8s426h6ahXxaUFJxvGoEk0562Z6inWj1TnKoGycTASwiLD+6kSYFALcjdosq9KIQ==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "glob": "7.1.2", - "jest-environment-jsdom": "22.4.3", - "jest-environment-node": "22.4.3", - "jest-get-type": "22.4.3", - "jest-jasmine2": "22.4.3", - "jest-regex-util": "22.4.3", - "jest-resolve": "22.4.3", - "jest-util": "22.4.3", - "jest-validate": "22.4.3", - "pretty-format": "22.4.3" - } - }, - "jest-diff": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz", - "integrity": "sha1-4YzD/v8K7vFZ0CMQ8mhtQGU3gDA=", - "dev": true, - "requires": { - "chalk": "2.3.2", - "diff": "3.5.0", - "jest-get-type": "22.4.3", - "pretty-format": "22.4.3" - } - }, "jest-docblock": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-22.4.3.tgz", - "integrity": "sha1-UIhvEytCsoDJA8WSNzu26Tu2ixk=", - "dev": true, - "requires": { - "detect-newline": "2.1.0" - } - }, - "jest-environment-jsdom": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz", - "integrity": "sha1-1n2qQVXjNRauzdNa/YLUq/D6ih4=", + "integrity": "sha512-uPKBEAw7YrEMcXueMKZXn/rbMxBiSv48fSqy3uEnmgOlQhSX+lthBqHb1fKWNVmFqAp9E/RsSdBfiV31LbzaOg==", "dev": true, "requires": { - "jest-mock": "22.4.3", - "jest-util": "22.4.3", - "jsdom": "11.6.2" + "detect-newline": "^2.1.0" } }, "jest-environment-node": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-22.4.3.tgz", - "integrity": "sha1-VMTqo3TIPdUqnah1m+FOvh0LkSk=", + "integrity": "sha512-reZl8XF6t/lMEuPWwo9OLfttyC26A5AMgDyEQ6DBgZuyfyeNUzYT8BFo6uxCCP/Av/b7eb9fTi3sIHFPBzmlRA==", "dev": true, "requires": { - "jest-mock": "22.4.3", - "jest-util": "22.4.3" + "jest-mock": "^22.4.3", + "jest-util": "^22.4.3" } }, "jest-get-type": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", - "integrity": "sha1-46hQTYR5NC3UQgI2syKGnxiQDOQ=", + "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", "dev": true }, - "jest-haste-map": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-22.4.3.tgz", - "integrity": "sha1-JYQvoro1AgB2esJ/ZY1YudXC4gs=", - "dev": true, - "requires": { - "fb-watchman": "2.0.0", - "graceful-fs": "4.1.11", - "jest-docblock": "22.4.3", - "jest-serializer": "22.4.3", - "jest-worker": "22.4.3", - "micromatch": "2.3.11", - "sane": "2.5.0" - } - }, "jest-jasmine2": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-22.4.3.tgz", - "integrity": "sha512-yZCPCJUcEY6R5KJB/VReo1AYI2b+5Ky+C+JA1v34jndJsRcLpU4IZX4rFJn7yDTtdNbO/nNqg+3SDIPNH2ecnw==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "co": "4.6.0", - "expect": "22.4.3", - "graceful-fs": "4.1.11", - "is-generator-fn": "1.0.0", - "jest-diff": "22.4.3", - "jest-matcher-utils": "22.4.3", - "jest-message-util": "22.4.3", - "jest-snapshot": "22.4.3", - "jest-util": "22.4.3", - "source-map-support": "0.5.4" + "version": "22.4.4", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-22.4.4.tgz", + "integrity": "sha512-nK3vdUl50MuH7vj/8at7EQVjPGWCi3d5+6aCi7Gxy/XMWdOdbH1qtO/LjKbqD8+8dUAEH+BVVh7HkjpCWC1CSw==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^22.4.0", + "graceful-fs": "^4.1.11", + "is-generator-fn": "^1.0.0", + "jest-diff": "^22.4.0", + "jest-matcher-utils": "^22.4.0", + "jest-message-util": "^22.4.0", + "jest-snapshot": "^22.4.0", + "jest-util": "^22.4.1", + "source-map-support": "^0.5.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } } }, "jest-junit": { @@ -2854,195 +3397,172 @@ "integrity": "sha1-9MQ1jlKGNkpDJNwUq93VJqrfvTg=", "dev": true, "requires": { - "mkdirp": "0.5.1", - "strip-ansi": "4.0.0", - "xml": "1.0.1" + "mkdirp": "^0.5.1", + "strip-ansi": "^4.0.0", + "xml": "^1.0.1" } }, "jest-leak-detector": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-22.4.3.tgz", - "integrity": "sha1-K3smMQOvroxStrkSQaLeQBF+WzU=", + "integrity": "sha512-NZpR/Ls7+ndO57LuXROdgCGz2RmUdC541tTImL9bdUtU3WadgFGm0yV+Ok4Fuia/1rLAn5KaJ+i76L6e3zGJYQ==", "dev": true, "requires": { - "pretty-format": "22.4.3" + "pretty-format": "^22.4.3" } }, "jest-matcher-utils": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz", - "integrity": "sha1-RjL+Qo68c+vBlNPHtl03sWH3EP8=", + "integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==", "dev": true, "requires": { - "chalk": "2.3.2", - "jest-get-type": "22.4.3", - "pretty-format": "22.4.3" + "chalk": "^2.0.1", + "jest-get-type": "^22.4.3", + "pretty-format": "^22.4.3" + }, + "dependencies": { + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } } }, "jest-message-util": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz", - "integrity": "sha1-zz04qv5L792/xFXlfWXVI545nrc=", + "integrity": "sha512-iAMeKxhB3Se5xkSjU0NndLLCHtP4n+GtCqV0bISKA5dmOXQfEbdEmYiu2qpnWBDCQdEafNDDU6Q+l6oBMd/+BA==", "dev": true, "requires": { - "@babel/code-frame": "7.0.0-beta.42", - "chalk": "2.3.2", - "micromatch": "2.3.11", - "slash": "1.0.0", - "stack-utils": "1.0.1" + "@babel/code-frame": "^7.0.0-beta.35", + "chalk": "^2.0.1", + "micromatch": "^2.3.11", + "slash": "^1.0.0", + "stack-utils": "^1.0.1" + }, + "dependencies": { + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } } }, "jest-mock": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-22.4.3.tgz", - "integrity": "sha1-9jui8HoVEXcs3Hl5czOX33cKq8c=", + "integrity": "sha512-+4R6mH5M1G4NK16CKg9N1DtCaFmuxhcIqF4lQK/Q1CIotqMs/XBemfpDPeVZBFow6iyUNu6EBT9ugdNOTT5o5Q==", "dev": true }, "jest-regex-util": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-22.4.3.tgz", - "integrity": "sha1-qCbrGRzfIlAhmMVAGh/ATenO9a8=", + "integrity": "sha512-LFg1gWr3QinIjb8j833bq7jtQopiwdAs67OGfkPrvy7uNUbVMfTXXcOKXJaeY5GgjobELkKvKENqq1xrUectWg==", "dev": true }, "jest-resolve": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-22.4.3.tgz", - "integrity": "sha1-DOnUOMhDgimqm5FpaOxrBcGrtOo=", - "dev": true, - "requires": { - "browser-resolve": "1.11.2", - "chalk": "2.3.2" - } - }, - "jest-resolve-dependencies": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-22.4.3.tgz", - "integrity": "sha1-4iVqWoRnMtw5acty88mtdyWoGV4=", - "dev": true, - "requires": { - "jest-regex-util": "22.4.3" - } - }, - "jest-runner": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-22.4.3.tgz", - "integrity": "sha512-U7PLlQPRlWNbvOHWOrrVay9sqhBJmiKeAdKIkvX4n1G2tsvzLlf77nBD28GL1N6tGv4RmuTfI8R8JrkvCa+IBg==", + "integrity": "sha512-u3BkD/MQBmwrOJDzDIaxpyqTxYH+XqAXzVJP51gt29H8jpj3QgKof5GGO2uPGKGeA1yTMlpbMs1gIQ6U4vcRhw==", "dev": true, "requires": { - "exit": "0.1.2", - "jest-config": "22.4.3", - "jest-docblock": "22.4.3", - "jest-haste-map": "22.4.3", - "jest-jasmine2": "22.4.3", - "jest-leak-detector": "22.4.3", - "jest-message-util": "22.4.3", - "jest-runtime": "22.4.3", - "jest-util": "22.4.3", - "jest-worker": "22.4.3", - "throat": "4.1.0" - } - }, - "jest-runtime": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-22.4.3.tgz", - "integrity": "sha512-Eat/esQjevhx9BgJEC8udye+FfoJ2qvxAZfOAWshYGS22HydHn5BgsvPdTtt9cp0fSl5LxYOFA1Pja9Iz2Zt8g==", - "dev": true, - "requires": { - "babel-core": "6.26.0", - "babel-jest": "22.4.3", - "babel-plugin-istanbul": "4.1.5", - "chalk": "2.3.2", - "convert-source-map": "1.5.1", - "exit": "0.1.2", - "graceful-fs": "4.1.11", - "jest-config": "22.4.3", - "jest-haste-map": "22.4.3", - "jest-regex-util": "22.4.3", - "jest-resolve": "22.4.3", - "jest-util": "22.4.3", - "jest-validate": "22.4.3", - "json-stable-stringify": "1.0.1", - "micromatch": "2.3.11", - "realpath-native": "1.0.0", - "slash": "1.0.0", - "strip-bom": "3.0.0", - "write-file-atomic": "2.3.0", - "yargs": "10.1.2" + "browser-resolve": "^1.11.2", + "chalk": "^2.0.1" }, "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } } } }, "jest-serializer": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-22.4.3.tgz", - "integrity": "sha1-pnm4Gn8RHkdmI19PDEbSMO4PdDY=", + "integrity": "sha512-uPaUAppx4VUfJ0QDerpNdF43F68eqKWCzzhUlKNDsUPhjOon7ZehR4C809GCqh765FoMRtTVUVnGvIoskkYHiw==", "dev": true }, "jest-snapshot": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-22.4.3.tgz", - "integrity": "sha1-tcm0KEb/ufrMt2uEExW6Z4hzYtI=", + "integrity": "sha512-JXA0gVs5YL0HtLDCGa9YxcmmV2LZbwJ+0MfyXBBc5qpgkEYITQFJP7XNhcHFbUvRiniRpRbGVfJrOoYhhGE0RQ==", "dev": true, "requires": { - "chalk": "2.3.2", - "jest-diff": "22.4.3", - "jest-matcher-utils": "22.4.3", - "mkdirp": "0.5.1", - "natural-compare": "1.4.0", - "pretty-format": "22.4.3" + "chalk": "^2.0.1", + "jest-diff": "^22.4.3", + "jest-matcher-utils": "^22.4.3", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^22.4.3" + }, + "dependencies": { + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } } }, "jest-util": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-22.4.3.tgz", - "integrity": "sha1-xw/sjuxIfDexCwgJ3AZKfs9qr6w=", + "integrity": "sha512-rfDfG8wyC5pDPNdcnAlZgwKnzHvZDu8Td2NJI/jAGKEGxJPYiE4F0ss/gSAkG4778Y23Hvbz+0GMrDJTeo7RjQ==", "dev": true, "requires": { - "callsites": "2.0.0", - "chalk": "2.3.2", - "graceful-fs": "4.1.11", - "is-ci": "1.1.0", - "jest-message-util": "22.4.3", - "mkdirp": "0.5.1", - "source-map": "0.6.1" + "callsites": "^2.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.11", + "is-ci": "^1.0.10", + "jest-message-util": "^22.4.3", + "mkdirp": "^0.5.1", + "source-map": "^0.6.0" }, "dependencies": { + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } }, - "jest-validate": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-22.4.3.tgz", - "integrity": "sha512-CfFM18W3GSP/xgmA4UouIx0ljdtfD2mjeBC6c89Gg17E44D4tQhAcTrZmf9djvipwU30kSTnk6CzcxdCCeSXfA==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "jest-config": "22.4.3", - "jest-get-type": "22.4.3", - "leven": "2.1.0", - "pretty-format": "22.4.3" - } - }, - "jest-worker": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-22.4.3.tgz", - "integrity": "sha1-XEIUF8uhwKv2S/Vr1ft5aNed1As=", - "dev": true, - "requires": { - "merge-stream": "1.0.1" - } - }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -3050,13 +3570,13 @@ "dev": true }, "js-yaml": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", - "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "requires": { - "argparse": "1.0.10", - "esprima": "4.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, "jsbn": { @@ -3066,37 +3586,49 @@ "optional": true }, "jsdom": { - "version": "11.6.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.6.2.tgz", - "integrity": "sha512-pAeZhpbSlUp5yQcS6cBQJwkbzmv4tWFaYxHbFVSxzXefqjvtRA851Z5N2P+TguVG9YeUDcgb8pdeVQRJh0XR3Q==", - "dev": true, - "requires": { - "abab": "1.0.4", - "acorn": "5.5.3", - "acorn-globals": "4.1.0", - "array-equal": "1.0.0", - "browser-process-hrtime": "0.1.2", - "content-type-parser": "1.0.2", - "cssom": "0.3.2", - "cssstyle": "0.2.37", - "domexception": "1.0.1", - "escodegen": "1.9.1", - "html-encoding-sniffer": "1.0.2", - "left-pad": "1.2.0", - "nwmatcher": "1.4.4", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", "parse5": "4.0.0", - "pn": "1.1.0", - "request": "2.87.0", - "request-promise-native": "1.0.5", - "sax": "1.2.4", - "symbol-tree": "3.2.2", - "tough-cookie": "2.3.3", - "w3c-hr-time": "1.0.1", - "webidl-conversions": "4.0.2", - "whatwg-encoding": "1.0.3", - "whatwg-url": "6.4.0", - "ws": "4.1.0", - "xml-name-validator": "3.0.0" + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + }, + "dependencies": { + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + } } }, "jsesc": { @@ -3119,8 +3651,9 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, "requires": { - "jsonify": "0.0.0" + "jsonify": "~0.0.0" } }, "json-stringify-safe": { @@ -3137,7 +3670,8 @@ "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true }, "jsprim": { "version": "1.4.1", @@ -3156,7 +3690,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } }, "lazy-cache": { @@ -3172,13 +3706,13 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "1.0.0" + "invert-kv": "^1.0.0" } }, "left-pad": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.2.0.tgz", - "integrity": "sha1-0wpzxrggHY99jnlWupYWCHpo4O4=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", "dev": true }, "leven": { @@ -3193,8 +3727,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" } }, "load-json-file": { @@ -3203,11 +3737,22 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } } }, "locate-path": { @@ -3216,14 +3761,14 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" } }, "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" }, "lodash.assign": { "version": "4.2.0", @@ -3248,12 +3793,12 @@ "dev": true }, "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "requires": { - "js-tokens": "3.0.2" + "js-tokens": "^3.0.0 || ^4.0.0" } }, "lru-cache": { @@ -3261,8 +3806,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "lrucache": { @@ -3276,7 +3821,7 @@ "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", "dev": true, "requires": { - "tmpl": "1.0.4" + "tmpl": "1.0.x" } }, "map-cache": { @@ -3291,16 +3836,22 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "1.0.1" + "object-visit": "^1.0.0" } }, + "math-random": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", + "dev": true + }, "mem": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "1.2.0" + "mimic-fn": "^1.0.0" } }, "merge": { @@ -3315,7 +3866,7 @@ "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", "dev": true, "requires": { - "readable-stream": "2.3.5" + "readable-stream": "^2.0.1" } }, "micromatch": { @@ -3324,19 +3875,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" } }, "mime-db": { @@ -3349,22 +3900,22 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "requires": { - "mime-db": "1.30.0" + "mime-db": "~1.30.0" } }, "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -3376,20 +3927,20 @@ "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -3412,322 +3963,119 @@ "nan": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha1-ltDNYQ69WNS03pzAxoKM2pnHVI8=", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", "dev": true, "optional": true }, "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "arr-diff": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", - "dev": true - } - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nock": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/nock/-/nock-9.2.3.tgz", - "integrity": "sha1-OXOAh9agSX06Fl+zUmErOKL5uS8=", - "dev": true, - "requires": { - "chai": "4.1.2", - "debug": "3.1.0", - "deep-equal": "1.0.1", - "json-stringify-safe": "5.0.1", - "lodash": "4.17.10", - "mkdirp": "0.5.1", - "propagate": "1.0.0", - "qs": "6.5.1", - "semver": "5.5.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", - "dev": true - } - } - }, - "node-cache": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-3.2.1.tgz", - "integrity": "sha1-p5WNMqikLZEZziWYZWfqLF+WZ3M=", - "requires": { - "clone": "1.0.2", - "lodash": "4.17.4" - } - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-notifier": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.2.1.tgz", - "integrity": "sha1-+jE90I9VF9sOJQLldY1mSsafneo=", - "dev": true, - "requires": { - "growly": "1.3.0", - "semver": "5.5.0", - "shellwords": "0.1.1", - "which": "1.3.0" - } - }, - "node-pre-gyp": { - "version": "0.6.39", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", - "integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==", - "dev": true, - "optional": true, - "requires": { - "detect-libc": "1.0.2", - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - }, - "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true, - "optional": true - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true, - "optional": true + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true, - "optional": true + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nock": { + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/nock/-/nock-9.2.3.tgz", + "integrity": "sha1-OXOAh9agSX06Fl+zUmErOKL5uS8=", + "dev": true, + "requires": { + "chai": "^4.1.2", + "debug": "^3.1.0", + "deep-equal": "^1.0.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.5", + "mkdirp": "^0.5.0", + "propagate": "^1.0.0", + "qs": "^6.5.1", + "semver": "^5.5.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, - "optional": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" + "ms": "2.0.0" } }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true } } }, + "node-cache": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-3.2.1.tgz", + "integrity": "sha1-p5WNMqikLZEZziWYZWfqLF+WZ3M=", + "requires": { + "clone": "1.0.x", + "lodash": "4.x" + } + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, "node-sha1": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/node-sha1/-/node-sha1-0.0.1.tgz", "integrity": "sha1-VmL8eZ8cPJXZPjAV3QSAZVFJhdM=" }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "2.6.0", - "is-builtin-module": "1.0.0", - "semver": "5.5.0", - "validate-npm-package-license": "3.0.3" + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, "normalize-path": { @@ -3736,7 +4084,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "1.1.0" + "remove-trailing-separator": "^1.0.1" } }, "npm-run-path": { @@ -3745,31 +4093,19 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "2.0.1" - } - }, - "npmlog": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz", - "integrity": "sha512-ocolIkZYZt8UveuiDS0yAkkIjid1o7lPG8cYm05yNYzBn8ykQtaiPMEGp8fY9tKdDgm8okpdKzkvu1y9hUYugA==", - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "path-key": "^2.0.0" } }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true }, - "nwmatcher": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz", - "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==", + "nwsapi": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.8.tgz", + "integrity": "sha512-7RZ+qbFGiVc6v14Y8DSZjPN1wZPOaMbiiP4tzf5eNuyOITAeOIA3cMhjuKUypVIqBgCSg1KaSyAv8Ocq/0ZJ1A==", "dev": true }, "oauth-sign": { @@ -3789,9 +4125,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "dependencies": { "define-property": { @@ -3800,52 +4136,15 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "is-descriptor": "^0.1.0" } } } }, "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", "dev": true }, "object-visit": { @@ -3854,7 +4153,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.0" }, "dependencies": { "isobject": { @@ -3871,8 +4170,8 @@ "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "dev": true, "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.11.0" + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" } }, "object.omit": { @@ -3881,8 +4180,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" } }, "object.pick": { @@ -3891,7 +4190,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" }, "dependencies": { "isobject": { @@ -3908,7 +4207,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "optimist": { @@ -3917,8 +4216,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" } }, "optionator": { @@ -3927,12 +4226,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" }, "dependencies": { "wordwrap": { @@ -3952,12 +4251,12 @@ "os-locale": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" } }, "os-tmpdir": { @@ -3966,17 +4265,6 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "osenv": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -3989,7 +4277,7 @@ "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", "dev": true, "requires": { - "p-try": "1.0.0" + "p-try": "^1.0.0" } }, "p-locate": { @@ -3998,7 +4286,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "1.2.0" + "p-limit": "^1.1.0" } }, "p-try": { @@ -4013,10 +4301,10 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" } }, "parse-json": { @@ -4025,13 +4313,13 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "1.3.1" + "error-ex": "^1.2.0" } }, "parse5": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha1-bXhlbj2o14tOwLkG98CO8d/j9gg=", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", "dev": true }, "pascalcase": { @@ -4059,9 +4347,9 @@ "dev": true }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "path-type": { @@ -4070,9 +4358,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "pathval": { @@ -4104,7 +4392,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "2.0.4" + "pinkie": "^2.0.0" } }, "pkg-dir": { @@ -4113,13 +4401,13 @@ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "dev": true, "requires": { - "find-up": "2.1.0" + "find-up": "^2.1.0" } }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha1-4vTO8OIZ9GPBeas3Rj5OHs3Muvs=", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, "posix-character-classes": { @@ -4143,11 +4431,11 @@ "pretty-format": { "version": "22.4.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz", - "integrity": "sha1-+HPXgIOanALpZkyKCC6e556qwW8=", + "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==", "dev": true, "requires": { - "ansi-regex": "3.0.0", - "ansi-styles": "3.2.1" + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" }, "dependencies": { "ansi-regex": { @@ -4161,13 +4449,13 @@ "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, "propagate": { @@ -4181,6 +4469,12 @@ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", + "dev": true + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -4192,65 +4486,27 @@ "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" }, "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", + "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" }, "dependencies": { "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true }, "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "rc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", - "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -4260,9 +4516,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" } }, "read-pkg-up": { @@ -4271,8 +4527,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" }, "dependencies": { "find-up": { @@ -4281,8 +4537,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "path-exists": { @@ -4291,33 +4547,24 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "2.0.1" + "pinkie-promise": "^2.0.0" } } } }, "readable-stream": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", - "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "realpath-native": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.0.0.tgz", - "integrity": "sha512-XJtlRJ9jf0E1H1SLeJyQ9PGzQD7S65h1pRXEcAeK48doKOnKxcgPeNohJvD5u/2sI9J1oke6E8bZHS/fmW1UiQ==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "util.promisify": "1.0.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "redis": { @@ -4325,9 +4572,9 @@ "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", "integrity": "sha1-ICKI4/WMSfYHnZevehDhMDrhSwI=", "requires": { - "double-ended-queue": "2.1.0-0", - "redis-commands": "1.3.1", - "redis-parser": "2.6.0" + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.6.0" } }, "redis-commands": { @@ -4343,26 +4590,26 @@ "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk=", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", "dev": true }, "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "0.1.3" + "is-equal-shallow": "^0.1.3" } }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, "remove-trailing-separator": { @@ -4372,9 +4619,9 @@ "dev": true }, "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", "dev": true }, "repeat-string": { @@ -4389,7 +4636,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "1.0.2" + "is-finite": "^1.0.0" } }, "request": { @@ -4397,26 +4644,26 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", "integrity": "sha1-MvACNc0I1IK00NaNuTqCnA7VdW4=", "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "har-validator": "5.0.3", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" } }, "request-etag": { @@ -4424,10 +4671,10 @@ "resolved": "https://registry.npmjs.org/request-etag/-/request-etag-2.0.3.tgz", "integrity": "sha1-E8kjdhfWxYnrcYX29FC4MTBovBc=", "requires": { - "lodash.assign": "4.2.0", - "lodash.clonedeep": "4.5.0", - "lru-cache": "4.1.2", - "request": "2.87.0" + "lodash.assign": "^4.0.0", + "lodash.clonedeep": "^4.0.1", + "lru-cache": "^4.0.0", + "request": "^2.67.0" } }, "request-promise-core": { @@ -4436,7 +4683,7 @@ "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", "dev": true, "requires": { - "lodash": "4.17.4" + "lodash": "^4.13.1" } }, "request-promise-native": { @@ -4446,8 +4693,8 @@ "dev": true, "requires": { "request-promise-core": "1.1.1", - "stealthy-require": "1.1.1", - "tough-cookie": "2.3.3" + "stealthy-require": "^1.1.0", + "tough-cookie": ">=2.3.3" } }, "require-directory": { @@ -4474,7 +4721,7 @@ "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", "dev": true, "requires": { - "resolve-from": "3.0.0" + "resolve-from": "^3.0.0" } }, "resolve-from": { @@ -4492,7 +4739,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, "right-align": { @@ -4502,17 +4749,14 @@ "dev": true, "optional": true, "requires": { - "align-text": "0.1.4" + "align-text": "^0.1.1" } }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", - "dev": true, - "requires": { - "glob": "7.1.2" - } + "rsvp": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", + "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", + "dev": true }, "safe-buffer": { "version": "5.1.1", @@ -4525,23 +4769,29 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "0.1.15" + "ret": "~0.1.10" } }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "sane": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.0.tgz", - "integrity": "sha512-glfKd7YH4UCrh/7dD+UESsr8ylKWRE7UQPoXuz28FgmcF0ViJQhCTCCZHICRKxf8G8O1KdLEn20dcICK54c7ew==", - "dev": true, - "requires": { - "anymatch": "2.0.0", - "exec-sh": "0.2.1", - "fb-watchman": "2.0.0", - "fsevents": "1.1.3", - "micromatch": "3.1.10", - "minimist": "1.2.0", - "walker": "1.0.7", - "watch": "0.18.0" + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.2.tgz", + "integrity": "sha1-tNwYYcIbQn6SlQej51HiosuKs/o=", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "capture-exit": "^1.2.0", + "exec-sh": "^0.2.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.3", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5", + "watch": "~0.18.0" }, "dependencies": { "arr-diff": { @@ -4557,41 +4807,30 @@ "dev": true }, "braces": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz", - "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "kind-of": "6.0.2", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -4602,13 +4841,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -4617,7 +4856,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -4626,24 +4865,64 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -4651,17 +4930,17 @@ "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -4670,7 +4949,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -4679,7 +4958,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -4690,10 +4969,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -4702,49 +4981,38 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } }, "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } + "kind-of": "^6.0.0" } }, "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "is-number": { @@ -4753,7 +5021,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -4762,7 +5030,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -4776,28 +5044,28 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.1", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } }, "minimist": { @@ -4811,7 +5079,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, "semver": { @@ -4828,13 +5096,13 @@ "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -4843,7 +5111,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -4854,7 +5122,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "1.0.0" + "shebang-regex": "^1.0.0" } }, "shebang-regex": { @@ -4866,7 +5134,7 @@ "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha1-1rkYHBpI05cyTISHHvvPxz/AZUs=", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, "signal-exit": { @@ -4881,108 +5149,51 @@ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", - "dev": true, - "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.1", - "use": "3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } + "is-descriptor": "^0.1.0" } }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-extendable": "^0.1.0" } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true } } }, "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" }, "dependencies": { "define-property": { @@ -4991,7 +5202,36 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "isobject": { @@ -4999,16 +5239,22 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.2.0" } }, "source-map": { @@ -5018,31 +5264,32 @@ "dev": true }, "source-map-resolve": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { - "atob": "2.1.0", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, "source-map-support": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.4.tgz", - "integrity": "sha512-PETSPG6BjY1AHs2t64vS2aqAgu6dMIMXJULWFBGbh2Gr8nVLbCFDo6i/RMMvviIQ2h1Z8+5gQhVKSn2je9nmdg==", + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", "dev": true, "requires": { - "source-map": "0.6.1" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" }, "dependencies": { "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -5056,42 +5303,42 @@ "spdx-correct": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha1-BaW01xU6GVvJLDxCW2nzsqlSTII=", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { - "spdx-expression-parse": "3.0.0", - "spdx-license-ids": "3.0.0" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, "spdx-exceptions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha1-LHrmEFbHFKW5ubKyr30xHvXHj+k=", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", "dev": true }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { - "spdx-exceptions": "2.1.0", - "spdx-license-ids": "3.0.0" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, "spdx-license-ids": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha1-enzShHDMbToc/m1miG9rxDDTrIc=", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", "dev": true }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "3.0.2" + "extend-shallow": "^3.0.0" } }, "sprintf-js": { @@ -5101,18 +5348,19 @@ "dev": true }, "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" } }, "stack-trace": { @@ -5132,8 +5380,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "dependencies": { "define-property": { @@ -5142,65 +5390,8 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-descriptor": "^0.1.0" } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true } } }, @@ -5210,47 +5401,32 @@ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "dev": true }, - "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", - "dev": true, - "requires": { - "astral-regex": "1.0.0", - "strip-ansi": "4.0.0" - } - }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" }, "dependencies": { "ansi-regex": { @@ -5262,13 +5438,10 @@ } }, "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true }, "strip-eof": { "version": "1.0.0", @@ -5276,20 +5449,13 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "optional": true - }, "supports-color": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", - "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "symbol-tree": { @@ -5298,45 +5464,17 @@ "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "dev": true }, - "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", - "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.9", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.3.5", - "rimraf": "2.6.2", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, "test-exclude": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.1.tgz", - "integrity": "sha1-36Ii8DSAvKaSB8pyizfXS0X3JPo=", + "integrity": "sha512-qpqlP/8Zl+sosLxBcVKl9vYy26T9NPalxSzzCP/OY6K7j938ui2oKgo+kRZYfxAeIpLqpbVnsHq1tyV70E4lWQ==", "dev": true, "requires": { - "arrify": "1.0.1", - "micromatch": "3.1.10", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "require-main-filename": "1.0.1" + "arrify": "^1.0.1", + "micromatch": "^3.1.8", + "object-assign": "^4.1.0", + "read-pkg-up": "^1.0.1", + "require-main-filename": "^1.0.1" }, "dependencies": { "arr-diff": { @@ -5352,41 +5490,30 @@ "dev": true }, "braces": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz", - "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "kind-of": "6.0.2", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -5397,13 +5524,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -5412,7 +5539,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -5421,24 +5548,64 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" } }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -5446,17 +5613,17 @@ "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -5465,7 +5632,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -5474,7 +5641,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -5485,10 +5652,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -5497,49 +5664,38 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } }, "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } + "kind-of": "^6.0.0" } }, "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, "is-number": { @@ -5548,7 +5704,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -5557,7 +5713,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -5571,28 +5727,28 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.1", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } } } @@ -5621,19 +5777,19 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "regex-not": "1.0.2", - "safe-regex": "1.1.0" + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" } }, "to-regex-range": { @@ -5642,8 +5798,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" }, "dependencies": { "is-number": { @@ -5652,7 +5808,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } } } @@ -5662,7 +5818,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", "requires": { - "punycode": "1.4.1" + "punycode": "^1.4.1" } }, "tr46": { @@ -5671,13 +5827,13 @@ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", "dev": true, "requires": { - "punycode": "2.1.0" + "punycode": "^2.1.0" }, "dependencies": { "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true } } @@ -5697,7 +5853,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { @@ -5712,7 +5868,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "1.1.2" + "prelude-ls": "~1.1.2" } }, "type-detect": { @@ -5728,24 +5884,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - } + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" } }, "uglify-to-browserify": { @@ -5755,23 +5896,16 @@ "dev": true, "optional": true }, - "uid-number": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", - "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", - "dev": true, - "optional": true - }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" }, "dependencies": { "extend-shallow": { @@ -5780,7 +5914,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "set-value": { @@ -5789,10 +5923,10 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" } } } @@ -5803,8 +5937,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, "dependencies": { "has-value": { @@ -5813,9 +5947,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "dependencies": { "isobject": { @@ -5850,21 +5984,10 @@ "dev": true }, "use": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", - "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true }, "util-deprecate": { "version": "1.0.2", @@ -5875,11 +5998,11 @@ "util.promisify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha1-RA9xZaRZyaFtwUXrjnLzVocJcDA=", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", "dev": true, "requires": { - "define-properties": "1.1.2", - "object.getownpropertydescriptors": "2.0.3" + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" } }, "uuid": { @@ -5888,13 +6011,13 @@ "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" }, "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha1-gWQ7y+8b3+zUYjeT3EZIlIupgzg=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { - "spdx-correct": "3.0.0", - "spdx-expression-parse": "3.0.0" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, "verror": { @@ -5902,9 +6025,9 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "1.0.0", + "assert-plus": "^1.0.0", "core-util-is": "1.0.2", - "extsprintf": "1.3.0" + "extsprintf": "^1.2.0" } }, "w3c-hr-time": { @@ -5913,7 +6036,7 @@ "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", "dev": true, "requires": { - "browser-process-hrtime": "0.1.2" + "browser-process-hrtime": "^0.1.2" } }, "walker": { @@ -5922,7 +6045,7 @@ "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", "dev": true, "requires": { - "makeerror": "1.0.11" + "makeerror": "1.0.x" } }, "watch": { @@ -5931,8 +6054,8 @@ "integrity": "sha1-KAlUdsbffJDJYxOJkMClQj60uYY=", "dev": true, "requires": { - "exec-sh": "0.2.1", - "minimist": "1.2.0" + "exec-sh": "^0.2.0", + "minimist": "^1.2.0" }, "dependencies": { "minimist": { @@ -5946,36 +6069,42 @@ "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha1-qFWYCx8LazWbodXZ+zmulB+qY60=", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", "dev": true }, "whatwg-encoding": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz", - "integrity": "sha1-V8I1vIZX6RTSTho5fTyC2u4Ka6M=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.4.tgz", + "integrity": "sha512-vM9KWN6MP2mIHZ86ytcyIv7e8Cj3KTfO2nd2c8PFDqcI4bxFmQp83ibq4wadq7rL9l9sZV6o9B0LTt8ygGAAXg==", "dev": true, "requires": { - "iconv-lite": "0.4.19" + "iconv-lite": "0.4.23" } }, + "whatwg-mimetype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz", + "integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==", + "dev": true + }, "whatwg-url": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.4.0.tgz", - "integrity": "sha512-Z0CVh/YE217Foyb488eo+iBv+r7eAQ0wSTyApi9n06jhcA3z6Nidg/EGvl0UFkg7kMdKxfBzzr+o9JF+cevgMg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", "dev": true, "requires": { - "lodash.sortby": "4.7.0", - "tr46": "1.0.1", - "webidl-conversions": "4.0.2" + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" } }, "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "which-module": { @@ -5984,50 +6113,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "wide-align": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "optional": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "2.1.1" - } - } - } - }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -6040,12 +6125,12 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.1.tgz", "integrity": "sha1-o6kmUQVWQmPGeFtFg7jIrKJv3tY=", "requires": { - "async": "1.0.0", - "colors": "1.0.3", - "cycle": "1.0.3", - "eyes": "0.1.8", - "isstream": "0.1.2", - "stack-trace": "0.0.10" + "async": "~1.0.0", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" }, "dependencies": { "async": { @@ -6067,8 +6152,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" }, "dependencies": { "is-fullwidth-code-point": { @@ -6077,7 +6162,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "string-width": { @@ -6086,9 +6171,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "strip-ansi": { @@ -6097,7 +6182,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } } } @@ -6111,22 +6196,21 @@ "write-file-atomic": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", - "integrity": "sha1-H/YVdcLipOjlENb6TiQ8zhg5mas=", + "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "signal-exit": "3.0.2" + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" } }, "ws": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", - "integrity": "sha1-qXm119TaaL9U7+BAiWfDJIaacok=", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", "dev": true, "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.1" + "async-limiter": "~1.0.0" } }, "xml": { @@ -6138,7 +6222,7 @@ "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha1-auc+Bt5NjG5H+fsYH3jWSK1FfGo=", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, "y18n": { @@ -6153,45 +6237,25 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "yargs": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", - "integrity": "sha1-RU0HTCsWpRpD4vt4B+T53mnMtcU=", - "dev": true, - "requires": { - "cliui": "4.0.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "8.1.0" - }, - "dependencies": { - "cliui": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.0.0.tgz", - "integrity": "sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw==", - "dev": true, - "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" - } - } + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" } }, "yargs-parser": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha1-8TdqM7Ziml0GN4KUTacyYx6WaVA=", + "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" }, "dependencies": { "camelcase": { From 511e783ab34469663bf39fc5528150d17deeadda Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Aug 2018 16:12:37 -0700 Subject: [PATCH 022/330] run npm audit only in current Node --- .circleci/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1d0ed68..c876306 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,7 +16,10 @@ node-template: &node-template command: npm test environment: JEST_JUNIT_OUTPUT: "reports/junit/js-test-results.xml" - - run: npm audit + - run: | + if [[ $CIRCLE_JOB == current-release ]]; then + npm audit + fi - store_test_results: path: reports/junit - store_artifacts: From 00e08c8d5c91cead0affb5ea7e8d79bc85d44989 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 23 Aug 2018 16:29:55 -0700 Subject: [PATCH 023/330] implement evaluation with explanation --- evaluate_flag.js | 148 +++++++++++++++++++++-------------- event_processor.js | 3 + flags_state.js | 8 +- index.d.ts | 100 ++++++++++++++++++++++- index.js | 124 ++++++++++++++++++----------- test/LDClient-test.js | 122 +++++++++++++++++++++++++---- test/evaluate_flag-test.js | 86 +++++++++++--------- test/event_processor-test.js | 17 ++++ 8 files changed, 446 insertions(+), 162 deletions(-) diff --git a/evaluate_flag.js b/evaluate_flag.js index c89e418..4e68bef 100644 --- a/evaluate_flag.js +++ b/evaluate_flag.js @@ -8,90 +8,95 @@ var builtins = ['key', 'ip', 'country', 'email', 'firstName', 'lastName', 'avata var noop = function(){}; +// Callback receives (err, detail, events) where detail has the properties "value", "variationIndex", and "reason"; +// detail will never be null even if there's an error. function evaluate(flag, user, featureStore, cb) { cb = cb || noop; if (!user || user.key === null || user.key === undefined) { - cb(null, null, null, null); + cb(null, errorResult('USER_NOT_SPECIFIED'), []); return; } if (!flag) { - cb(null, null, null, null); + cb(null, errorResult('FLAG_NOT_FOUND'), []); return; } + var events = []; + evalInternal(flag, user, featureStore, events, function(err, detail) { + cb(err, detail, events); + }); +} + +function evalInternal(flag, user, featureStore, events, cb) { + // If flag is off, return the off variation if (!flag.on) { - // Return the off variation if defined and valid - cb(null, flag.offVariation, getVariation(flag, flag.offVariation), null); + getOffResult(flag, { kind: 'OFF' }, function(err, detail) { + cb(err, detail); + }); return; } - evalInternal(flag, user, featureStore, [], function(err, variation, value, events) { - if (err) { - cb(err, variation, value, events); - return; - } - - if (variation === null) { - // Return the off variation if defined and valid - cb(null, flag.offVariation, getVariation(flag, flag.offVariation), events); + checkPrerequisites(flag, user, featureStore, events, function(err, failureReason) { + if (err != null || failureReason != null) { + getOffResult(flag, failureReason, cb); } else { - cb(err, variation, value, events); + evalRules(flag, user, featureStore, cb); } }); - return; } -function evalInternal(flag, user, featureStore, events, cb) { - // Evaluate prerequisites, if any +// Callback receives (err, reason) where reason is null if successful, or a "prerequisite failed" reason +function checkPrerequisites(flag, user, featureStore, events, cb) { if (flag.prerequisites) { async.mapSeries(flag.prerequisites, function(prereq, callback) { featureStore.get(dataKind.features, prereq.key, function(f) { // If the flag does not exist in the store or is not on, the prerequisite // is not satisfied - if (!f || !f.on) { - callback(new Error("Unsatisfied prerequisite"), null); + if (!f) { + callback({ key: prereq.key, err: new Error("Could not retrieve prerequisite feature flag \"" + prereq.key + "\"") }); + return; + } + if (!f.on) { + callback({ key: prereq.key }); return; } - evalInternal(f, user, featureStore, events, function(err, variation, value) { + evalInternal(f, user, featureStore, events, function(err, detail) { // If there was an error, the value is null, the variation index is out of range, // or the value does not match the indexed variation the prerequisite is not satisfied - events.push(createFlagEvent(f.key, f, user, variation, value, null, flag.key)); - if (err || value === null || variation != prereq.variation) { - callback(new Error("Unsatisfied prerequisite"), null) + events.push(createFlagEvent(f.key, f, user, detail, null, flag.key, true)); + if (err) { + callback({ key: prereq.key, err: err }); + } else if (detail.variationIndex != prereq.variation) { + callback({ key: prereq.key }); } else { // The prerequisite was satisfied - callback(null, null); + callback(null); } }); }); }, - function(err, results) { - // If the error is that prerequisites weren't satisfied, we don't return an error, - // because we want to serve the 'offVariation' - if (err) { - cb(null, null, null, events); - return; - } - evalRules(flag, user, featureStore, function(e, variation, value) { - cb(e, variation, value, events); - }); - }) + function(errInfo) { + if (errInfo) { + cb(errInfo.err, { 'kind': 'PREREQUISITE_FAILED', 'prerequisiteKey': errInfo.key }); + } else { + cb(null, null); + } + }); } else { - evalRules(flag, user, featureStore, function(e, variation, value) { - cb(e, variation, value, events); - }); + cb(null, null); } } +// Callback receives (err, detail) function evalRules(flag, user, featureStore, cb) { var i, j; var target; var variation; var rule; // Check target matches - for (i = 0; i < flag.targets.length; i++) { + for (i = 0; i < (flag.targets || []).length; i++) { target = flag.targets[i]; if (!target.values) { @@ -100,32 +105,30 @@ function evalRules(flag, user, featureStore, cb) { for (j = 0; j < target.values.length; j++) { if (user.key === target.values[j]) { - value = getVariation(flag, target.variation); - cb(value === null ? new Error("Undefined variation for flag " + flag.key) : null, - target.variation, value); + getVariation(flag, target.variation, { kind: 'TARGET_MATCH' }, cb); return; } } } - async.mapSeries(flag.rules, + i = 0; + async.mapSeries(flag.rules || [], function(rule, callback) { ruleMatchUser(rule, user, featureStore, function(matched) { - setImmediate(callback, matched ? rule : null, null); + var match = matched ? { index: i, rule: rule } : null; + setImmediate(callback, match, null); }); }, function(err, results) { // we use the "error" value to indicate that a rule was successfully matched (since we only care // about the first match, and mapSeries terminates on the first "error") if (err) { - var rule = err; - variation = variationForUser(rule, user, flag); + var reason = { kind: 'RULE_MATCH', ruleIndex: err.index, ruleId: err.rule.id }; + getResultForVariationOrRollout(err.rule, user, flag, reason, cb); } else { // no rule matched; check the fallthrough - variation = variationForUser(flag.fallthrough, user, flag); + getResultForVariationOrRollout(flag.fallthrough, user, flag, { kind: 'FALLTHROUGH' }, cb); } - cb(variation === null ? new Error("Undefined variation for flag " + flag.key) : null, - variation, getVariation(flag, variation)); } ); } @@ -255,16 +258,39 @@ function matchAny(matchFn, value, values) { return false; } -// Given an index, return the variation value, or null if -// the index is invalid -function getVariation(flag, index) { - if (index === null || index === undefined || index >= flag.variations.length) { - return null; +function getVariation(flag, index, reason, cb) { + if (index === null || index === undefined || index < 0 || index >= flag.variations.length) { + cb(new Error('Invalid variation index in flag', errResult('MALFORMED_FLAG'))); } else { - return flag.variations[index]; + cb(null, { value: flag.variations[index], variationIndex: index, reason: reason }); } } +function getOffResult(flag, reason, cb) { + if (flag.offVariation === null || flag.offVariation === undefined) { + cb(null, { value: null, variationIndex: null, reason: reason }); + } else { + getVariation(flag, flag.offVariation, reason, cb); + } +} + +function getResultForVariationOrRollout(r, user, flag, reason, cb) { + if (!r) { + cb(new Error('Fallthrough variation undefined'), errResult('MALFORMED_FLAG')); + } else { + var index = variationForUser(r, user, flag); + if (index === null) { + cb(new Error('Variation/rollout object with no variation or rollout'), errResult('MALFORMED_FLAG')); + } else { + getVariation(flag, index, reason, cb); + } + } +} + +function errorResult(errorKind) { + return { value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: errorKind }}; +} + // Given a variation or rollout 'r', select // the variation for the given user function variationForUser(r, user, flag) { @@ -337,13 +363,13 @@ function bucketableStringValue(value) { return null; } -function createFlagEvent(key, flag, user, variation, value, defaultVal, prereqOf) { - return { +function createFlagEvent(key, flag, user, detail, defaultVal, prereqOf, includeReason) { + var e = { "kind": "feature", "key": key, "user": user, - "variation": variation, - "value": value, + "variation": detail.variationIndex, + "value": detail.value, "default": defaultVal, "creationDate": new Date().getTime(), "version": flag ? flag.version : null, @@ -351,6 +377,10 @@ function createFlagEvent(key, flag, user, variation, value, defaultVal, prereqOf "trackEvents": flag ? flag.trackEvents : null, "debugEventsUntilDate": flag ? flag.debugEventsUntilDate : null }; + if (includeReason) { + e['reason'] = detail.reason; + } + return e; } module.exports = {evaluate: evaluate, bucketUser: bucketUser, createFlagEvent: createFlagEvent}; \ No newline at end of file diff --git a/event_processor.js b/event_processor.js index ab453b1..42bfdb2 100644 --- a/event_processor.js +++ b/event_processor.js @@ -59,6 +59,9 @@ function EventProcessor(sdkKey, config, errorReporter) { if (event.version) { out.version = event.version; } + if (event.reason) { + out.reason = event.reason; + } if (config.inlineUsersInEvents || debug) { out.user = userFilter.filterUser(event.user); } else { diff --git a/flags_state.js b/flags_state.js index 10f15c6..5a7a2eb 100644 --- a/flags_state.js +++ b/flags_state.js @@ -4,7 +4,7 @@ function FlagsStateBuilder(valid) { var flagValues = {}; var flagMetadata = {}; - builder.addFlag = function(flag, value, variation) { + builder.addFlag = function(flag, value, variation, reason) { flagValues[flag.key] = value; var meta = { version: flag.version, @@ -16,6 +16,9 @@ function FlagsStateBuilder(valid) { if (flag.debugEventsUntilDate !== undefined && flag.debugEventsUntilDate !== null) { meta.debugEventsUntilDate = flag.debugEventsUntilDate; } + if (reason) { + meta.reason = reason; + } flagMetadata[flag.key] = meta; }; @@ -24,6 +27,9 @@ function FlagsStateBuilder(valid) { valid: valid, allValues: function() { return flagValues; }, getFlagValue: function(key) { return flagValues[key]; }, + getFlagReason: function(key) { + return flagMetadata[key] ? flagMetadata[key].reason : null; + }, toJSON: function() { return Object.assign({}, flagValues, { $flagsState: flagMetadata, $valid: valid }); } diff --git a/index.d.ts b/index.d.ts index b5f5be8..6440831 100644 --- a/index.d.ts +++ b/index.d.ts @@ -53,7 +53,7 @@ declare module 'ldclient-node' { */ export interface LDFlagsState = { /** - * True if this object contains a valid snapshot of feature flag state, or false if the + * True if this object contains a valid snapshot of feature flag state, or false if the * state could not be computed (for instance, because the client was offline or there * was no user). */ @@ -86,6 +86,74 @@ declare module 'ldclient-node' { toJSON: () => object; }; + /** + * Describes the reason that a flag evaluation produced a particular value. This is + * part of the LDEvaluationDetail object returned by variationDetail(). + */ + export type LDEvaluationReason = { + /** + * The general category of the reason: + * + * 'OFF': the flag was off and therefore returned its configured off value + * + * 'FALLTHROUGH': the flag was on but the user did not match any targets or rules + * + * 'TARGET_MATCH': the user key was specifically targeted for this flag + * + * 'RULE_MATCH': the user matched one of the flag's rules + * + * 'PREREQUISITE_FAILED': the flag was considered off because it had at least one + * prerequisite flag that either was off or did not return the desired variation + * + * 'ERROR': the flag could not be evaluated, e.g. because it does not exist or due + * to an unexpected error + */ + kind: string; + + /** + * A further description of the error condition, if the kind was 'ERROR'. + */ + errorKind?: string; + + /** + * The index of the matched rule (0 for the first), if the kind was 'RULE_MATCH'. + */ + ruleIndex?: number; + + /** + * The unique identifier of the matched rule, if the kind was 'RULE_MATCH'. + */ + ruleId?: string; + + /** + * The key of the failed prerequisite flag, if the kind was 'PREREQUISITE_FAILED'. + */ + prerequisiteKey?: string; + }; + + /** + * An object returned by LDClient.variationDetail(), combining the result of a feature flag + * evaluation with information about how it was calculated. + */ + export type LDEvaluationDetail = { + /** + * The result of the flag evaluation. This will be either one of the flag's variations or + * the default value that was passed to variationDetail(). + */ + value: LDFlagValue; + + /** + * The index of the returned value within the flag's list of variations, e.g. 0 for the + * first variation - or null if the default value was returned. + */ + variationIndex?: number; + + /** + * An object describing the main factor that influenced the flag evaluation value. + */ + reason: LDEvaluationReason; + }; + /** * LaunchDarkly initialization options. */ @@ -524,6 +592,36 @@ declare module 'ldclient-node' { callback?: (err: any, res: LDFlagValue) => void ) => Promise; + /** + * Retrieves a flag's value, along with information about how it was calculated, in the form + * of an LDEvaluationDetail object. + * + * The reason property of the result will also be included in analytics events, if you are + * capturing detailed event data for this flag. + * + * @param key + * The key of the flag for which to retrieve the corresponding value. + * @param user + * The user for the variation. + * + * The variation call will automatically create a user in LaunchDarkly if a user with that user key doesn't exist already. + * + * @param defaultValue + * The value to use if the flag is not available (for example, if the + * user is offline or a flag is requested that does not exist). + * + * @param callback + * The callback to receive the result. + * + * @returns a Promise containing the flag value and explanation + */ + variationDetail: ( + key: string, + user: LDUser, + defaultValue: LDFlagValue, + callback?: (err: ay, res: LDEvaluationDetail) => void + ) => Promise; + toggle: ( key: string, user: LDUser, diff --git a/index.js b/index.js index 5f6f1fb..3415e03 100644 --- a/index.js +++ b/index.js @@ -152,54 +152,78 @@ var newClient = function(sdkKey, config) { client.variation = function(key, user, defaultVal, callback) { return wrapPromiseCallback(new Promise(function(resolve, reject) { - sanitizeUser(user); - var variationErr; + evaluateIfPossible(key, user, defaultVal, false, + function(detail) { + resolve(detail.value) + }, + reject); + }.bind(this)), callback); + }; - if (this.isOffline()) { - config.logger.info("Variation called in offline mode. Returning default value."); - return resolve(defaultVal); - } + client.variationDetail = function(key, user, defaultVal, callback) { + return wrapPromiseCallback(new Promise(function(resolve, reject) { + evaluateIfPossible(key, user, defaultVal, true, resolve, reject); + }.bind(this)), callback); + }; - else if (!key) { - variationErr = new errors.LDClientError('No feature flag key specified. Returning default value.'); - maybeReportError(variationError); - sendFlagEvent(key, null, user, null, defaultVal, defaultVal); - return resolve(defaultVal); - } + function errorResult(errorKind, defaultVal) { + return { value: defaultVal, variationIndex: null, reason: { kind: 'ERROR', errorKind: errorKind } }; + }; - else if (user && user.key === "") { - config.logger.warn("User key is blank. Flag evaluation will proceed, but the user will not be stored in LaunchDarkly"); - } + function evaluateIfPossible(key, user, defaultVal, includeReasonsInEvents, resolve, reject) { + if (!initComplete) { + config.featureStore.initialized(function(storeInited) { + if (storeInited) { + config.logger.warn("Variation called before LaunchDarkly client initialization completed (did you wait for the 'ready' event?) - using last known values from feature store") + variationInternal(key, user, defaultVal, includeReasonsInEvents, resolve, reject); + } else { + var err = new errors.LDClientError("Variation called before LaunchDarkly client initialization completed (did you wait for the 'ready' event?) - using default value"); + maybeReportError(variationErr); + var result = errorResult('CLIENT_NOT_READY', defaultVal); + sendFlagEvent(key, null, user, result, defaultVal, includeReasonsInEvents); + return resolve(result); + } + }); + } else { + variationInternal(key, user, defaultVal, includeReasonsInEvents, resolve, reject); + } + } - if (!initComplete) { - config.featureStore.initialized(function(storeInited) { - if (storeInited) { - config.logger.warn("Variation called before LaunchDarkly client initialization completed (did you wait for the 'ready' event?) - using last known values from feature store") - variationInternal(key, user, defaultVal, resolve, reject); - } else { - variationErr = new errors.LDClientError("Variation called before LaunchDarkly client initialization completed (did you wait for the 'ready' event?) - using default value"); - maybeReportError(variationErr); - sendFlagEvent(key, null, user, null, defaultVal, defaultVal); - return resolve(defaultVal); - } - }); - return; - } + // resolves to a "detail" object with properties "value", "variationIndex", "reason" + function variationInternal(key, user, defaultVal, includeReasonsInEvents, resolve, reject) { + if (client.isOffline()) { + config.logger.info("Variation called in offline mode. Returning default value."); + return resolve(errorResult('CLIENT_NOT_READY', defaultVal)); + } - variationInternal(key, user, defaultVal, resolve, reject); - }.bind(this)), callback); - } + else if (!key) { + err = new errors.LDClientError('No feature flag key specified. Returning default value.'); + maybeReportError(variationError); + return resolve(errorResult('FLAG_NOT_FOUND', defaultVal)); + } + + sanitizeUser(user); + if (user && user.key === "") { + config.logger.warn("User key is blank. Flag evaluation will proceed, but the user will not be stored in LaunchDarkly"); + } - function variationInternal(key, user, defaultVal, resolve, reject) { config.featureStore.get(dataKind.features, key, function(flag) { if (!user) { variationErr = new errors.LDClientError('No user specified. Returning default value.'); maybeReportError(variationErr); - sendFlagEvent(key, flag, user, null, defaultVal, defaultVal); - return resolve(defaultVal); + var result = errorResult('USER_NOT_SPECIFIED', defaultVal); + sendFlagEvent(key, flag, user, result, defaultVal, includeReasonsInEvents); + return resolve(result); } - evaluate.evaluate(flag, user, config.featureStore, function(err, variation, value, events) { + if (!flag) { + maybeReportError(new errors.LDClientError('Unknown feature flag "' + key + '"; returning default value')); + var result = errorResult('FLAG_NOT_FOUND', defaultVal); + sendFlagEvent(key, null, user, result, defaultVal, includeReasonsInEvents); + return resolve(result); + } + + evaluate.evaluate(flag, user, config.featureStore, function(err, detail, events) { var i; var version = flag ? flag.version : null; @@ -211,18 +235,20 @@ var newClient = function(sdkKey, config) { // have already been constructed, so we just have to push them onto the queue. if (events) { for (i = 0; i < events.length; i++) { - eventProcessor.sendEvent(events[i]); + var e = events[i]; + if (!includeReasonsInEvents) { + delete e['reason']; + } + eventProcessor.sendEvent(e); } } - if (value === null) { + if (detail.value === null) { config.logger.debug("Result value is null in variation"); - sendFlagEvent(key, flag, user, null, defaultVal, defaultVal); - return resolve(defaultVal); - } else { - sendFlagEvent(key, flag, user, variation, value, defaultVal); - return resolve(value); + detail.value = defaultVal; } + sendFlagEvent(key, flag, user, detail, defaultVal, includeReasonsInEvents); + return resolve(detail); }); }); } @@ -258,14 +284,18 @@ var newClient = function(sdkKey, config) { var builder = FlagsStateBuilder(true); var clientOnly = options.clientSideOnly; + var withReasons = options.withReasons; config.featureStore.all(dataKind.features, function(flags) { async.forEachOf(flags, function(flag, key, iterateeCb) { if (clientOnly && !flag.clientSide) { setImmediate(iterateeCb); } else { // At the moment, we don't send any events here - evaluate.evaluate(flag, user, config.featureStore, function(err, variation, value, events) { - builder.addFlag(flag, value, variation); + evaluate.evaluate(flag, user, config.featureStore, function(err, detail, events) { + if (err != null) { + maybeReportError(new Error('Error for feature flag "' + flag.key + '" while evaluating all flags: ' + err)); + } + builder.addFlag(flag, detail.value, detail.variationIndex, withReasons ? detail.reason : null); setImmediate(iterateeCb); }); } @@ -322,8 +352,8 @@ var newClient = function(sdkKey, config) { return eventProcessor.flush(callback); }; - function sendFlagEvent(key, flag, user, variation, value, defaultVal) { - var event = evaluate.createFlagEvent(key, flag, user, variation, value, defaultVal); + function sendFlagEvent(key, flag, user, detail, defaultVal, includeReasonsInEvents) { + var event = evaluate.createFlagEvent(key, flag, user, detail, defaultVal, null, includeReasonsInEvents); eventProcessor.sendEvent(event); } diff --git a/test/LDClient-test.js b/test/LDClient-test.js index bb132a7..7c0cdb6 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -143,6 +143,27 @@ describe('LDClient', function() { }); }); + it('evaluates a flag with variationDetail()', function(done) { + var flag = { + key: 'flagkey', + version: 1, + on: true, + targets: [], + fallthrough: { variation: 1 }, + variations: ['a', 'b'], + trackEvents: true + }; + var client = createOnlineClientWithFlags({ flagkey: flag }); + var user = { key: 'user' }; + client.on('ready', function() { + client.variationDetail(flag.key, user, 'c', function(err, result) { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'b', variationIndex: 1, reason: { kind: 'FALLTHROUGH' } }); + done(); + }); + }); + }); + it('generates an event for an existing feature', function(done) { var flag = { key: 'flagkey', @@ -174,6 +195,38 @@ describe('LDClient', function() { }); }); + it('generates an event for an existing feature with reason', function(done) { + var flag = { + key: 'flagkey', + version: 1, + on: true, + targets: [], + fallthrough: { variation: 1 }, + variations: ['a', 'b'], + trackEvents: true + }; + var client = createOnlineClientWithFlags({ flagkey: flag }); + var user = { key: 'user' }; + client.on('ready', function() { + client.variationDetail(flag.key, user, 'c', function(err, result) { + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'feature', + key: 'flagkey', + version: 1, + user: user, + variation: 1, + value: 'b', + default: 'c', + reason: { kind: 'FALLTHROUGH' }, + trackEvents: true + }); + done(); + }); + }); + }); + it('generates an event for an unknown feature', function(done) { var client = createOnlineClientWithFlags({}); var user = { key: 'user' }; @@ -333,28 +386,65 @@ describe('LDClient', function() { }); }); - it('should not overflow the call stack when evaluating a huge number of flags', function(done) { - var flagCount = 5000; - var flags = {}; - for (var i = 0; i < flagCount; i++) { - var key = 'feature' + i; - var flag = { - key: key, - version: 1, - on: false - }; - flags[key] = flag; - } - var client = createOnlineClientWithFlags(flags); + it('can include reasons in allFlagsState()', function(done) { + var flag = { + key: 'feature', + version: 100, + on: true, + targets: [], + fallthrough: { variation: 1 }, + variations: ['a', 'b'], + trackEvents: true, + debugEventsUntilDate: 1000 + }; + var client = createOnlineClientWithFlags({ feature: flag }); + var user = { key: 'user' }; client.on('ready', function() { - client.allFlags({key: 'user'}, function(err, result) { - expect(err).toEqual(null); - expect(Object.keys(result).length).toEqual(flagCount); + client.allFlagsState(user, { withReasons: true }, function(err, state) { + expect(err).toBeNull(); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({feature: 'b'}); + expect(state.getFlagValue('feature')).toEqual('b'); + expect(state.toJSON()).toEqual({ + feature: 'b', + $flagsState: { + feature: { + version: 100, + variation: 1, + reason: { kind: 'FALLTHROUGH' }, + trackEvents: true, + debugEventsUntilDate: 1000 + } + }, + $valid: true + }); done(); }); }); }); + // it('should not overflow the call stack when evaluating a huge number of flags', function(done) { + // var flagCount = 5000; + // var flags = {}; + // for (var i = 0; i < flagCount; i++) { + // var key = 'feature' + i; + // var flag = { + // key: key, + // version: 1, + // on: false + // }; + // flags[key] = flag; + // } + // var client = createOnlineClientWithFlags(flags); + // client.on('ready', function() { + // client.allFlags({key: 'user'}, function(err, result) { + // expect(err).toEqual(null); + // expect(Object.keys(result).length).toEqual(flagCount); + // done(); + // }); + // }); + // }); + it('should not crash when closing an offline client', function(done) { var client = LDClient.init('sdk_key', {offline: true}); expect(() => client.close()).not.toThrow(); diff --git a/test/evaluate_flag-test.js b/test/evaluate_flag-test.js index d4c3cf3..181e9c6 100644 --- a/test/evaluate_flag-test.js +++ b/test/evaluate_flag-test.js @@ -58,8 +58,9 @@ describe('evaluate', function() { variations: ['a', 'b', 'c'] }; var user = { key: 'x' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe('b'); + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(detail).toMatchObject({ value: 'b', variationIndex: 1, reason: { kind: 'OFF' } }); + expect(events).toMatchObject([]); done(); }); }); @@ -72,8 +73,9 @@ describe('evaluate', function() { variations: ['a', 'b', 'c'] }; var user = { key: 'x' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe(null); + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'OFF' } }); + expect(events).toMatchObject([]); done(); }); }); @@ -89,8 +91,9 @@ describe('evaluate', function() { variations: ['a', 'b', 'c'] }; var user = { key: 'x' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe('a'); + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(detail).toMatchObject({ value: 'a', variationIndex: 0, reason: { kind: 'FALLTHROUGH' } }); + expect(events).toMatchObject([]); done(); }); }); @@ -105,8 +108,10 @@ describe('evaluate', function() { variations: ['a', 'b', 'c'] }; var user = { key: 'x' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe('b'); + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(detail).toMatchObject({ value: 'b', variationIndex: 1, + reason: { kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'badfeature' } }); + expect(events).toMatchObject([]); done(); }); }); @@ -137,8 +142,9 @@ describe('evaluate', function() { var eventsShouldBe = [ { kind: 'feature', key: 'feature1', variation: 0, value: 'd', version: 2, prereqOf: 'feature0' } ]; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result, events) { - expect(result).toBe('b'); + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(detail).toMatchObject({ value: 'b', variationIndex: 1, + reason: { kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' } }); expect(events).toMatchObject(eventsShouldBe); done(); }); @@ -171,8 +177,8 @@ describe('evaluate', function() { var eventsShouldBe = [ { kind: 'feature', key: 'feature1', variation: 1, value: 'e', version: 2, prereqOf: 'feature0' } ]; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result, events) { - expect(result).toBe('a'); + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(detail).toMatchObject({ value: 'a', variationIndex: 0, reason: { kind: 'FALLTHROUGH' } }); expect(events).toMatchObject(eventsShouldBe); done(); }); @@ -185,6 +191,7 @@ describe('evaluate', function() { on: true, rules: [ { + id: 'id', clauses: [ { attribute: 'key', @@ -201,8 +208,10 @@ describe('evaluate', function() { variations: ['a', 'b', 'c'] }; var user = { key: 'userkey' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe('c'); + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(detail).toMatchObject({ value: 'c', variationIndex: 2, + reason: { kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'id' } }); + expect(events).toMatchObject([]); done(); }); }); @@ -223,16 +232,17 @@ describe('evaluate', function() { variations: ['a', 'b', 'c'] }; var user = { key: 'userkey' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe('c'); + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(detail).toMatchObject({ value: 'c', variationIndex: 2, reason: { kind: 'TARGET_MATCH' } }); + expect(events).toMatchObject([]); done(); }); }); function testClauseMatch(clause, user, shouldBe, done) { var flag = makeBooleanFlagWithOneClause(clause); - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe(shouldBe); + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(detail.value).toBe(shouldBe); done(); }); } @@ -270,8 +280,8 @@ describe('evaluate', function() { defineSegment(segment, function() { var flag = makeFlagWithSegmentMatch(segment); var user = { key: 'foo' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe(true); + evaluate.evaluate(flag, user, featureStore, function(err, detail) { + expect(detail.value).toBe(true); done(); }); }); @@ -286,8 +296,8 @@ describe('evaluate', function() { defineSegment(segment, function() { var flag = makeFlagWithSegmentMatch(segment); var user = { key: 'foo' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe(false); + evaluate.evaluate(flag, user, featureStore, function(err, detail) { + expect(detail.value).toBe(false); done(); }); }); @@ -302,8 +312,8 @@ describe('evaluate', function() { defineSegment(segment, function() { var flag = makeFlagWithSegmentMatch(segment); var user = { key: 'bar' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe(false); + evaluate.evaluate(flag, user, featureStore, function(err, detail) { + expect(detail.value).toBe(false); done(); }); }); @@ -319,8 +329,8 @@ describe('evaluate', function() { defineSegment(segment, function() { var flag = makeFlagWithSegmentMatch(segment); var user = { key: 'foo' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe(true); + evaluate.evaluate(flag, user, featureStore, function(err, detail) { + expect(detail.value).toBe(true); done(); }); }); @@ -346,8 +356,8 @@ describe('evaluate', function() { defineSegment(segment, function() { var flag = makeFlagWithSegmentMatch(segment); var user = { key: 'foo', email: 'test@example.com' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe(true); + evaluate.evaluate(flag, user, featureStore, function(err, detail) { + expect(detail.value).toBe(true); done(); }); }); @@ -373,8 +383,8 @@ describe('evaluate', function() { defineSegment(segment, function() { var flag = makeFlagWithSegmentMatch(segment); var user = { key: 'foo', email: 'test@example.com' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe(false); + evaluate.evaluate(flag, user, featureStore, function(err, detail) { + expect(detail.value).toBe(false); done(); }); }); @@ -404,8 +414,8 @@ describe('evaluate', function() { defineSegment(segment, function() { var flag = makeFlagWithSegmentMatch(segment); var user = { key: 'foo', email: 'test@example.com', name: 'bob' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe(true); + evaluate.evaluate(flag, user, featureStore, function(err, detail) { + expect(detail.value).toBe(true); done(); }); }); @@ -435,8 +445,8 @@ describe('evaluate', function() { defineSegment(segment, function() { var flag = makeFlagWithSegmentMatch(segment); var user = { key: 'foo', email: 'test@example.com', name: 'bob' }; - evaluate.evaluate(flag, user, featureStore, function(err, variation, result) { - expect(result).toBe(false); + evaluate.evaluate(flag, user, featureStore, function(err, detail) { + expect(detail.value).toBe(false); done(); }); }); @@ -463,9 +473,9 @@ describe('evaluate', function() { rules.push({ clauses: [clause], variation: 1 }); } flag.rules = rules; - evaluate.evaluate(flag, {key: 'user'}, featureStore, function(err, variation, value) { + evaluate.evaluate(flag, {key: 'user'}, featureStore, function(err, detail) { expect(err).toEqual(null); - expect(value).toEqual(false); + expect(detail.value).toEqual(false); done(); }); }); @@ -492,9 +502,9 @@ describe('evaluate', function() { } var rule = { clauses: clauses, variation: 1 }; flag.rules = [rule]; - evaluate.evaluate(flag, {key: 'user'}, featureStore, function(err, variation, value) { + evaluate.evaluate(flag, {key: 'user'}, featureStore, function(err, detail) { expect(err).toEqual(null); - expect(value).toEqual(true); + expect(detail.value).toEqual(true); done(); }); }); diff --git a/test/event_processor-test.js b/test/event_processor-test.js index 6ae8014..eb0f8f8 100644 --- a/test/event_processor-test.js +++ b/test/event_processor-test.js @@ -66,6 +66,7 @@ describe('EventProcessor', function() { expect(e.variation).toEqual(source.variation); expect(e.value).toEqual(source.value); expect(e.default).toEqual(source.default); + expect(e.reason).toEqual(source.reason); if (inlineUser) { expect(e.user).toEqual(inlineUser); } else { @@ -184,6 +185,22 @@ describe('EventProcessor', function() { }); }); + it('can include reason in feature event', function(done) { + var config = Object.assign({}, defaultConfig, { inlineUsersInEvents: true }); + ep = EventProcessor(sdkKey, config); + var e = { kind: 'feature', creationDate: 1000, user: user, key: 'flagkey', + version: 11, variation: 1, value: 'value', trackEvents: true, + reason: { kind: 'FALLTHROUGH' } }; + ep.sendEvent(e); + + flushAndGetRequest(function(output) { + expect(output.length).toEqual(2); + checkFeatureEvent(output[0], e, false, user); + checkSummaryEvent(output[1]); + done(); + }); + }); + it('still generates index event if inlineUsers is true but feature event is not tracked', function(done) { var config = Object.assign({}, defaultConfig, { inlineUsersInEvents: true }); ep = EventProcessor(sdkKey, config); From bece5f2c108564fc7b8a1b14378fe8845a794cf5 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 23 Aug 2018 16:34:06 -0700 Subject: [PATCH 024/330] add doc comment --- index.d.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/index.d.ts b/index.d.ts index 6440831..07ea0de 100644 --- a/index.d.ts +++ b/index.d.ts @@ -66,6 +66,13 @@ declare module 'ldclient-node' { */ getFlagValue: (key: string) => LDFlagValue; + /** + * Returns the evaluation reason for a feature flag at the time the state was recorded. + * It will be null if reasons were not recorded, or if there was no such flag. + * @param key the flag key + */ + getFlagReason: (key: string) => LDEvaluationReason; + /** * Returns a map of feature flag keys to values. If a flag would have evaluated to the * default value, its value will be null. From 69f27dac7b9c2636d7b47e1695b54cc7f4b828f8 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 23 Aug 2018 16:34:54 -0700 Subject: [PATCH 025/330] uncomment test --- test/LDClient-test.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/test/LDClient-test.js b/test/LDClient-test.js index 7c0cdb6..b07b258 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -423,27 +423,27 @@ describe('LDClient', function() { }); }); - // it('should not overflow the call stack when evaluating a huge number of flags', function(done) { - // var flagCount = 5000; - // var flags = {}; - // for (var i = 0; i < flagCount; i++) { - // var key = 'feature' + i; - // var flag = { - // key: key, - // version: 1, - // on: false - // }; - // flags[key] = flag; - // } - // var client = createOnlineClientWithFlags(flags); - // client.on('ready', function() { - // client.allFlags({key: 'user'}, function(err, result) { - // expect(err).toEqual(null); - // expect(Object.keys(result).length).toEqual(flagCount); - // done(); - // }); - // }); - // }); + it('should not overflow the call stack when evaluating a huge number of flags', function(done) { + var flagCount = 5000; + var flags = {}; + for (var i = 0; i < flagCount; i++) { + var key = 'feature' + i; + var flag = { + key: key, + version: 1, + on: false + }; + flags[key] = flag; + } + var client = createOnlineClientWithFlags(flags); + client.on('ready', function() { + client.allFlags({key: 'user'}, function(err, result) { + expect(err).toEqual(null); + expect(Object.keys(result).length).toEqual(flagCount); + done(); + }); + }); + }); it('should not crash when closing an offline client', function(done) { var client = LDClient.init('sdk_key', {offline: true}); From ad940ea4415d22671d8d85f4d6dcf020bb478f87 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 23 Aug 2018 17:22:00 -0700 Subject: [PATCH 026/330] tests for default logic --- index.js | 2 +- test/LDClient-test.js | 59 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 3415e03..6aad80e 100644 --- a/index.js +++ b/index.js @@ -243,7 +243,7 @@ var newClient = function(sdkKey, config) { } } - if (detail.value === null) { + if (detail.variationIndex === null) { config.logger.debug("Result value is null in variation"); detail.value = defaultVal; } diff --git a/test/LDClient-test.js b/test/LDClient-test.js index b07b258..e007864 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -143,6 +143,35 @@ describe('LDClient', function() { }); }); + it('returns default from variation() for unknown flag', function(done) { + var client = createOnlineClientWithFlags({ }); + var user = { key: 'user' }; + client.on('ready', function() { + client.variation('flagkey', user, 'default', function(err, result) { + expect(err).toBeNull(); + expect(result).toEqual('default'); + done(); + }); + }); + }); + + it('returns default from variation() for flag that evaluates to null', function(done) { + var flag = { + key: 'flagkey', + on: false, + offVariation: null + }; + var client = createOnlineClientWithFlags({ flagkey: flag }); + var user = { key: 'user' }; + client.on('ready', function() { + client.variation(flag.key, user, 'default', function(err, result) { + expect(err).toBeNull(); + expect(result).toEqual('default'); + done(); + }); + }); + }); + it('evaluates a flag with variationDetail()', function(done) { var flag = { key: 'flagkey', @@ -164,6 +193,36 @@ describe('LDClient', function() { }); }); + it('returns default from variationDetail() for unknown flag', function(done) { + var client = createOnlineClientWithFlags({ }); + var user = { key: 'user' }; + client.on('ready', function() { + client.variationDetail('flagkey', user, 'default', function(err, result) { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); + done(); + }); + }); + }); + + it('returns default from variationDetail() for flag that evaluates to null', function(done) { + var flag = { + key: 'flagkey', + on: false, + offVariation: null + }; + var client = createOnlineClientWithFlags({ flagkey: flag }); + var user = { key: 'user' }; + client.on('ready', function() { + client.variationDetail(flag.key, user, 'default', function(err, result) { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'default', variationIndex: null, reason: { kind: 'OFF' } }); + done(); + }); + }); + }); + it('generates an event for an existing feature', function(done) { var flag = { key: 'flagkey', From 45dad9d864acf4a0d17a1e2f6dadbd7d9ec9531c Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Fri, 24 Aug 2018 11:49:10 -0700 Subject: [PATCH 027/330] typo --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 5f6f1fb..7cb2044 100644 --- a/index.js +++ b/index.js @@ -233,7 +233,7 @@ var newClient = function(sdkKey, config) { } client.allFlags = function(user, callback) { - config.logger.warn("allFlags() is deprecated. Call 'allFlagsState' instead and call toJson() on the result"); + config.logger.warn("allFlags() is deprecated. Call 'allFlagsState' instead and call toJSON() on the result"); return wrapPromiseCallback( client.allFlagsState(user).then(function(state) { return state.allValues(); From 277c77c3d24075413181c242cef16f641de4ff20 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 27 Aug 2018 14:57:29 -0700 Subject: [PATCH 028/330] fix allFlagsState behavior when options are omitted --- index.js | 7 ++++++- test/LDClient-test.js | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 7cb2044..d70f12b 100644 --- a/index.js +++ b/index.js @@ -242,7 +242,12 @@ var newClient = function(sdkKey, config) { } client.allFlagsState = function(user, options, callback) { - options = options || {}; + if (callback === undefined && typeof(options) === 'function') { + callback = options; + options = {}; + } else { + options = options || {}; + } return wrapPromiseCallback(new Promise(function(resolve, reject) { sanitizeUser(user); diff --git a/test/LDClient-test.js b/test/LDClient-test.js index bb132a7..e297e45 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -333,6 +333,20 @@ describe('LDClient', function() { }); }); + it('can omit options parameter for allFlagsState()', function(done) { + var flag = { key: 'key', on: false, offVariation: 0, variations: ['value'] }; + var client = createOnlineClientWithFlags({ 'key': flag }); + var user = { key: 'user' }; + client.on('ready', function() { + client.allFlagsState(user, function(err, state) { + expect(err).toBeNull(); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({ 'key': 'value' }); + done(); + }); + }); + }); + it('should not overflow the call stack when evaluating a huge number of flags', function(done) { var flagCount = 5000; var flags = {}; From 1217a54784f7b0d955aad09e210d1c1ebbc5698d Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 29 Aug 2018 11:38:34 -0700 Subject: [PATCH 029/330] fix for ch22995 - send event for prerequisite even if it's off --- evaluate_flag.js | 8 +++----- test/evaluate_flag-test.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/evaluate_flag.js b/evaluate_flag.js index 4e68bef..d985778 100644 --- a/evaluate_flag.js +++ b/evaluate_flag.js @@ -58,17 +58,15 @@ function checkPrerequisites(flag, user, featureStore, events, cb) { callback({ key: prereq.key, err: new Error("Could not retrieve prerequisite feature flag \"" + prereq.key + "\"") }); return; } - if (!f.on) { - callback({ key: prereq.key }); - return; - } evalInternal(f, user, featureStore, events, function(err, detail) { // If there was an error, the value is null, the variation index is out of range, // or the value does not match the indexed variation the prerequisite is not satisfied events.push(createFlagEvent(f.key, f, user, detail, null, flag.key, true)); if (err) { callback({ key: prereq.key, err: err }); - } else if (detail.variationIndex != prereq.variation) { + } else if (!f.on || detail.variationIndex != prereq.variation) { + // Note that if the prerequisite flag is off, we don't consider it a match no matter what its + // off variation was. But we still evaluate it and generate an event. callback({ key: prereq.key }); } else { // The prerequisite was satisfied diff --git a/test/evaluate_flag-test.js b/test/evaluate_flag-test.js index 181e9c6..ca6ce35 100644 --- a/test/evaluate_flag-test.js +++ b/test/evaluate_flag-test.js @@ -116,6 +116,43 @@ describe('evaluate', function() { }); }); + it('returns off variation and event if prerequisite is off', function(done) { + var flag = { + key: 'feature0', + on: true, + prerequisites: [{key: 'feature1', variation: 1}], + fallthrough: { variation: 0 }, + offVariation: 1, + targets: [], + rules: [], + variations: ['a', 'b', 'c'], + version: 1 + }; + var flag1 = { + key: 'feature1', + on: false, + offVariation: 1, + // note that even though it returns the desired variation, it is still off and therefore not a match + fallthrough: { variation: 0 }, + targets: [], + rules: [], + variations: ['d', 'e'], + version: 2 + }; + defineFeatures([flag, flag1], function() { + var user = { key: 'x' }; + var eventsShouldBe = [ + { kind: 'feature', key: 'feature1', variation: 1, value: 'e', version: 2, prereqOf: 'feature0' } + ]; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(detail).toMatchObject({ value: 'b', variationIndex: 1, + reason: { kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'feature1' } }); + expect(events).toMatchObject(eventsShouldBe); + done(); + }); + }); + }); + it('returns off variation and event if prerequisite is not met', function(done) { var flag = { key: 'feature0', From 07b6eb490509633969f98c55599db118ebbcf4df Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 29 Aug 2018 12:06:35 -0700 Subject: [PATCH 030/330] validate TypeScript definitions in CI build --- index.d.ts | 2 +- package-lock.json | 6 ++++++ package.json | 3 ++- test-types.ts | 3 +++ tsconfig.json | 6 +++++- 5 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 test-types.ts diff --git a/index.d.ts b/index.d.ts index b94a870..ea534d0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -84,7 +84,7 @@ declare module 'ldclient-node' { * the needs of the JavaScript client. */ toJSON: () => object; - }; + } /** * LaunchDarkly initialization options. diff --git a/package-lock.json b/package-lock.json index db4b1e6..2468223 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5877,6 +5877,12 @@ "integrity": "sha1-dkb7XxiHHPu3dJ5pvTmmOI63RQw=", "dev": true }, + "typescript": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.1.tgz", + "integrity": "sha512-zQIMOmC+372pC/CCVLqnQ0zSBiY7HHodU7mpQdjiZddek4GMj31I3dUJ7gAs9o65X7mnRma6OokOkc6f9jjfBg==", + "dev": true + }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", diff --git a/package.json b/package.json index 2ac8809..25f47ee 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "devDependencies": { "jest": "22.4.3", "jest-junit": "3.6.0", - "nock": "9.2.3" + "nock": "9.2.3", + "typescript": "3.0.1" }, "jest": { "rootDir": ".", diff --git a/test-types.ts b/test-types.ts new file mode 100644 index 0000000..234febc --- /dev/null +++ b/test-types.ts @@ -0,0 +1,3 @@ + +// This file exists only so that we can run the TypeScript compiler in the CI build +// to valid our index.d.ts file. diff --git a/tsconfig.json b/tsconfig.json index 918b0e1..3b8c161 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,5 +5,9 @@ "lib": [ "es6" ] - } + }, + "files": [ + "index.d.ts", + "test-types.ts" + ] } \ No newline at end of file From 01c83716a25f619591a427249f6180a52524760c Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 29 Aug 2018 12:08:47 -0700 Subject: [PATCH 031/330] add tsc build step --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index c876306..62bef83 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,6 +16,7 @@ node-template: &node-template command: npm test environment: JEST_JUNIT_OUTPUT: "reports/junit/js-test-results.xml" + - run: tsc - run: | if [[ $CIRCLE_JOB == current-release ]]; then npm audit From a15959c6e0f3cdc4b7361b59864044a7c725678e Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 29 Aug 2018 12:11:16 -0700 Subject: [PATCH 032/330] fix tsc command --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 62bef83..b8e5767 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,7 +16,7 @@ node-template: &node-template command: npm test environment: JEST_JUNIT_OUTPUT: "reports/junit/js-test-results.xml" - - run: tsc + - run: node_modules/typescript/bin/tsc - run: | if [[ $CIRCLE_JOB == current-release ]]; then npm audit From aad1895de43a7e31654a78b9e2c2df46eb168771 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 29 Aug 2018 12:18:47 -0700 Subject: [PATCH 033/330] add npm script --- .circleci/config.yml | 2 +- package.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b8e5767..8b8a577 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,7 +16,7 @@ node-template: &node-template command: npm test environment: JEST_JUNIT_OUTPUT: "reports/junit/js-test-results.xml" - - run: node_modules/typescript/bin/tsc + - run: npm run check-typescript - run: | if [[ $CIRCLE_JOB == current-release ]]; then npm audit diff --git a/package.json b/package.json index 25f47ee..e7c7f52 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "LaunchDarkly SDK for Node.js", "main": "index.js", "scripts": { - "test": "jest --ci --forceExit" + "test": "jest --ci --forceExit", + "check-typescript": "node_modules/typescript/bin/tsc" }, "types": "./index.d.ts", "repository": { From 623956a1f55fea457c411db2efc96e47063f46ef Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 29 Aug 2018 12:28:18 -0700 Subject: [PATCH 034/330] typo --- test-types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-types.ts b/test-types.ts index 234febc..331dd28 100644 --- a/test-types.ts +++ b/test-types.ts @@ -1,3 +1,3 @@ // This file exists only so that we can run the TypeScript compiler in the CI build -// to valid our index.d.ts file. +// to validate our index.d.ts file. From 48e750b5ffef1e05fd0f094fc22dbb411baed256 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 29 Aug 2018 13:05:40 -0700 Subject: [PATCH 035/330] TS syntax error --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index efd592c..ab78012 100644 --- a/index.d.ts +++ b/index.d.ts @@ -626,7 +626,7 @@ declare module 'ldclient-node' { key: string, user: LDUser, defaultValue: LDFlagValue, - callback?: (err: ay, res: LDEvaluationDetail) => void + callback?: (err: any, res: LDEvaluationDetail) => void ) => Promise; toggle: ( From 7f2c79755553392d17a5ae65c894e3e462f26e3b Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 30 Aug 2018 18:19:42 -0700 Subject: [PATCH 036/330] fix "indirect put" and add tests for streaming --- feature_store.js | 2 +- streaming.js | 40 ++++- test/streaming-test.js | 321 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 355 insertions(+), 8 deletions(-) create mode 100644 test/streaming-test.js diff --git a/feature_store.js b/feature_store.js index c65a3d6..ec21e22 100644 --- a/feature_store.js +++ b/feature_store.js @@ -73,7 +73,7 @@ function InMemoryFeatureStore() { var items = this.allData[kind.namespace]; if (!items) { items = {}; - this.allData[kind] = items; + this.allData[kind.namespace] = items; } if (Object.hasOwnProperty.call(items, key)) { diff --git a/streaming.js b/streaming.js index 16b5498..18a67ee 100644 --- a/streaming.js +++ b/streaming.js @@ -3,18 +3,20 @@ var errors = require('./errors'); var EventSource = require('./eventsource'); var dataKind = require('./versioned_data_kind'); -function StreamProcessor(sdkKey, config, requestor) { +function StreamProcessor(sdkKey, config, requestor, eventSourceFactory) { var processor = {}, featureStore = config.featureStore, es; + eventSourceFactory = eventSourceFactory || EventSource; + function getKeyFromPath(kind, path) { return path.startsWith(kind.streamApiPath) ? path.substring(kind.streamApiPath.length) : null; } processor.start = function(fn) { var cb = fn || function(){}; - es = new EventSource(config.streamUri + "/all", + es = eventSourceFactory(config.streamUri + "/all", { agent: config.proxyAgent, headers: {'Authorization': sdkKey,'User-Agent': config.userAgent} @@ -24,10 +26,22 @@ function StreamProcessor(sdkKey, config, requestor) { cb(new errors.LDStreamingError(err.message, err.code)); }; + function reportJsonError(type, data) { + config.logger.error('Stream received invalid data in "' + type + '" message'); + config.logger.debug('Invalid JSON follows: ' + data); + cb(new errors.LDStreamingError('Malformed JSON data in event stream')); + } + es.addEventListener('put', function(e) { config.logger.debug('Received put event'); if (e && e.data) { - var all = JSON.parse(e.data); + var all; + try { + all = JSON.parse(e.data); + } catch (err) { + reportJsonError('put', e.data); + return; + } var initData = {}; initData[dataKind.features.namespace] = all.data.flags; initData[dataKind.segments.namespace] = all.data.segments; @@ -42,7 +56,13 @@ function StreamProcessor(sdkKey, config, requestor) { es.addEventListener('patch', function(e) { config.logger.debug('Received patch event'); if (e && e.data) { - var patch = JSON.parse(e.data); + var patch; + try { + patch = JSON.parse(e.data); + } catch (err) { + reportJsonError('patch', e.data); + return; + } for (var k in dataKind) { var kind = dataKind[k]; var key = getKeyFromPath(kind, patch.path); @@ -60,8 +80,14 @@ function StreamProcessor(sdkKey, config, requestor) { es.addEventListener('delete', function(e) { config.logger.debug('Received delete event'); if (e && e.data) { - var data = JSON.parse(e.data), - version = data.version; + var data, version; + try { + data = JSON.parse(e.data); + } catch (err) { + reportJsonError('delete', e.data); + return; + } + version = data.version; for (var k in dataKind) { var kind = dataKind[k]; var key = getKeyFromPath(kind, data.path); @@ -78,7 +104,7 @@ function StreamProcessor(sdkKey, config, requestor) { es.addEventListener('indirect/put', function(e) { config.logger.debug('Received indirect put event') - requestor.requestAllFlags(function (err, resp) { + requestor.requestAllData(function (err, resp) { if (err) { cb(err); } else { diff --git a/test/streaming-test.js b/test/streaming-test.js new file mode 100644 index 0000000..fea8ec5 --- /dev/null +++ b/test/streaming-test.js @@ -0,0 +1,321 @@ +var InMemoryFeatureStore = require('../feature_store'); +var StreamProcessor = require('../streaming'); +var dataKind = require('../versioned_data_kind'); + +describe('StreamProcessor', function() { + var sdkKey = 'SDK_KEY'; + + function fakeEventSource() { + var es = { handlers: {} }; + es.initialize = function(url, options) { + es.url = url; + es.options = options; + return es; + }; + es.addEventListener = function(type, handler) { + es.handlers[type] = handler; + }; + return es; + } + + function fakeLogger() { + return { + debug: jest.fn(), + error: jest.fn() + }; + } + + function expectJsonError(config, done) { + return function(err) { + expect(err).not.toBe(undefined); + expect(err.message).toEqual('Malformed JSON data in event stream'); + expect(config.logger.error).toHaveBeenCalled(); + done(); + } + } + + it('uses expected URL', function() { + var config = { streamUri: 'http://test' }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + sp.start(); + expect(es.url).toEqual(config.streamUri + '/all'); + }); + + it('sets expected headers', function() { + var config = { streamUri: 'http://test', userAgent: 'agent' }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + sp.start(); + expect(es.options.headers['Authorization']).toEqual(sdkKey); + expect(es.options.headers['User-Agent']).toEqual(config.userAgent); + }); + + describe('put message', function() { + var putData = { + data: { + flags: { + flagkey: { key: 'flagkey', version: 1 } + }, + segments: { + segkey: { key: 'segkey', version: 2 } + } + } + }; + + it('causes flags and segments to be stored', function(done) { + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + sp.start(); + + es.handlers.put({ data: JSON.stringify(putData) }); + + featureStore.initialized(function(flag) { + expect(flag).toEqual(true); + }); + + featureStore.get(dataKind.features, 'flagkey', function(f) { + expect(f.version).toEqual(1); + featureStore.get(dataKind.segments, 'segkey', function(s) { + expect(s.version).toEqual(2); + done(); + }); + }); + }); + + it('calls initialization callback', function(done) { + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + + var cb = function(err) { + expect(err).toBe(undefined); + done(); + } + + sp.start(cb); + es.handlers.put({ data: JSON.stringify(putData) }); + }); + + it('passes error to callback if data is invalid', function(done) { + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + + sp.start(expectJsonError(config, done)); + es.handlers.put({ data: '{not-good' }); + }); + }); + + describe('patch message', function() { + it('updates flag', function(done) { + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + + var patchData = { + path: '/flags/flagkey', + data: { key: 'flagkey', version: 1 } + }; + + sp.start(); + es.handlers.patch({ data: JSON.stringify(patchData) }); + + featureStore.get(dataKind.features, 'flagkey', function(f) { + expect(f.version).toEqual(1); + done(); + }); + }); + + it('updates segment', function(done) { + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + + var patchData = { + path: '/segments/segkey', + data: { key: 'segkey', version: 1 } + }; + + sp.start(); + es.handlers.patch({ data: JSON.stringify(patchData) }); + + featureStore.get(dataKind.segments, 'segkey', function(s) { + expect(s.version).toEqual(1); + done(); + }); + }); + + it('passes error to callback if data is invalid', function(done) { + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + + sp.start(expectJsonError(config, done)); + es.handlers.patch({ data: '{not-good' }); + }); + }); + + describe('delete message', function() { + it('deletes flag', function(done) { + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + + sp.start(); + + var flag = { key: 'flagkey', version: 1 } + featureStore.upsert(dataKind.features, flag, function() { + featureStore.get(dataKind.features, flag.key, function(f) { + expect(f).toEqual(flag); + + var deleteData = { path: '/flags/' + flag.key, version: 2 }; + es.handlers.delete({ data: JSON.stringify(deleteData) }); + + featureStore.get(dataKind.features, flag.key, function(f) { + expect(f).toBe(null); + done(); + }) + }); + }); + }); + + it('deletes segment', function(done) { + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + + sp.start(); + + var segment = { key: 'segkey', version: 1 } + featureStore.upsert(dataKind.segments, segment, function() { + featureStore.get(dataKind.segments, segment.key, function(s) { + expect(s).toEqual(segment); + + var deleteData = { path: '/segments/' + segment.key, version: 2 }; + es.handlers.delete({ data: JSON.stringify(deleteData) }); + + featureStore.get(dataKind.segments, segment.key, function(s) { + expect(s).toBe(null); + done(); + }) + }); + }); + }); + + it('passes error to callback if data is invalid', function(done) { + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, null, es.initialize); + + sp.start(expectJsonError(config, done)); + es.handlers.delete({ data: '{not-good' }); + }); + }); + + describe('indirect put message', function() { + var allData = { + flags: { + flagkey: { key: 'flagkey', version: 1 } + }, + segments: { + segkey: { key: 'segkey', version: 2 } + } + }; + var fakeRequestor = { + requestAllData: function(cb) { + cb(null, JSON.stringify(allData)); + } + }; + + it('requests and stores flags and segments', function(done) { + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, fakeRequestor, es.initialize); + + sp.start(); + + es.handlers['indirect/put']({}); + + setImmediate(function() { + featureStore.get(dataKind.features, 'flagkey', function(f) { + expect(f.version).toEqual(1); + featureStore.get(dataKind.segments, 'segkey', function(s) { + expect(s.version).toEqual(2); + featureStore.initialized(function(flag) { + expect(flag).toBe(true); + }); + done(); + }); + }); + }); + }); + }); + + describe('indirect patch message', function() { + it('requests and updates flag', function(done) { + var flag = { key: 'flagkey', version: 1 }; + var fakeRequestor = { + requestObject: function(kind, key, cb) { + expect(kind).toBe(dataKind.features); + expect(key).toEqual(flag.key); + cb(null, JSON.stringify(flag)); + } + }; + + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, fakeRequestor, es.initialize); + + sp.start(); + + es.handlers['indirect/patch']({ data: '/flags/flagkey' }); + + setImmediate(function() { + featureStore.get(dataKind.features, 'flagkey', function(f) { + expect(f.version).toEqual(1); + done(); + }); + }); + }); + + it('requests and updates segment', function(done) { + var segment = { key: 'segkey', version: 1 }; + var fakeRequestor = { + requestObject: function(kind, key, cb) { + expect(kind).toBe(dataKind.segments); + expect(key).toEqual(segment.key); + cb(null, JSON.stringify(segment)); + } + }; + + var featureStore = InMemoryFeatureStore(); + var config = { featureStore: featureStore, logger: fakeLogger() }; + var es = fakeEventSource(); + var sp = StreamProcessor(sdkKey, config, fakeRequestor, es.initialize); + + sp.start(); + + es.handlers['indirect/patch']({ data: '/segments/segkey' }); + + setImmediate(function() { + featureStore.get(dataKind.segments, 'segkey', function(s) { + expect(s.version).toEqual(1); + done(); + }); + }); + }); + }); +}); From 135b45871067f1f89f3472d09d9ae662623a0f8b Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 5 Sep 2018 10:42:19 -0700 Subject: [PATCH 037/330] fix error handling for malformed flags --- evaluate_flag.js | 6 +- test/evaluate_flag-test.js | 171 +++++++++++++++++++++++++++++++------ 2 files changed, 148 insertions(+), 29 deletions(-) diff --git a/evaluate_flag.js b/evaluate_flag.js index d985778..6eb1dcb 100644 --- a/evaluate_flag.js +++ b/evaluate_flag.js @@ -258,7 +258,7 @@ function matchAny(matchFn, value, values) { function getVariation(flag, index, reason, cb) { if (index === null || index === undefined || index < 0 || index >= flag.variations.length) { - cb(new Error('Invalid variation index in flag', errResult('MALFORMED_FLAG'))); + cb(new Error('Invalid variation index in flag'), errorResult('MALFORMED_FLAG')); } else { cb(null, { value: flag.variations[index], variationIndex: index, reason: reason }); } @@ -274,11 +274,11 @@ function getOffResult(flag, reason, cb) { function getResultForVariationOrRollout(r, user, flag, reason, cb) { if (!r) { - cb(new Error('Fallthrough variation undefined'), errResult('MALFORMED_FLAG')); + cb(new Error('Fallthrough variation undefined'), errorResult('MALFORMED_FLAG')); } else { var index = variationForUser(r, user, flag); if (index === null) { - cb(new Error('Variation/rollout object with no variation or rollout'), errResult('MALFORMED_FLAG')); + cb(new Error('Variation/rollout object with no variation or rollout'), errorResult('MALFORMED_FLAG')); } else { getVariation(flag, index, reason, cb); } diff --git a/test/evaluate_flag-test.js b/test/evaluate_flag-test.js index ca6ce35..61367da 100644 --- a/test/evaluate_flag-test.js +++ b/test/evaluate_flag-test.js @@ -28,6 +28,21 @@ function evalBooleanFlag(flag, user, cb) { }); } +function makeFlagWithRules(rules, fallthrough) { + if (!fallthrough) { + fallthrough = { variation: 0 }; + } + return { + key: 'feature', + on: true, + rules: rules, + targets: [], + fallthrough: fallthrough, + offVariation: 1, + variations: ['a', 'b', 'c'] + }; +} + function makeBooleanFlagWithOneClause(clause) { return { key: 'feature', @@ -80,17 +95,44 @@ describe('evaluate', function() { }); }); - it('returns fallthrough if flag is on and there are no rules', function(done) { + it('returns error if off variation is too high', function(done) { var flag = { key: 'feature', - on: true, - rules: [], - targets: [], - offVariation: null, + on: false, + offVariation: 99, + fallthrough: { variation: 0 }, + variations: ['a', 'b', 'c'] + }; + var user = { key: 'x' }; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(err).toEqual(Error('Invalid variation index in flag')); + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' } }); + expect(events).toMatchObject([]); + done(); + }); + }); + + it('returns error if off variation is negative', function(done) { + var flag = { + key: 'feature', + on: false, + offVariation: -1, fallthrough: { variation: 0 }, variations: ['a', 'b', 'c'] }; var user = { key: 'x' }; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(err).toEqual(Error('Invalid variation index in flag')); + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' } }); + expect(events).toMatchObject([]); + done(); + }); + }); + + it('returns fallthrough variation if flag is on and no rules match', function(done) { + var rule = { id: 'id', clauses: [{ attribute: 'key', op: 'in', values: ['other'] }], variation: 2 }; + var flag = makeFlagWithRules([rule], { variation: 0 }); + var user = { key: 'x' }; evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { expect(detail).toMatchObject({ value: 'a', variationIndex: 0, reason: { kind: 'FALLTHROUGH' } }); expect(events).toMatchObject([]); @@ -98,6 +140,54 @@ describe('evaluate', function() { }); }); + it('returns error if fallthrough variation is too high', function(done) { + var rule = { id: 'id', clauses: [{ attribute: 'key', op: 'in', values: ['other'] }], variation: 99 }; + var flag = makeFlagWithRules([rule], { variation: 99 }); + var user = { key: 'x' }; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(err).toEqual(Error('Invalid variation index in flag')); + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' }}); + expect(events).toMatchObject([]); + done(); + }); + }); + + it('returns error if fallthrough variation is negative', function(done) { + var rule = { id: 'id', clauses: [{ attribute: 'key', op: 'in', values: ['other'] }], variation: 99 }; + var flag = makeFlagWithRules([rule], { variation: -1 }); + var user = { key: 'x' }; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(err).toEqual(Error('Invalid variation index in flag')); + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' }}); + expect(events).toMatchObject([]); + done(); + }); + }); + + it('returns error if fallthrough has no variation or rollout', function(done) { + var rule = { id: 'id', clauses: [{ attribute: 'key', op: 'in', values: ['other'] }], variation: 99 }; + var flag = makeFlagWithRules([rule], { }); + var user = { key: 'x' }; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(err).toEqual(Error('Variation/rollout object with no variation or rollout')); + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' }}); + expect(events).toMatchObject([]); + done(); + }); + }); + + it('returns error if fallthrough has rollout with no variations', function(done) { + var rule = { id: 'id', clauses: [{ attribute: 'key', op: 'in', values: ['other'] }], variation: 99 }; + var flag = makeFlagWithRules([rule], { rollout: { variations: [] } }); + var user = { key: 'x' }; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(err).toEqual(Error('Variation/rollout object with no variation or rollout')); + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' }}); + expect(events).toMatchObject([]); + done(); + }); + }); + it('returns off variation if prerequisite is not found', function(done) { var flag = { key: 'feature0', @@ -223,27 +313,8 @@ describe('evaluate', function() { }); it('matches user from rules', function(done) { - var flag = { - key: 'feature0', - on: true, - rules: [ - { - id: 'id', - clauses: [ - { - attribute: 'key', - op: 'in', - values: ['userkey'] - } - ], - variation: 2 - } - ], - targets: [], - fallthrough: { variation: 0 }, - offVariation: 1, - variations: ['a', 'b', 'c'] - }; + var rule = { id: 'id', clauses: [{ attribute: 'key', op: 'in', values: ['userkey'] }], variation: 2 }; + var flag = makeFlagWithRules([rule]); var user = { key: 'userkey' }; evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { expect(detail).toMatchObject({ value: 'c', variationIndex: 2, @@ -253,6 +324,54 @@ describe('evaluate', function() { }); }); + it('returns error if rule variation is too high', function(done) { + var rule = { id: 'id', clauses: [{ attribute: 'key', op: 'in', values: ['userkey'] }], variation: 99 }; + var flag = makeFlagWithRules([rule]); + var user = { key: 'userkey' }; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(err).toEqual(Error('Invalid variation index in flag')); + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' }}); + expect(events).toMatchObject([]); + done(); + }); + }); + + it('returns error if rule variation is negative', function(done) { + var rule = { id: 'id', clauses: [{ attribute: 'key', op: 'in', values: ['userkey'] }], variation: -1 }; + var flag = makeFlagWithRules([rule]); + var user = { key: 'userkey' }; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(err).toEqual(Error('Invalid variation index in flag')); + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' }}); + expect(events).toMatchObject([]); + done(); + }); + }); + + it('returns error if rule has no variation or rollout', function(done) { + var rule = { id: 'id', clauses: [{ attribute: 'key', op: 'in', values: ['userkey'] }] }; + var flag = makeFlagWithRules([rule]); + var user = { key: 'userkey' }; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(err).toEqual(Error('Variation/rollout object with no variation or rollout')); + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' }}); + expect(events).toMatchObject([]); + done(); + }); + }); + + it('returns error if rule has rollout with no variations', function(done) { + var rule = { id: 'id', clauses: [{ attribute: 'key', op: 'in', values: ['userkey'] }], rollout: { variations: [] } }; + var flag = makeFlagWithRules([rule]); + var user = { key: 'userkey' }; + evaluate.evaluate(flag, user, featureStore, function(err, detail, events) { + expect(err).toEqual(Error('Variation/rollout object with no variation or rollout')); + expect(detail).toMatchObject({ value: null, variationIndex: null, reason: { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' }}); + expect(events).toMatchObject([]); + done(); + }); + }); + it('matches user from targets', function(done) { var flag = { key: 'feature0', From e4af191bc9a886cf66c475eea91bfdd8e05582f9 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 5 Sep 2018 17:44:52 -0700 Subject: [PATCH 038/330] prevent unhandled rejection from background flush --- event_processor.js | 2 +- index.d.ts | 8 +++++++- index.js | 10 +--------- test/event_processor-test.js | 20 ++++++++++++++++++++ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/event_processor.js b/event_processor.js index 42bfdb2..1a8ec59 100644 --- a/event_processor.js +++ b/event_processor.js @@ -226,7 +226,7 @@ function EventProcessor(sdkKey, config, errorReporter) { } flushTimer = setInterval(function() { - ep.flush(); + ep.flush().then(function() { } , function() { }); }, config.flushInterval * 1000); flushUsersTimer = setInterval(function() { userKeysCache.removeAll(); diff --git a/index.d.ts b/index.d.ts index ab78012..6636e1c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -733,7 +733,13 @@ declare module 'ldclient-node' { * These are flushed periodically (see configuration option: flushInterval) * and when the queue size limit (see configuration option: capacity) is reached. * - * @returns a Promise which resolves once flushing is finished + * @param callback + * A function which will be called when the flush completes; if omitted, you + * will receive a Promise instead + * + * @returns a Promise which resolves once flushing is finished, if you did not + * provide a callback; note that it will be rejected if the HTTP request fails, so be + * sure to provide a rejection handler if you are not using a callback */ flush: (callback?: (err: any, res: boolean) => void) => Promise; } diff --git a/index.js b/index.js index 79a97c5..65aad25 100644 --- a/index.js +++ b/index.js @@ -58,8 +58,7 @@ var newClient = function(sdkKey, config) { queue = [], requestor, updateProcessor, - eventProcessor, - flushTimer; + eventProcessor; config = configuration.validate(config); @@ -323,7 +322,6 @@ var newClient = function(sdkKey, config) { updateProcessor.close(); } config.featureStore.close(); - clearInterval(flushTimer); } client.isOffline = function() { @@ -362,10 +360,6 @@ var newClient = function(sdkKey, config) { eventProcessor.sendEvent(event); } - function backgroundFlush() { - client.flush().then(function() {}, function() {}); - } - function deprecatedMethod(oldName, newName) { client[oldName] = function() { config.logger.warn(messages.deprecated(oldName, newName)); @@ -377,8 +371,6 @@ var newClient = function(sdkKey, config) { deprecatedMethod('is_offline', 'isOffline'); deprecatedMethod('secure_mode_hash', 'secureModeHash'); - flushTimer = setInterval(backgroundFlush, config.flushInterval * 1000); - return client; }; diff --git a/test/event_processor-test.js b/test/event_processor-test.js index eb0f8f8..75646ce 100644 --- a/test/event_processor-test.js +++ b/test/event_processor-test.js @@ -481,4 +481,24 @@ describe('EventProcessor', function() { it('retries after a 503 error', function(done) { verifyRecoverableHttpError(done, 503); }); + + it('swallows errors from failed background flush', function(done) { + // This test verifies that when a background flush fails, we don't emit an unhandled + // promise rejection. Jest will fail the test if we do that. + + var config = Object.assign({}, defaultConfig, { flushInterval: 0.25 }); + ep = EventProcessor(sdkKey, config); + ep.sendEvent({ kind: 'identify', creationDate: 1000, user: user }); + + var req1 = nock(eventsUri).post('/bulk').reply(500); + var req2 = nock(eventsUri).post('/bulk').reply(500); + + // unfortunately we must wait for both the flush interval and the 1-second retry interval + var delay = 1500; + setTimeout(function() { + expect(req1.isDone()).toEqual(true); + expect(req2.isDone()).toEqual(true); + done(); + }, delay); + }); }); From 3ea8cb29aac5245208fcc92604216033aea6a75d Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 4 Oct 2018 19:27:47 -0700 Subject: [PATCH 039/330] add option to reduce front-end metadata for untracked flags --- flags_state.js | 19 +++++++++------ index.d.ts | 11 +++++++++ index.js | 3 ++- test/LDClient-test.js | 57 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 9 deletions(-) diff --git a/flags_state.js b/flags_state.js index 5a7a2eb..ba8b620 100644 --- a/flags_state.js +++ b/flags_state.js @@ -4,21 +4,24 @@ function FlagsStateBuilder(valid) { var flagValues = {}; var flagMetadata = {}; - builder.addFlag = function(flag, value, variation, reason) { + builder.addFlag = function(flag, value, variation, reason, detailsOnlyIfTracked) { flagValues[flag.key] = value; - var meta = { - version: flag.version, - trackEvents: flag.trackEvents - }; + var meta = {}; + if (!detailsOnlyIfTracked || flag.trackEvents || flag.debugEventsUntilDate) { + meta.version = flag.version; + if (reason) { + meta.reason = reason; + } + } if (variation !== undefined && variation !== null) { meta.variation = variation; } + if (flag.trackEvents) { + meta.trackEvents = true; + } if (flag.debugEventsUntilDate !== undefined && flag.debugEventsUntilDate !== null) { meta.debugEventsUntilDate = flag.debugEventsUntilDate; } - if (reason) { - meta.reason = reason; - } flagMetadata[flag.key] = meta; }; diff --git a/index.d.ts b/index.d.ts index 6636e1c..2b526ed 100644 --- a/index.d.ts +++ b/index.d.ts @@ -538,6 +538,17 @@ declare module 'ldclient-node' { * client-side SDK. By default, all flags are included. */ clientSideOnly?: boolean; + /** + * True if evaluation reason data should be captured in the state object (see LDClient.variationDetail). + * By default, it is not. + */ + withReasons?: boolean; + /** + * True if any flag metadata that is normally only used for event generation - such as flag versions and + * evaluation reasons - should be omitted for any flag that does not have event tracking or debugging turned on. + * This reduces the size of the JSON data if you are passing the flag state to the front end. + */ + detailsOnlyForTrackedFlags?: boolean; }; /** diff --git a/index.js b/index.js index 65aad25..3446b41 100644 --- a/index.js +++ b/index.js @@ -289,6 +289,7 @@ var newClient = function(sdkKey, config) { var builder = FlagsStateBuilder(true); var clientOnly = options.clientSideOnly; var withReasons = options.withReasons; + var detailsOnlyIfTracked = options.detailsOnlyForTrackedFlags; config.featureStore.all(dataKind.features, function(flags) { async.forEachOf(flags, function(flag, key, iterateeCb) { if (clientOnly && !flag.clientSide) { @@ -299,7 +300,7 @@ var newClient = function(sdkKey, config) { if (err != null) { maybeReportError(new Error('Error for feature flag "' + flag.key + '" while evaluating all flags: ' + err)); } - builder.addFlag(flag, detail.value, detail.variationIndex, withReasons ? detail.reason : null); + builder.addFlag(flag, detail.value, detail.variationIndex, withReasons ? detail.reason : null, detailsOnlyIfTracked); setImmediate(iterateeCb); }); } diff --git a/test/LDClient-test.js b/test/LDClient-test.js index a18023f..b2d2e42 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -496,6 +496,63 @@ describe('LDClient', function() { }); }); + it('can omit details for untracked flags', function(done) { + var flag1 = { + key: 'flag1', + version: 100, + offVariation: 0, + variations: ['value1'] + }; + var flag2 = { + key: 'flag2', + version: 200, + offVariation: 0, + variations: ['value2'], + trackEvents: true + }; + var flag3 = { + key: 'flag3', + version: 300, + offVariation: 0, + variations: ['value3'], + debugEventsUntilDate: 1000 + }; + var client = createOnlineClientWithFlags({ flag1: flag1, flag2: flag2, flag3: flag3 }); + var user = { key: 'user' }; + client.on('ready', function() { + client.allFlagsState(user, { withReasons: true, detailsOnlyForTrackedFlags: true }, function(err, state) { + expect(err).toBeNull(); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({flag1: 'value1', flag2: 'value2', flag3: 'value3'}); + expect(state.getFlagValue('flag1')).toEqual('value1'); + expect(state.toJSON()).toEqual({ + flag1: 'value1', + flag2: 'value2', + flag3: 'value3', + $flagsState: { + flag1: { + variation: 0 + }, + flag2: { + version: 200, + variation: 0, + reason: { kind: 'OFF' }, + trackEvents: true + }, + flag3: { + version: 300, + variation: 0, + reason: { kind: 'OFF' }, + debugEventsUntilDate: 1000 + } + }, + $valid: true + }); + done(); + }); + }); + }); + it('should not overflow the call stack when evaluating a huge number of flags', function(done) { var flagCount = 5000; var flags = {}; From 2aee263154f05ff5297c2de81568565ce27b1a56 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Fri, 5 Oct 2018 10:49:53 -0700 Subject: [PATCH 040/330] add unit tests for default value bug --- test/LDClient-test.js | 74 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/test/LDClient-test.js b/test/LDClient-test.js index a18023f..ec33599 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -111,7 +111,6 @@ describe('LDClient', function() { function createOnlineClientWithFlags(flagsMap) { var store = InMemoryFeatureStore(); var allData = {}; - var dummyUri = 'bad'; allData[dataKind.features.namespace] = flagsMap; store.init(allData); return LDClient.init('secret', { @@ -122,6 +121,19 @@ describe('LDClient', function() { }); } + function createOnlineClientWithFlagsInUninitializedStore(flagsMap) { + var store = InMemoryFeatureStore(); + for (var key in flagsMap) { + store.upsert(dataKind.features, flagsMap[key]); + } + return LDClient.init('secret', { + featureStore: store, + eventProcessor: eventProcessor, + updateProcessor: updateProcessor, + logger: logger + }); + } + it('evaluates a flag with variation()', function(done) { var flag = { key: 'flagkey', @@ -155,6 +167,35 @@ describe('LDClient', function() { }); }); + it('returns default from variation() if client and store are not initialized', function(done) { + var flag = { + key: 'flagkey', + version: 1, + on: false, + offVariation: 0, + variations: ['value'] + }; + client = createOnlineClientWithFlagsInUninitializedStore({ flagkey: flag }); + var user = { key: 'user' }; + client.variation('flagkey', user, 'default', function(err, result) { + expect(err).toBeNull(); + expect(result).toEqual('default'); + done(); + }); + }); + + it('returns default from variation() if flag key is not specified', function(done) { + var client = createOnlineClientWithFlags({ }); + var user = { key: 'user' }; + client.on('ready', function() { + client.variation(null, user, 'default', function(err, result) { + expect(err).toBeNull(); + expect(result).toEqual('default'); + done(); + }); + }); + }); + it('returns default from variation() for flag that evaluates to null', function(done) { var flag = { key: 'flagkey', @@ -206,6 +247,37 @@ describe('LDClient', function() { }); }); + it('returns default from variationDetail() if client and store are not initialized', function(done) { + var flag = { + key: 'flagkey', + version: 1, + on: false, + offVariation: 0, + variations: ['value'] + }; + client = createOnlineClientWithFlagsInUninitializedStore({ flagkey: flag }); + var user = { key: 'user' }; + client.variationDetail('flagkey', user, 'default', function(err, result) { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'CLIENT_NOT_READY' } }); + done(); + }); + }); + + it('returns default from variation() if flag key is not specified', function(done) { + var client = createOnlineClientWithFlags({ }); + var user = { key: 'user' }; + client.on('ready', function() { + client.variationDetail(null, user, 'default', function(err, result) { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); + done(); + }); + }); + }); + it('returns default from variationDetail() for flag that evaluates to null', function(done) { var flag = { key: 'flagkey', From 6d4feeaa856818119d99ef9cd284436d24134014 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Fri, 5 Oct 2018 12:58:43 -0700 Subject: [PATCH 041/330] refactor LDClient tests and add some new ones --- test/LDClient-evaluation-test.js | 465 +++++++++++++++++++++ test/LDClient-events-test.js | 192 +++++++++ test/LDClient-test.js | 666 +++---------------------------- test/stubs.js | 65 +++ 4 files changed, 782 insertions(+), 606 deletions(-) create mode 100644 test/LDClient-evaluation-test.js create mode 100644 test/LDClient-events-test.js create mode 100644 test/stubs.js diff --git a/test/LDClient-evaluation-test.js b/test/LDClient-evaluation-test.js new file mode 100644 index 0000000..12c0f2b --- /dev/null +++ b/test/LDClient-evaluation-test.js @@ -0,0 +1,465 @@ +var InMemoryFeatureStore = require('../feature_store'); +var LDClient = require('../index.js'); +var dataKind = require('../versioned_data_kind'); +var messages = require('../messages'); +var stubs = require('./stubs'); + +describe('LDClient', () => { + + var defaultUser = { key: 'user' }; + + var updateProcessor = { + start: callback => { + setImmediate(callback, updateProcessor.error); + } + }; + + function createClientWithFlagsInUninitializedStore(flagsMap) { + var store = InMemoryFeatureStore(); + for (var key in flagsMap) { + store.upsert(dataKind.features, flagsMap[key]); + } + return stubs.createClient({ featureStore: store }, {}); + } + + describe('variation()', () => { + it('evaluates an existing flag', done => { + var flag = { + key: 'flagkey', + version: 1, + on: true, + targets: [], + fallthrough: { variation: 1 }, + variations: ['a', 'b'], + trackEvents: true + }; + var client = stubs.createClient({}, { flagkey: flag }); + client.on('ready', () => { + client.variation(flag.key, defaultUser, 'c', (err, result) => { + expect(err).toBeNull(); + expect(result).toEqual('b'); + done(); + }); + }); + }); + + it('returns default for unknown flag', done => { + var client = stubs.createClient({}, {}); + client.on('ready', () => { + client.variation('flagkey', defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toEqual('default'); + done(); + }); + }); + }); + + it('returns default if client is offline', done => { + var flag = { + key: 'flagkey', + version: 1, + on: false, + offVariation: 0, + variations: ['value'] + }; + var logger = stubs.stubLogger(); + client = stubs.createClient({ offline: true, logger: logger }, { flagkey: flag }); + client.variation('flagkey', defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toEqual('default'); + expect(logger.info).toHaveBeenCalled(); + done(); + }); + }); + + it('returns default if client and store are not initialized', done => { + var flag = { + key: 'flagkey', + version: 1, + on: false, + offVariation: 0, + variations: ['value'] + }; + var client = createClientWithFlagsInUninitializedStore({ flagkey: flag }); + client.variation('flagkey', defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toEqual('default'); + done(); + }); + }); + + it('returns value from store if store is initialized but client is not', done => { + var flag = { + key: 'flagkey', + version: 1, + on: false, + offVariation: 0, + variations: ['value'] + }; + var logger = stubs.stubLogger(); + var updateProcessor = stubs.stubUpdateProcessor(); + updateProcessor.shouldInitialize = false; + client = stubs.createClient({ updateProcessor: updateProcessor, logger: logger }, { flagkey: flag }); + client.variation('flagkey', defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toEqual('value'); + expect(logger.warn).toHaveBeenCalled(); + done(); + }); + }); + + it('returns default if flag key is not specified', done => { + var client = stubs.createClient({}, {}); + client.on('ready', () => { + client.variation(null, defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toEqual('default'); + done(); + }); + }); + }); + + it('returns default for flag that evaluates to null', done => { + var flag = { + key: 'flagkey', + on: false, + offVariation: null + }; + var client = stubs.createClient({}, { flagkey: flag }); + client.on('ready', () => { + client.variation(flag.key, defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toEqual('default'); + done(); + }); + }); + }); + + it('allows deprecated method toggle()', done => { + var flag = { + key: 'flagkey', + on: false, + offVariation: 0, + variations: [true] + }; + var logger = stubs.stubLogger(); + var client = stubs.createClient({ logger: logger }, { flagkey: flag }); + client.on('ready', () => { + client.toggle(flag.key, defaultUser, false, (err, result) => { + expect(err).toBeNull(); + expect(result).toEqual(true); + expect(logger.warn).toHaveBeenCalled(); + done(); + }); + }); + }); + }); + + describe('variationDetail()', () => { + it('evaluates an existing flag', done => { + var flag = { + key: 'flagkey', + version: 1, + on: true, + targets: [], + fallthrough: { variation: 1 }, + variations: ['a', 'b'], + trackEvents: true + }; + var client = stubs.createClient({}, { flagkey: flag }); + client.on('ready', () => { + client.variationDetail(flag.key, defaultUser, 'c', (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'b', variationIndex: 1, reason: { kind: 'FALLTHROUGH' } }); + done(); + }); + }); + }); + + it('returns default for unknown flag', done => { + var client = stubs.createClient({}, { }); + client.on('ready', () => { + client.variationDetail('flagkey', defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); + done(); + }); + }); + }); + + it('returns default if client is offline', done => { + var flag = { + key: 'flagkey', + version: 1, + on: false, + offVariation: 0, + variations: ['value'] + }; + var logger = stubs.stubLogger(); + client = stubs.createClient({ offline: true, logger: logger }, { flagkey: flag }); + client.variationDetail('flagkey', defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'CLIENT_NOT_READY' }}); + expect(logger.info).toHaveBeenCalled(); + done(); + }); + }); + + it('returns default if client and store are not initialized', done => { + var flag = { + key: 'flagkey', + version: 1, + on: false, + offVariation: 0, + variations: ['value'] + }; + client = createClientWithFlagsInUninitializedStore({ flagkey: flag }); + client.variationDetail('flagkey', defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'CLIENT_NOT_READY' } }); + done(); + }); + }); + + it('returns value from store if store is initialized but client is not', done => { + var flag = { + key: 'flagkey', + version: 1, + on: false, + offVariation: 0, + variations: ['value'] + }; + var logger = stubs.stubLogger(); + var updateProcessor = stubs.stubUpdateProcessor(); + updateProcessor.shouldInitialize = false; + client = stubs.createClient({ updateProcessor: updateProcessor, logger: logger }, { flagkey: flag }); + client.variationDetail('flagkey', defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'value', variationIndex: 0, reason: { kind: 'OFF' }}) + expect(logger.warn).toHaveBeenCalled(); + done(); + }); + }); + + it('returns default if flag key is not specified', done => { + var client = stubs.createClient({}, { }); + client.on('ready', () => { + client.variationDetail(null, defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); + done(); + }); + }); + }); + + it('returns default for flag that evaluates to null', done => { + var flag = { + key: 'flagkey', + on: false, + offVariation: null + }; + var client = stubs.createClient({}, { flagkey: flag }); + client.on('ready', () => { + client.variationDetail(flag.key, defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toMatchObject({ value: 'default', variationIndex: null, reason: { kind: 'OFF' } }); + done(); + }); + }); + }); + }); + + describe('allFlags()', () => { + it('evaluates flags', done => { + var flag = { + key: 'feature', + version: 1, + offVariation: 1, + variations: ['a', 'b'] + }; + var logger = stubs.stubLogger(); + var client = stubs.createClient({ logger: logger }, { feature: flag }); + client.on('ready', () => { + client.allFlags(defaultUser, (err, results) => { + expect(err).toBeNull(); + expect(results).toEqual({feature: 'b'}); + expect(logger.warn).toHaveBeenCalledTimes(1); // deprecation warning + done(); + }); + }); + }); + + it('returns empty map in offline mode and logs a message', done => { + var flag = { + key: 'flagkey', + on: false, + offVariation: null + }; + var logger = stubs.stubLogger(); + var client = stubs.createClient({ offline: true, logger: logger }, { flagkey: flag }); + client.on('ready', () => { + client.allFlags(defaultUser, (err, result) => { + expect(result).toEqual({}); + expect(logger.info).toHaveBeenCalledTimes(1); + done(); + }); + }); + }); + + it('allows deprecated method all_flags', done => { + var logger = stubs.stubLogger(); + var client = stubs.createClient({ logger: logger }, {}); + client.on('ready', () => { + client.all_flags(defaultUser, (err, result) => { + expect(result).toEqual({}); + expect(logger.warn).toHaveBeenCalledWith(messages.deprecated('all_flags', 'allFlags')); + done(); + }); + }); + }); + + it('does not overflow the call stack when evaluating a huge number of flags', done => { + var flagCount = 5000; + var flags = {}; + for (var i = 0; i < flagCount; i++) { + var key = 'feature' + i; + var flag = { + key: key, + version: 1, + on: false + }; + flags[key] = flag; + } + var client = stubs.createClient({}, flags); + client.on('ready', () => { + client.allFlags(defaultUser, (err, result) => { + expect(err).toEqual(null); + expect(Object.keys(result).length).toEqual(flagCount); + done(); + }); + }); + }); + }); + + describe('allFlagsState()', () => { + it('captures flag state', done => { + var flag = { + key: 'feature', + version: 100, + offVariation: 1, + variations: ['a', 'b'], + trackEvents: true, + debugEventsUntilDate: 1000 + }; + var client = stubs.createClient({}, { feature: flag }); + client.on('ready', () => { + client.allFlagsState(defaultUser, {}, (err, state) => { + expect(err).toBeNull(); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({feature: 'b'}); + expect(state.getFlagValue('feature')).toEqual('b'); + expect(state.toJSON()).toEqual({ + feature: 'b', + $flagsState: { + feature: { + version: 100, + variation: 1, + trackEvents: true, + debugEventsUntilDate: 1000 + } + }, + $valid: true + }); + done(); + }); + }); + }); + + it('can filter for only client-side flags', done => { + var flag1 = { key: 'server-side-1', on: false, offVariation: 0, variations: ['a'], clientSide: false }; + var flag2 = { key: 'server-side-2', on: false, offVariation: 0, variations: ['b'], clientSide: false }; + var flag3 = { key: 'client-side-1', on: false, offVariation: 0, variations: ['value1'], clientSide: true }; + var flag4 = { key: 'client-side-2', on: false, offVariation: 0, variations: ['value2'], clientSide: true }; + var client = stubs.createClient({}, { + 'server-side-1': flag1, 'server-side-2': flag2, 'client-side-1': flag3, 'client-side-2': flag4 + }); + client.on('ready', () => { + client.allFlagsState(defaultUser, { clientSideOnly: true }, (err, state) => { + expect(err).toBeNull(); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({ 'client-side-1': 'value1', 'client-side-2': 'value2' }); + done(); + }); + }); + }); + + it('can omit options parameter', done => { + var flag = { key: 'key', on: false, offVariation: 0, variations: ['value'] }; + var client = stubs.createClient({}, { 'key': flag }); + client.on('ready', () => { + client.allFlagsState(defaultUser, (err, state) => { + expect(err).toBeNull(); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({ 'key': 'value' }); + done(); + }); + }); + }); + + it('can include reasons', done => { + var flag = { + key: 'feature', + version: 100, + offVariation: 1, + variations: ['a', 'b'], + trackEvents: true, + debugEventsUntilDate: 1000 + }; + var client = stubs.createClient({}, { feature: flag }); + client.on('ready', () => { + client.allFlagsState(defaultUser, { withReasons: true }, (err, state) => { + expect(err).toBeNull(); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({feature: 'b'}); + expect(state.getFlagValue('feature')).toEqual('b'); + expect(state.toJSON()).toEqual({ + feature: 'b', + $flagsState: { + feature: { + version: 100, + variation: 1, + reason: { kind: 'OFF' }, + trackEvents: true, + debugEventsUntilDate: 1000 + } + }, + $valid: true + }); + done(); + }); + }); + }); + + it('returns empty state in offline mode and logs a message', done => { + var flag = { + key: 'flagkey', + on: false, + offVariation: null + }; + var logger = stubs.stubLogger(); + var client = stubs.createClient({ offline: true, logger: logger }, { flagkey: flag }); + client.on('ready', () => { + client.allFlagsState(defaultUser, {}, (err, state) => { + expect(state.valid).toEqual(false); + expect(state.allValues()).toEqual({}); + expect(logger.info).toHaveBeenCalledTimes(1); + done(); + }); + }); + }); + }); +}); diff --git a/test/LDClient-events-test.js b/test/LDClient-events-test.js new file mode 100644 index 0000000..39fb1d9 --- /dev/null +++ b/test/LDClient-events-test.js @@ -0,0 +1,192 @@ +var InMemoryFeatureStore = require('../feature_store'); +var LDClient = require('../index.js'); +var dataKind = require('../versioned_data_kind'); +var messages = require('../messages'); +var stubs = require('./stubs'); + +describe('LDClient - analytics events', () => { + + var eventProcessor; + var defaultUser = { key: 'user' }; + + beforeEach(() => { + eventProcessor = stubs.stubEventProcessor(); + }); + + describe('feature event', () => { + it('generates event for existing feature', done => { + var flag = { + key: 'flagkey', + version: 1, + on: true, + targets: [], + fallthrough: { variation: 1 }, + variations: ['a', 'b'], + trackEvents: true + }; + var client = stubs.createClient({ eventProcessor: eventProcessor }, { flagkey: flag }); + client.on('ready', () => { + client.variation(flag.key, defaultUser, 'c', (err, result) => { + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'feature', + key: 'flagkey', + version: 1, + user: defaultUser, + variation: 1, + value: 'b', + default: 'c', + trackEvents: true + }); + done(); + }); + }); + }); + + it('generates event for existing feature with reason', done => { + var flag = { + key: 'flagkey', + version: 1, + on: true, + targets: [], + fallthrough: { variation: 1 }, + variations: ['a', 'b'], + trackEvents: true + }; + var client = stubs.createClient({ eventProcessor: eventProcessor }, { flagkey: flag }); + client.on('ready', () => { + client.variationDetail(flag.key, defaultUser, 'c', (err, result) => { + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'feature', + key: 'flagkey', + version: 1, + user: defaultUser, + variation: 1, + value: 'b', + default: 'c', + reason: { kind: 'FALLTHROUGH' }, + trackEvents: true + }); + done(); + }); + }); + }); + + it('generates event for unknown feature', done => { + var client = stubs.createClient({ eventProcessor: eventProcessor }, {}); + client.on('ready', () => { + client.variation('flagkey', defaultUser, 'c', (err, result) => { + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'feature', + key: 'flagkey', + version: null, + user: defaultUser, + variation: null, + value: 'c', + default: 'c', + trackEvents: null + }); + done(); + }); + }); + }); + + it('generates event for existing feature when user key is missing', done => { + var flag = { + key: 'flagkey', + version: 1, + on: true, + targets: [], + fallthrough: { variation: 1 }, + variations: ['a', 'b'], + trackEvents: true + }; + var client = stubs.createClient({ eventProcessor: eventProcessor }, { flagkey: flag }); + var badUser = { name: 'Bob' }; + client.on('ready', () => { + client.variation(flag.key, badUser, 'c', (err, result) => { + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'feature', + key: 'flagkey', + version: 1, + user: badUser, + variation: null, + value: 'c', + default: 'c', + trackEvents: true + }); + done(); + }); + }); + }); + + it('generates event for existing feature when user is null', done => { + var flag = { + key: 'flagkey', + version: 1, + on: true, + targets: [], + fallthrough: { variation: 1 }, + variations: ['a', 'b'], + trackEvents: true + }; + var client = stubs.createClient({ eventProcessor: eventProcessor }, { flagkey: flag }); + client.on('ready', () => { + client.variation(flag.key, null, 'c', (err, result) => { + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'feature', + key: 'flagkey', + version: 1, + user: null, + variation: null, + value: 'c', + default: 'c', + trackEvents: true + }); + done(); + }); + }); + }); + }); + + it('generates an event for identify()', done => { + var client = stubs.createClient({ eventProcessor: eventProcessor }, {}); + client.on('ready', () => { + client.identify(defaultUser); + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'identify', + key: defaultUser.key, + user: defaultUser + }); + done(); + }); + }); + + it('generates an event for track()', done => { + var data = { thing: 'stuff' }; + var client = stubs.createClient({ eventProcessor: eventProcessor }, {}); + client.on('ready', () => { + client.track('eventkey', defaultUser, data); + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'custom', + key: 'eventkey', + user: defaultUser, + data: data + }); + done(); + }); + }); +}); diff --git a/test/LDClient-test.js b/test/LDClient-test.js index ec33599..3dbc22a 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -2,631 +2,88 @@ var InMemoryFeatureStore = require('../feature_store'); var LDClient = require('../index.js'); var dataKind = require('../versioned_data_kind'); var messages = require('../messages'); +var stubs = require('./stubs'); -describe('LDClient', function() { +describe('LDClient', () => { - var logger = {}; - - var eventProcessor = { - events: [], - sendEvent: function(event) { - eventProcessor.events.push(event); - }, - flush: function(callback) { - if (callback) { - setImmediate(callback); - } else { - return Promise.resolve(null); - } - }, - close: function() {} - }; - - var updateProcessor = { - start: function(callback) { - setImmediate(callback, updateProcessor.error); - } - }; - - beforeEach(function() { - logger.debug = jest.fn(); - logger.info = jest.fn(); - logger.warn = jest.fn(); - logger.error = jest.fn(); - eventProcessor.events = []; - updateProcessor.error = null; - }); - - it('should trigger the ready event in offline mode', function(done) { - var client = LDClient.init('sdk_key', {offline: true}); - client.on('ready', function() { - done(); - }); - }); - - it('returns true for isOffline in offline mode', function(done) { - var client = LDClient.init('sdk_key', {offline: true}); - client.on('ready', function() { - expect(client.isOffline()).toEqual(true); - done(); - }); - }); - - it('allows deprecated method is_offline', function(done) { - var client = LDClient.init('sdk_key', {offline: true, logger: logger}); - client.on('ready', function() { - expect(client.is_offline()).toEqual(true); - expect(logger.warn).toHaveBeenCalledWith(messages.deprecated('is_offline', 'isOffline')); - done(); - }); - }); - - it('should correctly compute the secure mode hash for a known message and secret', function() { - var client = LDClient.init('secret', {offline: true}); - var hash = client.secureModeHash({"key": "Message"}); - expect(hash).toEqual("aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597"); - }); - - it('allows deprecated method secure_mode_hash', function() { - var client = LDClient.init('secret', {offline: true, logger: logger}); - var hash = client.secure_mode_hash({"key": "Message"}); - expect(hash).toEqual("aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597"); - expect(logger.warn).toHaveBeenCalledWith(messages.deprecated('secure_mode_hash', 'secureModeHash')); - }); - - it('returns empty map for allFlags in offline mode and logs a message', function(done) { - var client = LDClient.init('secret', {offline: true, logger: logger}); - client.on('ready', function() { - client.allFlags({key: 'user'}, function(err, result) { - expect(result).toEqual({}); - expect(logger.info).toHaveBeenCalledTimes(1); - done(); - }); - }); - }); - - it('returns empty state for allFlagsState in offline mode and logs a message', function(done) { - var client = LDClient.init('secret', {offline: true, logger: logger}); - client.on('ready', function() { - client.allFlagsState({key: 'user'}, {}, function(err, state) { - expect(state.valid).toEqual(false); - expect(state.allValues()).toEqual({}); - expect(logger.info).toHaveBeenCalledTimes(1); - done(); - }); - }); - }); - - it('allows deprecated method all_flags', function(done) { - var client = LDClient.init('secret', {offline: true, logger: logger}); - client.on('ready', function() { - client.all_flags({key: 'user'}, function(err, result) { - expect(result).toEqual({}); - expect(logger.warn).toHaveBeenCalledWith(messages.deprecated('all_flags', 'allFlags')); - done(); - }); - }); - }); - - function createOnlineClientWithFlags(flagsMap) { - var store = InMemoryFeatureStore(); - var allData = {}; - allData[dataKind.features.namespace] = flagsMap; - store.init(allData); - return LDClient.init('secret', { - featureStore: store, - eventProcessor: eventProcessor, - updateProcessor: updateProcessor, - logger: logger - }); - } - - function createOnlineClientWithFlagsInUninitializedStore(flagsMap) { - var store = InMemoryFeatureStore(); - for (var key in flagsMap) { - store.upsert(dataKind.features, flagsMap[key]); - } - return LDClient.init('secret', { - featureStore: store, - eventProcessor: eventProcessor, - updateProcessor: updateProcessor, - logger: logger - }); - } - - it('evaluates a flag with variation()', function(done) { - var flag = { - key: 'flagkey', - version: 1, - on: true, - targets: [], - fallthrough: { variation: 1 }, - variations: ['a', 'b'], - trackEvents: true - }; - var client = createOnlineClientWithFlags({ flagkey: flag }); - var user = { key: 'user' }; - client.on('ready', function() { - client.variation(flag.key, user, 'c', function(err, result) { - expect(err).toBeNull(); - expect(result).toEqual('b'); - done(); - }); - }); - }); - - it('returns default from variation() for unknown flag', function(done) { - var client = createOnlineClientWithFlags({ }); - var user = { key: 'user' }; - client.on('ready', function() { - client.variation('flagkey', user, 'default', function(err, result) { - expect(err).toBeNull(); - expect(result).toEqual('default'); - done(); - }); - }); - }); - - it('returns default from variation() if client and store are not initialized', function(done) { - var flag = { - key: 'flagkey', - version: 1, - on: false, - offVariation: 0, - variations: ['value'] - }; - client = createOnlineClientWithFlagsInUninitializedStore({ flagkey: flag }); - var user = { key: 'user' }; - client.variation('flagkey', user, 'default', function(err, result) { - expect(err).toBeNull(); - expect(result).toEqual('default'); - done(); - }); - }); - - it('returns default from variation() if flag key is not specified', function(done) { - var client = createOnlineClientWithFlags({ }); - var user = { key: 'user' }; - client.on('ready', function() { - client.variation(null, user, 'default', function(err, result) { - expect(err).toBeNull(); - expect(result).toEqual('default'); - done(); - }); - }); - }); - - it('returns default from variation() for flag that evaluates to null', function(done) { - var flag = { - key: 'flagkey', - on: false, - offVariation: null - }; - var client = createOnlineClientWithFlags({ flagkey: flag }); - var user = { key: 'user' }; - client.on('ready', function() { - client.variation(flag.key, user, 'default', function(err, result) { - expect(err).toBeNull(); - expect(result).toEqual('default'); - done(); - }); - }); - }); - - it('evaluates a flag with variationDetail()', function(done) { - var flag = { - key: 'flagkey', - version: 1, - on: true, - targets: [], - fallthrough: { variation: 1 }, - variations: ['a', 'b'], - trackEvents: true - }; - var client = createOnlineClientWithFlags({ flagkey: flag }); - var user = { key: 'user' }; - client.on('ready', function() { - client.variationDetail(flag.key, user, 'c', function(err, result) { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'b', variationIndex: 1, reason: { kind: 'FALLTHROUGH' } }); - done(); - }); - }); - }); - - it('returns default from variationDetail() for unknown flag', function(done) { - var client = createOnlineClientWithFlags({ }); - var user = { key: 'user' }; - client.on('ready', function() { - client.variationDetail('flagkey', user, 'default', function(err, result) { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'default', variationIndex: null, - reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); - done(); - }); - }); - }); - - it('returns default from variationDetail() if client and store are not initialized', function(done) { - var flag = { - key: 'flagkey', - version: 1, - on: false, - offVariation: 0, - variations: ['value'] - }; - client = createOnlineClientWithFlagsInUninitializedStore({ flagkey: flag }); - var user = { key: 'user' }; - client.variationDetail('flagkey', user, 'default', function(err, result) { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'default', variationIndex: null, - reason: { kind: 'ERROR', errorKind: 'CLIENT_NOT_READY' } }); - done(); - }); - }); - - it('returns default from variation() if flag key is not specified', function(done) { - var client = createOnlineClientWithFlags({ }); - var user = { key: 'user' }; - client.on('ready', function() { - client.variationDetail(null, user, 'default', function(err, result) { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'default', variationIndex: null, - reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); + describe('ready event', () => { + it('is fired in offline mode', done => { + var client = LDClient.init('sdk_key', { offline: true }); + client.on('ready', () => { done(); }); }); }); - it('returns default from variationDetail() for flag that evaluates to null', function(done) { - var flag = { - key: 'flagkey', - on: false, - offVariation: null - }; - var client = createOnlineClientWithFlags({ flagkey: flag }); - var user = { key: 'user' }; - client.on('ready', function() { - client.variationDetail(flag.key, user, 'default', function(err, result) { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'default', variationIndex: null, reason: { kind: 'OFF' } }); - done(); - }); - }); - }); - - it('generates an event for an existing feature', function(done) { - var flag = { - key: 'flagkey', - version: 1, - on: true, - targets: [], - fallthrough: { variation: 1 }, - variations: ['a', 'b'], - trackEvents: true - }; - var client = createOnlineClientWithFlags({ flagkey: flag }); - var user = { key: 'user' }; - client.on('ready', function() { - client.variation(flag.key, user, 'c', function(err, result) { - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'feature', - key: 'flagkey', - version: 1, - user: user, - variation: 1, - value: 'b', - default: 'c', - trackEvents: true - }); - done(); - }); - }); - }); - - it('generates an event for an existing feature with reason', function(done) { - var flag = { - key: 'flagkey', - version: 1, - on: true, - targets: [], - fallthrough: { variation: 1 }, - variations: ['a', 'b'], - trackEvents: true - }; - var client = createOnlineClientWithFlags({ flagkey: flag }); - var user = { key: 'user' }; - client.on('ready', function() { - client.variationDetail(flag.key, user, 'c', function(err, result) { - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'feature', - key: 'flagkey', - version: 1, - user: user, - variation: 1, - value: 'b', - default: 'c', - reason: { kind: 'FALLTHROUGH' }, - trackEvents: true - }); - done(); - }); - }); - }); - - it('generates an event for an unknown feature', function(done) { - var client = createOnlineClientWithFlags({}); - var user = { key: 'user' }; - client.on('ready', function() { - client.variation('flagkey', user, 'c', function(err, result) { - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'feature', - key: 'flagkey', - version: null, - user: user, - variation: null, - value: 'c', - default: 'c', - trackEvents: null - }); - done(); - }); - }); - }); - - it('generates an event for an existing feature even if user key is missing', function(done) { - var flag = { - key: 'flagkey', - version: 1, - on: true, - targets: [], - fallthrough: { variation: 1 }, - variations: ['a', 'b'], - trackEvents: true - }; - var client = createOnlineClientWithFlags({ flagkey: flag }); - var user = { name: 'Bob' }; - client.on('ready', function() { - client.variation(flag.key, user, 'c', function(err, result) { - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'feature', - key: 'flagkey', - version: 1, - user: user, - variation: null, - value: 'c', - default: 'c', - trackEvents: true - }); - done(); - }); - }); - }); - - it('generates an event for an existing feature even if user is null', function(done) { - var flag = { - key: 'flagkey', - version: 1, - on: true, - targets: [], - fallthrough: { variation: 1 }, - variations: ['a', 'b'], - trackEvents: true - }; - var client = createOnlineClientWithFlags({ flagkey: flag }); - client.on('ready', function() { - client.variation(flag.key, null, 'c', function(err, result) { - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'feature', - key: 'flagkey', - version: 1, - user: null, - variation: null, - value: 'c', - default: 'c', - trackEvents: true - }); - done(); - }); - }); - }); - - it('evaluates a flag with allFlags()', function(done) { - var flag = { - key: 'feature', - version: 1, - on: true, - targets: [], - fallthrough: { variation: 1 }, - variations: ['a', 'b'] - }; - var client = createOnlineClientWithFlags({ feature: flag }); - var user = { key: 'user' }; - client.on('ready', function() { - client.allFlags(user, function(err, results) { - expect(err).toBeNull(); - expect(results).toEqual({feature: 'b'}); - expect(logger.warn).toHaveBeenCalledTimes(1); // deprecation warning - done(); - }); - }); - }); + describe('failed event', () => { + it('is fired if initialization fails', done => { + var updateProcessor = stubs.stubUpdateProcessor(); + updateProcessor.error = { status: 403 }; + var client = stubs.createClient({ updateProcessor: updateProcessor }, {}); - it('captures flag state with allFlagsState()', function(done) { - var flag = { - key: 'feature', - version: 100, - on: true, - targets: [], - fallthrough: { variation: 1 }, - variations: ['a', 'b'], - trackEvents: true, - debugEventsUntilDate: 1000 - }; - var client = createOnlineClientWithFlags({ feature: flag }); - var user = { key: 'user' }; - client.on('ready', function() { - client.allFlagsState(user, {}, function(err, state) { - expect(err).toBeNull(); - expect(state.valid).toEqual(true); - expect(state.allValues()).toEqual({feature: 'b'}); - expect(state.getFlagValue('feature')).toEqual('b'); - expect(state.toJSON()).toEqual({ - feature: 'b', - $flagsState: { - feature: { - version: 100, - variation: 1, - trackEvents: true, - debugEventsUntilDate: 1000 - } - }, - $valid: true - }); + client.on('failed', err => { + expect(err).toEqual(updateProcessor.error); done(); }); }); }); - it('can filter for only client-side flags with allFlagsState()', function(done) { - var flag1 = { key: 'server-side-1', on: false, offVariation: 0, variations: ['a'], clientSide: false }; - var flag2 = { key: 'server-side-2', on: false, offVariation: 0, variations: ['b'], clientSide: false }; - var flag3 = { key: 'client-side-1', on: false, offVariation: 0, variations: ['value1'], clientSide: true }; - var flag4 = { key: 'client-side-2', on: false, offVariation: 0, variations: ['value2'], clientSide: true }; - var client = createOnlineClientWithFlags({ - 'server-side-1': flag1, 'server-side-2': flag2, 'client-side-1': flag3, 'client-side-2': flag4 - }); - var user = { key: 'user' }; - client.on('ready', function() { - client.allFlagsState(user, { clientSideOnly: true }, function(err, state) { - expect(err).toBeNull(); - expect(state.valid).toEqual(true); - expect(state.allValues()).toEqual({ 'client-side-1': 'value1', 'client-side-2': 'value2' }); + describe('isOffline()', () => { + it('returns true in offline mode', done => { + var client = LDClient.init('sdk_key', {offline: true}); + client.on('ready', () => { + expect(client.isOffline()).toEqual(true); done(); }); }); - }); - it('can omit options parameter for allFlagsState()', function(done) { - var flag = { key: 'key', on: false, offVariation: 0, variations: ['value'] }; - var client = createOnlineClientWithFlags({ 'key': flag }); - var user = { key: 'user' }; - client.on('ready', function() { - client.allFlagsState(user, function(err, state) { - expect(err).toBeNull(); - expect(state.valid).toEqual(true); - expect(state.allValues()).toEqual({ 'key': 'value' }); + it('allows deprecated method is_offline', done => { + var logger = stubs.stubLogger(); + var client = LDClient.init('sdk_key', {offline: true, logger: logger}); + client.on('ready', () => { + expect(client.is_offline()).toEqual(true); + expect(logger.warn).toHaveBeenCalledWith(messages.deprecated('is_offline', 'isOffline')); done(); }); }); }); - it('can include reasons in allFlagsState()', function(done) { - var flag = { - key: 'feature', - version: 100, - on: true, - targets: [], - fallthrough: { variation: 1 }, - variations: ['a', 'b'], - trackEvents: true, - debugEventsUntilDate: 1000 - }; - var client = createOnlineClientWithFlags({ feature: flag }); - var user = { key: 'user' }; - client.on('ready', function() { - client.allFlagsState(user, { withReasons: true }, function(err, state) { - expect(err).toBeNull(); - expect(state.valid).toEqual(true); - expect(state.allValues()).toEqual({feature: 'b'}); - expect(state.getFlagValue('feature')).toEqual('b'); - expect(state.toJSON()).toEqual({ - feature: 'b', - $flagsState: { - feature: { - version: 100, - variation: 1, - reason: { kind: 'FALLTHROUGH' }, - trackEvents: true, - debugEventsUntilDate: 1000 - } - }, - $valid: true - }); - done(); - }); + describe('secureModeHash()', () => { + it('correctly computes hash for a known message and secret', () => { + var client = LDClient.init('secret', {offline: true}); + var hash = client.secureModeHash({"key": "Message"}); + expect(hash).toEqual("aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597"); }); - }); - it('should not overflow the call stack when evaluating a huge number of flags', function(done) { - var flagCount = 5000; - var flags = {}; - for (var i = 0; i < flagCount; i++) { - var key = 'feature' + i; - var flag = { - key: key, - version: 1, - on: false - }; - flags[key] = flag; - } - var client = createOnlineClientWithFlags(flags); - client.on('ready', function() { - client.allFlags({key: 'user'}, function(err, result) { - expect(err).toEqual(null); - expect(Object.keys(result).length).toEqual(flagCount); - done(); - }); + it('allows deprecated method secure_mode_hash', () => { + var logger = stubs.stubLogger(); + var client = LDClient.init('secret', {offline: true, logger: logger}); + var hash = client.secure_mode_hash({"key": "Message"}); + expect(hash).toEqual("aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597"); + expect(logger.warn).toHaveBeenCalledWith(messages.deprecated('secure_mode_hash', 'secureModeHash')); }); }); - it('should not crash when closing an offline client', function(done) { - var client = LDClient.init('sdk_key', {offline: true}); - expect(() => client.close()).not.toThrow(); - done(); - }); - - describe('waitUntilReady()', function () { - it('should resolve waitUntilReady() when ready', function(done) { - var client = LDClient.init('secret', {offline: true}); - var callback = jest.fn(); - - client.waitUntilReady().then(callback) - .then(() => { - expect(callback).toHaveBeenCalled(); - done(); - }).catch(done.error) + describe('waitUntilReady()', () => { + it('resolves when ready', done => { + var client = stubs.createClient({}, {}); + client.waitUntilReady().then(done) + .catch(done.error) }); - it('should resolve waitUntilReady() even if the client is already ready', function(done) { - var client = LDClient.init('secret', {offline: true}); - var callback = jest.fn(); - - client.waitUntilReady() - .then(() => { - client.waitUntilReady().then(callback) - .then(() => { - expect(callback).toHaveBeenCalled(); - done(); - }).catch(done.error) - }).catch(done.error) + it('resolves immediately if the client is already ready', done => { + var client = stubs.createClient({}, {}); + client.waitUntilReady().then(() => { + client.waitUntilReady().then(done) + .catch(done.error) + }).catch(done.error); }); }); - describe('waitForInitialization()', function () { - it('should resolve when ready', function(done) { + describe('waitForInitialization()', () => { + it('resolves when ready', done => { var callback = jest.fn(); - var client = createOnlineClientWithFlags({}); + var client = stubs.createClient({}, {}); client.waitForInitialization().then(callback) .then(() => { @@ -636,9 +93,9 @@ describe('LDClient', function() { }).catch(done.error) }); - it('should resolve even if the client is already ready', function(done) { + it('resolves immediately if the client is already ready', done => { var callback = jest.fn(); - var client = createOnlineClientWithFlags({}); + var client = stubs.createClient({}, {}); client.waitForInitialization() .then(() => { @@ -651,9 +108,10 @@ describe('LDClient', function() { }).catch(done.error) }); - it('should be rejected if initialization fails', function(done) { + it('is rejected if initialization fails', done => { + var updateProcessor = stubs.stubUpdateProcessor(); updateProcessor.error = { status: 403 }; - var client = createOnlineClientWithFlags({}); + var client = stubs.createClient({ updateProcessor: updateProcessor }, {}); client.waitForInitialization() .catch(err => { @@ -663,15 +121,11 @@ describe('LDClient', function() { }); }); - describe('failed event', function() { - it('should be fired if initialization fails', function(done) { - updateProcessor.error = { status: 403 }; - var client = createOnlineClientWithFlags({}); - - client.on('failed', err => { - expect(err).toEqual(updateProcessor.error); - done(); - }); + describe('close()', () => { + it('does not crash when closing an offline client', done => { + var client = LDClient.init('sdk_key', {offline: true}); + expect(() => client.close()).not.toThrow(); + done(); }); - }) + }); }); diff --git a/test/stubs.js b/test/stubs.js new file mode 100644 index 0000000..b455b3f --- /dev/null +++ b/test/stubs.js @@ -0,0 +1,65 @@ +var InMemoryFeatureStore = require('../feature_store'); +var LDClient = require('../index.js'); +var dataKind = require('../versioned_data_kind'); + +function stubEventProcessor() { + var eventProcessor = { + events: [], + sendEvent: function(event) { + eventProcessor.events.push(event); + }, + flush: function(callback) { + if (callback) { + setImmediate(callback); + } else { + return Promise.resolve(null); + } + }, + close: function() {} + }; + return eventProcessor; +} + +function stubLogger() { + return { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn() + }; +} + +function stubUpdateProcessor() { + var updateProcessor = { + start: function(callback) { + if (updateProcessor.shouldInitialize) { + setImmediate(callback, updateProcessor.error); + } + }, + shouldInitialize: true + }; + return updateProcessor; +} + +function createClient(overrideOptions, flagsMap) { + var store = InMemoryFeatureStore(); + if (flagsMap !== undefined) { + var allData = {}; + allData[dataKind.features.namespace] = flagsMap; + store.init(allData); + } + var defaults = { + featureStore: store, + eventProcessor: stubEventProcessor(), + updateProcessor: stubUpdateProcessor(), + logger: stubLogger() + }; + return LDClient.init('secret', Object.assign({}, defaults, overrideOptions)); +} + +module.exports = { + createClient: createClient, + stubEventProcessor: stubEventProcessor, + stubLogger: stubLogger, + stubUpdateProcessor: stubUpdateProcessor +}; From 1a6c80a244674b2a746e7c687a843650cb7d0b0c Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Fri, 5 Oct 2018 13:02:23 -0700 Subject: [PATCH 042/330] rm unused vars --- test/LDClient-evaluation-test.js | 6 ------ test/LDClient-events-test.js | 4 ---- test/LDClient-test.js | 2 -- 3 files changed, 12 deletions(-) diff --git a/test/LDClient-evaluation-test.js b/test/LDClient-evaluation-test.js index 12c0f2b..b6d25a8 100644 --- a/test/LDClient-evaluation-test.js +++ b/test/LDClient-evaluation-test.js @@ -8,12 +8,6 @@ describe('LDClient', () => { var defaultUser = { key: 'user' }; - var updateProcessor = { - start: callback => { - setImmediate(callback, updateProcessor.error); - } - }; - function createClientWithFlagsInUninitializedStore(flagsMap) { var store = InMemoryFeatureStore(); for (var key in flagsMap) { diff --git a/test/LDClient-events-test.js b/test/LDClient-events-test.js index 39fb1d9..2df637a 100644 --- a/test/LDClient-events-test.js +++ b/test/LDClient-events-test.js @@ -1,7 +1,3 @@ -var InMemoryFeatureStore = require('../feature_store'); -var LDClient = require('../index.js'); -var dataKind = require('../versioned_data_kind'); -var messages = require('../messages'); var stubs = require('./stubs'); describe('LDClient - analytics events', () => { diff --git a/test/LDClient-test.js b/test/LDClient-test.js index 3dbc22a..1c3bb05 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -1,6 +1,4 @@ -var InMemoryFeatureStore = require('../feature_store'); var LDClient = require('../index.js'); -var dataKind = require('../versioned_data_kind'); var messages = require('../messages'); var stubs = require('./stubs'); From 1fa81a4ba144a007f5a7932f82a966f8b42c4781 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 8 Oct 2018 12:42:39 -0700 Subject: [PATCH 043/330] fix broken event source construction --- streaming.js | 2 +- test/streaming-test.js | 37 ++++++++++++++++++------------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/streaming.js b/streaming.js index 18a67ee..4f53488 100644 --- a/streaming.js +++ b/streaming.js @@ -16,7 +16,7 @@ function StreamProcessor(sdkKey, config, requestor, eventSourceFactory) { processor.start = function(fn) { var cb = fn || function(){}; - es = eventSourceFactory(config.streamUri + "/all", + es = new eventSourceFactory(config.streamUri + "/all", { agent: config.proxyAgent, headers: {'Authorization': sdkKey,'User-Agent': config.userAgent} diff --git a/test/streaming-test.js b/test/streaming-test.js index fea8ec5..10996d1 100644 --- a/test/streaming-test.js +++ b/test/streaming-test.js @@ -7,13 +7,12 @@ describe('StreamProcessor', function() { function fakeEventSource() { var es = { handlers: {} }; - es.initialize = function(url, options) { + es.constructor = function(url, options) { es.url = url; es.options = options; - return es; - }; - es.addEventListener = function(type, handler) { - es.handlers[type] = handler; + this.addEventListener = function(type, handler) { + es.handlers[type] = handler; + }; }; return es; } @@ -37,7 +36,7 @@ describe('StreamProcessor', function() { it('uses expected URL', function() { var config = { streamUri: 'http://test' }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); sp.start(); expect(es.url).toEqual(config.streamUri + '/all'); }); @@ -45,7 +44,7 @@ describe('StreamProcessor', function() { it('sets expected headers', function() { var config = { streamUri: 'http://test', userAgent: 'agent' }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); sp.start(); expect(es.options.headers['Authorization']).toEqual(sdkKey); expect(es.options.headers['User-Agent']).toEqual(config.userAgent); @@ -67,7 +66,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); sp.start(); es.handlers.put({ data: JSON.stringify(putData) }); @@ -89,7 +88,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); var cb = function(err) { expect(err).toBe(undefined); @@ -104,7 +103,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); sp.start(expectJsonError(config, done)); es.handlers.put({ data: '{not-good' }); @@ -116,7 +115,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); var patchData = { path: '/flags/flagkey', @@ -136,7 +135,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); var patchData = { path: '/segments/segkey', @@ -156,7 +155,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); sp.start(expectJsonError(config, done)); es.handlers.patch({ data: '{not-good' }); @@ -168,7 +167,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); sp.start(); @@ -192,7 +191,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); sp.start(); @@ -216,7 +215,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, null, es.initialize); + var sp = StreamProcessor(sdkKey, config, null, es.constructor); sp.start(expectJsonError(config, done)); es.handlers.delete({ data: '{not-good' }); @@ -242,7 +241,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, fakeRequestor, es.initialize); + var sp = StreamProcessor(sdkKey, config, fakeRequestor, es.constructor); sp.start(); @@ -277,7 +276,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, fakeRequestor, es.initialize); + var sp = StreamProcessor(sdkKey, config, fakeRequestor, es.constructor); sp.start(); @@ -304,7 +303,7 @@ describe('StreamProcessor', function() { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); - var sp = StreamProcessor(sdkKey, config, fakeRequestor, es.initialize); + var sp = StreamProcessor(sdkKey, config, fakeRequestor, es.constructor); sp.start(); From 8af9fd0c22fe785c9d1b063bfe35e27c3a0895ec Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Wed, 7 Nov 2018 14:57:17 -0800 Subject: [PATCH 044/330] Factor out caching and update queue from redis store for use in dynamodb store --- .gitignore | 3 +- feature_store_utils.js | 69 ++++++++++++++++++++++++++++++++++++++++++ redis_feature_store.js | 62 ++++++++----------------------------- 3 files changed, 83 insertions(+), 51 deletions(-) create mode 100644 feature_store_utils.js diff --git a/.gitignore b/.gitignore index 4ba85cf..5a9d795 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ junit.xml node_modules/ -npm-debug.log \ No newline at end of file +npm-debug.log +yarn.lock diff --git a/feature_store_utils.js b/feature_store_utils.js new file mode 100644 index 0000000..96dd08c --- /dev/null +++ b/feature_store_utils.js @@ -0,0 +1,69 @@ +var NodeCache = require('node-cache'); + +function cacheKey(kind, key) { + return kind.namespace + ":" + key; +} + +function StoreCache(ttl, getFallback) { + var cache = ttl ? new NodeCache({ stdTTL: ttl }) : null; + + this.get = function(kind, key, cb) { + if (cache) { + item = cache.get(cacheKey(kind, key)); + if (item) { + cb(item); + return; + } + } + + getFallback(kind, key, function (item) { + if (cache) { + cache.set(cacheKey(kind, key), item); + } + cb(item); + }); + }; + + this.set = function(kind, key, newItem) { + cache.set(cacheKey(kind, key), newItem); + }; + + this.flush = function() { + cache.flushAll(); + }; + + this.close = function() { + cache && cache.close(); + }; +} + +// queue +function UpdateQueue() { + var updateQueue = []; + this.enqueue = function(updateFn, fnArgs, cb) { + updateQueue.push(arguments); + if (updateQueue.length === 1) { + // if nothing else is in progress, we can start this one right away + executePendingUpdates(); + } + }; + function executePendingUpdates() { + if (updateQueue.length > 0) { + const entry = updateQueue[0]; + console.log(entry); + const fn = entry[0]; + const args = entry[1]; + const cb = entry[2]; + const newCb = function() { + updateQueue.shift(); + if (updateQueue.length > 0) { + setImmediate(executePendingUpdates); + } + cb && cb(); + }; + fn.apply(null, args.concat([newCb])); + } + } +} + +module.exports = { StoreCache, UpdateQueue }; diff --git a/redis_feature_store.js b/redis_feature_store.js index 7b35504..b2ca98c 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -1,7 +1,7 @@ var redis = require('redis'), - NodeCache = require( "node-cache" ), winston = require('winston'), - dataKind = require('./versioned_data_kind'); + dataKind = require('./versioned_data_kind'), +util = require('./feature_store_utils'); var noop = function(){}; @@ -12,8 +12,8 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { var client = redis.createClient(redisOpts), store = {}, itemsPrefix = (prefix || "launchdarkly") + ":", - cache = cacheTTL ? new NodeCache({ stdTTL: cacheTTL}) : null, - updateQueue = [], + cache = new util.StoreCache(cacheTTL, doGet), + updateQueue = new util.UpdateQueue(), inited = false, checkedInit = false; @@ -55,23 +55,11 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { return itemsPrefix + kind.namespace; } - function cacheKey(kind, key) { - return kind.namespace + ":" + key; - } - // A helper that performs a get with the redis client function doGet(kind, key, cb) { var item; cb = cb || noop; - if (cacheTTL) { - item = cache.get(cacheKey(kind, key)); - if (item) { - cb(item); - return; - } - } - if (!connected) { logger.warn('Attempted to fetch key ' + key + ' while Redis connection is down'); cb(null); @@ -79,6 +67,7 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { } client.hget(itemsKey(kind), key, function(err, obj) { + 51 if (err) { logger.error("Error fetching key " + key + " from Redis in '" + kind.namespace + "'", err); cb(null); @@ -89,30 +78,9 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { }); } - function executePendingUpdates() { - if (updateQueue.length > 0) { - const entry = updateQueue[0]; - const fn = entry[0]; - const args = entry[1]; - const cb = entry[2]; - const newCb = function() { - updateQueue.shift(); - if (updateQueue.length > 0) { - setImmediate(executePendingUpdates); - } - cb && cb(); - }; - fn.apply(store, args.concat([newCb])); - } - } - // Places an update operation on the queue. var serializeFn = function(updateFn, fnArgs, cb) { - updateQueue.push([updateFn, fnArgs, cb]); - if (updateQueue.length == 1) { - // if nothing else is in progress, we can start this one right away - executePendingUpdates(); - } + updateQueue.enqueue(updateFn.bind(store), fnArgs, cb); }; store.get = function(kind, key, cb) { @@ -162,9 +130,7 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { store._init = function(allData, cb) { var multi = client.multi(); - if (cacheTTL) { - cache.flushAll(); - } + cache.flush(); for (var kindNamespace in allData) { if (Object.hasOwnProperty.call(allData, kindNamespace)) { @@ -177,9 +143,7 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { if (Object.hasOwnProperty.call(items, key)) { stringified[key] = JSON.stringify(items[key]); } - if (cacheTTL) { - cache.set(cacheKey(kind, key), items[key]); - } + cache.set(kind, key, items[key]); } // Redis does not allow hmset() with an empty object if (Object.keys(stringified).length > 0) { @@ -244,8 +208,8 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { updateItemWithVersioning(kind, newItem, cb, resultFn); } else { resultFn(err); - if (!err && cacheTTL) { - cache.set(cacheKey(kind, newItem.key), newItem); + if (!err) { + cache.set(kind, newItem.key, newItem); } cb(); } @@ -280,12 +244,10 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { store.close = function() { client.quit(); - if (cacheTTL) { - cache.close(); - } + cache.close(); }; return store; } -module.exports = RedisFeatureStore; \ No newline at end of file +module.exports = RedisFeatureStore; From 38b8df3c53bc32a0ccb9e5f92b443a0ce5906d35 Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Wed, 7 Nov 2018 15:00:14 -0800 Subject: [PATCH 045/330] fix formatting --- feature_store_utils.js | 1 - redis_feature_store.js | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/feature_store_utils.js b/feature_store_utils.js index 96dd08c..36c4304 100644 --- a/feature_store_utils.js +++ b/feature_store_utils.js @@ -37,7 +37,6 @@ function StoreCache(ttl, getFallback) { }; } -// queue function UpdateQueue() { var updateQueue = []; this.enqueue = function(updateFn, fnArgs, cb) { diff --git a/redis_feature_store.js b/redis_feature_store.js index b2ca98c..8fc0897 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -1,7 +1,7 @@ var redis = require('redis'), winston = require('winston'), dataKind = require('./versioned_data_kind'), -util = require('./feature_store_utils'); + util = require('./feature_store_utils'); var noop = function(){}; @@ -67,7 +67,6 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { } client.hget(itemsKey(kind), key, function(err, obj) { - 51 if (err) { logger.error("Error fetching key " + key + " from Redis in '" + kind.namespace + "'", err); cb(null); From c69ca148a6ac3a3d6e905052c4d000d6eae4c9b7 Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Wed, 7 Nov 2018 15:09:48 -0800 Subject: [PATCH 046/330] fix audit --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index e841e98..92ed44a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ldclient-node", - "version": "5.4.2", + "version": "5.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3855,9 +3855,9 @@ } }, "merge": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", - "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", + "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", "dev": true }, "merge-stream": { From ed6b677f848a9a5ba76332f5ac40750bc3e48b89 Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Wed, 7 Nov 2018 15:11:16 -0800 Subject: [PATCH 047/330] consistency --- feature_store_utils.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/feature_store_utils.js b/feature_store_utils.js index 36c4304..1c836c3 100644 --- a/feature_store_utils.js +++ b/feature_store_utils.js @@ -17,19 +17,17 @@ function StoreCache(ttl, getFallback) { } getFallback(kind, key, function (item) { - if (cache) { - cache.set(cacheKey(kind, key), item); - } + cache && cache.set(cacheKey(kind, key), item); cb(item); }); }; this.set = function(kind, key, newItem) { - cache.set(cacheKey(kind, key), newItem); + cache && cache.set(cacheKey(kind, key), newItem); }; this.flush = function() { - cache.flushAll(); + cache && cache.flushAll(); }; this.close = function() { From 874c0af65623b66278fbf224e524e38844ba6946 Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Wed, 7 Nov 2018 16:45:58 -0800 Subject: [PATCH 048/330] remove yarn stuff --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5a9d795..406e2c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ junit.xml node_modules/ npm-debug.log -yarn.lock From da403186dca59f2e7528497987f0d8a2aada0ff2 Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Wed, 7 Nov 2018 16:52:37 -0800 Subject: [PATCH 049/330] make the caching into a wrapper around any feature store --- caching_store_wrapper.js | 72 ++++++++++++++++++++++++++++++++++++++++ feature_store_utils.js | 66 ------------------------------------ redis_feature_store.js | 18 +++++----- update_queue.js | 29 ++++++++++++++++ 4 files changed, 109 insertions(+), 76 deletions(-) create mode 100644 caching_store_wrapper.js delete mode 100644 feature_store_utils.js create mode 100644 update_queue.js diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js new file mode 100644 index 0000000..c8dfd86 --- /dev/null +++ b/caching_store_wrapper.js @@ -0,0 +1,72 @@ +var NodeCache = require('node-cache'), + dataKind = require('./versioned_data_kind'); + +function cacheKey(kind, key) { + return kind.namespace + ":" + key; +} + +function CachingStoreWrapper(ttl, underlyingStore) { + var cache = ttl ? new NodeCache({ stdTTL: ttl }) : null; + + this.init = function(allData, cb) { + cache && cache.flushAll(); + + // populate cache with initial data + for (var kindNamespace in allData) { + if (Object.hasOwnProperty.call(allData, kindNamespace)) { + var kind = dataKind[kindNamespace]; + var items = allData[kindNamespace]; + for (var key in items) { + cache.set(kind, key, items[key]); + } + } + } + + underlyingStore.init(allData, cb); + }; + + this.initialized = function(cb) { + underlyingStore.initialized(cb); + }; + + this.all = function(kind, cb) { + underlyingStore.all(kind, cb); + }; + + this.get = function(kind, key, cb) { + if (cache) { + item = cache.get(cacheKey(kind, key)); + if (item) { + cb(item); + return; + } + } + + underlyingStore.get(kind, key, function (item) { + cache && cache.set(cacheKey(kind, key), item); + cb(item); + }); + }; + + this.upsert = function(kind, newItem, cb, resultFn) { + underlyingStore.upsert(kind, newItem, cb, function(err) { + if (!err) { + cache && cache.set(kind, newItem.key, newItem); + } + resultFn(err); + }); + }; + + this.delete = function(kind, key, version, cb) { + cache && cache.del(cacheKey(kind, key)); + underlyingStore.delete(kind, key, version, cb); + }; + + this.close = function() { + cache && cache.close(); + underlyingStore.close(); + }; +} + +module.exports = CachingStoreWrapper; + diff --git a/feature_store_utils.js b/feature_store_utils.js deleted file mode 100644 index 1c836c3..0000000 --- a/feature_store_utils.js +++ /dev/null @@ -1,66 +0,0 @@ -var NodeCache = require('node-cache'); - -function cacheKey(kind, key) { - return kind.namespace + ":" + key; -} - -function StoreCache(ttl, getFallback) { - var cache = ttl ? new NodeCache({ stdTTL: ttl }) : null; - - this.get = function(kind, key, cb) { - if (cache) { - item = cache.get(cacheKey(kind, key)); - if (item) { - cb(item); - return; - } - } - - getFallback(kind, key, function (item) { - cache && cache.set(cacheKey(kind, key), item); - cb(item); - }); - }; - - this.set = function(kind, key, newItem) { - cache && cache.set(cacheKey(kind, key), newItem); - }; - - this.flush = function() { - cache && cache.flushAll(); - }; - - this.close = function() { - cache && cache.close(); - }; -} - -function UpdateQueue() { - var updateQueue = []; - this.enqueue = function(updateFn, fnArgs, cb) { - updateQueue.push(arguments); - if (updateQueue.length === 1) { - // if nothing else is in progress, we can start this one right away - executePendingUpdates(); - } - }; - function executePendingUpdates() { - if (updateQueue.length > 0) { - const entry = updateQueue[0]; - console.log(entry); - const fn = entry[0]; - const args = entry[1]; - const cb = entry[2]; - const newCb = function() { - updateQueue.shift(); - if (updateQueue.length > 0) { - setImmediate(executePendingUpdates); - } - cb && cb(); - }; - fn.apply(null, args.concat([newCb])); - } - } -} - -module.exports = { StoreCache, UpdateQueue }; diff --git a/redis_feature_store.js b/redis_feature_store.js index 8fc0897..22e90d3 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -1,19 +1,24 @@ var redis = require('redis'), winston = require('winston'), dataKind = require('./versioned_data_kind'), - util = require('./feature_store_utils'); + CachingStoreWrapper = require('./caching_store_wrapper'); + UpdateQueue = require('./update_queue'); var noop = function(){}; function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { + return new CachingStoreWrapper(cacheTTL, new RedisFeatureStoreNoCache(redisOpts, prefix, logger)); +} + +// TODO better name? +function RedisFeatureStoreNoCache(redisOpts, prefix, logger) { var client = redis.createClient(redisOpts), store = {}, itemsPrefix = (prefix || "launchdarkly") + ":", - cache = new util.StoreCache(cacheTTL, doGet), - updateQueue = new util.UpdateQueue(), + updateQueue = new UpdateQueue(), inited = false, checkedInit = false; @@ -129,8 +134,6 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { store._init = function(allData, cb) { var multi = client.multi(); - cache.flush(); - for (var kindNamespace in allData) { if (Object.hasOwnProperty.call(allData, kindNamespace)) { var kind = dataKind[kindNamespace]; @@ -142,7 +145,6 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { if (Object.hasOwnProperty.call(items, key)) { stringified[key] = JSON.stringify(items[key]); } - cache.set(kind, key, items[key]); } // Redis does not allow hmset() with an empty object if (Object.keys(stringified).length > 0) { @@ -207,9 +209,6 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { updateItemWithVersioning(kind, newItem, cb, resultFn); } else { resultFn(err); - if (!err) { - cache.set(kind, newItem.key, newItem); - } cb(); } }); @@ -243,7 +242,6 @@ function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { store.close = function() { client.quit(); - cache.close(); }; return store; diff --git a/update_queue.js b/update_queue.js new file mode 100644 index 0000000..7c57fa4 --- /dev/null +++ b/update_queue.js @@ -0,0 +1,29 @@ + +function UpdateQueue() { + var updateQueue = []; + this.enqueue = function(updateFn, fnArgs, cb) { + updateQueue.push(arguments); + if (updateQueue.length === 1) { + // if nothing else is in progress, we can start this one right away + executePendingUpdates(); + } + }; + function executePendingUpdates() { + if (updateQueue.length > 0) { + const entry = updateQueue[0]; + const fn = entry[0]; + const args = entry[1]; + const cb = entry[2]; + const newCb = function() { + updateQueue.shift(); + if (updateQueue.length > 0) { + setImmediate(executePendingUpdates); + } + cb && cb(); + }; + fn.apply(null, args.concat([newCb])); + } + } +} + +module.exports = UpdateQueue; From b933e277b386c11dfb46d93af65282f78ee3071c Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Wed, 7 Nov 2018 16:53:22 -0800 Subject: [PATCH 050/330] maybe better name? --- redis_feature_store.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/redis_feature_store.js b/redis_feature_store.js index 22e90d3..4f501ac 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -9,11 +9,10 @@ var noop = function(){}; function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { - return new CachingStoreWrapper(cacheTTL, new RedisFeatureStoreNoCache(redisOpts, prefix, logger)); + return new CachingStoreWrapper(cacheTTL, new UncachedRedisFeatureStore(redisOpts, prefix, logger)); } -// TODO better name? -function RedisFeatureStoreNoCache(redisOpts, prefix, logger) { +function UncachedRedisFeatureStore(redisOpts, prefix, logger) { var client = redis.createClient(redisOpts), store = {}, From caea5e8ca610eaed57b8d5a525e7119a51960a07 Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Wed, 7 Nov 2018 16:58:01 -0800 Subject: [PATCH 051/330] Simplify things by just not adding the wrapper if a cache is not requested --- caching_store_wrapper.js | 22 ++++++++++------------ redis_feature_store.js | 6 +++++- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index c8dfd86..b3c03be 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -6,10 +6,10 @@ function cacheKey(kind, key) { } function CachingStoreWrapper(ttl, underlyingStore) { - var cache = ttl ? new NodeCache({ stdTTL: ttl }) : null; + var cache = new NodeCache({ stdTTL: ttl }); this.init = function(allData, cb) { - cache && cache.flushAll(); + cache.flushAll(); // populate cache with initial data for (var kindNamespace in allData) { @@ -34,16 +34,14 @@ function CachingStoreWrapper(ttl, underlyingStore) { }; this.get = function(kind, key, cb) { - if (cache) { - item = cache.get(cacheKey(kind, key)); - if (item) { - cb(item); - return; - } + item = cache.get(cacheKey(kind, key)); + if (item) { + cb(item); + return; } underlyingStore.get(kind, key, function (item) { - cache && cache.set(cacheKey(kind, key), item); + cache.set(cacheKey(kind, key), item); cb(item); }); }; @@ -51,19 +49,19 @@ function CachingStoreWrapper(ttl, underlyingStore) { this.upsert = function(kind, newItem, cb, resultFn) { underlyingStore.upsert(kind, newItem, cb, function(err) { if (!err) { - cache && cache.set(kind, newItem.key, newItem); + cache.set(kind, newItem.key, newItem); } resultFn(err); }); }; this.delete = function(kind, key, version, cb) { - cache && cache.del(cacheKey(kind, key)); + cache.del(cacheKey(kind, key)); underlyingStore.delete(kind, key, version, cb); }; this.close = function() { - cache && cache.close(); + cache.close(); underlyingStore.close(); }; } diff --git a/redis_feature_store.js b/redis_feature_store.js index 4f501ac..b48ffed 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -9,7 +9,11 @@ var noop = function(){}; function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { - return new CachingStoreWrapper(cacheTTL, new UncachedRedisFeatureStore(redisOpts, prefix, logger)); + var store = new UncachedRedisFeatureStore(redisOpts, prefix, logger); + if (cacheTTL) { + store = new CachingStoreWrapper(cacheTTL, store); + } + return store; } function UncachedRedisFeatureStore(redisOpts, prefix, logger) { From 657526ee92c8623746a915f9bd5628bdd4ce9cba Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Thu, 8 Nov 2018 10:17:53 -0800 Subject: [PATCH 052/330] switch param order --- caching_store_wrapper.js | 2 +- redis_feature_store.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index b3c03be..c5ab083 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -5,7 +5,7 @@ function cacheKey(kind, key) { return kind.namespace + ":" + key; } -function CachingStoreWrapper(ttl, underlyingStore) { +function CachingStoreWrapper(underlyingStore, ttl) { var cache = new NodeCache({ stdTTL: ttl }); this.init = function(allData, cb) { diff --git a/redis_feature_store.js b/redis_feature_store.js index b48ffed..ab02c0f 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -11,7 +11,7 @@ var noop = function(){}; function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { var store = new UncachedRedisFeatureStore(redisOpts, prefix, logger); if (cacheTTL) { - store = new CachingStoreWrapper(cacheTTL, store); + store = new CachingStoreWrapper(store, cacheTTL); } return store; } From c46a0dd2c5074891fb47b8e89ec9227212a2b71c Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Thu, 8 Nov 2018 16:21:59 -0800 Subject: [PATCH 053/330] Fix caching behavior with upsert, move some more functionality to the wrapper --- caching_store_wrapper.js | 67 ++++++++++++++++++++------------ redis_feature_store.js | 82 ++++++++++------------------------------ 2 files changed, 63 insertions(+), 86 deletions(-) diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index c5ab083..6c3e856 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -1,32 +1,48 @@ var NodeCache = require('node-cache'), - dataKind = require('./versioned_data_kind'); + dataKind = require('./versioned_data_kind'), + UpdateQueue = require('./update_queue'); function cacheKey(kind, key) { return kind.namespace + ":" + key; } function CachingStoreWrapper(underlyingStore, ttl) { - var cache = new NodeCache({ stdTTL: ttl }); + var cache = ttl ? new NodeCache({ stdTTL: ttl }) : null; + var queue = new UpdateQueue(); + var initialized; this.init = function(allData, cb) { - cache.flushAll(); + initialized = undefined; + queue.enqueue(function(cb) { + cache.flushAll(); - // populate cache with initial data - for (var kindNamespace in allData) { - if (Object.hasOwnProperty.call(allData, kindNamespace)) { - var kind = dataKind[kindNamespace]; - var items = allData[kindNamespace]; - for (var key in items) { - cache.set(kind, key, items[key]); + // populate cache with initial data + for (var kindNamespace in allData) { + if (Object.hasOwnProperty.call(allData, kindNamespace)) { + var kind = dataKind[kindNamespace]; + var items = allData[kindNamespace]; + for (var key in items) { + cache.set(kind, key, items[key]); + } } } - } - underlyingStore.init(allData, cb); + underlyingStore.init(allData, cb); + }, [], cb); }; this.initialized = function(cb) { - underlyingStore.initialized(cb); + if (initialized === undefined) { + underlyingStore.initialized(function(inited) { + initialized = inited; + cb(initialized); + }); + } else { + // if we already have a cached value for initialized, don't bother trying + // to call the underlying store until someone calls init, since it won't + // change. + cb(initialized); + } }; this.all = function(kind, cb) { @@ -34,30 +50,31 @@ function CachingStoreWrapper(underlyingStore, ttl) { }; this.get = function(kind, key, cb) { - item = cache.get(cacheKey(kind, key)); - if (item) { + item = cache && cache.get(cacheKey(kind, key)); + if (item && !item.deleted) { cb(item); return; } underlyingStore.get(kind, key, function (item) { - cache.set(cacheKey(kind, key), item); + cache && cache.set(cacheKey(kind, key), item); cb(item); }); }; - this.upsert = function(kind, newItem, cb, resultFn) { - underlyingStore.upsert(kind, newItem, cb, function(err) { - if (!err) { - cache.set(kind, newItem.key, newItem); - } - resultFn(err); - }); + this.upsert = function(kind, newItem, cb) { + queue.enqueue(function (cb) { + underlyingStore.upsertInternal(kind, newItem, function(err, attemptedWrite) { + if (attemptedWrite && !err) { + var red = cache && cache.set(cacheKey(kind, newItem.key), newItem); + } + cb(); + }); + }, [], cb); }; this.delete = function(kind, key, version, cb) { - cache.del(cacheKey(kind, key)); - underlyingStore.delete(kind, key, version, cb); + this.upsert(kind, { key: key, version: version, deleted: true }, cb); }; this.close = function() { diff --git a/redis_feature_store.js b/redis_feature_store.js index ab02c0f..b087d6c 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -2,18 +2,13 @@ var redis = require('redis'), winston = require('winston'), dataKind = require('./versioned_data_kind'), CachingStoreWrapper = require('./caching_store_wrapper'); - UpdateQueue = require('./update_queue'); var noop = function(){}; function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { - var store = new UncachedRedisFeatureStore(redisOpts, prefix, logger); - if (cacheTTL) { - store = new CachingStoreWrapper(store, cacheTTL); - } - return store; + return new CachingStoreWrapper(new UncachedRedisFeatureStore(redisOpts, prefix, logger), cacheTTL); } function UncachedRedisFeatureStore(redisOpts, prefix, logger) { @@ -21,7 +16,6 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { var client = redis.createClient(redisOpts), store = {}, itemsPrefix = (prefix || "launchdarkly") + ":", - updateQueue = new UpdateQueue(), inited = false, checkedInit = false; @@ -85,11 +79,6 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { }); } - // Places an update operation on the queue. - var serializeFn = function(updateFn, fnArgs, cb) { - updateQueue.enqueue(updateFn.bind(store), fnArgs, cb); - }; - store.get = function(kind, key, cb) { cb = cb || noop; doGet(kind, key, function(item) { @@ -131,10 +120,6 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { }; store.init = function(allData, cb) { - serializeFn(store._init, [allData], cb); - }; - - store._init = function(allData, cb) { var multi = client.multi(); for (var kindNamespace in allData) { @@ -166,34 +151,21 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { }); }; - store.delete = function(kind, key, version, cb) { - serializeFn(store._delete, [kind, key, version], cb); - }; - - store._delete = function(kind, key, version, cb) { - var deletedItem = { key: key, version: version, deleted: true }; - updateItemWithVersioning(kind, deletedItem, cb, - function(err) { - if (err) { - logger.error("Error deleting key " + key + " in '" + kind.namespace + "'", err); - } - }); - } - store.upsert = function(kind, item, cb) { - serializeFn(store._upsert, [kind, item], cb); + store.upsertInternal(kind, item, function() { cb(); }); + }; - store._upsert = function(kind, item, cb) { - updateItemWithVersioning(kind, item, cb, - function(err) { - if (err) { - logger.error("Error upserting key " + key + " in '" + kind.namespace + "'", err); - } - }); + store.upsertInternal = function(kind, item, cb) { + updateItemWithVersioning(kind, item, function(err, attemptedWrite) { + if (err) { + logger.error("Error upserting key " + key + " in '" + kind.namespace + "'", err); + } + cb(err, attemptedWrite); + }); } - function updateItemWithVersioning(kind, newItem, cb, resultFn) { + function updateItemWithVersioning(kind, newItem, cb) { client.watch(itemsKey(kind)); var multi = client.multi(); // test_transaction_hook is instrumentation, set only by the unit tests @@ -202,7 +174,7 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { doGet(kind, newItem.key, function(oldItem) { if (oldItem && oldItem.version >= newItem.version) { multi.discard(); - cb(); + cb(null, false); } else { multi.hset(itemsKey(kind), newItem.key, JSON.stringify(newItem)); multi.exec(function(err, replies) { @@ -211,8 +183,7 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { logger.debug("Concurrent modification detected, retrying"); updateItemWithVersioning(kind, newItem, cb, resultFn); } else { - resultFn(err); - cb(); + cb(err, true); } }); } @@ -222,25 +193,14 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { store.initialized = function(cb) { cb = cb || noop; - if (inited) { - // Once we've determined that we're initialized, we can never become uninitialized again - cb(true); - } - else if (checkedInit) { - // We don't want to hit Redis for this question more than once; if we've already checked there - // and it wasn't populated, we'll continue to say we're uninited until init() has been called - cb(false); - } - else { - var inited = false; - client.exists(itemsKey(dataKind.features), function(err, obj) { - if (!err && obj) { - inited = true; - } - checkedInit = true; - cb(inited); - }); - } + var inited = false; + client.exists(itemsKey(dataKind.features), function(err, obj) { + if (!err && obj) { + inited = true; + } + checkedInit = true; + cb(inited); + }); }; store.close = function() { From 4133909aecbb3398cc64c1ec50c0d270c44a360e Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Fri, 9 Nov 2018 11:21:18 -0800 Subject: [PATCH 054/330] clean up init logic --- redis_feature_store.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/redis_feature_store.js b/redis_feature_store.js index b087d6c..0b2133b 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -15,9 +15,7 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { var client = redis.createClient(redisOpts), store = {}, - itemsPrefix = (prefix || "launchdarkly") + ":", - inited = false, - checkedInit = false; + itemsPrefix = (prefix || "launchdarkly") + ":"; logger = (logger || new winston.Logger({ @@ -144,8 +142,6 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { multi.exec(function(err, replies) { if (err) { logger.error("Error initializing Redis store", err); - } else { - inited = true; } cb(); }); @@ -193,13 +189,8 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { store.initialized = function(cb) { cb = cb || noop; - var inited = false; client.exists(itemsKey(dataKind.features), function(err, obj) { - if (!err && obj) { - inited = true; - } - checkedInit = true; - cb(inited); + cb(Boolean(!err && obj)); }); }; From a1b90543aac3b49d1fdaadcb33507afc25bcb0ef Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Fri, 9 Nov 2018 15:53:58 -0800 Subject: [PATCH 055/330] Implement more correct init logic, caching 'all' calls, and other minor cleanups --- caching_store_wrapper.js | 85 ++++++++++++++++++++++++++++------------ redis_feature_store.js | 9 +---- 2 files changed, 62 insertions(+), 32 deletions(-) diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index 6c3e856..a601d34 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -6,52 +6,77 @@ function cacheKey(kind, key) { return kind.namespace + ":" + key; } +function allCacheKey(kind) { + return "$all:" + kind.namespace; +} + +var initializedKey = "$checkedInit"; + + function CachingStoreWrapper(underlyingStore, ttl) { var cache = ttl ? new NodeCache({ stdTTL: ttl }) : null; var queue = new UpdateQueue(); - var initialized; + var initialized = false; this.init = function(allData, cb) { - initialized = undefined; - queue.enqueue(function(cb) { - cache.flushAll(); - - // populate cache with initial data - for (var kindNamespace in allData) { - if (Object.hasOwnProperty.call(allData, kindNamespace)) { - var kind = dataKind[kindNamespace]; - var items = allData[kindNamespace]; - for (var key in items) { - cache.set(kind, key, items[key]); + if (cache) { + queue.enqueue(function(cb) { + cache.del(initializedKey); + cache.flushAll(); + + // populate cache with initial data + for (var kindNamespace in allData) { + if (Object.hasOwnProperty.call(allData, kindNamespace)) { + var kind = dataKind[kindNamespace]; + var items = allData[kindNamespace]; + cache.set(allCacheKey(kind), items); + for (var key in items) { + cache.set(cacheKey(kind, key), items[key]); + } } } - } - underlyingStore.init(allData, cb); - }, [], cb); + underlyingStore.init(allData, cb); + }, [], cb); + } }; this.initialized = function(cb) { - if (initialized === undefined) { + if (initialized) { + cb(initialized); + } else if (cache && cache.get(initializedKey)) { + return false; + } else { underlyingStore.initialized(function(inited) { initialized = inited; + if (!initialized) { + cache && cache.set(initializedKey, true); + } cb(initialized); }); - } else { - // if we already have a cached value for initialized, don't bother trying - // to call the underlying store until someone calls init, since it won't - // change. - cb(initialized); } }; this.all = function(kind, cb) { - underlyingStore.all(kind, cb); + var items = cache && cache.get(allCacheKey(kind)); + if (items) { + cb(items); + return; + } + + underlyingStore.all(kind, function(items) { + cache.set(allCacheKey(kind), items) + cb(items); + }); }; this.get = function(kind, key, cb) { - item = cache && cache.get(cacheKey(kind, key)); - if (item && !item.deleted) { + var item = cache && cache.get(cacheKey(kind, key)); + if (item) { + if (item.deleted) { + cb(null); + return; + } cb(item); return; } @@ -64,9 +89,10 @@ function CachingStoreWrapper(underlyingStore, ttl) { this.upsert = function(kind, newItem, cb) { queue.enqueue(function (cb) { + flushAllCaches(); underlyingStore.upsertInternal(kind, newItem, function(err, attemptedWrite) { if (attemptedWrite && !err) { - var red = cache && cache.set(cacheKey(kind, newItem.key), newItem); + cache && cache.set(cacheKey(kind, newItem.key), newItem); } cb(); }); @@ -81,6 +107,15 @@ function CachingStoreWrapper(underlyingStore, ttl) { cache.close(); underlyingStore.close(); }; + + function flushAllCaches() { + if (!cache) { + return; + } + for (var kindNamespace in dataKind) { + cache.del(allCacheKey(dataKind[kindNamespace])); + } + } } module.exports = CachingStoreWrapper; diff --git a/redis_feature_store.js b/redis_feature_store.js index 0b2133b..6c23bd6 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -8,10 +8,10 @@ var noop = function(){}; function RedisFeatureStore(redisOpts, cacheTTL, prefix, logger) { - return new CachingStoreWrapper(new UncachedRedisFeatureStore(redisOpts, prefix, logger), cacheTTL); + return new CachingStoreWrapper(new redisFeatureStoreInternal(redisOpts, prefix, logger), cacheTTL); } -function UncachedRedisFeatureStore(redisOpts, prefix, logger) { +function redisFeatureStoreInternal(redisOpts, prefix, logger) { var client = redis.createClient(redisOpts), store = {}, @@ -147,11 +147,6 @@ function UncachedRedisFeatureStore(redisOpts, prefix, logger) { }); }; - store.upsert = function(kind, item, cb) { - store.upsertInternal(kind, item, function() { cb(); }); - - }; - store.upsertInternal = function(kind, item, cb) { updateItemWithVersioning(kind, item, function(err, attemptedWrite) { if (err) { From bfa9e124bd258800b4a25364f74bc66746124b1e Mon Sep 17 00:00:00 2001 From: Kevin Brackbill Date: Fri, 9 Nov 2018 15:56:42 -0800 Subject: [PATCH 056/330] undo change to gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 406e2c6..4ba85cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ junit.xml node_modules/ -npm-debug.log +npm-debug.log \ No newline at end of file From 43c556603894dada9dcb0efd6265a852ffe4589d Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Fri, 9 Nov 2018 17:29:24 -0800 Subject: [PATCH 057/330] add tests for CachingStoreWrapper + misc bug fixes --- caching_store_wrapper.js | 66 +++--- redis_feature_store.js | 4 +- test/caching_store_wrapper-test.js | 362 +++++++++++++++++++++++++++++ 3 files changed, 400 insertions(+), 32 deletions(-) create mode 100644 test/caching_store_wrapper-test.js diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index a601d34..2ca0732 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -19,33 +19,37 @@ function CachingStoreWrapper(underlyingStore, ttl) { var initialized = false; this.init = function(allData, cb) { - if (cache) { - queue.enqueue(function(cb) { - cache.del(initializedKey); - cache.flushAll(); - - // populate cache with initial data - for (var kindNamespace in allData) { - if (Object.hasOwnProperty.call(allData, kindNamespace)) { - var kind = dataKind[kindNamespace]; - var items = allData[kindNamespace]; - cache.set(allCacheKey(kind), items); - for (var key in items) { - cache.set(cacheKey(kind, key), items[key]); + queue.enqueue(function(cb) { + underlyingStore.init(allData, function() { + initialized = true; + + if (cache) { + cache.del(initializedKey); + cache.flushAll(); + + // populate cache with initial data + for (var kindNamespace in allData) { + if (Object.hasOwnProperty.call(allData, kindNamespace)) { + var kind = dataKind[kindNamespace]; + var items = allData[kindNamespace]; + cache.set(allCacheKey(kind), items); + for (var key in items) { + cache.set(cacheKey(kind, key), items[key]); + } } } } - underlyingStore.init(allData, cb); - }, [], cb); - } + cb(); + }); + }, [], cb); }; this.initialized = function(cb) { if (initialized) { - cb(initialized); + cb(true); } else if (cache && cache.get(initializedKey)) { - return false; + cb(false); } else { underlyingStore.initialized(function(inited) { initialized = inited; @@ -65,34 +69,36 @@ function CachingStoreWrapper(underlyingStore, ttl) { } underlyingStore.all(kind, function(items) { - cache.set(allCacheKey(kind), items) + cache && cache.set(allCacheKey(kind), items); cb(items); }); }; this.get = function(kind, key, cb) { - var item = cache && cache.get(cacheKey(kind, key)); - if (item) { - if (item.deleted) { - cb(null); + if (cache) { + var item = cache.get(cacheKey(kind, key)); + if (item !== undefined) { + cb(itemOnlyIfNotDeleted(item)); return; } - cb(item); - return; } - underlyingStore.get(kind, key, function (item) { + underlyingStore.get(kind, key, function(item) { cache && cache.set(cacheKey(kind, key), item); - cb(item); + cb(itemOnlyIfNotDeleted(item)); }); }; + function itemOnlyIfNotDeleted(item) { + return (!item || item.deleted) ? null : item; + } + this.upsert = function(kind, newItem, cb) { queue.enqueue(function (cb) { flushAllCaches(); - underlyingStore.upsertInternal(kind, newItem, function(err, attemptedWrite) { - if (attemptedWrite && !err) { - cache && cache.set(cacheKey(kind, newItem.key), newItem); + underlyingStore.upsertInternal(kind, newItem, function(err, updatedItem) { + if (!err) { + cache && cache.set(cacheKey(kind, newItem.key), updatedItem); } cb(); }); diff --git a/redis_feature_store.js b/redis_feature_store.js index 6c23bd6..1c9f01d 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -165,7 +165,7 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { doGet(kind, newItem.key, function(oldItem) { if (oldItem && oldItem.version >= newItem.version) { multi.discard(); - cb(null, false); + cb(null, oldItem); } else { multi.hset(itemsKey(kind), newItem.key, JSON.stringify(newItem)); multi.exec(function(err, replies) { @@ -174,7 +174,7 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { logger.debug("Concurrent modification detected, retrying"); updateItemWithVersioning(kind, newItem, cb, resultFn); } else { - cb(err, true); + cb(err, newItem); } }); } diff --git a/test/caching_store_wrapper-test.js b/test/caching_store_wrapper-test.js new file mode 100644 index 0000000..db8addc --- /dev/null +++ b/test/caching_store_wrapper-test.js @@ -0,0 +1,362 @@ +var CachingStoreWrapper = require('../caching_store_wrapper'); +var features = require('../versioned_data_kind').features; + +function MockCore() { + const c = { + data: { features: {} }, + inited: false, + initQueriedCount: 0, + + init: function(newData, cb) { + c.data = newData; + cb(); + }, + + get: function(kind, key, cb) { + cb(c.data[kind.namespace][key]); + }, + + all: function(kind, cb) { + cb(c.data[kind.namespace]); + }, + + upsertInternal: function(kind, item, cb) { + const oldItem = c.data[kind.namespace][item.key]; + if (oldItem && oldItem.version >= item.version) { + cb(null, oldItem); + } else { + c.data[kind.namespace][item.key] = item; + cb(null, item); + } + }, + + initialized: function(cb) { + c.initQueriedCount++; + cb(c.inited); + }, + + forceSet: function(kind, item) { + c.data[kind.namespace][item.key] = item; + }, + + forceRemove: function(kind, key) { + delete c.data[kind.namespace][key]; + } + }; + return c; +} + +const cacheSeconds = 15; + +function runCachedAndUncachedTests(name, testFn) { + describe(name, function() { + const core1 = MockCore(); + const wrapper1 = new CachingStoreWrapper(core1, cacheSeconds); + it('cached', function(done) { testFn(done, wrapper1, core1, true); }); + + const core2 = MockCore(); + const wrapper2 = new CachingStoreWrapper(core2, 0); + it('uncached', function(done) { testFn(done, wrapper2, core2, false); }); + }); +} + +function runCachedTestOnly(name, testFn) { + it(name, function(done) { + const core = MockCore(); + const wrapper = new CachingStoreWrapper(core, cacheSeconds); + testFn(done, wrapper, core); + }); +} + +describe('CachingStoreWrapper', function() { + + runCachedAndUncachedTests('get()', function(done, wrapper, core, isCached) { + const flagv1 = { key: 'flag', version: 1 }; + const flagv2 = { key: 'flag', version: 2 }; + + core.forceSet(features, flagv1); + + wrapper.get(features, flagv1.key, function(item) { + expect(item).toEqual(flagv1); + + core.forceSet(features, flagv2); // Make a change that bypasses the cache + + wrapper.get(features, flagv1.key, function(item) { + // If cached, it should return the cached value rather than calling the underlying getter + expect(item).toEqual(isCached ? flagv1 : flagv2); + + done(); + }); + }); + }); + + runCachedAndUncachedTests('get() with deleted item', function(done, wrapper, core, isCached) { + const flagv1 = { key: 'flag', version: 1, deleted: true }; + const flagv2 = { key: 'flag', version: 2, deleted: false }; + + core.forceSet(features, flagv1); + + wrapper.get(features, flagv1.key, function(item) { + expect(item).toBe(null); + + core.forceSet(features, flagv2); // Make a change that bypasses the cache + + wrapper.get(features, flagv2.key, function(item) { + // If cached, the deleted state should persist in the cache + expect(item).toEqual(isCached ? null : flagv2); + + done(); + }); + }); + }); + + runCachedAndUncachedTests('get() with missing item', function(done, wrapper, core, isCached) { + const flag = { key: 'flag', version: 1 }; + + wrapper.get(features, flag.key, function(item) { + expect(item).toBe(null); + + core.forceSet(features, flag); + + wrapper.get(features, flag.key, function(item) { + // If cached, the previous null result should persist in the cache + expect(item).toEqual(isCached ? null : flag); + + done(); + }); + }); + }); + + runCachedTestOnly('cached get() uses values from init()', function(done, wrapper, core) { + const flagv1 = { key: 'flag', version: 1 }; + const flagv2 = { key: 'flag', version: 2 }; + + const allData = { features: { 'flag': flagv1 } }; + + wrapper.init(allData, function() { + expect(core.data).toEqual(allData); + + core.forceSet(features, flagv2); + + wrapper.get(features, flagv1.key, function(item) { + expect(item).toEqual(flagv1); + + done(); + }); + }); + }); + + runCachedAndUncachedTests('all()', function(done, wrapper, core, isCached) { + const flag1 = { key: 'flag1', version: 1 }; + const flag2 = { key: 'flag2', version: 1 }; + + core.forceSet(features, flag1); + core.forceSet(features, flag2); + + wrapper.all(features, function(items) { + expect(items).toEqual({ 'flag1': flag1, 'flag2': flag2 }); + + core.forceRemove(features, flag2.key); + + wrapper.all(features, function(items) { + if (isCached) { + expect(items).toEqual({ 'flag1': flag1, 'flag2': flag2 }); + } else { + expect(items).toEqual({ 'flag1': flag1 }); + } + + done(); + }); + }); + }); + + runCachedTestOnly('cached all() uses values from init()', function(done, wrapper, core) { + const flag1 = { key: 'flag1', version: 1 }; + const flag2 = { key: 'flag2', version: 1 }; + + const allData = { features: { flag1: flag1, flag2: flag2 } }; + + wrapper.init(allData, function() { + core.forceRemove(features, flag2.key); + + wrapper.all(features, function(items) { + expect(items).toEqual({ flag1: flag1, flag2: flag2 }); + + done(); + }); + }); + }); + + runCachedTestOnly('cached all() uses fresh values if there has been an update', function(done, wrapper, core) { + const flag1v1 = { key: 'flag1', version: 1 }; + const flag1v2 = { key: 'flag1', version: 2 }; + const flag2v1 = { key: 'flag2', version: 1 }; + const flag2v2 = { key: 'flag2', version: 2 }; + + const allData = { features: { flag1: flag1v1, flag2: flag2v2 } }; + + wrapper.init(allData, function() { + expect(core.data).toEqual(allData); + + // make a change to flag1 using the wrapper - this should flush the cache + wrapper.upsert(features, flag1v2, function() { + // make a change to flag2 that bypasses the cache + core.forceSet(features, flag2v2); + + // we should now see both changes since the cache was flushed + wrapper.all(features, function(items) { + expect(items).toEqual({ flag1: flag1v2, flag2: flag2v2 }); + + done(); + }); + }); + }); + }); + + runCachedAndUncachedTests('upsert() - successful', function(done, wrapper, core, isCached) { + const flagv1 = { key: 'flag', version: 1 }; + const flagv2 = { key: 'flag', version: 2 }; + + wrapper.upsert(features, flagv1, function() { + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv1); + + wrapper.upsert(features, flagv2, function() { + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv2); + + // if we have a cache, verify that the new item is now cached by writing a different value + // to the underlying data - get() should still return the cached item + if (isCached) { + const flagv3 = { key: 'flag', version: 3 }; + core.forceSet(features, flagv3); + } + + wrapper.get(features, flagv1.key, function(item) { + expect(item).toEqual(flagv2); + + done(); + }); + }); + }); + }); + + runCachedTestOnly('cached upsert() - unsuccessful', function(done, wrapper, core) { + const flagv1 = { key: 'flag', version: 1 }; + const flagv2 = { key: 'flag', version: 2 }; + + core.forceSet(features, flagv2); // this is now in the underlying data, but not in the cache + + wrapper.upsert(features, flagv1, function() { + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv2); // value in store remains the same + + // the cache should now contain flagv2 - check this by making another change that bypasses + // the cache, and verifying that get() uses the cached value instead + const flagv3 = { key: 'flag', version: 3 }; + core.forceSet(features, flagv3); + + wrapper.get(features, flagv1.key, function(item) { + expect(item).toEqual(flagv2); + + done(); + }); + }); + }); + + runCachedAndUncachedTests('delete()', function(done, wrapper, core, isCached) { + const flagv1 = { key: 'flag', version: 1 }; + const flagv2 = { key: 'flag', version: 2, deleted: true }; + const flagv3 = { key: 'flag', version: 3 }; + + core.forceSet(features, flagv1); + + wrapper.get(features, flagv1.key, function(item) { + expect(item).toEqual(flagv1); + + wrapper.delete(features, flagv1.key, flagv2.version); + + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv2); + + // make a change to the flag that bypasses the cache + core.forceSet(features, flagv3); + + wrapper.get(features, flagv1.key, function(item) { + expect(item).toEqual(isCached ? null : flagv3); + + done(); + }); + }); + }); + + describe('initialized()', function() { + it('calls underlying initialized() only if not already inited', function(done) { + const core = MockCore(); + const wrapper = new CachingStoreWrapper(core, 0); + + wrapper.initialized(function(value) { + expect(value).toEqual(false); + expect(core.initQueriedCount).toEqual(1); + + core.inited = true; + + wrapper.initialized(function(value) { + expect(value).toEqual(true); + expect(core.initQueriedCount).toEqual(2); + + core.inited = false; // this should have no effect since we already returned true + + wrapper.initialized(function(value) { + expect(value).toEqual(true); + expect(core.initQueriedCount).toEqual(2); + + done(); + }); + }); + }); + }); + + it('will not call initialized() if init() has been called', function(done) { + const core = MockCore(); + const wrapper = new CachingStoreWrapper(core, 0); + + wrapper.initialized(function(value) { + expect(value).toEqual(false); + expect(core.initQueriedCount).toEqual(1); + + const allData = { features: {} }; + wrapper.init(allData, function() { + wrapper.initialized(function(value) { + expect(value).toEqual(true); + expect(core.initQueriedCount).toEqual(1); + + done(); + }); + }); + }); + }); + + it('can cache false result', function(done) { + const core = MockCore(); + const wrapper = new CachingStoreWrapper(core, 1); // cache TTL = 1 second + + wrapper.initialized(function(value) { + expect(value).toEqual(false); + expect(core.initQueriedCount).toEqual(1); + + core.inited = true; + + wrapper.initialized(function(value) { + expect(value).toEqual(false); + expect(core.initQueriedCount).toEqual(1); + + setTimeout(function() { + wrapper.initialized(function(value) { + expect(value).toEqual(true); + expect(core.initQueriedCount).toEqual(2); + + done(); + }); + }, 1100); + }); + }); + }); + }); +}); From 5f14c1543cbd5433c5128ea297bfb6287cdafb02 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Fri, 9 Nov 2018 18:11:32 -0800 Subject: [PATCH 058/330] basic tests for UpdateQueue --- test/update_queue-test.js | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 test/update_queue-test.js diff --git a/test/update_queue-test.js b/test/update_queue-test.js new file mode 100644 index 0000000..7770bce --- /dev/null +++ b/test/update_queue-test.js @@ -0,0 +1,61 @@ +var UpdateQueue = require('../update_queue'); + +describe('UpdateQueue', function() { + it('executes task immediately if there are no pending tasks', function(done) { + const q = new UpdateQueue(); + + var updated = false; + const updateFn = function(a, b, cb) { + expect(a).toEqual(1); + expect(b).toEqual(2); + updated = true; + cb(); + }; + + q.enqueue(updateFn, [1, 2], function() { + expect(updated).toEqual(true); + done(); + }); + }); + + it('serializes async tasks in the order submitted', function(done) { + const q = new UpdateQueue(); + + var progress = []; + + // This simulates a condition in which events are being received asynchronously and each + // event triggers an asynchronous task. We want to make sure that the tasks are executed in + // the order submitted, even if one is submitted during the execution of the previous one. + const taskFn = function(i, cb) { + progress.push('start ' + i); + // assume that we're doing something asynchronous here - make sure it takes a little time + setTimeout(cb, 20); + }; + + const expected = [ + 'submit 1', + 'start 1', // note, this one executes immediately because there was nothing pening + 'submit 2', + 'submit 3', + 'end 1', + 'start 2', + 'end 2', + 'start 3', + 'end 3' + ]; + + for (var i = 1; i <= 3; i++) { + const j = i; + setImmediate(function() { + progress.push('submit ' + j); + q.enqueue(taskFn, [j], function() { + progress.push('end ' + j); + if (j >= 3) { + expect(progress).toEqual(expected); + done(); + } + }); + }); + }; + }); +}); \ No newline at end of file From 773e11ecf53919a9ab33134b20b4ef09d259f467 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Fri, 9 Nov 2018 19:11:12 -0800 Subject: [PATCH 059/330] typo --- test/update_queue-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/update_queue-test.js b/test/update_queue-test.js index 7770bce..b182855 100644 --- a/test/update_queue-test.js +++ b/test/update_queue-test.js @@ -34,7 +34,7 @@ describe('UpdateQueue', function() { const expected = [ 'submit 1', - 'start 1', // note, this one executes immediately because there was nothing pening + 'start 1', // note, this one executes immediately because there was nothing pending 'submit 2', 'submit 3', 'end 1', @@ -58,4 +58,4 @@ describe('UpdateQueue', function() { }); }; }); -}); \ No newline at end of file +}); From 02f516add7046f67dcb656574b8361eefe04fe52 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Fri, 9 Nov 2018 20:30:08 -0800 Subject: [PATCH 060/330] add some more feature store test coverage + misc Redis fixes --- caching_store_wrapper.js | 2 + redis_feature_store.js | 13 ++- test/feature_store-test.js | 4 +- test/feature_store_test_base.js | 163 ++++++++++++++++++++++++++++++- test/redis_feature_store-test.js | 66 +++++-------- 5 files changed, 197 insertions(+), 51 deletions(-) diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index 2ca0732..2824bd7 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -18,6 +18,8 @@ function CachingStoreWrapper(underlyingStore, ttl) { var queue = new UpdateQueue(); var initialized = false; + this.underlyingStore = underlyingStore; + this.init = function(allData, cb) { queue.enqueue(function(cb) { underlyingStore.init(allData, function() { diff --git a/redis_feature_store.js b/redis_feature_store.js index 1c9f01d..eedf438 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -15,7 +15,8 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { var client = redis.createClient(redisOpts), store = {}, - itemsPrefix = (prefix || "launchdarkly") + ":"; + itemsPrefix = (prefix || "launchdarkly") + ":", + initedKey = itemsPrefix + "$inited"; logger = (logger || new winston.Logger({ @@ -139,6 +140,8 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { } } + multi.set(initedKey, ""); + multi.exec(function(err, replies) { if (err) { logger.error("Error initializing Redis store", err); @@ -159,8 +162,8 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { function updateItemWithVersioning(kind, newItem, cb) { client.watch(itemsKey(kind)); var multi = client.multi(); - // test_transaction_hook is instrumentation, set only by the unit tests - var prepare = store.test_transaction_hook || function(prepareCb) { prepareCb(); }; + // testUpdateHook is instrumentation, used only by the unit tests + var prepare = store.testUpdateHook || function(prepareCb) { prepareCb(); }; prepare(function() { doGet(kind, newItem.key, function(oldItem) { if (oldItem && oldItem.version >= newItem.version) { @@ -172,7 +175,7 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { if (!err && replies === null) { // This means the EXEC failed because someone modified the watched key logger.debug("Concurrent modification detected, retrying"); - updateItemWithVersioning(kind, newItem, cb, resultFn); + updateItemWithVersioning(kind, newItem, cb); } else { cb(err, newItem); } @@ -184,7 +187,7 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { store.initialized = function(cb) { cb = cb || noop; - client.exists(itemsKey(dataKind.features), function(err, obj) { + client.exists(initedKey, function(err, obj) { cb(Boolean(!err && obj)); }); }; diff --git a/test/feature_store-test.js b/test/feature_store-test.js index 4ca19ce..71ab1db 100644 --- a/test/feature_store-test.js +++ b/test/feature_store-test.js @@ -1,8 +1,8 @@ var InMemoryFeatureStore = require('../feature_store'); -var allFeatureStoreTests = require('./feature_store_test_base'); +var testBase = require('./feature_store_test_base'); describe('InMemoryFeatureStore', function() { - allFeatureStoreTests(function() { + testBase.baseFeatureStoreTests(function() { return new InMemoryFeatureStore(); }) }); diff --git a/test/feature_store_test_base.js b/test/feature_store_test_base.js index 5bb874e..a11eca0 100644 --- a/test/feature_store_test_base.js +++ b/test/feature_store_test_base.js @@ -1,6 +1,17 @@ var dataKind = require('../versioned_data_kind'); -function allFeatureStoreTests(makeStore) { +// The following tests should be run on every feature store implementation. If this type of +// store supports caching, the tests should be run once with caching enabled and once with +// caching disabled. +// +// Parameters: +// - makeStore(): creates an instance of the feature store. +// - clearExistingData(callback): if specified, will be called before each test to clear any +// storage that the store instances may be sharing. +// - isCached: true if the instances returned by makeStore() have caching enabled. If +// applicable, + +function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { var feature1 = { key: 'foo', version: 10 @@ -10,6 +21,14 @@ function allFeatureStoreTests(makeStore) { version: 10 }; + beforeEach(function(done) { + if (clearExistingData) { + clearExistingData(done); + } else { + done(); + } + }); + function initedStore(cb) { var store = makeStore(); var initData = {}; @@ -31,6 +50,70 @@ function allFeatureStoreTests(makeStore) { }); }); + it('init() completely replaces previous data', function(done) { + var store = makeStore(); + var flags = { + first: { key: 'thing', version: 1 }, + second: { key: 'stuff', version: 1 } + }; + var segments = { first: { key: 'thing', version: 2 } }; + var initData = {}; + initData[dataKind.features.namespace] = flags; + initData[dataKind.segments.namespace] = segments; + + store.init(initData, function() { + store.all(dataKind.features, function(items) { + expect(items).toEqual(flags); + store.all(dataKind.segments, function(items) { + expect(items).toEqual(segments); + + var newFlags = { first: { key: 'thing', version: 3 } }; + var newSegments = { first: { key: 'thing', version: 4 } }; + var initData = {}; + initData[dataKind.features.namespace] = newFlags; + initData[dataKind.segments.namespace] = newSegments; + + store.init(initData, function() { + store.all(dataKind.features, function(items) { + expect(items).toEqual(newFlags); + store.all(dataKind.segments, function(items) { + expect(items).toEqual(newSegments); + + done(); + }) + }) + }); + }); + }); + }); + }); + + if (!isCached && clearExistingData) { + function testInitStateDetection(desc, initData) { + it(desc, function(done) { + var store1 = makeStore(); + var store2 = makeStore(); + + store1.initialized(function(result) { + expect(result).toBe(false); + + store2.init(initData, function() { + store1.initialized(function(result) { + expect(result).toBe(true); + done(); + }); + }); + }); + }); + } + + testInitStateDetection('can detect if another instance has initialized the store', + { features: { foo: feature1 } }); + + testInitStateDetection('can detect if another instance has initialized the store, even with empty data', + { features: {} }); + } + it('gets existing feature', function(done) { initedStore(function(store) { store.get(dataKind.features, feature1.key, function(result) { @@ -165,4 +248,80 @@ function allFeatureStoreTests(makeStore) { }); } -module.exports = allFeatureStoreTests; +// The following tests require that the feature store can be instrumented in such a way as to run +// some test code in the middle of an upsert operation. +// +// Parameters: +// - makeStore(): creates a normal feature store. +// - makeStoreWithHook(hook): creates a feature store that operates on the same underlying data as +// the first store. This store will call the hook function (passing a callback) immediately before +// it attempts to make any update. + +function concurrentModificationTests(makeStore, makeStoreWithHook) { + + var flagKey = 'flag'; + var initialVersion = 1; + + var competingStore = makeStore(); + + function makeFlagWithVersion(v) { + return { key: flagKey, version: v }; + } + + function withInitedStore(store, cb) { + var allData = { features: {} }; + allData['features'][flagKey] = makeFlagWithVersion(initialVersion); + store.init(allData, cb); + } + + function writeCompetingVersions(flagVersionsToWrite) { + var i = 0; + return function(callback) { + if (i < flagVersionsToWrite.length) { + var newFlag = makeFlagWithVersion(flagVersionsToWrite[i]); + i++; + competingStore.upsert(dataKind.features, newFlag, callback); + } else { + callback(); + } + }; + } + + it('handles upsert race condition against other client with lower version', function(done) { + var myDesiredVersion = 10; + var competingStoreVersions = [ 2, 3, 4 ]; // proves that we can retry multiple times if necessary + + var myStore = makeStoreWithHook(writeCompetingVersions(competingStoreVersions)); + + withInitedStore(myStore, function() { + myStore.upsert(dataKind.features, makeFlagWithVersion(myDesiredVersion), function() { + myStore.get(dataKind.features, flagKey, function(result) { + expect(result.version).toEqual(myDesiredVersion); + done(); + }); + }); + }); + }); + + it('handles upsert race condition against other client with higher version', function(done) { + var myDesiredVersion = 2; + var competingStoreVersion = 3; + + var myStore = makeStoreWithHook(writeCompetingVersions([ competingStoreVersion ])); + + withInitedStore(myStore, function() { + myStore.upsert(dataKind.features, makeFlagWithVersion(myDesiredVersion), function() { + myStore.get(dataKind.features, flagKey, function(result) { + expect(result.version).toEqual(competingStoreVersion); + done(); + }); + }); + }); + }); +} + +module.exports = { + baseFeatureStoreTests: baseFeatureStoreTests, + concurrentModificationTests: concurrentModificationTests +}; + diff --git a/test/redis_feature_store-test.js b/test/redis_feature_store-test.js index 282d9dd..1ba8837 100644 --- a/test/redis_feature_store-test.js +++ b/test/redis_feature_store-test.js @@ -1,54 +1,36 @@ var RedisFeatureStore = require('../redis_feature_store'); -var allFeatureStoreTests = require('./feature_store_test_base'); +var testBase = require('./feature_store_test_base'); var dataKind = require('../versioned_data_kind'); var redis = require('redis'); describe('RedisFeatureStore', function() { var redisOpts = { url: 'redis://localhost:6379' }; - function makeStore() { - return new RedisFeatureStore(redisOpts, 30000); + var extraRedisClient = redis.createClient(redisOpts); + + function makeCachedStore() { + return new RedisFeatureStore(redisOpts, 30); + } + + function makeUncachedStore() { + return new RedisFeatureStore(redisOpts, 0); } - allFeatureStoreTests(makeStore); - - it('handles upsert race condition against external client correctly', function(done) { - var store = makeStore(); - var otherClient = redis.createClient(redisOpts); - - var feature1 = { - key: 'foo', - version: 1 - }; - var intermediateVer = { key: feature1.key, version: feature1.version }; - var finalVer = { key: feature1.key, version: 10 }; - - var initData = {}; - initData[dataKind.features.namespace] = { - 'foo': feature1 - }; - - store.init(initData, function() { - var tries = 0; - // This function will be called in between the WATCH and the update transaction. - // We're testing that the store will detect this concurrent modification and will - // transparently retry the update. - store.test_transaction_hook = function(cb) { - if (tries < 3) { - tries++; - intermediateVer.version++; - otherClient.hset("launchdarkly:features", "foo", JSON.stringify(intermediateVer), cb); - } else { - cb(); - } - }; - store.upsert(dataKind.features, finalVer, function() { - store.get(dataKind.features, feature1.key, function(result) { - otherClient.quit(); - expect(result).toEqual(finalVer); - done(); - }); - }); + function clearExistingData(callback) { + extraRedisClient.flushdb(callback); + } + + testBase.baseFeatureStoreTests(makeCachedStore, clearExistingData, true); + testBase.baseFeatureStoreTests(makeUncachedStore, clearExistingData, false); + + testBase.concurrentModificationTests(makeUncachedStore, + function(hook) { + var store = makeCachedStore(); + store.underlyingStore.testUpdateHook = hook; + return store; }); + + afterAll(function() { + extraRedisClient.quit(); }); }); From d72bb162e9ffe14b23572a9ef565a6c96d0fa571 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 12 Nov 2018 12:15:25 -0800 Subject: [PATCH 061/330] fix invalid data in feature store unit test --- test/feature_store_test_base.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/feature_store_test_base.js b/test/feature_store_test_base.js index a11eca0..f253e0b 100644 --- a/test/feature_store_test_base.js +++ b/test/feature_store_test_base.js @@ -53,10 +53,10 @@ function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { it('init() completely replaces previous data', function(done) { var store = makeStore(); var flags = { - first: { key: 'thing', version: 1 }, - second: { key: 'stuff', version: 1 } + first: { key: 'first', version: 1 }, + second: { key: 'second', version: 1 } }; - var segments = { first: { key: 'thing', version: 2 } }; + var segments = { first: { key: 'first', version: 2 } }; var initData = {}; initData[dataKind.features.namespace] = flags; initData[dataKind.segments.namespace] = segments; @@ -67,8 +67,8 @@ function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { store.all(dataKind.segments, function(items) { expect(items).toEqual(segments); - var newFlags = { first: { key: 'thing', version: 3 } }; - var newSegments = { first: { key: 'thing', version: 4 } }; + var newFlags = { first: { key: 'first', version: 3 } }; + var newSegments = { first: { key: 'first', version: 4 } }; var initData = {}; initData[dataKind.features.namespace] = newFlags; initData[dataKind.segments.namespace] = newSegments; From b9d1986a2beefcde479965cf1ad77124ebb3be16 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 12 Nov 2018 15:03:50 -0800 Subject: [PATCH 062/330] make internal feature store API more consistent, add comment --- caching_store_wrapper.js | 16 ++++++++---- redis_feature_store.js | 8 +++--- test.js | 41 ++++++++++++++++++++++++++++++ test/caching_store_wrapper-test.js | 8 +++--- 4 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 test.js diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index 2824bd7..9a65ff6 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -12,7 +12,13 @@ function allCacheKey(kind) { var initializedKey = "$checkedInit"; - +/* + CachingStoreWrapper provides commonly needed functionality for implementations of an + SDK feature store. The underlyingStore must implement a simplified interface for + querying and updating the data store (see redis_feature_store.js for an example) + while CachingStoreWrapper adds optional caching of stored items and of the + initialized state, and ensures that asynchronous operations are serialized correctly. +*/ function CachingStoreWrapper(underlyingStore, ttl) { var cache = ttl ? new NodeCache({ stdTTL: ttl }) : null; var queue = new UpdateQueue(); @@ -22,7 +28,7 @@ function CachingStoreWrapper(underlyingStore, ttl) { this.init = function(allData, cb) { queue.enqueue(function(cb) { - underlyingStore.init(allData, function() { + underlyingStore.initInternal(allData, function() { initialized = true; if (cache) { @@ -53,7 +59,7 @@ function CachingStoreWrapper(underlyingStore, ttl) { } else if (cache && cache.get(initializedKey)) { cb(false); } else { - underlyingStore.initialized(function(inited) { + underlyingStore.initializedInternal(function(inited) { initialized = inited; if (!initialized) { cache && cache.set(initializedKey, true); @@ -70,7 +76,7 @@ function CachingStoreWrapper(underlyingStore, ttl) { return; } - underlyingStore.all(kind, function(items) { + underlyingStore.getAllInternal(kind, function(items) { cache && cache.set(allCacheKey(kind), items); cb(items); }); @@ -85,7 +91,7 @@ function CachingStoreWrapper(underlyingStore, ttl) { } } - underlyingStore.get(kind, key, function(item) { + underlyingStore.getInternal(kind, key, function(item) { cache && cache.set(cacheKey(kind, key), item); cb(itemOnlyIfNotDeleted(item)); }); diff --git a/redis_feature_store.js b/redis_feature_store.js index eedf438..aedbd6c 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -78,7 +78,7 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { }); } - store.get = function(kind, key, cb) { + store.getInternal = function(kind, key, cb) { cb = cb || noop; doGet(kind, key, function(item) { if (item && !item.deleted) { @@ -89,7 +89,7 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { }); }; - store.all = function(kind, cb) { + store.getAllInternal = function(kind, cb) { cb = cb || noop; if (!connected) { logger.warn('Attempted to fetch all keys while Redis connection is down'); @@ -118,7 +118,7 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { }); }; - store.init = function(allData, cb) { + store.initInternal = function(allData, cb) { var multi = client.multi(); for (var kindNamespace in allData) { @@ -185,7 +185,7 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { }); } - store.initialized = function(cb) { + store.initializedInternal = function(cb) { cb = cb || noop; client.exists(initedKey, function(err, obj) { cb(Boolean(!err && obj)); diff --git a/test.js b/test.js new file mode 100644 index 0000000..bccf6b5 --- /dev/null +++ b/test.js @@ -0,0 +1,41 @@ + +var LaunchDarkly = require('ldclient-node'); + +var config = { + baseUri: 'https://ld-stg.global.ssl.fastly.net', + streamUri: 'https://stream-stg.launchdarkly.com', + eventsUri: 'https://events-stg.launchdarkly.comx', + stream: true +}; + +// TODO : Enter your LaunchDarkly SDK key here +var ldclient = LaunchDarkly.init("sdk-1bac0f17-f688-4b56-a699-57094f34703b", config); + +user = { + "firstName":"Bob", + "lastName":"Loblaw", + "key":"bob@example.com", + "custom":{ + "groups":"beta_testers" + } +}; + +// ldclient.on('error', function(e) { +// console.log("*** " + e); +// }); + +ldclient.once('ready', function() { + function doit() { + ldclient.variation("YOUR_FEATURE_FLAG_KEY", user, false, function(err, showFeature) { + if (showFeature) { + // application code to show the feature + console.log("Showing your feature to " + user.key ); + } else { + // the code to run if the feature is off + console.log("Not showing your feature to " + user.key); + } + }); + setTimeout(doit, 1000); + } + setTimeout(doit, 1000); +}); diff --git a/test/caching_store_wrapper-test.js b/test/caching_store_wrapper-test.js index db8addc..83dcd55 100644 --- a/test/caching_store_wrapper-test.js +++ b/test/caching_store_wrapper-test.js @@ -7,16 +7,16 @@ function MockCore() { inited: false, initQueriedCount: 0, - init: function(newData, cb) { + initInternal: function(newData, cb) { c.data = newData; cb(); }, - get: function(kind, key, cb) { + getInternal: function(kind, key, cb) { cb(c.data[kind.namespace][key]); }, - all: function(kind, cb) { + getAllInternal: function(kind, cb) { cb(c.data[kind.namespace]); }, @@ -30,7 +30,7 @@ function MockCore() { } }, - initialized: function(cb) { + initializedInternal: function(cb) { c.initQueriedCount++; cb(c.inited); }, From 469ea5f07d0f7622a41211162387405a70d7e60c Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 12 Nov 2018 15:11:28 -0800 Subject: [PATCH 063/330] use published tunnel package instead of fork --- package-lock.json | 5 +++-- package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 92ed44a..45308a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5845,8 +5845,9 @@ "dev": true }, "tunnel": { - "version": "https://github.com/launchdarkly/node-tunnel/tarball/d860e57650cce1ea655d00854c81babe6b47e02c", - "integrity": "sha1-DxkgfzcgRtPUaCGDy+INSgR8zdk=" + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" }, "tunnel-agent": { "version": "0.6.0", diff --git a/package.json b/package.json index 8c00a3d..9ab7c3f 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "request": "2.87.0", "request-etag": "^2.0.3", "semver": "5.5.0", - "tunnel": "https://github.com/launchdarkly/node-tunnel/tarball/d860e57650cce1ea655d00854c81babe6b47e02c", + "tunnel": "0.0.6", "winston": "2.4.1" }, "engines": { From 281b090e5d86277cb6a7eaa3ee66e50068060ab1 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Mon, 12 Nov 2018 15:18:40 -0800 Subject: [PATCH 064/330] rm test code --- test.js | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 test.js diff --git a/test.js b/test.js deleted file mode 100644 index bccf6b5..0000000 --- a/test.js +++ /dev/null @@ -1,41 +0,0 @@ - -var LaunchDarkly = require('ldclient-node'); - -var config = { - baseUri: 'https://ld-stg.global.ssl.fastly.net', - streamUri: 'https://stream-stg.launchdarkly.com', - eventsUri: 'https://events-stg.launchdarkly.comx', - stream: true -}; - -// TODO : Enter your LaunchDarkly SDK key here -var ldclient = LaunchDarkly.init("sdk-1bac0f17-f688-4b56-a699-57094f34703b", config); - -user = { - "firstName":"Bob", - "lastName":"Loblaw", - "key":"bob@example.com", - "custom":{ - "groups":"beta_testers" - } -}; - -// ldclient.on('error', function(e) { -// console.log("*** " + e); -// }); - -ldclient.once('ready', function() { - function doit() { - ldclient.variation("YOUR_FEATURE_FLAG_KEY", user, false, function(err, showFeature) { - if (showFeature) { - // application code to show the feature - console.log("Showing your feature to " + user.key ); - } else { - // the code to run if the feature is off - console.log("Not showing your feature to " + user.key); - } - }); - setTimeout(doit, 1000); - } - setTimeout(doit, 1000); -}); From 74657e8370805c1a2ecac65b28060f5d4198d20c Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Tue, 13 Nov 2018 12:25:49 -0800 Subject: [PATCH 065/330] filter out deleted items in wrapper's all() --- caching_store_wrapper.js | 11 +++++++++-- test/caching_store_wrapper-test.js | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index 9a65ff6..ec71f54 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -77,8 +77,15 @@ function CachingStoreWrapper(underlyingStore, ttl) { } underlyingStore.getAllInternal(kind, function(items) { - cache && cache.set(allCacheKey(kind), items); - cb(items); + var filteredItems = {}; + Object.keys(items).forEach(function(key) { + var item = items[key]; + if (item && !item.deleted) { + filteredItems[key] = item; + } + }); + cache && cache.set(allCacheKey(kind), filteredItems); + cb(filteredItems); }); }; diff --git a/test/caching_store_wrapper-test.js b/test/caching_store_wrapper-test.js index 83dcd55..fd6169c 100644 --- a/test/caching_store_wrapper-test.js +++ b/test/caching_store_wrapper-test.js @@ -170,6 +170,30 @@ describe('CachingStoreWrapper', function() { }); }); + runCachedAndUncachedTests('all() with deleted item', function(done, wrapper, core, isCached) { + const flag1 = { key: 'flag1', version: 1 }; + const flag2 = { key: 'flag2', version: 1, deleted: true }; + + core.forceSet(features, flag1); + core.forceSet(features, flag2); + + wrapper.all(features, function(items) { + expect(items).toEqual({ 'flag1': flag1 }); + + core.forceRemove(features, flag1.key); + + wrapper.all(features, function(items) { + if (isCached) { + expect(items).toEqual({ 'flag1': flag1 }); + } else { + expect(items).toEqual({ }); + } + + done(); + }); + }); + }); + runCachedTestOnly('cached all() uses values from init()', function(done, wrapper, core) { const flag1 = { key: 'flag1', version: 1 }; const flag2 = { key: 'flag2', version: 1 }; From 0c0b1122f4e2f07992963e69209b34a32c5acc7b Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Tue, 13 Nov 2018 12:28:51 -0800 Subject: [PATCH 066/330] remove redundant filtering --- redis_feature_store.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/redis_feature_store.js b/redis_feature_store.js index aedbd6c..009aae1 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -106,11 +106,8 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { items = obj; for (var key in items) { - if (Object.hasOwnProperty.call(items,key)) { - var item = JSON.parse(items[key]); - if (!item.deleted) { - results[key] = item; - } + if (Object.hasOwnProperty.call(items, key)) { + results[key] = JSON.parse(items[key]); } } cb(results); From bbacd128821a1fb0a3a8ef96296d276814145b87 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 14 Nov 2018 18:39:44 -0800 Subject: [PATCH 067/330] fix error handling when getting all items from store --- caching_store_wrapper.js | 4 ++++ test/caching_store_wrapper-test.js | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index ec71f54..0929dff 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -77,6 +77,10 @@ function CachingStoreWrapper(underlyingStore, ttl) { } underlyingStore.getAllInternal(kind, function(items) { + if (items === null || items === undefined) { + cb(items); + return; + } var filteredItems = {}; Object.keys(items).forEach(function(key) { var item = items[key]; diff --git a/test/caching_store_wrapper-test.js b/test/caching_store_wrapper-test.js index fd6169c..64179b0 100644 --- a/test/caching_store_wrapper-test.js +++ b/test/caching_store_wrapper-test.js @@ -6,6 +6,8 @@ function MockCore() { data: { features: {} }, inited: false, initQueriedCount: 0, + getAllError: false, + upsertError: null, initInternal: function(newData, cb) { c.data = newData; @@ -17,10 +19,14 @@ function MockCore() { }, getAllInternal: function(kind, cb) { - cb(c.data[kind.namespace]); + cb(c.getAllError ? null : c.data[kind.namespace]); }, upsertInternal: function(kind, item, cb) { + if (c.upsertError) { + cb(upsertError, null); + return; + } const oldItem = c.data[kind.namespace][item.key]; if (oldItem && oldItem.version >= item.version) { cb(null, oldItem); @@ -194,6 +200,15 @@ describe('CachingStoreWrapper', function() { }); }); + runCachedAndUncachedTests('all() error condition', function(done, wrapper, core, isCached) { + core.getAllError = true; + + wrapper.all(features, function(items) { + expect(items).toBe(null); + done(); + }); + }); + runCachedTestOnly('cached all() uses values from init()', function(done, wrapper, core) { const flag1 = { key: 'flag1', version: 1 }; const flag2 = { key: 'flag2', version: 1 }; From 99e5852a6c532a4a2db916b32c413e3ca1941203 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 14 Nov 2018 18:54:10 -0800 Subject: [PATCH 068/330] fix error in close of uncached store --- caching_store_wrapper.js | 2 +- test/caching_store_wrapper-test.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index 0929dff..246eda4 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -129,7 +129,7 @@ function CachingStoreWrapper(underlyingStore, ttl) { }; this.close = function() { - cache.close(); + cache && cache.close(); underlyingStore.close(); }; diff --git a/test/caching_store_wrapper-test.js b/test/caching_store_wrapper-test.js index 64179b0..9b3a9da 100644 --- a/test/caching_store_wrapper-test.js +++ b/test/caching_store_wrapper-test.js @@ -8,6 +8,7 @@ function MockCore() { initQueriedCount: 0, getAllError: false, upsertError: null, + closed: false, initInternal: function(newData, cb) { c.data = newData; @@ -41,6 +42,10 @@ function MockCore() { cb(c.inited); }, + close: function() { + c.closed = true; + }, + forceSet: function(kind, item) { c.data[kind.namespace][item.key] = item; }, @@ -398,4 +403,12 @@ describe('CachingStoreWrapper', function() { }); }); }); + + describe('close()', function() { + runCachedAndUncachedTests('closes underlying store', function(done, wrapper, core) { + wrapper.close(); + expect(core.closed).toBe(true); + done(); + }); + }); }); From 92297bca8d3256b2aceed270fbab59e398ff498f Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 14 Nov 2018 18:56:15 -0800 Subject: [PATCH 069/330] add test for upsert error --- test/caching_store_wrapper-test.js | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/caching_store_wrapper-test.js b/test/caching_store_wrapper-test.js index 9b3a9da..e39d68a 100644 --- a/test/caching_store_wrapper-test.js +++ b/test/caching_store_wrapper-test.js @@ -25,7 +25,7 @@ function MockCore() { upsertInternal: function(kind, item, cb) { if (c.upsertError) { - cb(upsertError, null); + cb(c.upsertError, null); return; } const oldItem = c.data[kind.namespace][item.key]; @@ -283,6 +283,34 @@ describe('CachingStoreWrapper', function() { }); }); + runCachedAndUncachedTests('upsert() - error', function(done, wrapper, core, isCached) { + const flagv1 = { key: 'flag', version: 1 }; + const flagv2 = { key: 'flag', version: 2 }; + + wrapper.upsert(features, flagv1, function() { + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv1); + + core.upsertError = new Error('sorry'); + + wrapper.upsert(features, flagv2, function() { + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv1); + + // if we have a cache, verify that the old item is still cached by writing a different value + // to the underlying data - get() should still return the cached item + if (isCached) { + const flagv3 = { key: 'flag', version: 3 }; + core.forceSet(features, flagv3); + wrapper.get(features, flagv1.key, function(item) { + expect(item).toEqual(flagv1); + done(); + }); + } else { + done(); + } + }); + }); + }); + runCachedTestOnly('cached upsert() - unsuccessful', function(done, wrapper, core) { const flagv1 = { key: 'flag', version: 1 }; const flagv2 = { key: 'flag', version: 2 }; From ca32313452b2669a9ab1349e92167f511b19ca87 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 14 Nov 2018 19:28:58 -0800 Subject: [PATCH 070/330] update package.lock --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 45308a6..a42cde1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ldclient-node", - "version": "5.5.0", + "version": "5.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { From 4beb25e5b4bbb87cfb364f09097ff7a9b40471d2 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 14 Nov 2018 21:47:07 -0800 Subject: [PATCH 071/330] implement loading flags from a file --- README.md | 4 + file_data_source.js | 162 +++++++++++++++++++++ index.js | 41 ++++-- package-lock.json | 14 ++ package.json | 4 +- test/file_data_source-test.js | 265 ++++++++++++++++++++++++++++++++++ 6 files changed, 475 insertions(+), 15 deletions(-) create mode 100644 file_data_source.js create mode 100644 test/file_data_source-test.js diff --git a/README.md b/README.md index ab8074e..868331f 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,10 @@ Your first feature flag }); }); +Using flag data from a file +--------------------------- + +For testing purposes, the SDK can be made to read feature flag state from a file or files instead of connecting to LaunchDarkly. See [`file_data_source.js`](https://github.com/launchdarkly/node-client/blob/master/file_data_source.js) for more details. Learn more ----------- diff --git a/file_data_source.js b/file_data_source.js new file mode 100644 index 0000000..a970df5 --- /dev/null +++ b/file_data_source.js @@ -0,0 +1,162 @@ +var fs = require('fs'), + winston = require('winston'), + yaml = require('yaml'), + dataKind = require('./versioned_data_kind'); + +/* + FileDataSource provides a way to use local files as a source of feature flag state, instead of + connecting to LaunchDarkly. This would typically be used in a test environment. + + To use this component, call FileDataSource(options) and store the result in the "updateProcessor" + property of your LaunchDarkly client configuration. In the options, set "paths" to the file + paths of your data file(s): + + var dataSource = FileDataSource({ paths: [ myFilePath ] }); + var config = { updateProcessor: dataSource }; + + Flag data files can be either JSON or YAML. They contain an object with three possible + properties: + + - "flags": Full feature flag definitions. + - "flagValues": Simplified feature flags, just a map of flag keys to values. + - "segments": User segment definitions. + + The format of the data in "flags" and "segments" is defined by the LaunchDarkly application + and is subject to change. You can query existing flags and segments from LaunchDarkly in JSON + format by querying https://app.launchdarkly.com/sdk/latest-all and passing your SDK key in + the Authorization header. + + You can also specify that flags should be reloaded whenever a file is modified, by setting + "autoUpdate: true" in the options. This feature uses Node's fs.watch() API, so it is subject to + the limitations described here: https://nodejs.org/docs/latest/api/fs.html#fs_fs_watch_filename_options_listener + + For more details, see the LaunchDarkly reference guide: + https://docs.launchdarkly.com/v2.0/docs/reading-flags-from-a-file +*/ +function FileDataSource(options) { + var paths = (options && options.paths) || []; + var autoUpdate = !!options.autoUpdate; + + return config => { + var featureStore = config.featureStore; + var watchers = []; + var pendingUpdate = false; + var logger = options.logger || config.logger || defaultLogger(); + var inited = false; + + function defaultLogger() { + return new winston.Logger({ + level: 'info', + transports: [ new (winston.transports.Console)() ] + }); + } + + async function loadFile(path, allData) { + var data = await new Promise((resolve, reject) => + fs.readFile(path, 'utf8', (err, data) => + err ? reject(err) : resolve(data) + )); + var parsed = parseData(data) || {}; + var addItem = (kind, item) => { + if (!allData[kind.namespace]) { + allData[kind.namespace] = {}; + } + if (allData[kind.namespace][item.key]) { + throw new Error('found duplicate key: "' + item.key + '"'); + } else { + allData[kind.namespace][item.key] = item; + } + } + Object.values(parsed.flags || {}).forEach(item => { + addItem(dataKind.features, item); + }); + Object.entries(parsed.flagValues || {}).forEach(e => { + addItem(dataKind.features, makeFlagWithValue(e[0], e[1])); + }); + Object.values(parsed.segments || {}).forEach(item => { + addItem(dataKind.segments, item); + }); + } + + async function loadAll() { + pendingUpdate = false; + var allData = {}; + for (var i = 0; i < paths.length; i++) { + try { + await loadFile(paths[i], allData); + } catch (e) { + throw new Error('Unable to load flags: ' + e + ' [' + paths[i] + ']'); + } + } + await new Promise(resolve => featureStore.init(allData, resolve)); + inited = true; + } + + function parseData(data) { + // Every valid JSON document is also a valid YAML document (for parsers that comply + // with the spec, which this one does) so we can parse both with the same parser. + return yaml.parse(data); + } + + function makeFlagWithValue(key, value) { + return { + key: key, + on: true, + fallthrough: { variation: 0 }, + variations: [ value ] + }; + } + + function startWatching() { + var reload = () => { + loadAll().then(() => { + logger && logger.warn('Reloaded flags from file data'); + }).catch(() => {}); + }; + paths.forEach(path => { + var watcher = fs.watch(path, { persistent: false }, (event, filename) => { + if (!pendingUpdate) { // coalesce updates to avoid reloading repeatedly + pendingUpdate = true; + setTimeout(reload, 0); + } + }); + watchers.push(watcher); + }); + } + + function stopWatching() { + watchers.forEach(w => w.close()); + watchers = []; + } + + fds = {}; + + fds.start = fn => { + var cb = fn || (() => {}); + + if (autoUpdate) { + startWatching(); + } + + loadAll().then(() => cb(), err => cb(err)); + }; + + fds.stop = () => { + if (autoUpdate) { + stopWatching(); + } + }; + + fds.initialized = () => { + return inited; + }; + + fds.close = () => { + fds.stop(); + }; + + return fds; + } +} + +module.exports = FileDataSource; diff --git a/index.js b/index.js index 0a2980e..4fe9738 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ var FeatureStoreEventWrapper = require('./feature_store_event_wrapper'); var RedisFeatureStore = require('./redis_feature_store'); +var FileDataSource = require('./file_data_source'); var Requestor = require('./requestor'); var EventEmitter = require('events').EventEmitter; var EventProcessor = require('./event_processor'); @@ -55,13 +56,12 @@ var newClient = function(sdkKey, config) { var client = new EventEmitter(), initComplete = false, failure, - queue = [], requestor, updateProcessor, eventProcessor; config = configuration.validate(config); - + // Initialize global tunnel if proxy options are set if (config.proxyHost && config.proxyPort ) { config.proxyAgent = createProxyAgent(config); @@ -85,22 +85,34 @@ var newClient = function(sdkKey, config) { throw new Error("You must configure the client with an SDK key"); } + var createDefaultUpdateProcessor = function(config) { + if (config.useLdd || config.offline) { + return NullUpdateProcessor(); + } else { + requestor = Requestor(sdkKey, config); + + if (config.stream) { + config.logger.info("Initializing stream processor to receive feature flag updates"); + return StreamingProcessor(sdkKey, config, requestor); + } else { + config.logger.info("Initializing polling processor to receive feature flag updates"); + config.logger.warn("You should only disable the streaming API if instructed to do so by LaunchDarkly support"); + return PollingProcessor(config, requestor); + } + } + } + var updateProcessorFactory = createDefaultUpdateProcessor; if (config.updateProcessor) { - updateProcessor = config.updateProcessor; - } else if (config.useLdd || config.offline) { - updateProcessor = NullUpdateProcessor(); - } else { - requestor = Requestor(sdkKey, config); - - if (config.stream) { - config.logger.info("Initializing stream processor to receive feature flag updates"); - updateProcessor = StreamingProcessor(sdkKey, config, requestor); + if (typeof config.updateProcessor === 'function') { + updateProcessorFactory = config.updateProcessor; } else { - config.logger.info("Initializing polling processor to receive feature flag updates"); - config.logger.warn("You should only disable the streaming API if instructed to do so by LaunchDarkly support"); - updateProcessor = PollingProcessor(config, requestor); + updateProcessor = config.updateProcessor; } } + if (!updateProcessor) { + updateProcessor = updateProcessorFactory(config); + } + updateProcessor.start(function(err) { if (err) { var error; @@ -378,6 +390,7 @@ var newClient = function(sdkKey, config) { module.exports = { init: newClient, RedisFeatureStore: RedisFeatureStore, + FileDataSource: FileDataSource, errors: errors }; diff --git a/package-lock.json b/package-lock.json index a42cde1..52f51bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5759,6 +5759,15 @@ "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", "dev": true }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -6243,6 +6252,11 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, + "yaml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.0.1.tgz", + "integrity": "sha512-ysU56qumPH0tEML2hiFVAo+rCnG/0+oO2Ye3fN4c40GBN7kX1fYhDqSoX7OohimcI/Xkkr1DdaUlg5+afinRqA==" + }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", diff --git a/package.json b/package.json index d4d8ddf..1697949 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "request-etag": "^2.0.3", "semver": "5.5.0", "tunnel": "0.0.6", - "winston": "2.4.1" + "winston": "2.4.1", + "yaml": "1.0.1" }, "engines": { "node": ">= 0.8.x" @@ -43,6 +44,7 @@ "jest": "22.4.3", "jest-junit": "3.6.0", "nock": "9.2.3", + "tmp": "0.0.33", "typescript": "3.0.1" }, "jest": { diff --git a/test/file_data_source-test.js b/test/file_data_source-test.js new file mode 100644 index 0000000..093c02f --- /dev/null +++ b/test/file_data_source-test.js @@ -0,0 +1,265 @@ +var fs = require('fs'); +var tmp = require('tmp'); +var dataKind = require('../versioned_data_kind'); + +var LaunchDarkly = require('../index'); +var FileDataSource = require('../file_data_source'); +var InMemoryFeatureStore = require('../feature_store'); + +var flag1Key = 'flag1'; +var flag2Key = 'flag2'; +var flag2Value = 'value2'; +var segment1Key = 'seg1'; + +var flag1 = { + "key": flag1Key, + "on": true, + "fallthrough": { + "variation": 2 + }, + "variations": [ "fall", "off", "on" ] +}; + +var segment1 = { + "key": segment1Key, + "include": ["user1"] +}; + +var flagOnlyJson = ` +{ + "flags": { + "${flag1Key}": ${ JSON.stringify(flag1) } + } +}`; + +var segmentOnlyJson = ` +{ + "segments": { + "${segment1Key}": ${ JSON.stringify(segment1) } + } +}`; + +var allPropertiesJson = ` +{ + "flags": { + "${flag1Key}": ${ JSON.stringify(flag1) } + }, + "flagValues": { + "${flag2Key}": "${flag2Value}" + }, + "segments": { + "${segment1Key}": ${ JSON.stringify(segment1) } + } +}`; + +var allPropertiesYaml = ` +flags: + ${flag1Key}: + key: ${flag1Key} + on: true + fallthrough: + variation: 2 + variations: + - fall + - off + - on +flagValues: + ${flag2Key}: "${flag2Value}" +segments: + ${segment1Key}: + key: ${segment1Key} + include: + - user1 +`; + +describe('FileDataSource', function() { + var store; + var dataSources = []; + + beforeEach(() => { + store = InMemoryFeatureStore(); + dataSources = []; + }); + + afterEach(() => { + dataSources.forEach(s => s.close()); + }); + + function makeTempFile(content) { + return new Promise((resolve, reject) => { + tmp.file(function(err, path, fd) { + if (err) { + reject(err); + } else { + replaceFileContent(path, content).then(() => resolve(path)); + } + }); + }); + } + + function replaceFileContent(path, content) { + return new Promise((resolve, reject) => { + fs.writeFile(path, content, function(err) { + err ? reject(err) : resolve(); + }); + }); + } + + function setupDataSource(options) { + var factory = FileDataSource(options); + var ds = factory({ featureStore: store }); + dataSources.push(ds); + return ds; + } + + function sorted(a) { + var a1 = Array.from(a); + a1.sort(); + return a1; + } + + function asyncify(f) { + return new Promise(resolve => f(resolve)); + } + + function sleep(millis) { + return new Promise(resolve => { + setTimeout(resolve, millis); + }); + } + + it('does not load flags prior to start', async () => { + var path = await makeTempFile('{"flagValues":{"key":"value"}}'); + var fds = setupDataSource({ paths: [path] }); + + expect(fds.initialized()).toBe(false); + expect(await asyncify(cb => store.initialized(cb))).toBe(false); + expect(await asyncify(cb => store.all(dataKind.features, cb))).toEqual({}); + expect(await asyncify(cb => store.all(dataKind.segments, cb))).toEqual({}); + }); + + async function testLoadAllProperties(content) { + var path = await makeTempFile(content); + var fds = setupDataSource({ paths: [path] }); + await asyncify(fds.start); + + expect(fds.initialized()).toBe(true); + expect(await asyncify(cb => store.initialized(cb))).toBe(true); + var items = await asyncify(cb => store.all(dataKind.features, cb)); + expect(sorted(Object.keys(items))).toEqual([ flag1Key, flag2Key ]); + var flag = await asyncify(cb => store.get(dataKind.features, flag1Key, cb)); + expect(flag).toEqual(flag1); + items = await asyncify(cb => store.all(dataKind.segments, cb)); + expect(items).toEqual({ seg1: segment1 }); + } + + it('loads flags on start - from JSON', () => testLoadAllProperties(allPropertiesJson)); + + it('loads flags on start - from YAML', () => testLoadAllProperties(allPropertiesYaml)); + + it('does not load if file is missing', async () => { + var fds = setupDataSource({ paths: ['no-such-file'] }); + await asyncify(fds.start); + + expect(fds.initialized()).toBe(false); + expect(await asyncify(cb => store.initialized(cb))).toBe(false); + }); + + it('does not load if file data is malformed', async () => { + var path = await makeTempFile('{x'); + var fds = setupDataSource({ paths: [path] }); + await asyncify(fds.start); + + expect(fds.initialized()).toBe(false); + expect(await asyncify(cb => store.initialized(cb))).toBe(false); + }); + + it('can load multiple files', async () => { + var path1 = await makeTempFile(flagOnlyJson); + var path2 = await makeTempFile(segmentOnlyJson); + var fds = setupDataSource({ paths: [path1, path2] }); + await asyncify(fds.start); + + expect(fds.initialized()).toBe(true); + expect(await asyncify(cb => store.initialized(cb))).toBe(true); + + var items = await asyncify(cb => store.all(dataKind.features, cb)); + expect(Object.keys(items)).toEqual([ flag1Key ]); + items = await asyncify(cb => store.all(dataKind.segments, cb)); + expect(Object.keys(items)).toEqual([ segment1Key ]); + }); + + it('does not allow duplicate keys', async () => { + var path1 = await makeTempFile(flagOnlyJson); + var path2 = await makeTempFile(flagOnlyJson); + var fds = setupDataSource({ paths: [path1, path2] }); + await asyncify(fds.start); + + expect(fds.initialized()).toBe(false); + expect(await asyncify(store.initialized)).toBe(false); + }); + + it('does not reload modified file if auto-update is off', async () => { + var path = await makeTempFile(flagOnlyJson); + var fds = setupDataSource({ paths: [path] }); + await asyncify(fds.start); + + var items = await asyncify(cb => store.all(dataKind.segments, cb)); + expect(Object.keys(items).length).toEqual(0); + + await sleep(200); + await replaceFileContent(path, segmentOnlyJson); + await sleep(200); + + items = await asyncify(cb => store.all(dataKind.segments, cb)); + expect(Object.keys(items).length).toEqual(0); + }); + + it('reloads modified file if auto-update is on', async () => { + var path = await makeTempFile(flagOnlyJson); + var fds = setupDataSource({ paths: [path], autoUpdate: true }); + await asyncify(fds.start); + + var items = await asyncify(cb => store.all(dataKind.segments, cb)); + expect(Object.keys(items).length).toEqual(0); + + await sleep(200); + await replaceFileContent(path, segmentOnlyJson); + await sleep(200); + + items = await asyncify(cb => store.all(dataKind.segments, cb)); + expect(Object.keys(items).length).toEqual(1); + }); + + it('evaluates simplified flag with client as expected', async () => { + var path = await makeTempFile(allPropertiesJson); + var factory = FileDataSource({ paths: [ path ]}); + var config = { updateProcessor: factory, sendEvents: false }; + var client = LaunchDarkly.init('dummy-key', config); + var user = { key: 'userkey' }; + + try { + await client.waitForInitialization(); + var result = await client.variation(flag2Key, user, ''); + expect(result).toEqual(flag2Value); + } finally { + client.close(); + } + }); + + it('evaluates full flag with client as expected', async () => { + var path = await makeTempFile(allPropertiesJson); + var factory = FileDataSource({ paths: [ path ]}); + var config = { updateProcessor: factory, sendEvents: false }; + var client = LaunchDarkly.init('dummy-key', config); + var user = { key: 'userkey' }; + + try { + await client.waitForInitialization(); + var result = await client.variation(flag1Key, user, ''); + expect(result).toEqual('on'); + } finally { + client.close(); + } + }); +}); \ No newline at end of file From de27e98fcaf5bb8765d958f95d8b4f2d61ac5591 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 14 Nov 2018 22:22:53 -0800 Subject: [PATCH 072/330] don't use async/await in client code (Node 6 compatibility) --- file_data_source.js | 73 +++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/file_data_source.js b/file_data_source.js index a970df5..0f13cbb 100644 --- a/file_data_source.js +++ b/file_data_source.js @@ -51,45 +51,54 @@ function FileDataSource(options) { }); } - async function loadFile(path, allData) { - var data = await new Promise((resolve, reject) => + function loadFilePromise(path, allData) { + return new Promise((resolve, reject) => fs.readFile(path, 'utf8', (err, data) => - err ? reject(err) : resolve(data) - )); - var parsed = parseData(data) || {}; - var addItem = (kind, item) => { - if (!allData[kind.namespace]) { - allData[kind.namespace] = {}; - } - if (allData[kind.namespace][item.key]) { - throw new Error('found duplicate key: "' + item.key + '"'); - } else { - allData[kind.namespace][item.key] = item; + err ? reject(err) : resolve(data)) + ).then(data => { + var parsed = parseData(data) || {}; + var addItem = (kind, item) => { + if (!allData[kind.namespace]) { + allData[kind.namespace] = {}; + } + if (allData[kind.namespace][item.key]) { + throw new Error('found duplicate key: "' + item.key + '"'); + } else { + allData[kind.namespace][item.key] = item; + } } - } - Object.values(parsed.flags || {}).forEach(item => { - addItem(dataKind.features, item); - }); - Object.entries(parsed.flagValues || {}).forEach(e => { - addItem(dataKind.features, makeFlagWithValue(e[0], e[1])); - }); - Object.values(parsed.segments || {}).forEach(item => { - addItem(dataKind.segments, item); + Object.values(parsed.flags || {}).forEach(item => { + addItem(dataKind.features, item); + }); + Object.entries(parsed.flagValues || {}).forEach(e => { + addItem(dataKind.features, makeFlagWithValue(e[0], e[1])); + }); + Object.values(parsed.segments || {}).forEach(item => { + addItem(dataKind.segments, item); + }); }); } - async function loadAll() { + function loadAllPromise() { pendingUpdate = false; var allData = {}; + var p = Promise.resolve(); for (var i = 0; i < paths.length; i++) { - try { - await loadFile(paths[i], allData); - } catch (e) { - throw new Error('Unable to load flags: ' + e + ' [' + paths[i] + ']'); - } + (path => { + p = p.then(() => loadFilePromise(path, allData)) + .catch(e => { + throw new Error('Unable to load flags: ' + e + ' [' + path + ']'); + }); + })(paths[i]); } - await new Promise(resolve => featureStore.init(allData, resolve)); - inited = true; + return p.then(() => initStorePromise(allData)); + } + + function initStorePromise(data) { + return new Promise(resolve => featureStore.init(data, () => { + inited = true; + resolve(); + })); } function parseData(data) { @@ -109,7 +118,7 @@ function FileDataSource(options) { function startWatching() { var reload = () => { - loadAll().then(() => { + loadAllPromise().then(() => { logger && logger.warn('Reloaded flags from file data'); }).catch(() => {}); }; @@ -138,7 +147,7 @@ function FileDataSource(options) { startWatching(); } - loadAll().then(() => cb(), err => cb(err)); + loadAllPromise().then(() => cb(), err => cb(err)); }; fds.stop = () => { From 56e1c9e3790aecab5c0e9845cc28e78c9632c75d Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 14 Nov 2018 22:34:54 -0800 Subject: [PATCH 073/330] comment edit --- file_data_source.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file_data_source.js b/file_data_source.js index 0f13cbb..4d5d432 100644 --- a/file_data_source.js +++ b/file_data_source.js @@ -11,7 +11,7 @@ var fs = require('fs'), property of your LaunchDarkly client configuration. In the options, set "paths" to the file paths of your data file(s): - var dataSource = FileDataSource({ paths: [ myFilePath ] }); + var dataSource = LaunchDarkly.FileDataSource({ paths: [ myFilePath ] }); var config = { updateProcessor: dataSource }; Flag data files can be either JSON or YAML. They contain an object with three possible From 94e515544216d64c45597c8cec7d6f5ad1b08773 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 9 Jan 2019 19:02:36 -0800 Subject: [PATCH 074/330] fix some undefined variable mistakes --- evaluate_flag.js | 7 +++---- event_processor.js | 2 +- index.js | 4 ++-- redis_feature_store.js | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/evaluate_flag.js b/evaluate_flag.js index 6eb1dcb..cad0ef4 100644 --- a/evaluate_flag.js +++ b/evaluate_flag.js @@ -344,11 +344,10 @@ function bucketUser(user, key, attr, salt) { idHash += "." + user.secondary; } - hashKey = util.format("%s.%s.%s", key, salt, idHash); - hashVal = parseInt(sha1(hashKey).substring(0,15), 16); + var hashKey = util.format("%s.%s.%s", key, salt, idHash); + var hashVal = parseInt(sha1(hashKey).substring(0,15), 16); - result = hashVal / 0xFFFFFFFFFFFFFFF; - return result; + return hashVal / 0xFFFFFFFFFFFFFFF; } function bucketableStringValue(value) { diff --git a/event_processor.js b/event_processor.js index 1a8ec59..cc27216 100644 --- a/event_processor.js +++ b/event_processor.js @@ -44,7 +44,7 @@ function EventProcessor(sdkKey, config, errorReporter) { function makeOutputEvent(event) { switch (event.kind) { case 'feature': - debug = !!event.debug; + var debug = !!event.debug; var out = { kind: debug ? 'debug' : 'feature', creationDate: event.creationDate, diff --git a/index.js b/index.js index 0a2980e..cb10d53 100644 --- a/index.js +++ b/index.js @@ -196,7 +196,7 @@ var newClient = function(sdkKey, config) { } else if (!key) { - err = new errors.LDClientError('No feature flag key specified. Returning default value.'); + var err = new errors.LDClientError('No feature flag key specified. Returning default value.'); maybeReportError(err); return resolve(errorResult('FLAG_NOT_FOUND', defaultVal)); } @@ -208,7 +208,7 @@ var newClient = function(sdkKey, config) { config.featureStore.get(dataKind.features, key, function(flag) { if (!user) { - variationErr = new errors.LDClientError('No user specified. Returning default value.'); + var variationErr = new errors.LDClientError('No user specified. Returning default value.'); maybeReportError(variationErr); var result = errorResult('USER_NOT_SPECIFIED', defaultVal); sendFlagEvent(key, flag, user, result, defaultVal, includeReasonsInEvents); diff --git a/redis_feature_store.js b/redis_feature_store.js index 009aae1..da1aecb 100644 --- a/redis_feature_store.js +++ b/redis_feature_store.js @@ -27,8 +27,8 @@ function redisFeatureStoreInternal(redisOpts, prefix, logger) { }) ); - connected = false; - initialConnect = true; + var connected = false; + var initialConnect = true; client.on('error', function(err) { // Note that we *must* have an error listener or else any connection error will trigger an // uncaught exception. From bc5994c147ac2509239905bf27c6a4d59a6c6452 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 9 Jan 2019 19:43:18 -0800 Subject: [PATCH 075/330] use Babel for tests and migrate test code to newer syntax --- .babelrc | 16 + package-lock.json | 576 ++++++++++++++++++++++++++++- package.json | 3 + test/LDClient-evaluation-test.js | 418 ++++++++++----------- test/LDClient-events-test.js | 206 +++++------ test/LDClient-test.js | 48 +-- test/async_utils.js | 15 + test/caching_store_wrapper-test.js | 374 ++++++++----------- test/feature_store_test_base.js | 256 +++++-------- test/streaming-test.js | 168 ++++----- 10 files changed, 1219 insertions(+), 861 deletions(-) create mode 100644 .babelrc create mode 100644 test/async_utils.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..568faf9 --- /dev/null +++ b/.babelrc @@ -0,0 +1,16 @@ +{ + "env": { + "test": { + "presets": [ + [ + "env", + { + "targets": { + "node": "6" + } + } + ] + ] + } + } +} diff --git a/package-lock.json b/package-lock.json index a42cde1..9664b2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ldclient-node", - "version": "5.6.0", + "version": "5.6.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -547,9 +547,9 @@ } }, "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", + "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", "dev": true, "requires": { "babel-code-frame": "^6.26.0", @@ -562,15 +562,15 @@ "babel-traverse": "^6.26.0", "babel-types": "^6.26.0", "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", + "convert-source-map": "^1.5.0", + "debug": "^2.6.8", "json5": "^0.5.1", "lodash": "^4.17.4", "minimatch": "^3.0.4", "path-is-absolute": "^1.0.1", - "private": "^0.1.8", + "private": "^0.1.7", "slash": "^1.0.0", - "source-map": "^0.5.7" + "source-map": "^0.5.6" } }, "babel-generator": { @@ -589,6 +589,133 @@ "trim-right": "^1.0.1" } }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, "babel-helpers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", @@ -600,13 +727,13 @@ } }, "babel-jest": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.4.tgz", - "integrity": "sha512-A9NB6/lZhYyypR9ATryOSDcqBaqNdzq4U+CN+/wcMsLcmKkPxQEoTKLajGfd3IkxNyVBT8NewUK2nWyGbSzHEQ==", + "version": "22.4.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.3.tgz", + "integrity": "sha512-BgSjmtl3mW3i+VeVHEr9d2zFSAT66G++pJcHQiUjd00pkW+voYXFctIm/indcqOWWXw5a1nUpR1XWszD9fJ1qg==", "dev": true, "requires": { "babel-plugin-istanbul": "^4.1.5", - "babel-preset-jest": "^22.4.4" + "babel-preset-jest": "^22.4.3" } }, "babel-messages": { @@ -618,6 +745,15 @@ "babel-runtime": "^6.22.0" } }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, "babel-plugin-istanbul": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", @@ -636,12 +772,343 @@ "integrity": "sha512-DUvGfYaAIlkdnygVIEl0O4Av69NtuQWcrjMOv6DODPuhuGLDnbsARz3AwiiI/EkIMMlxQDUcrZ9yoyJvTNjcVQ==", "dev": true }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, "babel-plugin-syntax-object-rest-spread": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", "dev": true }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-env": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", + "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^2.1.2", + "invariant": "^2.2.2", + "semver": "^5.3.0" + } + }, "babel-preset-jest": { "version": "22.4.4", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-22.4.4.tgz", @@ -854,6 +1321,16 @@ "resolve": "1.1.7" } }, + "browserslist": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", + "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000792", + "electron-to-chromium": "^1.3.30" + } + }, "bser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", @@ -913,6 +1390,12 @@ "dev": true, "optional": true }, + "caniuse-lite": { + "version": "1.0.30000927", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000927.tgz", + "integrity": "sha512-ogq4NbUWf1uG/j66k0AmiO3GjqJAlQyF8n4w8a954cbCyFKmYGvRtgz6qkq2fWuduTXHibX7GyYL5Pg58Aks2g==", + "dev": true + }, "capture-exit": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-1.2.0.tgz", @@ -1371,6 +1854,12 @@ "safer-buffer": "^2.1.0" } }, + "electron-to-chromium": { + "version": "1.3.100", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.100.tgz", + "integrity": "sha512-cEUzis2g/RatrVf8x26L8lK5VEls1AGnLHk6msluBUg/NTB4wcXzExTsGscFq+Vs4WBBU2zbLLySvD4C0C3hwg==", + "dev": true + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -3148,6 +3637,18 @@ "strip-bom": "3.0.0", "write-file-atomic": "^2.1.0", "yargs": "^10.0.3" + }, + "dependencies": { + "babel-jest": { + "version": "22.4.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.4.tgz", + "integrity": "sha512-A9NB6/lZhYyypR9ATryOSDcqBaqNdzq4U+CN+/wcMsLcmKkPxQEoTKLajGfd3IkxNyVBT8NewUK2nWyGbSzHEQ==", + "dev": true, + "requires": { + "babel-plugin-istanbul": "^4.1.5", + "babel-preset-jest": "^22.4.4" + } + } } }, "jest-snapshot": { @@ -4587,12 +5088,29 @@ "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk=", "dev": true }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", @@ -4612,6 +5130,40 @@ "safe-regex": "^1.1.0" } }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", diff --git a/package.json b/package.json index 30361a7..6d5a4aa 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,9 @@ "node": ">= 0.8.x" }, "devDependencies": { + "babel-core": "6.26.0", + "babel-jest": "22.4.3", + "babel-preset-env": "1.6.1", "jest": "22.4.3", "jest-junit": "3.6.0", "nock": "9.2.3", diff --git a/test/LDClient-evaluation-test.js b/test/LDClient-evaluation-test.js index 1794e4e..4ff1dfe 100644 --- a/test/LDClient-evaluation-test.js +++ b/test/LDClient-evaluation-test.js @@ -17,7 +17,7 @@ describe('LDClient', () => { } describe('variation()', () => { - it('evaluates an existing flag', done => { + it('evaluates an existing flag', async () => { var flag = { key: 'flagkey', version: 1, @@ -28,27 +28,19 @@ describe('LDClient', () => { trackEvents: true }; var client = stubs.createClient({}, { flagkey: flag }); - client.on('ready', () => { - client.variation(flag.key, defaultUser, 'c', (err, result) => { - expect(err).toBeNull(); - expect(result).toEqual('b'); - done(); - }); - }); + await client.waitForInitialization(); + var result = await client.variation(flag.key, defaultUser, 'c'); + expect(result).toEqual('b'); }); - it('returns default for unknown flag', done => { + it('returns default for unknown flag', async () => { var client = stubs.createClient({}, {}); - client.on('ready', () => { - client.variation('flagkey', defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toEqual('default'); - done(); - }); - }); + await client.waitForInitialization(); + var result = await client.variation('flagkey', defaultUser, 'default'); + expect(result).toEqual('default'); }); - it('returns default if client is offline', done => { + it('returns default if client is offline', async () => { var flag = { key: 'flagkey', version: 1, @@ -57,16 +49,14 @@ describe('LDClient', () => { variations: ['value'] }; var logger = stubs.stubLogger(); - client = stubs.createClient({ offline: true, logger: logger }, { flagkey: flag }); - client.variation('flagkey', defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toEqual('default'); - expect(logger.info).toHaveBeenCalled(); - done(); - }); + var client = stubs.createClient({ offline: true, logger: logger }, { flagkey: flag }); + await client.waitForInitialization(); + var result = await client.variation('flagkey', defaultUser, 'default'); + expect(result).toEqual('default'); + expect(logger.info).toHaveBeenCalled(); }); - it('returns default if client and store are not initialized', done => { + it('returns default if client and store are not initialized', async () => { var flag = { key: 'flagkey', version: 1, @@ -75,14 +65,11 @@ describe('LDClient', () => { variations: ['value'] }; var client = createClientWithFlagsInUninitializedStore({ flagkey: flag }); - client.variation('flagkey', defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toEqual('default'); - done(); - }); + var result = await client.variation('flagkey', defaultUser, 'default'); + expect(result).toEqual('default'); }); - it('returns value from store if store is initialized but client is not', done => { + it('returns value from store if store is initialized but client is not', async () => { var flag = { key: 'flagkey', version: 1, @@ -93,40 +80,29 @@ describe('LDClient', () => { var logger = stubs.stubLogger(); var updateProcessor = stubs.stubUpdateProcessor(); updateProcessor.shouldInitialize = false; - client = stubs.createClient({ updateProcessor: updateProcessor, logger: logger }, { flagkey: flag }); - client.variation('flagkey', defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toEqual('value'); - expect(logger.warn).toHaveBeenCalled(); - done(); - }); + var client = stubs.createClient({ updateProcessor: updateProcessor, logger: logger }, { flagkey: flag }); + var result = await client.variation('flagkey', defaultUser, 'default'); + expect(result).toEqual('value'); + expect(logger.warn).toHaveBeenCalled(); }); - it('returns default if flag key is not specified', done => { + it('returns default if flag key is not specified', async () => { var client = stubs.createClient({}, {}); - client.on('ready', () => { - client.variation(null, defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toEqual('default'); - done(); - }); - }); + await client.waitForInitialization(); + var result = await client.variation(null, defaultUser, 'default'); + expect(result).toEqual('default'); }); - it('returns default for flag that evaluates to null', done => { + it('returns default for flag that evaluates to null', async () => { var flag = { key: 'flagkey', on: false, offVariation: null }; var client = stubs.createClient({}, { flagkey: flag }); - client.on('ready', () => { - client.variation(flag.key, defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toEqual('default'); - done(); - }); - }); + await client.waitForInitialization(); + var result = await client.variation(flag.key, defaultUser, 'default'); + expect(result).toEqual('default'); }); it('allows deprecated method toggle()', done => { @@ -147,10 +123,21 @@ describe('LDClient', () => { }); }); }); + + it('can use a callback instead of a Promise', done => { + var client = stubs.createClient({}, {}); + client.on('ready', () => { + client.variation('flagkey', defaultUser, 'default', (err, result) => { + expect(err).toBeNull(); + expect(result).toEqual('default'); + done(); + }); + }); + }); }); describe('variationDetail()', () => { - it('evaluates an existing flag', done => { + it('evaluates an existing flag', async () => { var flag = { key: 'flagkey', version: 1, @@ -161,28 +148,20 @@ describe('LDClient', () => { trackEvents: true }; var client = stubs.createClient({}, { flagkey: flag }); - client.on('ready', () => { - client.variationDetail(flag.key, defaultUser, 'c', (err, result) => { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'b', variationIndex: 1, reason: { kind: 'FALLTHROUGH' } }); - done(); - }); - }); + await client.waitForInitialization(); + var result = await client.variationDetail(flag.key, defaultUser, 'c'); + expect(result).toMatchObject({ value: 'b', variationIndex: 1, reason: { kind: 'FALLTHROUGH' } }); }); - it('returns default for unknown flag', done => { + it('returns default for unknown flag', async () => { var client = stubs.createClient({}, { }); - client.on('ready', () => { - client.variationDetail('flagkey', defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'default', variationIndex: null, - reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); - done(); - }); - }); + await client.waitForInitialization(); + var result = await client.variationDetail('flagkey', defaultUser, 'default'); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); }); - it('returns default if client is offline', done => { + it('returns default if client is offline', async () => { var flag = { key: 'flagkey', version: 1, @@ -191,17 +170,15 @@ describe('LDClient', () => { variations: ['value'] }; var logger = stubs.stubLogger(); - client = stubs.createClient({ offline: true, logger: logger }, { flagkey: flag }); - client.variationDetail('flagkey', defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'default', variationIndex: null, - reason: { kind: 'ERROR', errorKind: 'CLIENT_NOT_READY' }}); - expect(logger.info).toHaveBeenCalled(); - done(); - }); + var client = stubs.createClient({ offline: true, logger: logger }, { flagkey: flag }); + await client.waitForInitialization(); + var result = await client.variationDetail('flagkey', defaultUser, 'default'); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'CLIENT_NOT_READY' }}); + expect(logger.info).toHaveBeenCalled(); }); - it('returns default if client and store are not initialized', done => { + it('returns default if client and store are not initialized', async () => { var flag = { key: 'flagkey', version: 1, @@ -209,16 +186,13 @@ describe('LDClient', () => { offVariation: 0, variations: ['value'] }; - client = createClientWithFlagsInUninitializedStore({ flagkey: flag }); - client.variationDetail('flagkey', defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'default', variationIndex: null, - reason: { kind: 'ERROR', errorKind: 'CLIENT_NOT_READY' } }); - done(); - }); + var client = createClientWithFlagsInUninitializedStore({ flagkey: flag }); + var result = await client.variationDetail('flagkey', defaultUser, 'default'); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'CLIENT_NOT_READY' } }); }); - it('returns value from store if store is initialized but client is not', done => { + it('returns value from store if store is initialized but client is not', async () => { var flag = { key: 'flagkey', version: 1, @@ -229,38 +203,39 @@ describe('LDClient', () => { var logger = stubs.stubLogger(); var updateProcessor = stubs.stubUpdateProcessor(); updateProcessor.shouldInitialize = false; - client = stubs.createClient({ updateProcessor: updateProcessor, logger: logger }, { flagkey: flag }); - client.variationDetail('flagkey', defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'value', variationIndex: 0, reason: { kind: 'OFF' }}) - expect(logger.warn).toHaveBeenCalled(); - done(); - }); + var client = stubs.createClient({ updateProcessor: updateProcessor, logger: logger }, { flagkey: flag }); + var result = await client.variationDetail('flagkey', defaultUser, 'default'); + expect(result).toMatchObject({ value: 'value', variationIndex: 0, reason: { kind: 'OFF' }}) + expect(logger.warn).toHaveBeenCalled(); }); - it('returns default if flag key is not specified', done => { + it('returns default if flag key is not specified', async () => { var client = stubs.createClient({}, { }); - client.on('ready', () => { - client.variationDetail(null, defaultUser, 'default', (err, result) => { - expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'default', variationIndex: null, - reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); - done(); - }); - }); + await client.waitForInitialization(); + var result = await client.variationDetail(null, defaultUser, 'default'); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); }); - it('returns default for flag that evaluates to null', done => { + it('returns default for flag that evaluates to null', async () => { var flag = { key: 'flagkey', on: false, offVariation: null }; var client = stubs.createClient({}, { flagkey: flag }); + await client.waitForInitialization(); + var result = await client.variationDetail(flag.key, defaultUser, 'default'); + expect(result).toMatchObject({ value: 'default', variationIndex: null, reason: { kind: 'OFF' } }); + }); + + it('can use a callback instead of a Promise', done => { + var client = stubs.createClient({}, {}); client.on('ready', () => { - client.variationDetail(flag.key, defaultUser, 'default', (err, result) => { + client.variationDetail('flagkey', defaultUser, 'default', (err, result) => { expect(err).toBeNull(); - expect(result).toMatchObject({ value: 'default', variationIndex: null, reason: { kind: 'OFF' } }); + expect(result).toMatchObject({ value: 'default', variationIndex: null, + reason: { kind: 'ERROR', errorKind: 'FLAG_NOT_FOUND' } }); done(); }); }); @@ -268,7 +243,7 @@ describe('LDClient', () => { }); describe('allFlags()', () => { - it('evaluates flags', done => { + it('evaluates flags', async () => { var flag = { key: 'feature', version: 1, @@ -277,17 +252,13 @@ describe('LDClient', () => { }; var logger = stubs.stubLogger(); var client = stubs.createClient({ logger: logger }, { feature: flag }); - client.on('ready', () => { - client.allFlags(defaultUser, (err, results) => { - expect(err).toBeNull(); - expect(results).toEqual({feature: 'b'}); - expect(logger.warn).toHaveBeenCalledTimes(1); // deprecation warning - done(); - }); - }); + await client.waitForInitialization(); + var result = await client.allFlags(defaultUser); + expect(result).toEqual({feature: 'b'}); + expect(logger.warn).toHaveBeenCalledTimes(1); // deprecation warning }); - it('returns empty map in offline mode and logs a message', done => { + it('returns empty map in offline mode and logs a message', async () => { var flag = { key: 'flagkey', on: false, @@ -295,13 +266,10 @@ describe('LDClient', () => { }; var logger = stubs.stubLogger(); var client = stubs.createClient({ offline: true, logger: logger }, { flagkey: flag }); - client.on('ready', () => { - client.allFlags(defaultUser, (err, result) => { - expect(result).toEqual({}); - expect(logger.info).toHaveBeenCalledTimes(1); - done(); - }); - }); + await client.waitForInitialization(); + var result = await client.allFlags(defaultUser); + expect(result).toEqual({}); + expect(logger.info).toHaveBeenCalledTimes(1); }); it('allows deprecated method all_flags', done => { @@ -316,7 +284,7 @@ describe('LDClient', () => { }); }); - it('does not overflow the call stack when evaluating a huge number of flags', done => { + it('does not overflow the call stack when evaluating a huge number of flags', async () => { var flagCount = 5000; var flags = {}; for (var i = 0; i < flagCount; i++) { @@ -329,10 +297,16 @@ describe('LDClient', () => { flags[key] = flag; } var client = stubs.createClient({}, flags); + await client.waitForInitialization(); + var result = await client.allFlags(defaultUser); + expect(Object.keys(result).length).toEqual(flagCount); + }); + + it('can use a callback instead of a Promise', done => { + var client = stubs.createClient({ offline: true }, { }); client.on('ready', () => { client.allFlags(defaultUser, (err, result) => { - expect(err).toEqual(null); - expect(Object.keys(result).length).toEqual(flagCount); + expect(result).toEqual({}); done(); }); }); @@ -340,7 +314,7 @@ describe('LDClient', () => { }); describe('allFlagsState()', () => { - it('captures flag state', done => { + it('captures flag state', async () => { var flag = { key: 'feature', version: 100, @@ -350,30 +324,26 @@ describe('LDClient', () => { debugEventsUntilDate: 1000 }; var client = stubs.createClient({}, { feature: flag }); - client.on('ready', () => { - client.allFlagsState(defaultUser, {}, (err, state) => { - expect(err).toBeNull(); - expect(state.valid).toEqual(true); - expect(state.allValues()).toEqual({feature: 'b'}); - expect(state.getFlagValue('feature')).toEqual('b'); - expect(state.toJSON()).toEqual({ - feature: 'b', - $flagsState: { - feature: { - version: 100, - variation: 1, - trackEvents: true, - debugEventsUntilDate: 1000 - } - }, - $valid: true - }); - done(); - }); + await client.waitForInitialization(); + var state = await client.allFlagsState(defaultUser); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({feature: 'b'}); + expect(state.getFlagValue('feature')).toEqual('b'); + expect(state.toJSON()).toEqual({ + feature: 'b', + $flagsState: { + feature: { + version: 100, + variation: 1, + trackEvents: true, + debugEventsUntilDate: 1000 + } + }, + $valid: true }); }); - it('can filter for only client-side flags', done => { + it('can filter for only client-side flags', async () => { var flag1 = { key: 'server-side-1', on: false, offVariation: 0, variations: ['a'], clientSide: false }; var flag2 = { key: 'server-side-2', on: false, offVariation: 0, variations: ['b'], clientSide: false }; var flag3 = { key: 'client-side-1', on: false, offVariation: 0, variations: ['value1'], clientSide: true }; @@ -381,30 +351,13 @@ describe('LDClient', () => { var client = stubs.createClient({}, { 'server-side-1': flag1, 'server-side-2': flag2, 'client-side-1': flag3, 'client-side-2': flag4 }); - client.on('ready', () => { - client.allFlagsState(defaultUser, { clientSideOnly: true }, (err, state) => { - expect(err).toBeNull(); - expect(state.valid).toEqual(true); - expect(state.allValues()).toEqual({ 'client-side-1': 'value1', 'client-side-2': 'value2' }); - done(); - }); - }); + await client.waitForInitialization(); + var state = await client.allFlagsState(defaultUser, { clientSideOnly: true }); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({ 'client-side-1': 'value1', 'client-side-2': 'value2' }); }); - it('can omit options parameter', done => { - var flag = { key: 'key', on: false, offVariation: 0, variations: ['value'] }; - var client = stubs.createClient({}, { 'key': flag }); - client.on('ready', () => { - client.allFlagsState(defaultUser, (err, state) => { - expect(err).toBeNull(); - expect(state.valid).toEqual(true); - expect(state.allValues()).toEqual({ 'key': 'value' }); - done(); - }); - }); - }); - - it('can include reasons', done => { + it('can include reasons', async () => { var flag = { key: 'feature', version: 100, @@ -414,31 +367,27 @@ describe('LDClient', () => { debugEventsUntilDate: 1000 }; var client = stubs.createClient({}, { feature: flag }); - client.on('ready', () => { - client.allFlagsState(defaultUser, { withReasons: true }, (err, state) => { - expect(err).toBeNull(); - expect(state.valid).toEqual(true); - expect(state.allValues()).toEqual({feature: 'b'}); - expect(state.getFlagValue('feature')).toEqual('b'); - expect(state.toJSON()).toEqual({ - feature: 'b', - $flagsState: { - feature: { - version: 100, - variation: 1, - reason: { kind: 'OFF' }, - trackEvents: true, - debugEventsUntilDate: 1000 - } - }, - $valid: true - }); - done(); - }); + await client.waitForInitialization(); + var state = await client.allFlagsState(defaultUser, { withReasons: true }); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({feature: 'b'}); + expect(state.getFlagValue('feature')).toEqual('b'); + expect(state.toJSON()).toEqual({ + feature: 'b', + $flagsState: { + feature: { + version: 100, + variation: 1, + reason: { kind: 'OFF' }, + trackEvents: true, + debugEventsUntilDate: 1000 + } + }, + $valid: true }); }); - it('can omit details for untracked flags', done => { + it('can omit details for untracked flags', async () => { var flag1 = { key: 'flag1', version: 100, @@ -461,41 +410,37 @@ describe('LDClient', () => { }; var client = stubs.createClient({}, { flag1: flag1, flag2: flag2, flag3: flag3 }); var user = { key: 'user' }; - client.on('ready', function() { - client.allFlagsState(user, { withReasons: true, detailsOnlyForTrackedFlags: true }, function(err, state) { - expect(err).toBeNull(); - expect(state.valid).toEqual(true); - expect(state.allValues()).toEqual({flag1: 'value1', flag2: 'value2', flag3: 'value3'}); - expect(state.getFlagValue('flag1')).toEqual('value1'); - expect(state.toJSON()).toEqual({ - flag1: 'value1', - flag2: 'value2', - flag3: 'value3', - $flagsState: { - flag1: { - variation: 0 - }, - flag2: { - version: 200, - variation: 0, - reason: { kind: 'OFF' }, - trackEvents: true - }, - flag3: { - version: 300, - variation: 0, - reason: { kind: 'OFF' }, - debugEventsUntilDate: 1000 - } - }, - $valid: true - }); - done(); - }); + await client.waitForInitialization(); + var state = await client.allFlagsState(defaultUser, { withReasons: true, detailsOnlyForTrackedFlags: true }); + expect(state.valid).toEqual(true); + expect(state.allValues()).toEqual({flag1: 'value1', flag2: 'value2', flag3: 'value3'}); + expect(state.getFlagValue('flag1')).toEqual('value1'); + expect(state.toJSON()).toEqual({ + flag1: 'value1', + flag2: 'value2', + flag3: 'value3', + $flagsState: { + flag1: { + variation: 0 + }, + flag2: { + version: 200, + variation: 0, + reason: { kind: 'OFF' }, + trackEvents: true + }, + flag3: { + version: 300, + variation: 0, + reason: { kind: 'OFF' }, + debugEventsUntilDate: 1000 + } + }, + $valid: true }); }); - it('returns empty state in offline mode and logs a message', done => { + it('returns empty state in offline mode and logs a message', async () => { var flag = { key: 'flagkey', on: false, @@ -503,11 +448,28 @@ describe('LDClient', () => { }; var logger = stubs.stubLogger(); var client = stubs.createClient({ offline: true, logger: logger }, { flagkey: flag }); + await client.waitForInitialization(); + var state = await client.allFlagsState(defaultUser); + expect(state.valid).toEqual(false); + expect(state.allValues()).toEqual({}); + expect(logger.info).toHaveBeenCalledTimes(1); + }); + + it('can use a callback instead of a Promise', done => { + var client = stubs.createClient({ offline: true }, { }); client.on('ready', () => { client.allFlagsState(defaultUser, {}, (err, state) => { expect(state.valid).toEqual(false); - expect(state.allValues()).toEqual({}); - expect(logger.info).toHaveBeenCalledTimes(1); + done(); + }); + }); + }); + + it('can omit options parameter with callback', done => { + var client = stubs.createClient({ offline: true }, { }); + client.on('ready', () => { + client.allFlagsState(defaultUser, (err, state) => { + expect(state.valid).toEqual(false); done(); }); }); diff --git a/test/LDClient-events-test.js b/test/LDClient-events-test.js index 2df637a..94f7628 100644 --- a/test/LDClient-events-test.js +++ b/test/LDClient-events-test.js @@ -10,7 +10,7 @@ describe('LDClient - analytics events', () => { }); describe('feature event', () => { - it('generates event for existing feature', done => { + it('generates event for existing feature', async () => { var flag = { key: 'flagkey', version: 1, @@ -21,26 +21,24 @@ describe('LDClient - analytics events', () => { trackEvents: true }; var client = stubs.createClient({ eventProcessor: eventProcessor }, { flagkey: flag }); - client.on('ready', () => { - client.variation(flag.key, defaultUser, 'c', (err, result) => { - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'feature', - key: 'flagkey', - version: 1, - user: defaultUser, - variation: 1, - value: 'b', - default: 'c', - trackEvents: true - }); - done(); - }); + await client.waitForInitialization(); + await client.variation(flag.key, defaultUser, 'c'); + + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'feature', + key: 'flagkey', + version: 1, + user: defaultUser, + variation: 1, + value: 'b', + default: 'c', + trackEvents: true }); }); - it('generates event for existing feature with reason', done => { + it('generates event for existing feature with reason', async () => { var flag = { key: 'flagkey', version: 1, @@ -51,48 +49,44 @@ describe('LDClient - analytics events', () => { trackEvents: true }; var client = stubs.createClient({ eventProcessor: eventProcessor }, { flagkey: flag }); - client.on('ready', () => { - client.variationDetail(flag.key, defaultUser, 'c', (err, result) => { - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'feature', - key: 'flagkey', - version: 1, - user: defaultUser, - variation: 1, - value: 'b', - default: 'c', - reason: { kind: 'FALLTHROUGH' }, - trackEvents: true - }); - done(); - }); + await client.waitForInitialization(); + await client.variationDetail(flag.key, defaultUser, 'c'); + + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'feature', + key: 'flagkey', + version: 1, + user: defaultUser, + variation: 1, + value: 'b', + default: 'c', + reason: { kind: 'FALLTHROUGH' }, + trackEvents: true }); }); - it('generates event for unknown feature', done => { + it('generates event for unknown feature', async () => { var client = stubs.createClient({ eventProcessor: eventProcessor }, {}); - client.on('ready', () => { - client.variation('flagkey', defaultUser, 'c', (err, result) => { - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'feature', - key: 'flagkey', - version: null, - user: defaultUser, - variation: null, - value: 'c', - default: 'c', - trackEvents: null - }); - done(); - }); + await client.waitForInitialization(); + await client.variation('flagkey', defaultUser, 'c'); + + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'feature', + key: 'flagkey', + version: null, + user: defaultUser, + variation: null, + value: 'c', + default: 'c', + trackEvents: null }); }); - it('generates event for existing feature when user key is missing', done => { + it('generates event for existing feature when user key is missing', async () => { var flag = { key: 'flagkey', version: 1, @@ -104,26 +98,24 @@ describe('LDClient - analytics events', () => { }; var client = stubs.createClient({ eventProcessor: eventProcessor }, { flagkey: flag }); var badUser = { name: 'Bob' }; - client.on('ready', () => { - client.variation(flag.key, badUser, 'c', (err, result) => { - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'feature', - key: 'flagkey', - version: 1, - user: badUser, - variation: null, - value: 'c', - default: 'c', - trackEvents: true - }); - done(); - }); + await client.waitForInitialization(); + await client.variation(flag.key, badUser, 'c'); + + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'feature', + key: 'flagkey', + version: 1, + user: badUser, + variation: null, + value: 'c', + default: 'c', + trackEvents: true }); }); - it('generates event for existing feature when user is null', done => { + it('generates event for existing feature when user is null', async () => { var flag = { key: 'flagkey', version: 1, @@ -134,55 +126,51 @@ describe('LDClient - analytics events', () => { trackEvents: true }; var client = stubs.createClient({ eventProcessor: eventProcessor }, { flagkey: flag }); - client.on('ready', () => { - client.variation(flag.key, null, 'c', (err, result) => { - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'feature', - key: 'flagkey', - version: 1, - user: null, - variation: null, - value: 'c', - default: 'c', - trackEvents: true - }); - done(); - }); - }); - }); - }); + await client.waitForInitialization(); + await client.variation(flag.key, null, 'c'); - it('generates an event for identify()', done => { - var client = stubs.createClient({ eventProcessor: eventProcessor }, {}); - client.on('ready', () => { - client.identify(defaultUser); expect(eventProcessor.events).toHaveLength(1); var e = eventProcessor.events[0]; expect(e).toMatchObject({ - kind: 'identify', - key: defaultUser.key, - user: defaultUser + kind: 'feature', + key: 'flagkey', + version: 1, + user: null, + variation: null, + value: 'c', + default: 'c', + trackEvents: true }); - done(); }); }); - it('generates an event for track()', done => { + it('generates an event for identify()', async () => { + var client = stubs.createClient({ eventProcessor: eventProcessor }, {}); + await client.waitForInitialization(); + + client.identify(defaultUser); + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'identify', + key: defaultUser.key, + user: defaultUser + }); + }); + + it('generates an event for track()', async () => { var data = { thing: 'stuff' }; var client = stubs.createClient({ eventProcessor: eventProcessor }, {}); - client.on('ready', () => { - client.track('eventkey', defaultUser, data); - expect(eventProcessor.events).toHaveLength(1); - var e = eventProcessor.events[0]; - expect(e).toMatchObject({ - kind: 'custom', - key: 'eventkey', - user: defaultUser, - data: data - }); - done(); + await client.waitForInitialization(); + + client.track('eventkey', defaultUser, data); + expect(eventProcessor.events).toHaveLength(1); + var e = eventProcessor.events[0]; + expect(e).toMatchObject({ + kind: 'custom', + key: 'eventkey', + user: defaultUser, + data: data }); }); }); diff --git a/test/LDClient-test.js b/test/LDClient-test.js index 1c3bb05..fb6de51 100644 --- a/test/LDClient-test.js +++ b/test/LDClient-test.js @@ -63,59 +63,35 @@ describe('LDClient', () => { }); describe('waitUntilReady()', () => { - it('resolves when ready', done => { + it('resolves when ready', async () => { var client = stubs.createClient({}, {}); - client.waitUntilReady().then(done) - .catch(done.error) + await client.waitUntilReady(); }); - it('resolves immediately if the client is already ready', done => { + it('resolves immediately if the client is already ready', async () => { var client = stubs.createClient({}, {}); - client.waitUntilReady().then(() => { - client.waitUntilReady().then(done) - .catch(done.error) - }).catch(done.error); + await client.waitUntilReady(); + await client.waitUntilReady(); }); }); describe('waitForInitialization()', () => { - it('resolves when ready', done => { - var callback = jest.fn(); + it('resolves when ready', async () => { var client = stubs.createClient({}, {}); - - client.waitForInitialization().then(callback) - .then(() => { - expect(callback).toHaveBeenCalled(); - expect(callback.mock.calls[0][0]).toBe(client); - done(); - }).catch(done.error) + await client.waitForInitialization(); }); - it('resolves immediately if the client is already ready', done => { - var callback = jest.fn(); + it('resolves immediately if the client is already ready', async () => { var client = stubs.createClient({}, {}); - - client.waitForInitialization() - .then(() => { - client.waitForInitialization().then(callback) - .then(() => { - expect(callback).toHaveBeenCalled(); - expect(callback.mock.calls[0][0]).toBe(client); - done(); - }).catch(done.error) - }).catch(done.error) + await client.waitForInitialization(); + await client.waitForInitialization(); }); - it('is rejected if initialization fails', done => { + it('is rejected if initialization fails', async () => { var updateProcessor = stubs.stubUpdateProcessor(); updateProcessor.error = { status: 403 }; var client = stubs.createClient({ updateProcessor: updateProcessor }, {}); - - client.waitForInitialization() - .catch(err => { - expect(err).toEqual(updateProcessor.error); - done(); - }); + await expect(client.waitForInitialization()).rejects.toThrow(); }); }); diff --git a/test/async_utils.js b/test/async_utils.js new file mode 100644 index 0000000..1215b15 --- /dev/null +++ b/test/async_utils.js @@ -0,0 +1,15 @@ + +function asyncify(f) { + return new Promise(resolve => f(resolve)); +} + +function sleepAsync(millis) { + return new Promise(resolve => { + setTimeout(resolve, millis); + }); +} + +module.exports = { + asyncify: asyncify, + sleepAsync: sleepAsync +}; diff --git a/test/caching_store_wrapper-test.js b/test/caching_store_wrapper-test.js index e39d68a..b774552 100644 --- a/test/caching_store_wrapper-test.js +++ b/test/caching_store_wrapper-test.js @@ -1,5 +1,6 @@ var CachingStoreWrapper = require('../caching_store_wrapper'); var features = require('../versioned_data_kind').features; +const { asyncify, sleepAsync } = require('./async_utils'); function MockCore() { const c = { @@ -63,175 +64,145 @@ function runCachedAndUncachedTests(name, testFn) { describe(name, function() { const core1 = MockCore(); const wrapper1 = new CachingStoreWrapper(core1, cacheSeconds); - it('cached', function(done) { testFn(done, wrapper1, core1, true); }); + it('cached', async () => await testFn(wrapper1, core1, true)); const core2 = MockCore(); const wrapper2 = new CachingStoreWrapper(core2, 0); - it('uncached', function(done) { testFn(done, wrapper2, core2, false); }); + it('uncached', async () => await testFn(wrapper2, core2, false)); }); } function runCachedTestOnly(name, testFn) { - it(name, function(done) { + it(name, async () => { const core = MockCore(); const wrapper = new CachingStoreWrapper(core, cacheSeconds); - testFn(done, wrapper, core); + await testFn(wrapper, core); }); } describe('CachingStoreWrapper', function() { - runCachedAndUncachedTests('get()', function(done, wrapper, core, isCached) { + runCachedAndUncachedTests('get()', async (wrapper, core, isCached) => { const flagv1 = { key: 'flag', version: 1 }; const flagv2 = { key: 'flag', version: 2 }; core.forceSet(features, flagv1); - wrapper.get(features, flagv1.key, function(item) { - expect(item).toEqual(flagv1); + var item = await asyncify(cb => wrapper.get(features, flagv1.key, cb)); + expect(item).toEqual(flagv1); - core.forceSet(features, flagv2); // Make a change that bypasses the cache + core.forceSet(features, flagv2); // Make a change that bypasses the cache - wrapper.get(features, flagv1.key, function(item) { - // If cached, it should return the cached value rather than calling the underlying getter - expect(item).toEqual(isCached ? flagv1 : flagv2); - - done(); - }); - }); + item = await asyncify(cb => wrapper.get(features, flagv1.key, cb)); + // If cached, it should return the cached value rather than calling the underlying getter + expect(item).toEqual(isCached ? flagv1 : flagv2); }); - runCachedAndUncachedTests('get() with deleted item', function(done, wrapper, core, isCached) { + runCachedAndUncachedTests('get() with deleted item', async (wrapper, core, isCached) => { const flagv1 = { key: 'flag', version: 1, deleted: true }; const flagv2 = { key: 'flag', version: 2, deleted: false }; core.forceSet(features, flagv1); - wrapper.get(features, flagv1.key, function(item) { - expect(item).toBe(null); - - core.forceSet(features, flagv2); // Make a change that bypasses the cache + var item = await asyncify(cb => wrapper.get(features, flagv1.key, cb)); + expect(item).toBe(null); - wrapper.get(features, flagv2.key, function(item) { - // If cached, the deleted state should persist in the cache - expect(item).toEqual(isCached ? null : flagv2); + core.forceSet(features, flagv2); // Make a change that bypasses the cache - done(); - }); - }); + item = await asyncify(cb => wrapper.get(features, flagv2.key, cb)); + // If cached, the deleted state should persist in the cache + expect(item).toEqual(isCached ? null : flagv2); }); - runCachedAndUncachedTests('get() with missing item', function(done, wrapper, core, isCached) { + runCachedAndUncachedTests('get() with missing item', async (wrapper, core, isCached) => { const flag = { key: 'flag', version: 1 }; - wrapper.get(features, flag.key, function(item) { - expect(item).toBe(null); - - core.forceSet(features, flag); + var item = await asyncify(cb => wrapper.get(features, flag.key, cb)); + expect(item).toBe(null); - wrapper.get(features, flag.key, function(item) { - // If cached, the previous null result should persist in the cache - expect(item).toEqual(isCached ? null : flag); + core.forceSet(features, flag); - done(); - }); - }); + item = await asyncify(cb => wrapper.get(features, flag.key, cb)); + // If cached, the previous null result should persist in the cache + expect(item).toEqual(isCached ? null : flag); }); - runCachedTestOnly('cached get() uses values from init()', function(done, wrapper, core) { + runCachedTestOnly('cached get() uses values from init()', async (wrapper, core) => { const flagv1 = { key: 'flag', version: 1 }; const flagv2 = { key: 'flag', version: 2 }; const allData = { features: { 'flag': flagv1 } }; - wrapper.init(allData, function() { - expect(core.data).toEqual(allData); + await asyncify(cb => wrapper.init(allData, cb)); + expect(core.data).toEqual(allData); - core.forceSet(features, flagv2); + core.forceSet(features, flagv2); - wrapper.get(features, flagv1.key, function(item) { - expect(item).toEqual(flagv1); - - done(); - }); - }); + var item = await asyncify(cb => wrapper.get(features, flagv1.key, cb)); + expect(item).toEqual(flagv1); }); - runCachedAndUncachedTests('all()', function(done, wrapper, core, isCached) { + runCachedAndUncachedTests('all()', async (wrapper, core, isCached) => { const flag1 = { key: 'flag1', version: 1 }; const flag2 = { key: 'flag2', version: 1 }; core.forceSet(features, flag1); core.forceSet(features, flag2); - wrapper.all(features, function(items) { - expect(items).toEqual({ 'flag1': flag1, 'flag2': flag2 }); - - core.forceRemove(features, flag2.key); + var items = await asyncify(cb => wrapper.all(features, cb)); + expect(items).toEqual({ 'flag1': flag1, 'flag2': flag2 }); - wrapper.all(features, function(items) { - if (isCached) { - expect(items).toEqual({ 'flag1': flag1, 'flag2': flag2 }); - } else { - expect(items).toEqual({ 'flag1': flag1 }); - } + core.forceRemove(features, flag2.key); - done(); - }); - }); + items = await asyncify(cb => wrapper.all(features, cb)); + if (isCached) { + expect(items).toEqual({ 'flag1': flag1, 'flag2': flag2 }); + } else { + expect(items).toEqual({ 'flag1': flag1 }); + } }); - runCachedAndUncachedTests('all() with deleted item', function(done, wrapper, core, isCached) { + runCachedAndUncachedTests('all() with deleted item', async (wrapper, core, isCached) => { const flag1 = { key: 'flag1', version: 1 }; const flag2 = { key: 'flag2', version: 1, deleted: true }; core.forceSet(features, flag1); core.forceSet(features, flag2); - wrapper.all(features, function(items) { - expect(items).toEqual({ 'flag1': flag1 }); - - core.forceRemove(features, flag1.key); + var items = await asyncify(cb => wrapper.all(features, cb)); + expect(items).toEqual({ 'flag1': flag1 }); - wrapper.all(features, function(items) { - if (isCached) { - expect(items).toEqual({ 'flag1': flag1 }); - } else { - expect(items).toEqual({ }); - } + core.forceRemove(features, flag1.key); - done(); - }); - }); + items = await asyncify(cb => wrapper.all(features, cb)); + if (isCached) { + expect(items).toEqual({ 'flag1': flag1 }); + } else { + expect(items).toEqual({ }); + } }); - runCachedAndUncachedTests('all() error condition', function(done, wrapper, core, isCached) { + runCachedAndUncachedTests('all() error condition', async (wrapper, core, isCached) => { core.getAllError = true; - wrapper.all(features, function(items) { - expect(items).toBe(null); - done(); - }); + var items = await asyncify(cb => wrapper.all(features, cb)); + expect(items).toBe(null); }); - runCachedTestOnly('cached all() uses values from init()', function(done, wrapper, core) { + runCachedTestOnly('cached all() uses values from init()', async (wrapper, core) => { const flag1 = { key: 'flag1', version: 1 }; const flag2 = { key: 'flag2', version: 1 }; const allData = { features: { flag1: flag1, flag2: flag2 } }; - wrapper.init(allData, function() { - core.forceRemove(features, flag2.key); - - wrapper.all(features, function(items) { - expect(items).toEqual({ flag1: flag1, flag2: flag2 }); + await asyncify(cb => wrapper.init(allData, cb)); + core.forceRemove(features, flag2.key); - done(); - }); - }); + var items = await asyncify(cb => wrapper.all(features, cb)); + expect(items).toEqual({ flag1: flag1, flag2: flag2 }); }); - runCachedTestOnly('cached all() uses fresh values if there has been an update', function(done, wrapper, core) { + runCachedTestOnly('cached all() uses fresh values if there has been an update', async (wrapper, core) => { const flag1v1 = { key: 'flag1', version: 1 }; const flag1v2 = { key: 'flag1', version: 2 }; const flag2v1 = { key: 'flag2', version: 1 }; @@ -239,204 +210,165 @@ describe('CachingStoreWrapper', function() { const allData = { features: { flag1: flag1v1, flag2: flag2v2 } }; - wrapper.init(allData, function() { - expect(core.data).toEqual(allData); - - // make a change to flag1 using the wrapper - this should flush the cache - wrapper.upsert(features, flag1v2, function() { - // make a change to flag2 that bypasses the cache - core.forceSet(features, flag2v2); + await asyncify(cb => wrapper.init(allData, cb)); + expect(core.data).toEqual(allData); - // we should now see both changes since the cache was flushed - wrapper.all(features, function(items) { - expect(items).toEqual({ flag1: flag1v2, flag2: flag2v2 }); + // make a change to flag1 using the wrapper - this should flush the cache + await asyncify(cb => wrapper.upsert(features, flag1v2, cb)); + // make a change to flag2 that bypasses the cache + core.forceSet(features, flag2v2); - done(); - }); - }); - }); + // we should now see both changes since the cache was flushed + var items = await asyncify(cb => wrapper.all(features, cb)); + expect(items).toEqual({ flag1: flag1v2, flag2: flag2v2 }); }); - runCachedAndUncachedTests('upsert() - successful', function(done, wrapper, core, isCached) { + runCachedAndUncachedTests('upsert() - successful', async (wrapper, core, isCached) => { const flagv1 = { key: 'flag', version: 1 }; const flagv2 = { key: 'flag', version: 2 }; - wrapper.upsert(features, flagv1, function() { - expect(core.data[features.namespace][flagv1.key]).toEqual(flagv1); - - wrapper.upsert(features, flagv2, function() { - expect(core.data[features.namespace][flagv1.key]).toEqual(flagv2); + await asyncify(cb => wrapper.upsert(features, flagv1, cb)); + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv1); - // if we have a cache, verify that the new item is now cached by writing a different value - // to the underlying data - get() should still return the cached item - if (isCached) { - const flagv3 = { key: 'flag', version: 3 }; - core.forceSet(features, flagv3); - } + await asyncify(cb => wrapper.upsert(features, flagv2, cb)); + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv2); - wrapper.get(features, flagv1.key, function(item) { - expect(item).toEqual(flagv2); + // if we have a cache, verify that the new item is now cached by writing a different value + // to the underlying data - get() should still return the cached item + if (isCached) { + const flagv3 = { key: 'flag', version: 3 }; + core.forceSet(features, flagv3); + } - done(); - }); - }); - }); + var item = await asyncify(cb => wrapper.get(features, flagv1.key, cb)); + expect(item).toEqual(flagv2); }); - runCachedAndUncachedTests('upsert() - error', function(done, wrapper, core, isCached) { + runCachedAndUncachedTests('upsert() - error', async (wrapper, core, isCached) => { const flagv1 = { key: 'flag', version: 1 }; const flagv2 = { key: 'flag', version: 2 }; - wrapper.upsert(features, flagv1, function() { - expect(core.data[features.namespace][flagv1.key]).toEqual(flagv1); - - core.upsertError = new Error('sorry'); - - wrapper.upsert(features, flagv2, function() { - expect(core.data[features.namespace][flagv1.key]).toEqual(flagv1); - - // if we have a cache, verify that the old item is still cached by writing a different value - // to the underlying data - get() should still return the cached item - if (isCached) { - const flagv3 = { key: 'flag', version: 3 }; - core.forceSet(features, flagv3); - wrapper.get(features, flagv1.key, function(item) { - expect(item).toEqual(flagv1); - done(); - }); - } else { - done(); - } - }); - }); + await asyncify(cb => wrapper.upsert(features, flagv1, cb)); + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv1); + + core.upsertError = new Error('sorry'); + + await asyncify(cb => wrapper.upsert(features, flagv2, cb)); + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv1); + + // if we have a cache, verify that the old item is still cached by writing a different value + // to the underlying data - get() should still return the cached item + if (isCached) { + const flagv3 = { key: 'flag', version: 3 }; + core.forceSet(features, flagv3); + var item = await asyncify(cb => wrapper.get(features, flagv1.key, cb)); + expect(item).toEqual(flagv1); + } }); - runCachedTestOnly('cached upsert() - unsuccessful', function(done, wrapper, core) { + runCachedTestOnly('cached upsert() - unsuccessful', async (wrapper, core) => { const flagv1 = { key: 'flag', version: 1 }; const flagv2 = { key: 'flag', version: 2 }; core.forceSet(features, flagv2); // this is now in the underlying data, but not in the cache - wrapper.upsert(features, flagv1, function() { - expect(core.data[features.namespace][flagv1.key]).toEqual(flagv2); // value in store remains the same - - // the cache should now contain flagv2 - check this by making another change that bypasses - // the cache, and verifying that get() uses the cached value instead - const flagv3 = { key: 'flag', version: 3 }; - core.forceSet(features, flagv3); + await asyncify(cb => wrapper.upsert(features, flagv1, cb)); + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv2); // value in store remains the same - wrapper.get(features, flagv1.key, function(item) { - expect(item).toEqual(flagv2); + // the cache should now contain flagv2 - check this by making another change that bypasses + // the cache, and verifying that get() uses the cached value instead + const flagv3 = { key: 'flag', version: 3 }; + core.forceSet(features, flagv3); - done(); - }); - }); + var item = await asyncify(cb => wrapper.get(features, flagv1.key, cb)); + expect(item).toEqual(flagv2); }); - runCachedAndUncachedTests('delete()', function(done, wrapper, core, isCached) { + runCachedAndUncachedTests('delete()', async (wrapper, core, isCached) => { const flagv1 = { key: 'flag', version: 1 }; const flagv2 = { key: 'flag', version: 2, deleted: true }; const flagv3 = { key: 'flag', version: 3 }; core.forceSet(features, flagv1); - wrapper.get(features, flagv1.key, function(item) { - expect(item).toEqual(flagv1); + var item = await asyncify(cb => wrapper.get(features, flagv1.key, cb)); + expect(item).toEqual(flagv1); - wrapper.delete(features, flagv1.key, flagv2.version); + await asyncify(cb => wrapper.delete(features, flagv1.key, flagv2.version, cb)); - expect(core.data[features.namespace][flagv1.key]).toEqual(flagv2); + expect(core.data[features.namespace][flagv1.key]).toEqual(flagv2); - // make a change to the flag that bypasses the cache - core.forceSet(features, flagv3); - - wrapper.get(features, flagv1.key, function(item) { - expect(item).toEqual(isCached ? null : flagv3); + // make a change to the flag that bypasses the cache + core.forceSet(features, flagv3); - done(); - }); - }); + var item = await asyncify(cb => wrapper.get(features, flagv1.key, cb)); + expect(item).toEqual(isCached ? null : flagv3); }); describe('initialized()', function() { - it('calls underlying initialized() only if not already inited', function(done) { + it('calls underlying initialized() only if not already inited', async () => { const core = MockCore(); const wrapper = new CachingStoreWrapper(core, 0); - wrapper.initialized(function(value) { - expect(value).toEqual(false); - expect(core.initQueriedCount).toEqual(1); + var value = await asyncify(cb => wrapper.initialized(cb)); + expect(value).toEqual(false); + expect(core.initQueriedCount).toEqual(1); - core.inited = true; + core.inited = true; - wrapper.initialized(function(value) { - expect(value).toEqual(true); - expect(core.initQueriedCount).toEqual(2); + value = await asyncify(cb => wrapper.initialized(cb)); + expect(value).toEqual(true); + expect(core.initQueriedCount).toEqual(2); - core.inited = false; // this should have no effect since we already returned true + core.inited = false; // this should have no effect since we already returned true - wrapper.initialized(function(value) { - expect(value).toEqual(true); - expect(core.initQueriedCount).toEqual(2); - - done(); - }); - }); - }); + value = await asyncify(cb => wrapper.initialized(cb)); + expect(value).toEqual(true); + expect(core.initQueriedCount).toEqual(2); }); - it('will not call initialized() if init() has been called', function(done) { + it('will not call initialized() if init() has been called', async () => { const core = MockCore(); const wrapper = new CachingStoreWrapper(core, 0); - wrapper.initialized(function(value) { - expect(value).toEqual(false); - expect(core.initQueriedCount).toEqual(1); + var value = await asyncify(cb => wrapper.initialized(cb)); + expect(value).toEqual(false); + expect(core.initQueriedCount).toEqual(1); - const allData = { features: {} }; - wrapper.init(allData, function() { - wrapper.initialized(function(value) { - expect(value).toEqual(true); - expect(core.initQueriedCount).toEqual(1); + const allData = { features: {} }; + await asyncify(cb => wrapper.init(allData, cb)); - done(); - }); - }); - }); + value = await asyncify(cb => wrapper.initialized(cb)); + expect(value).toEqual(true); + expect(core.initQueriedCount).toEqual(1); }); - it('can cache false result', function(done) { + it('can cache false result', async () => { const core = MockCore(); const wrapper = new CachingStoreWrapper(core, 1); // cache TTL = 1 second - wrapper.initialized(function(value) { - expect(value).toEqual(false); - expect(core.initQueriedCount).toEqual(1); - - core.inited = true; + var value = await asyncify(cb => wrapper.initialized(cb)); + expect(value).toEqual(false); + expect(core.initQueriedCount).toEqual(1); - wrapper.initialized(function(value) { - expect(value).toEqual(false); - expect(core.initQueriedCount).toEqual(1); + core.inited = true; - setTimeout(function() { - wrapper.initialized(function(value) { - expect(value).toEqual(true); - expect(core.initQueriedCount).toEqual(2); + value = await asyncify(cb => wrapper.initialized(cb)); + expect(value).toEqual(false); + expect(core.initQueriedCount).toEqual(1); - done(); - }); - }, 1100); - }); - }); + await sleepAsync(1100); + + value = await asyncify(cb => wrapper.initialized(cb)); + expect(value).toEqual(true); + expect(core.initQueriedCount).toEqual(2); }); }); describe('close()', function() { - runCachedAndUncachedTests('closes underlying store', function(done, wrapper, core) { + runCachedAndUncachedTests('closes underlying store', async (wrapper, core) => { wrapper.close(); expect(core.closed).toBe(true); - done(); }); }); }); diff --git a/test/feature_store_test_base.js b/test/feature_store_test_base.js index f253e0b..911f1da 100644 --- a/test/feature_store_test_base.js +++ b/test/feature_store_test_base.js @@ -1,4 +1,5 @@ var dataKind = require('../versioned_data_kind'); +const { asyncify } = require('./async_utils'); // The following tests should be run on every feature store implementation. If this type of // store supports caching, the tests should be run once with caching enabled and once with @@ -21,7 +22,7 @@ function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { version: 10 }; - beforeEach(function(done) { + beforeEach(done => { if (clearExistingData) { clearExistingData(done); } else { @@ -29,28 +30,24 @@ function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { } }); - function initedStore(cb) { + async function initedStore() { var store = makeStore(); var initData = {}; initData[dataKind.features.namespace] = { 'foo': feature1, 'bar': feature2 }; - store.init(initData, function() { - cb(store); - }); + await asyncify(cb => store.init(initData, cb)); + return store; } - it('is initialized after calling init()', function(done) { - initedStore(function(store) { - store.initialized(function(result) { - expect(result).toBe(true); - done(); - }); - }); + it('is initialized after calling init()', async () => { + var store = await initedStore(); + var result = await asyncify(cb => store.initialized(cb)); + expect(result).toBe(true); }); - it('init() completely replaces previous data', function(done) { + it('init() completely replaces previous data', async () => { var store = makeStore(); var flags = { first: { key: 'first', version: 1 }, @@ -61,49 +58,37 @@ function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { initData[dataKind.features.namespace] = flags; initData[dataKind.segments.namespace] = segments; - store.init(initData, function() { - store.all(dataKind.features, function(items) { - expect(items).toEqual(flags); - store.all(dataKind.segments, function(items) { - expect(items).toEqual(segments); - - var newFlags = { first: { key: 'first', version: 3 } }; - var newSegments = { first: { key: 'first', version: 4 } }; - var initData = {}; - initData[dataKind.features.namespace] = newFlags; - initData[dataKind.segments.namespace] = newSegments; - - store.init(initData, function() { - store.all(dataKind.features, function(items) { - expect(items).toEqual(newFlags); - store.all(dataKind.segments, function(items) { - expect(items).toEqual(newSegments); - - done(); - }) - }) - }); - }); - }); - }); + await asyncify(cb => store.init(initData, cb)); + var items = await asyncify(cb => store.all(dataKind.features, cb)); + expect(items).toEqual(flags); + items = await asyncify(cb => store.all(dataKind.segments, cb)); + expect(items).toEqual(segments); + + var newFlags = { first: { key: 'first', version: 3 } }; + var newSegments = { first: { key: 'first', version: 4 } }; + var initData = {}; + initData[dataKind.features.namespace] = newFlags; + initData[dataKind.segments.namespace] = newSegments; + + await asyncify(cb => store.init(initData, cb)); + items = await asyncify(cb => store.all(dataKind.features, cb)); + expect(items).toEqual(newFlags); + items = await asyncify(cb => store.all(dataKind.segments, cb)); + expect(items).toEqual(newSegments); }); if (!isCached && clearExistingData) { function testInitStateDetection(desc, initData) { - it(desc, function(done) { + it(desc, async () => { var store1 = makeStore(); var store2 = makeStore(); - store1.initialized(function(result) { - expect(result).toBe(false); + var result = await asyncify(cb => store1.initialized(cb)); + expect(result).toBe(false); - store2.init(initData, function() { - store1.initialized(function(result) { - expect(result).toBe(true); - done(); - }); - }); - }); + await asyncify(cb => store2.init(initData, cb)); + result = await asyncify(cb => store1.initialized(cb)); + expect(result).toBe(true); }); } @@ -114,76 +99,56 @@ function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { { features: {} }); } - it('gets existing feature', function(done) { - initedStore(function(store) { - store.get(dataKind.features, feature1.key, function(result) { - expect(result).toEqual(feature1); - done(); - }); - }); + it('gets existing feature', async () => { + var store = await initedStore(); + var result = await asyncify(cb => store.get(dataKind.features, feature1.key, cb)); + expect(result).toEqual(feature1); }); - it('does not get nonexisting feature', function(done) { - initedStore(function(store) { - store.get(dataKind.features, 'biz', function(result) { - expect(result).toBe(null); - done(); - }); - }); + it('does not get nonexisting feature', async () => { + var store = await initedStore(); + var result = await asyncify(cb => store.get(dataKind.features, 'biz', cb)); + expect(result).toBe(null); }); - it('gets all features', function(done) { - initedStore(function(store) { - store.all(dataKind.features, function(result) { - expect(result).toEqual({ - 'foo': feature1, - 'bar': feature2 - }); - done(); - }); + it('gets all features', async () => { + var store = await initedStore(); + var result = await asyncify(cb => store.all(dataKind.features, cb)); + expect(result).toEqual({ + 'foo': feature1, + 'bar': feature2 }); }); - it('upserts with newer version', function(done) { + it('upserts with newer version', async () => { var newVer = { key: feature1.key, version: feature1.version + 1 }; - initedStore(function(store) { - store.upsert(dataKind.features, newVer, function(result) { - store.get(dataKind.features, feature1.key, function(result) { - expect(result).toEqual(newVer); - done(); - }); - }); - }); + var store = await initedStore(); + await asyncify(cb => store.upsert(dataKind.features, newVer, cb)); + var result = await asyncify(cb => store.get(dataKind.features, feature1.key, cb)); + expect(result).toEqual(newVer); }); - it('does not upsert with older version', function(done) { + it('does not upsert with older version', async () => { var oldVer = { key: feature1.key, version: feature1.version - 1 }; - initedStore(function(store) { - store.upsert(dataKind.features, oldVer, function(result) { - store.get(dataKind.features, feature1.key, function(result) { - expect(result).toEqual(feature1); - done(); - }); - }); - }); + var store = await initedStore(); + await asyncify(cb => store.upsert(dataKind.features, oldVer, cb)); + var result = await asyncify(cb => store.get(dataKind.features, feature1.key, cb)); + expect(result).toEqual(feature1); }); - it('upserts new feature', function(done) { + it('upserts new feature', async () => { var newFeature = { key: 'biz', version: 99 }; - initedStore(function(store) { - store.upsert(dataKind.features, newFeature, function(result) { - store.get(dataKind.features, newFeature.key, function(result) { - expect(result).toEqual(newFeature); - done(); - }); - }); - }); + var store = await initedStore(); + await asyncify(cb => store.upsert(dataKind.features, newFeature, cb)); + var result = await asyncify(cb => store.get(dataKind.features, newFeature.key, cb)); + expect(result).toEqual(newFeature); }); - it('handles upsert race condition within same client correctly', function(done) { + it('handles upsert race condition within same client correctly', done => { + // Not sure if there is a way to do this one with async/await var ver1 = { key: feature1.key, version: feature1.version + 1 }; var ver2 = { key: feature1.key, version: feature1.version + 2 }; - initedStore(function(store) { + initedStore().then(store => { var counter = 0; var combinedCallback = function() { counter++; @@ -201,50 +166,33 @@ function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { }); }); - it('deletes with newer version', function(done) { - initedStore(function(store) { - store.delete(dataKind.features, feature1.key, feature1.version + 1, function(result) { - store.get(dataKind.features, feature1.key, function(result) { - expect(result).toBe(null); - done(); - }); - }); - }); + it('deletes with newer version', async () => { + var store = await initedStore(); + await asyncify(cb => store.delete(dataKind.features, feature1.key, feature1.version + 1, cb)); + var result = await asyncify(cb => store.get(dataKind.features, feature1.key, cb)); + expect(result).toBe(null); }); - it('does not delete with older version', function(done) { - initedStore(function(store) { - store.delete(dataKind.features, feature1.key, feature1.version - 1, function(result) { - store.get(dataKind.features, feature1.key, function(result) { - expect(result).not.toBe(null); - done(); - }); - }); - }); + it('does not delete with older version', async () => { + var store = await initedStore(); + await asyncify(cb => store.delete(dataKind.features, feature1.key, feature1.version - 1, cb)); + var result = await asyncify(cb => store.get(dataKind.features, feature1.key, cb)); + expect(result).not.toBe(null); }); - it('allows deleting unknown feature', function(done) { - initedStore(function(store) { - store.delete(dataKind.features, 'biz', 99, function(result) { - store.get(dataKind.features, 'biz', function(result) { - expect(result).toBe(null); - done(); - }); - }); - }); + it('allows deleting unknown feature', async () => { + var store = await initedStore(); + await asyncify(cb => store.delete(dataKind.features, 'biz', 99, cb)); + var result = await asyncify(cb => store.get(dataKind.features, 'biz', cb)); + expect(result).toBe(null); }); - it('does not upsert older version after delete', function(done) { - initedStore(function(store) { - store.delete(dataKind.features, feature1.key, feature1.version + 1, function(result) { - store.upsert(dataKind.features, feature1, function(result) { - store.get(dataKind.features, feature1.key, function(result) { - expect(result).toBe(null); - done(); - }); - }); - }); - }); + it('does not upsert older version after delete', async () => { + var store = await initedStore(); + await asyncify(cb => store.delete(dataKind.features, feature1.key, feature1.version + 1, cb)); + await asyncify(cb => store.upsert(dataKind.features, feature1, cb)); + var result = await asyncify(cb => store.get(dataKind.features, feature1.key, cb)); + expect(result).toBe(null); }); } @@ -268,10 +216,10 @@ function concurrentModificationTests(makeStore, makeStoreWithHook) { return { key: flagKey, version: v }; } - function withInitedStore(store, cb) { + async function initStore(store) { var allData = { features: {} }; allData['features'][flagKey] = makeFlagWithVersion(initialVersion); - store.init(allData, cb); + await asyncify(cb => store.init(allData, cb)); } function writeCompetingVersions(flagVersionsToWrite) { @@ -287,36 +235,28 @@ function concurrentModificationTests(makeStore, makeStoreWithHook) { }; } - it('handles upsert race condition against other client with lower version', function(done) { + it('handles upsert race condition against other client with lower version', async () => { var myDesiredVersion = 10; var competingStoreVersions = [ 2, 3, 4 ]; // proves that we can retry multiple times if necessary var myStore = makeStoreWithHook(writeCompetingVersions(competingStoreVersions)); - withInitedStore(myStore, function() { - myStore.upsert(dataKind.features, makeFlagWithVersion(myDesiredVersion), function() { - myStore.get(dataKind.features, flagKey, function(result) { - expect(result.version).toEqual(myDesiredVersion); - done(); - }); - }); - }); + await initStore(myStore); + await asyncify(cb => myStore.upsert(dataKind.features, makeFlagWithVersion(myDesiredVersion), cb)); + var result = await asyncify(cb => myStore.get(dataKind.features, flagKey, cb)); + expect(result.version).toEqual(myDesiredVersion); }); - it('handles upsert race condition against other client with higher version', function(done) { + it('handles upsert race condition against other client with higher version', async () => { var myDesiredVersion = 2; var competingStoreVersion = 3; var myStore = makeStoreWithHook(writeCompetingVersions([ competingStoreVersion ])); - withInitedStore(myStore, function() { - myStore.upsert(dataKind.features, makeFlagWithVersion(myDesiredVersion), function() { - myStore.get(dataKind.features, flagKey, function(result) { - expect(result.version).toEqual(competingStoreVersion); - done(); - }); - }); - }); + await initStore(myStore); + await asyncify(cb => myStore.upsert(dataKind.features, makeFlagWithVersion(myDesiredVersion), cb)); + var result = await asyncify(cb => myStore.get(dataKind.features, flagKey, cb)); + expect(result.version).toEqual(competingStoreVersion); }); } diff --git a/test/streaming-test.js b/test/streaming-test.js index 10996d1..1c0b47d 100644 --- a/test/streaming-test.js +++ b/test/streaming-test.js @@ -1,6 +1,7 @@ var InMemoryFeatureStore = require('../feature_store'); var StreamProcessor = require('../streaming'); var dataKind = require('../versioned_data_kind'); +const { asyncify, sleepAsync } = require('./async_utils'); describe('StreamProcessor', function() { var sdkKey = 'SDK_KEY'; @@ -24,13 +25,10 @@ describe('StreamProcessor', function() { }; } - function expectJsonError(config, done) { - return function(err) { - expect(err).not.toBe(undefined); - expect(err.message).toEqual('Malformed JSON data in event stream'); - expect(config.logger.error).toHaveBeenCalled(); - done(); - } + function expectJsonError(err, config) { + expect(err).not.toBe(undefined); + expect(err.message).toEqual('Malformed JSON data in event stream'); + expect(config.logger.error).toHaveBeenCalled(); } it('uses expected URL', function() { @@ -62,7 +60,7 @@ describe('StreamProcessor', function() { } }; - it('causes flags and segments to be stored', function(done) { + it('causes flags and segments to be stored', async () => { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); @@ -71,47 +69,42 @@ describe('StreamProcessor', function() { es.handlers.put({ data: JSON.stringify(putData) }); - featureStore.initialized(function(flag) { - expect(flag).toEqual(true); - }); - - featureStore.get(dataKind.features, 'flagkey', function(f) { - expect(f.version).toEqual(1); - featureStore.get(dataKind.segments, 'segkey', function(s) { - expect(s.version).toEqual(2); - done(); - }); - }); + var flag = await asyncify(cb => featureStore.initialized(cb)); + expect(flag).toEqual(true); + + var f = await asyncify(cb => featureStore.get(dataKind.features, 'flagkey', cb)); + expect(f.version).toEqual(1); + var s = await asyncify(cb => featureStore.get(dataKind.segments, 'segkey', cb)); + expect(s.version).toEqual(2); }); - it('calls initialization callback', function(done) { + it('calls initialization callback', async () => { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); var sp = StreamProcessor(sdkKey, config, null, es.constructor); - var cb = function(err) { - expect(err).toBe(undefined); - done(); - } - - sp.start(cb); + var waitUntilStarted = asyncify(cb => sp.start(cb)); es.handlers.put({ data: JSON.stringify(putData) }); + var result = await waitUntilStarted; + expect(result).toBe(undefined); }); - it('passes error to callback if data is invalid', function(done) { + it('passes error to callback if data is invalid', async () => { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); var sp = StreamProcessor(sdkKey, config, null, es.constructor); - sp.start(expectJsonError(config, done)); + var waitUntilStarted = asyncify(cb => sp.start(cb)); es.handlers.put({ data: '{not-good' }); + var result = await waitUntilStarted; + expectJsonError(result, config); }); }); describe('patch message', function() { - it('updates flag', function(done) { + it('updates flag', async () => { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); @@ -125,13 +118,11 @@ describe('StreamProcessor', function() { sp.start(); es.handlers.patch({ data: JSON.stringify(patchData) }); - featureStore.get(dataKind.features, 'flagkey', function(f) { - expect(f.version).toEqual(1); - done(); - }); + var f = await asyncify(cb => featureStore.get(dataKind.features, 'flagkey', cb)); + expect(f.version).toEqual(1); }); - it('updates segment', function(done) { + it('updates segment', async () => { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); @@ -145,25 +136,25 @@ describe('StreamProcessor', function() { sp.start(); es.handlers.patch({ data: JSON.stringify(patchData) }); - featureStore.get(dataKind.segments, 'segkey', function(s) { - expect(s.version).toEqual(1); - done(); - }); + var s = await asyncify(cb => featureStore.get(dataKind.segments, 'segkey', cb)); + expect(s.version).toEqual(1); }); - it('passes error to callback if data is invalid', function(done) { + it('passes error to callback if data is invalid', async () => { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); var sp = StreamProcessor(sdkKey, config, null, es.constructor); - sp.start(expectJsonError(config, done)); + var waitForCallback = asyncify(cb => sp.start(cb)); es.handlers.patch({ data: '{not-good' }); + var result = await waitForCallback; + expectJsonError(result, config); }); }); describe('delete message', function() { - it('deletes flag', function(done) { + it('deletes flag', async () => { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); @@ -172,22 +163,18 @@ describe('StreamProcessor', function() { sp.start(); var flag = { key: 'flagkey', version: 1 } - featureStore.upsert(dataKind.features, flag, function() { - featureStore.get(dataKind.features, flag.key, function(f) { - expect(f).toEqual(flag); - - var deleteData = { path: '/flags/' + flag.key, version: 2 }; - es.handlers.delete({ data: JSON.stringify(deleteData) }); - - featureStore.get(dataKind.features, flag.key, function(f) { - expect(f).toBe(null); - done(); - }) - }); - }); + await asyncify(cb => featureStore.upsert(dataKind.features, flag, cb)); + var f = await asyncify(cb => featureStore.get(dataKind.features, flag.key, cb)); + expect(f).toEqual(flag); + + var deleteData = { path: '/flags/' + flag.key, version: 2 }; + es.handlers.delete({ data: JSON.stringify(deleteData) }); + + var f = await asyncify(cb => featureStore.get(dataKind.features, flag.key, cb)); + expect(f).toBe(null); }); - it('deletes segment', function(done) { + it('deletes segment', async () => { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); @@ -196,29 +183,27 @@ describe('StreamProcessor', function() { sp.start(); var segment = { key: 'segkey', version: 1 } - featureStore.upsert(dataKind.segments, segment, function() { - featureStore.get(dataKind.segments, segment.key, function(s) { - expect(s).toEqual(segment); - - var deleteData = { path: '/segments/' + segment.key, version: 2 }; - es.handlers.delete({ data: JSON.stringify(deleteData) }); - - featureStore.get(dataKind.segments, segment.key, function(s) { - expect(s).toBe(null); - done(); - }) - }); - }); + await asyncify(cb => featureStore.upsert(dataKind.segments, segment, cb)); + var s = await asyncify(cb => featureStore.get(dataKind.segments, segment.key, cb)); + expect(s).toEqual(segment); + + var deleteData = { path: '/segments/' + segment.key, version: 2 }; + es.handlers.delete({ data: JSON.stringify(deleteData) }); + + s = await asyncify(cb => featureStore.get(dataKind.segments, segment.key, cb)); + expect(s).toBe(null); }); - it('passes error to callback if data is invalid', function(done) { + it('passes error to callback if data is invalid', async () => { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); var sp = StreamProcessor(sdkKey, config, null, es.constructor); - sp.start(expectJsonError(config, done)); + var waitForResult = asyncify(cb => sp.start(cb)); es.handlers.delete({ data: '{not-good' }); + var result = await waitForResult; + expectJsonError(result, config); }); }); @@ -237,7 +222,7 @@ describe('StreamProcessor', function() { } }; - it('requests and stores flags and segments', function(done) { + it('requests and stores flags and segments', async () => { var featureStore = InMemoryFeatureStore(); var config = { featureStore: featureStore, logger: fakeLogger() }; var es = fakeEventSource(); @@ -247,23 +232,18 @@ describe('StreamProcessor', function() { es.handlers['indirect/put']({}); - setImmediate(function() { - featureStore.get(dataKind.features, 'flagkey', function(f) { - expect(f.version).toEqual(1); - featureStore.get(dataKind.segments, 'segkey', function(s) { - expect(s.version).toEqual(2); - featureStore.initialized(function(flag) { - expect(flag).toBe(true); - }); - done(); - }); - }); - }); + await sleepAsync(0); + var f = await asyncify(cb => featureStore.get(dataKind.features, 'flagkey', cb)); + expect(f.version).toEqual(1); + var s = await asyncify(cb => featureStore.get(dataKind.segments, 'segkey', cb)); + expect(s.version).toEqual(2); + var value = await asyncify(cb => featureStore.initialized(cb)); + expect(value).toBe(true); }); }); describe('indirect patch message', function() { - it('requests and updates flag', function(done) { + it('requests and updates flag', async () => { var flag = { key: 'flagkey', version: 1 }; var fakeRequestor = { requestObject: function(kind, key, cb) { @@ -282,15 +262,12 @@ describe('StreamProcessor', function() { es.handlers['indirect/patch']({ data: '/flags/flagkey' }); - setImmediate(function() { - featureStore.get(dataKind.features, 'flagkey', function(f) { - expect(f.version).toEqual(1); - done(); - }); - }); + await sleepAsync(0); + var f = await asyncify(cb => featureStore.get(dataKind.features, 'flagkey', cb)); + expect(f.version).toEqual(1); }); - it('requests and updates segment', function(done) { + it('requests and updates segment', async () => { var segment = { key: 'segkey', version: 1 }; var fakeRequestor = { requestObject: function(kind, key, cb) { @@ -309,12 +286,9 @@ describe('StreamProcessor', function() { es.handlers['indirect/patch']({ data: '/segments/segkey' }); - setImmediate(function() { - featureStore.get(dataKind.segments, 'segkey', function(s) { - expect(s.version).toEqual(1); - done(); - }); - }); + await sleepAsync(0); + var s = await asyncify(cb => featureStore.get(dataKind.segments, 'segkey', cb)); + expect(s.version).toEqual(1); }); }); }); From f82b59480a7d805011fd6aa96dfa089481cae1f0 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 9 Jan 2019 19:50:21 -0800 Subject: [PATCH 076/330] use async_utils --- test/file_data_source-test.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/test/file_data_source-test.js b/test/file_data_source-test.js index 093c02f..bcafb01 100644 --- a/test/file_data_source-test.js +++ b/test/file_data_source-test.js @@ -1,6 +1,7 @@ var fs = require('fs'); var tmp = require('tmp'); var dataKind = require('../versioned_data_kind'); +const { asyncify, sleepAsync } = require('./async_utils'); var LaunchDarkly = require('../index'); var FileDataSource = require('../file_data_source'); @@ -118,16 +119,6 @@ describe('FileDataSource', function() { return a1; } - function asyncify(f) { - return new Promise(resolve => f(resolve)); - } - - function sleep(millis) { - return new Promise(resolve => { - setTimeout(resolve, millis); - }); - } - it('does not load flags prior to start', async () => { var path = await makeTempFile('{"flagValues":{"key":"value"}}'); var fds = setupDataSource({ paths: [path] }); @@ -207,9 +198,9 @@ describe('FileDataSource', function() { var items = await asyncify(cb => store.all(dataKind.segments, cb)); expect(Object.keys(items).length).toEqual(0); - await sleep(200); + await sleepAsync(200); await replaceFileContent(path, segmentOnlyJson); - await sleep(200); + await sleepAsync(200); items = await asyncify(cb => store.all(dataKind.segments, cb)); expect(Object.keys(items).length).toEqual(0); @@ -223,9 +214,9 @@ describe('FileDataSource', function() { var items = await asyncify(cb => store.all(dataKind.segments, cb)); expect(Object.keys(items).length).toEqual(0); - await sleep(200); + await sleepAsync(200); await replaceFileContent(path, segmentOnlyJson); - await sleep(200); + await sleepAsync(200); items = await asyncify(cb => store.all(dataKind.segments, cb)); expect(Object.keys(items).length).toEqual(1); From f6f037f58fa8100fc8fff7a0a5388a57cbeb7980 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 9 Jan 2019 19:53:41 -0800 Subject: [PATCH 077/330] misc test fixes --- file_data_source.js | 2 +- test/file_data_source-test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/file_data_source.js b/file_data_source.js index 4d5d432..ef3a7c7 100644 --- a/file_data_source.js +++ b/file_data_source.js @@ -138,7 +138,7 @@ function FileDataSource(options) { watchers = []; } - fds = {}; + var fds = {}; fds.start = fn => { var cb = fn || (() => {}); diff --git a/test/file_data_source-test.js b/test/file_data_source-test.js index bcafb01..e5a9cc4 100644 --- a/test/file_data_source-test.js +++ b/test/file_data_source-test.js @@ -187,7 +187,7 @@ describe('FileDataSource', function() { await asyncify(fds.start); expect(fds.initialized()).toBe(false); - expect(await asyncify(store.initialized)).toBe(false); + expect(await asyncify(cb => store.initialized(cb))).toBe(false); }); it('does not reload modified file if auto-update is off', async () => { From ff2426b0398b4f69815028b4bfe1ac1076fe6523 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 9 Jan 2019 20:00:57 -0800 Subject: [PATCH 078/330] ES2015 compatibility --- file_data_source.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/file_data_source.js b/file_data_source.js index ef3a7c7..0c19ce9 100644 --- a/file_data_source.js +++ b/file_data_source.js @@ -67,14 +67,14 @@ function FileDataSource(options) { allData[kind.namespace][item.key] = item; } } - Object.values(parsed.flags || {}).forEach(item => { - addItem(dataKind.features, item); + Object.keys(parsed.flags || {}).forEach(key => { + addItem(dataKind.features, parsed.flags[key]); }); - Object.entries(parsed.flagValues || {}).forEach(e => { - addItem(dataKind.features, makeFlagWithValue(e[0], e[1])); + Object.keys(parsed.flagValues || {}).forEach(key => { + addItem(dataKind.features, makeFlagWithValue(key, parsed.flagValues[key])); }); - Object.values(parsed.segments || {}).forEach(item => { - addItem(dataKind.segments, item); + Object.keys(parsed.segments || {}).forEach(key => { + addItem(dataKind.segments, parsed.segments[key]); }); }); } From 144b73ff160dda6127a218e555007ea00600c354 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 10 Jan 2019 11:31:29 -0800 Subject: [PATCH 079/330] documentation fixes for FileDataSource --- README.md | 2 +- file_data_source.js | 26 +-------------------- index.d.ts | 56 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 868331f..ab01d33 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Your first feature flag Using flag data from a file --------------------------- -For testing purposes, the SDK can be made to read feature flag state from a file or files instead of connecting to LaunchDarkly. See [`file_data_source.js`](https://github.com/launchdarkly/node-client/blob/master/file_data_source.js) for more details. +For testing purposes, the SDK can be made to read feature flag state from a file or files instead of connecting to LaunchDarkly. See `FileDataSource` in the [TypeScript API documentation](https://github.com/launchdarkly/node-client/blob/master/index.d.ts) for more details. Learn more ----------- diff --git a/file_data_source.js b/file_data_source.js index 0c19ce9..2ff3616 100644 --- a/file_data_source.js +++ b/file_data_source.js @@ -7,31 +7,7 @@ var fs = require('fs'), FileDataSource provides a way to use local files as a source of feature flag state, instead of connecting to LaunchDarkly. This would typically be used in a test environment. - To use this component, call FileDataSource(options) and store the result in the "updateProcessor" - property of your LaunchDarkly client configuration. In the options, set "paths" to the file - paths of your data file(s): - - var dataSource = LaunchDarkly.FileDataSource({ paths: [ myFilePath ] }); - var config = { updateProcessor: dataSource }; - - Flag data files can be either JSON or YAML. They contain an object with three possible - properties: - - - "flags": Full feature flag definitions. - - "flagValues": Simplified feature flags, just a map of flag keys to values. - - "segments": User segment definitions. - - The format of the data in "flags" and "segments" is defined by the LaunchDarkly application - and is subject to change. You can query existing flags and segments from LaunchDarkly in JSON - format by querying https://app.launchdarkly.com/sdk/latest-all and passing your SDK key in - the Authorization header. - - You can also specify that flags should be reloaded whenever a file is modified, by setting - "autoUpdate: true" in the options. This feature uses Node's fs.watch() API, so it is subject to - the limitations described here: https://nodejs.org/docs/latest/api/fs.html#fs_fs_watch_filename_options_listener - - For more details, see the LaunchDarkly reference guide: - https://docs.launchdarkly.com/v2.0/docs/reading-flags-from-a-file + See documentation in index.d.ts. */ function FileDataSource(options) { var paths = (options && options.paths) || []; diff --git a/index.d.ts b/index.d.ts index 2b526ed..1fb101d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -754,6 +754,62 @@ declare module 'ldclient-node' { */ flush: (callback?: (err: any, res: boolean) => void) => Promise; } + + /** + * Configuration for FileDataSource. + */ + export interface FileDataSourceOptions { + /** + * The path(s) of the file(s) that FileDataSource will read. + */ + paths: Array; + + /** + * True if FileDataSource should reload flags whenever one of the data files is modified. + * This feature uses Node's fs.watch() API, so it is subject to + * the limitations described here: https://nodejs.org/docs/latest/api/fs.html#fs_fs_watch_filename_options_listener + */ + autoUpdate?: boolean; + + /** + * Configures a logger for warnings and errors. This can be a custom logger or an instance of + * winston.Logger. By default, it uses the same logger as the rest of the SDK. + */ + logger?: LDLogger | object; + } + + /** + * Creates an object that allows you to use local files as a source of feature flag state, + * instead of connecting to LaunchDarkly. This would typically be used in a test environment. + *

+ * To use this component, call FileDataSource(options) and store the result in the "updateProcessor" + * property of your LaunchDarkly client configuration: + *

+   *     var dataSource = LaunchDarkly.FileDataSource({ paths: [ myFilePath ] });
+   *     var config = { updateProcessor: dataSource };
+   * 
+ *

+ * Flag data files can be either JSON or YAML. They contain an object with three possible + * properties: + *

    + *
  • "flags": Full feature flag definitions. + *
  • "flagValues": Simplified feature flags, just a map of flag keys to values. + *
  • "segments": User segment definitions. + *
+ *

+ * The format of the data in "flags" and "segments" is defined by the LaunchDarkly application + * and is subject to change. You can query existing flags and segments from LaunchDarkly in JSON + * format by querying https://app.launchdarkly.com/sdk/latest-all and passing your SDK key in + * the Authorization header. + *

+ * For more details, see the LaunchDarkly reference guide: + * https://docs.launchdarkly.com/v2.0/docs/reading-flags-from-a-file + * + * @param options configuration for the data source; you should at least set the "paths" property + */ + export function FileDataSource( + options: FileDataSourceOptions + ): object; } declare module 'ldclient-node/streaming' { From b6f11f7b7e18f4710622d6d48821fc5fc0584fa1 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 10 Jan 2019 18:12:41 -0800 Subject: [PATCH 080/330] implement dependency ordering for feature store data --- caching_store_wrapper.js | 119 +++++++++++++++++++++++++---- feature_store.js | 15 +++- test/caching_store_wrapper-test.js | 74 ++++++++++++++++-- versioned_data_kind.js | 12 ++- 4 files changed, 193 insertions(+), 27 deletions(-) diff --git a/caching_store_wrapper.js b/caching_store_wrapper.js index 246eda4..88541e1 100644 --- a/caching_store_wrapper.js +++ b/caching_store_wrapper.js @@ -15,9 +15,50 @@ var initializedKey = "$checkedInit"; /* CachingStoreWrapper provides commonly needed functionality for implementations of an SDK feature store. The underlyingStore must implement a simplified interface for - querying and updating the data store (see redis_feature_store.js for an example) - while CachingStoreWrapper adds optional caching of stored items and of the - initialized state, and ensures that asynchronous operations are serialized correctly. + querying and updating the data store, while CachingStoreWrapper adds optional caching of + stored items and of the initialized state, and ensures that asynchronous operations are + serialized correctly. + + The underlyingStore object must have the following methods: + + - getInternal(kind, key, callback): Queries a single item from the data store. The kind + parameter is an object with a "namespace" property that uniquely identifies the + category of data (features, segments), and the key is the unique key within that + category. It calls the callback with the resulting item as a parameter, or, if no such + item exists, null/undefined. It should not attempt to filter out any items, nor to + cache any items. + + - getAllInternal(kind, callback): Queries all items in a given category from the data + store, calling the callback with an object where each key is the item's key and each + value is the item. It should not attempt to filter out any items, nor to cache any items. + + - upsertInternal(kind, newItem, callback): Adds or updates a single item. If an item with + the same key already exists (in the category specified by "kind"), it should update it + only if the new item's "version" property is greater than the old one. On completion, it + should call the callback with the final state of the item, i.e. if the update succeeded + then it passes the item that was passed in, and if the update failed due to the version + check then it passes the item that is currently in the data store (this ensures that + caching works correctly). Note that deletions are implemented by upserting a placeholder + item with the property "deleted: true". + + - initializedInternal(callback): Tests whether the data store contains a complete data + set, meaning that initInternal() or initOrdereInternal() has been called at least once. + In a shared data store, it should be able to detect this even if the store was + initialized by a different process, i.e. the test should be based on looking at what is + in the data store. The method does not need to worry about caching this value; + CachingStoreWrapper will only call it when necessary. Call callback with true or false. + + - initInternal(allData, callback): Replaces the entire contents of the data store. This + should be done atomically (i.e. within a transaction); if that isn't possible, use + initOrderedInternal() instead. The allData parameter is an object where each key is one + of the "kind" objects, and each value is an object with the keys and values of all + items of that kind. Call callback with no parameters when done. + OR: + - initOrderedInternal(collections, callback): Replaces the entire contents of the data + store. The collections parameter is an array of objects, each of which has "kind" and + "items" properties; "items" is an array of data items. Each array should be processed + in the specified order. The store should delete any obsolete items only after writing + all of the items provided. */ function CachingStoreWrapper(underlyingStore, ttl) { var cache = ttl ? new NodeCache({ stdTTL: ttl }) : null; @@ -28,7 +69,10 @@ function CachingStoreWrapper(underlyingStore, ttl) { this.init = function(allData, cb) { queue.enqueue(function(cb) { - underlyingStore.initInternal(allData, function() { + // The underlying store can either implement initInternal, which receives unordered data, + // or initOrderedInternal, which receives ordered data (for implementations that cannot do + // an atomic update and therefore need to be told what order to do the operations in). + var afterInit = function() { initialized = true; if (cache) { @@ -36,20 +80,25 @@ function CachingStoreWrapper(underlyingStore, ttl) { cache.flushAll(); // populate cache with initial data - for (var kindNamespace in allData) { - if (Object.hasOwnProperty.call(allData, kindNamespace)) { - var kind = dataKind[kindNamespace]; - var items = allData[kindNamespace]; - cache.set(allCacheKey(kind), items); - for (var key in items) { - cache.set(cacheKey(kind, key), items[key]); - } - } - } + Object.keys(allData).forEach(function(kindNamespace) { + var kind = dataKind[kindNamespace]; + var items = allData[kindNamespace]; + cache.set(allCacheKey(kind), items); + Object.keys(items).forEach(function(key) { + cache.set(cacheKey(kind, key), items[key]); + }); + }); } cb(); - }); + }; + + if (underlyingStore.initOrderedInternal) { + var orderedData = sortAllCollections(allData); + underlyingStore.initOrderedInternal(orderedData, afterInit); + } else { + underlyingStore.initInternal(allData, afterInit); + } }, [], cb); }; @@ -141,6 +190,46 @@ function CachingStoreWrapper(underlyingStore, ttl) { cache.del(allCacheKey(dataKind[kindNamespace])); } } + + // This and the next function are used by init() to provide the best ordering of items + // to write the underlying store, if the store supports the initOrderedInternal method. + function sortAllCollections(dataMap) { + var result = []; + Object.keys(dataMap).forEach(function(kindNamespace) { + var kind = dataKind[kindNamespace]; + result.push({ kind: kind, items: sortCollection(kind, dataMap[kindNamespace]) }); + }); + var kindPriority = function(kind) { + return kind.priority === undefined ? kind.namespace.length : kind.priority + }; + result.sort(function(i1, i2) { + return kindPriority(i1.kind) - kindPriority(i2.kind); + }); + return result; + } + + function sortCollection(kind, itemsMap) { + var itemsOut = []; + var remainingItems = new Set(Object.keys(itemsMap)); + var addWithDependenciesFirst = function(key) { + if (remainingItems.has(key)) { + remainingItems.delete(key); + var item = itemsMap[key]; + if (kind.getDependencyKeys) { + kind.getDependencyKeys(item).forEach(function(prereqKey) { + addWithDependenciesFirst(prereqKey); + }); + } + itemsOut.push(item); + } + }; + while (remainingItems.size > 0) { + // pick a random item that hasn't been updated yet + var key = remainingItems.values().next().value; + addWithDependenciesFirst(key); + } + return itemsOut; + } } module.exports = CachingStoreWrapper; diff --git a/feature_store.js b/feature_store.js index ec21e22..083928e 100644 --- a/feature_store.js +++ b/feature_store.js @@ -1,8 +1,17 @@ var dataKind = require('./versioned_data_kind'); -// An in-memory store with an async interface. -// It's async as other implementations (e.g. the RedisFeatureStore) -// may be async, and we want to retain interface compatibility. +// The default in-memory implementation of a feature store, which holds feature flags and +// other related data received from LaunchDarkly. +// +// Other implementations of the same interface can be used by passing them in the featureStore +// property of the client configuration (that's why the interface here is async, even though +// the in-memory store doesn't do anything asynchronous - because other implementations may +// need to be async). The interface is defined by LDFeatureStore in index.d.ts. There is a +// Redis-backed implementation in RedisFeatureStore; for other options, see +// [https://docs.launchdarkly.com/v2.0/docs/using-a-persistent-feature-store]. +// +// Additional implementations should use CachingStoreWrapper if possible. + var noop = function(){}; function InMemoryFeatureStore() { var store = {allData:{}}; diff --git a/test/caching_store_wrapper-test.js b/test/caching_store_wrapper-test.js index b774552..0696011 100644 --- a/test/caching_store_wrapper-test.js +++ b/test/caching_store_wrapper-test.js @@ -1,5 +1,6 @@ var CachingStoreWrapper = require('../caching_store_wrapper'); var features = require('../versioned_data_kind').features; +var segments = require('../versioned_data_kind').segments; const { asyncify, sleepAsync } = require('./async_utils'); function MockCore() { @@ -58,23 +59,38 @@ function MockCore() { return c; } +function MockOrderedCore() { + const c = { + data: { features: {} }, + + initOrderedInternal: function(newData, cb) { + c.data = newData; + cb(); + }, + // don't bother mocking the rest of the stuff since the wrapper behaves identically except for init + }; + return c; +} + const cacheSeconds = 15; -function runCachedAndUncachedTests(name, testFn) { +function runCachedAndUncachedTests(name, testFn, coreFn) { + var makeCore = coreFn ? coreFn : MockCore; describe(name, function() { - const core1 = MockCore(); + const core1 = makeCore(); const wrapper1 = new CachingStoreWrapper(core1, cacheSeconds); - it('cached', async () => await testFn(wrapper1, core1, true)); + it('cached', async () => await testFn(wrapper1, core1, true), 1000); - const core2 = MockCore(); + const core2 = makeCore(); const wrapper2 = new CachingStoreWrapper(core2, 0); - it('uncached', async () => await testFn(wrapper2, core2, false)); + it('uncached', async () => await testFn(wrapper2, core2, false), 1000); }); } -function runCachedTestOnly(name, testFn) { +function runCachedTestOnly(name, testFn, coreFn) { + var makeCore = coreFn ? coreFn : MockCore; it(name, async () => { - const core = MockCore(); + const core = makeCore(); const wrapper = new CachingStoreWrapper(core, cacheSeconds); await testFn(wrapper, core); }); @@ -371,4 +387,48 @@ describe('CachingStoreWrapper', function() { expect(core.closed).toBe(true); }); }); + + describe('core that uses initOrdered()', function() { + runCachedAndUncachedTests('receives properly ordered data for init', async (wrapper, core) => { + var dependencyOrderingTestData = {}; + dependencyOrderingTestData[features.namespace] = { + a: { key: "a", prerequisites: [ { key: "b" }, { key: "c" } ] }, + b: { key: "b", prerequisites: [ { key: "c" }, { key: "e" } ] }, + c: { key: "c" }, + d: { key: "d" }, + e: { key: "e" }, + f: { key: "f" } + }; + dependencyOrderingTestData[segments.namespace] = { + o: { key: "o" } + }; + await asyncify(cb => wrapper.init(dependencyOrderingTestData, cb)); + + var receivedData = core.data; + expect(receivedData.length).toEqual(2); + + // Segments should always come first + expect(receivedData[0].kind).toEqual(segments); + expect(receivedData[0].items.length).toEqual(1); + + // Features should be ordered so that a flag always appears after its prerequisites, if any + expect(receivedData[1].kind).toEqual(features); + var featuresMap = dependencyOrderingTestData[features.namespace]; + var featuresList = receivedData[1].items; + expect(featuresList.length).toEqual(Object.keys(featuresMap).length); + for (var itemIndex in featuresList) { + var item = featuresList[itemIndex]; + (item.prerequisites || []).forEach(function(prereq) { + var prereqKey = prereq.key; + var prereqItem = featuresMap[prereqKey]; + var prereqIndex = featuresList.indexOf(prereqItem); + if (prereqIndex > itemIndex) { + var allKeys = featuresList.map(f => f.key); + throw new Error(item.key + " depends on " + prereqKey + ", but " + item.key + + " was listed first; keys in order are [" + allKeys.join(", ") + "]"); + } + }); + } + }, MockOrderedCore); + }); }); diff --git a/versioned_data_kind.js b/versioned_data_kind.js index 21834ca..d71b2c3 100644 --- a/versioned_data_kind.js +++ b/versioned_data_kind.js @@ -12,13 +12,21 @@ var features = { namespace: 'features', streamApiPath: '/flags/', - requestPath: '/sdk/latest-flags/' + requestPath: '/sdk/latest-flags/', + priority: 1, + getDependencyKeys: function(flag) { + if (!flag.prerequisites || !flag.prerequisites.length) { + return []; + } + return flag.prerequisites.map(function(p) { return p.key; }); + } }; var segments = { namespace: 'segments', streamApiPath: '/segments/', - requestPath: '/sdk/latest-segments/' + requestPath: '/sdk/latest-segments/', + priority: 0 }; module.exports = { From 0bb793dd139fed39ab2913f0a41922eed0852f8d Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 10 Jan 2019 20:37:21 -0800 Subject: [PATCH 081/330] readme fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab01d33..4b83453 100644 --- a/README.md +++ b/README.md @@ -88,9 +88,9 @@ About LaunchDarkly * [JavaScript](http://docs.launchdarkly.com/docs/js-sdk-reference "LaunchDarkly JavaScript SDK") * [PHP](http://docs.launchdarkly.com/docs/php-sdk-reference "LaunchDarkly PHP SDK") * [Python](http://docs.launchdarkly.com/docs/python-sdk-reference "LaunchDarkly Python SDK") - * [Python Twisted](http://docs.launchdarkly.com/docs/python-twisted-sdk-reference "LaunchDarkly Python Twisted SDK") * [Go](http://docs.launchdarkly.com/docs/go-sdk-reference "LaunchDarkly Go SDK") * [Node.JS](http://docs.launchdarkly.com/docs/node-sdk-reference "LaunchDarkly Node SDK") + * [Electron](http://docs.launchdarkly.com/docs/electron-sdk-reference "LaunchDarkly Electron SDK") * [.NET](http://docs.launchdarkly.com/docs/dotnet-sdk-reference "LaunchDarkly .Net SDK") * [Ruby](http://docs.launchdarkly.com/docs/ruby-sdk-reference "LaunchDarkly Ruby SDK") * [iOS](http://docs.launchdarkly.com/docs/ios-sdk-reference "LaunchDarkly iOS SDK") From b6ab85b4d10076ca51ad989270aa863630c063bc Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 16 Jan 2019 11:33:08 -0800 Subject: [PATCH 082/330] add feature store test for prefix option --- package-lock.json | 2 +- test/feature_store_test_base.js | 18 ++++++++++++++++-- test/redis_feature_store-test.js | 8 ++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3b99cfd..b69f93f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ldclient-node", - "version": "5.6.2", + "version": "5.7.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/test/feature_store_test_base.js b/test/feature_store_test_base.js index 911f1da..dd809f0 100644 --- a/test/feature_store_test_base.js +++ b/test/feature_store_test_base.js @@ -6,9 +6,10 @@ const { asyncify } = require('./async_utils'); // caching disabled. // // Parameters: -// - makeStore(): creates an instance of the feature store. +// - makeStore(options): creates an instance of the feature store; add options to the +// configuration, if provided // - clearExistingData(callback): if specified, will be called before each test to clear any -// storage that the store instances may be sharing. +// storage that the store instances may be sharing; this also implies that the feature store // - isCached: true if the instances returned by makeStore() have caching enabled. If // applicable, @@ -97,6 +98,19 @@ function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { testInitStateDetection('can detect if another instance has initialized the store, even with empty data', { features: {} }); + + it('is independent from other instances with different prefixes', async () => { + var flag = { key: 'flag', version: 1 }; + var storeA = makeStore({ prefix: 'a' }); + await asyncify(cb => storeA.init({ features: { flag: flag } }, cb)); + var storeB = makeStore({ prefix: 'b' }); + await asyncify(cb => storeB.init({ features: { } }, cb)); + var storeB1 = makeStore({ prefix: 'b' }); // this ensures we're not just reading cached data + var item = await asyncify(cb => storeB1.get(dataKind.features, 'flag', cb)); + expect(item).toBe(null); + item = await asyncify(cb => storeA.get(dataKind.features, 'flag', cb)); + expect(item).toEqual(flag); + }); } it('gets existing feature', async () => { diff --git a/test/redis_feature_store-test.js b/test/redis_feature_store-test.js index 1ba8837..ed0f4e3 100644 --- a/test/redis_feature_store-test.js +++ b/test/redis_feature_store-test.js @@ -8,12 +8,12 @@ describe('RedisFeatureStore', function() { var extraRedisClient = redis.createClient(redisOpts); - function makeCachedStore() { - return new RedisFeatureStore(redisOpts, 30); + function makeCachedStore(options) { + return new RedisFeatureStore(redisOpts, 30, options && options.prefix); } - function makeUncachedStore() { - return new RedisFeatureStore(redisOpts, 0); + function makeUncachedStore(options) { + return new RedisFeatureStore(redisOpts, 0, options && options.prefix); } function clearExistingData(callback) { From 9b206f95b7d083b362adc0e7d7a6bc3e7b0ced85 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 16 Jan 2019 11:40:14 -0800 Subject: [PATCH 083/330] revise test interface for backward compatibility --- test/feature_store_test_base.js | 35 ++++++++++++++++---------------- test/redis_feature_store-test.js | 14 ++++++++----- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/test/feature_store_test_base.js b/test/feature_store_test_base.js index dd809f0..1afe1dd 100644 --- a/test/feature_store_test_base.js +++ b/test/feature_store_test_base.js @@ -6,14 +6,13 @@ const { asyncify } = require('./async_utils'); // caching disabled. // // Parameters: -// - makeStore(options): creates an instance of the feature store; add options to the -// configuration, if provided +// - makeStore(): creates an instance of the feature store // - clearExistingData(callback): if specified, will be called before each test to clear any // storage that the store instances may be sharing; this also implies that the feature store -// - isCached: true if the instances returned by makeStore() have caching enabled. If -// applicable, +// - isCached: true if the instances returned by makeStore() have caching enabled. +// - makeStoreWithPrefix(prefix): creates an uncached instance of the store with a key prefix -function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { +function baseFeatureStoreTests(makeStore, clearExistingData, isCached, makeStoreWithPrefix) { var feature1 = { key: 'foo', version: 10 @@ -99,18 +98,20 @@ function baseFeatureStoreTests(makeStore, clearExistingData, isCached) { testInitStateDetection('can detect if another instance has initialized the store, even with empty data', { features: {} }); - it('is independent from other instances with different prefixes', async () => { - var flag = { key: 'flag', version: 1 }; - var storeA = makeStore({ prefix: 'a' }); - await asyncify(cb => storeA.init({ features: { flag: flag } }, cb)); - var storeB = makeStore({ prefix: 'b' }); - await asyncify(cb => storeB.init({ features: { } }, cb)); - var storeB1 = makeStore({ prefix: 'b' }); // this ensures we're not just reading cached data - var item = await asyncify(cb => storeB1.get(dataKind.features, 'flag', cb)); - expect(item).toBe(null); - item = await asyncify(cb => storeA.get(dataKind.features, 'flag', cb)); - expect(item).toEqual(flag); - }); + if (makeStoreWithPrefix) { + it('is independent from other instances with different prefixes', async () => { + var flag = { key: 'flag', version: 1 }; + var storeA = makeStoreWithPrefix('a'); + await asyncify(cb => storeA.init({ features: { flag: flag } }, cb)); + var storeB = makeStoreWithPrefix('b'); + await asyncify(cb => storeB.init({ features: { } }, cb)); + var storeB1 = makeStoreWithPrefix('b'); // this ensures we're not just reading cached data + var item = await asyncify(cb => storeB1.get(dataKind.features, 'flag', cb)); + expect(item).toBe(null); + item = await asyncify(cb => storeA.get(dataKind.features, 'flag', cb)); + expect(item).toEqual(flag); + }); + } } it('gets existing feature', async () => { diff --git a/test/redis_feature_store-test.js b/test/redis_feature_store-test.js index ed0f4e3..8c790a6 100644 --- a/test/redis_feature_store-test.js +++ b/test/redis_feature_store-test.js @@ -8,12 +8,16 @@ describe('RedisFeatureStore', function() { var extraRedisClient = redis.createClient(redisOpts); - function makeCachedStore(options) { - return new RedisFeatureStore(redisOpts, 30, options && options.prefix); + function makeCachedStore() { + return new RedisFeatureStore(redisOpts, 30); } - function makeUncachedStore(options) { - return new RedisFeatureStore(redisOpts, 0, options && options.prefix); + function makeUncachedStore() { + return new RedisFeatureStore(redisOpts, 0); + } + + function makeStoreWithPrefix(prefix) { + return new RedisFeatureStore(redisOpts, 0, prefix); } function clearExistingData(callback) { @@ -21,7 +25,7 @@ describe('RedisFeatureStore', function() { } testBase.baseFeatureStoreTests(makeCachedStore, clearExistingData, true); - testBase.baseFeatureStoreTests(makeUncachedStore, clearExistingData, false); + testBase.baseFeatureStoreTests(makeUncachedStore, clearExistingData, false, makeStoreWithPrefix); testBase.concurrentModificationTests(makeUncachedStore, function(hook) { From 8a4c846fccf874acddd1952386011a22049893ed Mon Sep 17 00:00:00 2001 From: Harpo roeder Date: Mon, 4 Feb 2019 16:28:03 -0800 Subject: [PATCH 084/330] add azure pipeline with redis, run tests --- azure-pipelines.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..7dfd3c8 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,26 @@ +jobs: + - job: build + pool: + vmImage: 'vs2017-win2016' + steps: + - task: PowerShell@2 + displayName: 'Setup Redis' + inputs: + targetType: inline + workingDirectory: $(System.DefaultWorkingDirectory) + script: | + iwr -outf redis.zip https://github.com/MicrosoftArchive/redis/releases/download/win-3.0.504/Redis-x64-3.0.504.zip + mkdir redis + Expand-Archive -Path redis.zip -DestinationPath redis + cd redis + ./redis-server --service-install + ./redis-server --service-start + - task: PowerShell@2 + displayName: 'Setup SDK and Test' + inputs: + targetType: inline + workingDirectory: $(System.DefaultWorkingDirectory) + script: | + node --version + npm install + npm test From c504e2d68f03c81faeb7b12fc2b9409d867cf2ee Mon Sep 17 00:00:00 2001 From: Harpo roeder Date: Mon, 4 Feb 2019 16:35:06 -0800 Subject: [PATCH 085/330] add redis ping --- azure-pipelines.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7dfd3c8..c82ef16 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,6 +15,8 @@ jobs: cd redis ./redis-server --service-install ./redis-server --service-start + Start-Sleep -s 5 + ./redis-cli ping - task: PowerShell@2 displayName: 'Setup SDK and Test' inputs: From 8edd79a867f5d3b2361ef7da5f39f1e1487c222f Mon Sep 17 00:00:00 2001 From: Harpo roeder Date: Mon, 4 Feb 2019 17:08:42 -0800 Subject: [PATCH 086/330] made indent consistent --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c82ef16..9b15ace 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -23,6 +23,6 @@ jobs: targetType: inline workingDirectory: $(System.DefaultWorkingDirectory) script: | - node --version - npm install - npm test + node --version + npm install + npm test From e8be2b0a3ab9bf3fa0e0cf85efbdac5c302d390c Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 6 Feb 2019 11:34:22 -0800 Subject: [PATCH 087/330] add basic TypeDoc build --- .gitignore | 3 +- docs/Makefile | 8 + docs/typedoc.js | 12 + package-lock.json | 2715 ++++++++++++++++++++++++--------------------- package.json | 1 + 5 files changed, 1465 insertions(+), 1274 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/typedoc.js diff --git a/.gitignore b/.gitignore index 4ba85cf..ebd42dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ junit.xml node_modules/ -npm-debug.log \ No newline at end of file +npm-debug.log +docs/build/ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..b8a2973 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,8 @@ + +TYPEDOC_OPTIONS=--name "LaunchDarkly Node SDK" --includeDeclarations --exclude "**/node_modules/**" --exclude test-types.ts + +.PHONY: html + +html: + rm -rf build/* + cd .. && ./node_modules/.bin/typedoc --options ./docs/typedoc.js diff --git a/docs/typedoc.js b/docs/typedoc.js new file mode 100644 index 0000000..f00ca93 --- /dev/null +++ b/docs/typedoc.js @@ -0,0 +1,12 @@ + +module.exports = { + out: './docs/build/html', + includes: '.', + exclude: [ + '**/node_modules/**', + 'test-types.ts' + ], + name: 'LaunchDarkly Node SDK', + theme: 'default', + includeDeclarations: true +}; diff --git a/package-lock.json b/package-lock.json index b69f93f..08fb5a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ldclient-node", - "version": "5.7.0", + "version": "5.7.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -19,9 +19,9 @@ "integrity": "sha512-96V6XHAh9XHzjmucShCP8tULwXsC446doZ6REaLVdZDPNj3NsWbsC7OBeY+u6UWiFxHTTv6YmA4Veh4wXuucYw==", "dev": true, "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" + "chalk": "2.4.1", + "esutils": "2.0.2", + "js-tokens": "4.0.0" }, "dependencies": { "chalk": { @@ -30,9 +30,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "js-tokens": { @@ -48,6 +48,56 @@ "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha1-gaZzHOTfQ2GeXIyUU4Oz5iqJ6oY=" }, + "@types/fs-extra": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.4.tgz", + "integrity": "sha512-DsknoBvD8s+RFfSGjmERJ7ZOP1HI0UZRA3FSI+Zakhrc/Gy26YQsLI+m5V5DHxroHRJqCDLKJp7Hixn8zyaF7g==", + "dev": true, + "requires": { + "@types/node": "10.5.2" + } + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "1.2.0", + "@types/minimatch": "3.0.3", + "@types/node": "10.5.2" + } + }, + "@types/handlebars": { + "version": "4.0.40", + "resolved": "https://registry.npmjs.org/@types/handlebars/-/handlebars-4.0.40.tgz", + "integrity": "sha512-sGWNtsjNrLOdKha2RV1UeF8+UbQnPSG7qbe5wwbni0mw4h2gHXyPFUMOC+xwGirIiiydM/HSqjDO4rk6NFB18w==", + "dev": true + }, + "@types/highlight.js": { + "version": "9.12.3", + "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.3.tgz", + "integrity": "sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ==", + "dev": true + }, + "@types/lodash": { + "version": "4.14.120", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.120.tgz", + "integrity": "sha512-jQ21kQ120mo+IrDs1nFNVm/AsdFxIx2+vZ347DbogHJPd/JzKNMOqU6HCYin1W6v8l5R9XSO2/e9cxmn7HAnVw==", + "dev": true + }, + "@types/marked": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.4.2.tgz", + "integrity": "sha512-cDB930/7MbzaGF6U3IwSQp6XBru8xWajF5PV2YZZeV8DyiliTuld11afVztGI9+yJZ29il5E+NpGA6ooV/Cjkg==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, "@types/node": { "version": "10.5.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.2.tgz", @@ -58,8 +108,18 @@ "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.6.tgz", "integrity": "sha1-NnTQehOtdrzNpMN9w5CeTpV1fn4=", "requires": { - "@types/events": "*", - "@types/node": "*" + "@types/events": "1.2.0", + "@types/node": "10.5.2" + } + }, + "@types/shelljs": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.2.tgz", + "integrity": "sha512-vVp7BCQn0yUQgpiohrdxAhHdm/bTlXshB4HG3LEBq1PgvjKiyeYHohIPIv0QBt/jipb140iMS5Xy1iR6qKovKw==", + "dev": true, + "requires": { + "@types/glob": "7.1.1", + "@types/node": "10.5.2" } }, "abab": { @@ -80,7 +140,7 @@ "integrity": "sha1-q3FgJdvhfFTT74HTLs4rLZn+JTg=", "dev": true, "requires": { - "acorn": "^5.0.0" + "acorn": "5.7.1" } }, "ajv": { @@ -88,10 +148,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, "align-text": { @@ -100,9 +160,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, "amdefine": { @@ -123,7 +183,7 @@ "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.2" } }, "anymatch": { @@ -132,8 +192,8 @@ "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", "dev": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" }, "dependencies": { "arr-diff": { @@ -154,16 +214,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.3", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -172,7 +232,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -183,13 +243,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -198,7 +258,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -207,7 +267,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -216,7 +276,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -225,7 +285,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -236,7 +296,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -245,7 +305,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -256,9 +316,9 @@ "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, "kind-of": { @@ -275,14 +335,14 @@ "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -291,7 +351,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -300,7 +360,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -311,10 +371,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -323,7 +383,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -334,7 +394,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -343,7 +403,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -352,9 +412,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "is-number": { @@ -363,7 +423,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -372,7 +432,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -395,19 +455,19 @@ "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } } } @@ -418,7 +478,7 @@ "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", "dev": true, "requires": { - "default-require-extensions": "^2.0.0" + "default-require-extensions": "2.0.0" } }, "argparse": { @@ -427,7 +487,7 @@ "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "arr-diff": { @@ -436,7 +496,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "arr-flatten": { @@ -474,7 +534,7 @@ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "requires": { - "safer-buffer": "~2.1.0" + "safer-buffer": "2.1.2" } }, "assert-plus": { @@ -505,7 +565,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", "integrity": "sha1-YaKau2/MAm/qd+VtHG7FOnlZUfQ=", "requires": { - "lodash": "^4.14.0" + "lodash": "4.17.10" } }, "async-limiter": { @@ -541,9 +601,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" } }, "babel-core": { @@ -552,25 +612,25 @@ "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.0", - "debug": "^2.6.8", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.7", - "slash": "^1.0.0", - "source-map": "^0.5.6" + "babel-code-frame": "6.26.0", + "babel-generator": "6.26.1", + "babel-helpers": "6.24.1", + "babel-messages": "6.23.0", + "babel-register": "6.26.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "convert-source-map": "1.5.1", + "debug": "2.6.9", + "json5": "0.5.1", + "lodash": "4.17.10", + "minimatch": "3.0.4", + "path-is-absolute": "1.0.1", + "private": "0.1.8", + "slash": "1.0.0", + "source-map": "0.5.7" } }, "babel-generator": { @@ -579,14 +639,14 @@ "integrity": "sha1-GERAjTuPDTWkBOp6wYDwh6YBvZA=", "dev": true, "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.10", + "source-map": "0.5.7", + "trim-right": "1.0.1" } }, "babel-helper-builder-binary-assignment-operator-visitor": { @@ -595,9 +655,9 @@ "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", "dev": true, "requires": { - "babel-helper-explode-assignable-expression": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-explode-assignable-expression": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-call-delegate": { @@ -606,10 +666,10 @@ "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", "dev": true, "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-define-map": { @@ -618,10 +678,10 @@ "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", "dev": true, "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.10" } }, "babel-helper-explode-assignable-expression": { @@ -630,9 +690,9 @@ "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-function-name": { @@ -641,11 +701,11 @@ "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", "dev": true, "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-get-function-arity": { @@ -654,8 +714,8 @@ "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-hoist-variables": { @@ -664,8 +724,8 @@ "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-optimise-call-expression": { @@ -674,8 +734,8 @@ "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-regex": { @@ -684,9 +744,9 @@ "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.10" } }, "babel-helper-remap-async-to-generator": { @@ -695,11 +755,11 @@ "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", "dev": true, "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helper-replace-supers": { @@ -708,12 +768,12 @@ "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", "dev": true, "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-optimise-call-expression": "6.24.1", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-helpers": { @@ -722,8 +782,8 @@ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-jest": { @@ -732,8 +792,8 @@ "integrity": "sha512-BgSjmtl3mW3i+VeVHEr9d2zFSAT66G++pJcHQiUjd00pkW+voYXFctIm/indcqOWWXw5a1nUpR1XWszD9fJ1qg==", "dev": true, "requires": { - "babel-plugin-istanbul": "^4.1.5", - "babel-preset-jest": "^22.4.3" + "babel-plugin-istanbul": "4.1.6", + "babel-preset-jest": "22.4.4" } }, "babel-messages": { @@ -742,7 +802,7 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-check-es2015-constants": { @@ -751,7 +811,7 @@ "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-istanbul": { @@ -760,10 +820,10 @@ "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==", "dev": true, "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.13.0", - "find-up": "^2.1.0", - "istanbul-lib-instrument": "^1.10.1", - "test-exclude": "^4.2.1" + "babel-plugin-syntax-object-rest-spread": "6.13.0", + "find-up": "2.1.0", + "istanbul-lib-instrument": "1.10.1", + "test-exclude": "4.2.1" } }, "babel-plugin-jest-hoist": { @@ -802,9 +862,9 @@ "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", "dev": true, "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-helper-remap-async-to-generator": "6.24.1", + "babel-plugin-syntax-async-functions": "6.13.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-arrow-functions": { @@ -813,7 +873,7 @@ "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-block-scoped-functions": { @@ -822,7 +882,7 @@ "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-block-scoping": { @@ -831,11 +891,11 @@ "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.10" } }, "babel-plugin-transform-es2015-classes": { @@ -844,15 +904,15 @@ "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", "dev": true, "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-define-map": "6.26.0", + "babel-helper-function-name": "6.24.1", + "babel-helper-optimise-call-expression": "6.24.1", + "babel-helper-replace-supers": "6.24.1", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-computed-properties": { @@ -861,8 +921,8 @@ "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-plugin-transform-es2015-destructuring": { @@ -871,7 +931,7 @@ "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-duplicate-keys": { @@ -880,8 +940,8 @@ "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-for-of": { @@ -890,7 +950,7 @@ "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-function-name": { @@ -899,9 +959,9 @@ "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", "dev": true, "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-literals": { @@ -910,7 +970,7 @@ "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-modules-amd": { @@ -919,9 +979,9 @@ "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-plugin-transform-es2015-modules-commonjs": { @@ -930,10 +990,10 @@ "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", "dev": true, "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" + "babel-plugin-transform-strict-mode": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-modules-systemjs": { @@ -942,9 +1002,9 @@ "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", "dev": true, "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-plugin-transform-es2015-modules-umd": { @@ -953,9 +1013,9 @@ "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" } }, "babel-plugin-transform-es2015-object-super": { @@ -964,8 +1024,8 @@ "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", "dev": true, "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" + "babel-helper-replace-supers": "6.24.1", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-parameters": { @@ -974,12 +1034,12 @@ "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", "dev": true, "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-call-delegate": "6.24.1", + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-shorthand-properties": { @@ -988,8 +1048,8 @@ "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-spread": { @@ -998,7 +1058,7 @@ "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-sticky-regex": { @@ -1007,9 +1067,9 @@ "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", "dev": true, "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-regex": "6.26.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-plugin-transform-es2015-template-literals": { @@ -1018,7 +1078,7 @@ "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-typeof-symbol": { @@ -1027,7 +1087,7 @@ "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-es2015-unicode-regex": { @@ -1036,9 +1096,9 @@ "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", "dev": true, "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" + "babel-helper-regex": "6.26.0", + "babel-runtime": "6.26.0", + "regexpu-core": "2.0.0" } }, "babel-plugin-transform-exponentiation-operator": { @@ -1047,9 +1107,9 @@ "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", "dev": true, "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", + "babel-plugin-syntax-exponentiation-operator": "6.13.0", + "babel-runtime": "6.26.0" } }, "babel-plugin-transform-regenerator": { @@ -1058,7 +1118,7 @@ "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", "dev": true, "requires": { - "regenerator-transform": "^0.10.0" + "regenerator-transform": "0.10.1" } }, "babel-plugin-transform-strict-mode": { @@ -1067,8 +1127,8 @@ "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" } }, "babel-preset-env": { @@ -1077,36 +1137,36 @@ "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", "dev": true, "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-to-generator": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.23.0", - "babel-plugin-transform-es2015-classes": "^6.23.0", - "babel-plugin-transform-es2015-computed-properties": "^6.22.0", - "babel-plugin-transform-es2015-destructuring": "^6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", - "babel-plugin-transform-es2015-for-of": "^6.23.0", - "babel-plugin-transform-es2015-function-name": "^6.22.0", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.22.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-umd": "^6.23.0", - "babel-plugin-transform-es2015-object-super": "^6.22.0", - "babel-plugin-transform-es2015-parameters": "^6.23.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", - "babel-plugin-transform-exponentiation-operator": "^6.22.0", - "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^2.1.2", - "invariant": "^2.2.2", - "semver": "^5.3.0" + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-syntax-trailing-function-commas": "6.22.0", + "babel-plugin-transform-async-to-generator": "6.24.1", + "babel-plugin-transform-es2015-arrow-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoping": "6.26.0", + "babel-plugin-transform-es2015-classes": "6.24.1", + "babel-plugin-transform-es2015-computed-properties": "6.24.1", + "babel-plugin-transform-es2015-destructuring": "6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-plugin-transform-es2015-function-name": "6.24.1", + "babel-plugin-transform-es2015-literals": "6.22.0", + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", + "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", + "babel-plugin-transform-es2015-modules-umd": "6.24.1", + "babel-plugin-transform-es2015-object-super": "6.24.1", + "babel-plugin-transform-es2015-parameters": "6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", + "babel-plugin-transform-es2015-spread": "6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "6.24.1", + "babel-plugin-transform-es2015-template-literals": "6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "6.24.1", + "babel-plugin-transform-exponentiation-operator": "6.24.1", + "babel-plugin-transform-regenerator": "6.26.0", + "browserslist": "2.11.3", + "invariant": "2.2.4", + "semver": "5.5.0" } }, "babel-preset-jest": { @@ -1115,8 +1175,8 @@ "integrity": "sha512-+dxMtOFwnSYWfum0NaEc0O03oSdwBsjx4tMSChRDPGwu/4wSY6Q6ANW3wkjKpJzzguaovRs/DODcT4hbSN8yiA==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "^22.4.4", - "babel-plugin-syntax-object-rest-spread": "^6.13.0" + "babel-plugin-jest-hoist": "22.4.4", + "babel-plugin-syntax-object-rest-spread": "6.13.0" } }, "babel-register": { @@ -1125,13 +1185,13 @@ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" + "babel-core": "6.26.0", + "babel-runtime": "6.26.0", + "core-js": "2.5.7", + "home-or-tmp": "2.0.0", + "lodash": "4.17.10", + "mkdirp": "0.5.1", + "source-map-support": "0.4.18" }, "dependencies": { "source-map-support": { @@ -1140,7 +1200,7 @@ "integrity": "sha1-Aoam3ovkJkEzhZTpfM6nXwosWF8=", "dev": true, "requires": { - "source-map": "^0.5.6" + "source-map": "0.5.7" } } } @@ -1151,8 +1211,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.5.7", + "regenerator-runtime": "0.11.1" } }, "babel-template": { @@ -1161,11 +1221,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.10" } }, "babel-traverse": { @@ -1174,15 +1234,15 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.4", + "lodash": "4.17.10" } }, "babel-types": { @@ -1191,10 +1251,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "1.0.3" } }, "babylon": { @@ -1215,13 +1275,13 @@ "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", "dev": true, "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" }, "dependencies": { "define-property": { @@ -1230,7 +1290,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -1239,7 +1299,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -1248,7 +1308,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -1257,9 +1317,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "isobject": { @@ -1282,7 +1342,7 @@ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "brace-expansion": { @@ -1291,7 +1351,7 @@ "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -1301,9 +1361,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.3" } }, "browser-process-hrtime": { @@ -1327,8 +1387,8 @@ "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000792", - "electron-to-chromium": "^1.3.30" + "caniuse-lite": "1.0.30000927", + "electron-to-chromium": "1.3.100" } }, "bser": { @@ -1337,7 +1397,7 @@ "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", "dev": true, "requires": { - "node-int64": "^0.4.0" + "node-int64": "0.4.0" } }, "buffer-from": { @@ -1358,15 +1418,15 @@ "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", "dev": true, "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" }, "dependencies": { "isobject": { @@ -1402,7 +1462,7 @@ "integrity": "sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28=", "dev": true, "requires": { - "rsvp": "^3.3.3" + "rsvp": "3.6.2" } }, "caseless": { @@ -1417,8 +1477,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chai": { @@ -1427,12 +1487,12 @@ "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", "dev": true, "requires": { - "assertion-error": "^1.0.1", - "check-error": "^1.0.1", - "deep-eql": "^3.0.0", - "get-func-name": "^2.0.0", - "pathval": "^1.0.0", - "type-detect": "^4.0.0" + "assertion-error": "1.1.0", + "check-error": "1.0.2", + "deep-eql": "3.0.1", + "get-func-name": "2.0.0", + "pathval": "1.1.0", + "type-detect": "4.0.8" } }, "chalk": { @@ -1441,11 +1501,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" }, "dependencies": { "ansi-styles": { @@ -1460,7 +1520,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "supports-color": { @@ -1489,10 +1549,10 @@ "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", "dev": true, "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" }, "dependencies": { "define-property": { @@ -1501,7 +1561,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "isobject": { @@ -1519,8 +1579,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -1555,8 +1615,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, "color-convert": { @@ -1584,7 +1644,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "compare-versions": { @@ -1634,9 +1694,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.2", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "cssom": { @@ -1651,7 +1711,7 @@ "integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==", "dev": true, "requires": { - "cssom": "0.3.x" + "cssom": "0.3.4" } }, "cycle": { @@ -1664,7 +1724,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "data-urls": { @@ -1673,9 +1733,9 @@ "integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==", "dev": true, "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^7.0.0" + "abab": "2.0.0", + "whatwg-mimetype": "2.1.0", + "whatwg-url": "7.0.0" }, "dependencies": { "whatwg-url": { @@ -1684,9 +1744,9 @@ "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", "dev": true, "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" + "lodash.sortby": "4.7.0", + "tr46": "1.0.1", + "webidl-conversions": "4.0.2" } } } @@ -1718,7 +1778,7 @@ "integrity": "sha1-38lARACtHI/gI+faHfHBR8S0RN8=", "dev": true, "requires": { - "type-detect": "^4.0.0" + "type-detect": "4.0.8" } }, "deep-equal": { @@ -1739,7 +1799,7 @@ "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", "dev": true, "requires": { - "strip-bom": "^3.0.0" + "strip-bom": "3.0.0" } }, "define-properties": { @@ -1748,7 +1808,7 @@ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "object-keys": "1.0.12" } }, "define-property": { @@ -1757,8 +1817,8 @@ "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", "dev": true, "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -1767,7 +1827,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -1776,7 +1836,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -1785,9 +1845,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "isobject": { @@ -1815,7 +1875,7 @@ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "detect-newline": { @@ -1836,7 +1896,7 @@ "integrity": "sha1-k3RCZEymoxJh7zbj7Gd/6AVYLJA=", "dev": true, "requires": { - "webidl-conversions": "^4.0.2" + "webidl-conversions": "4.0.2" } }, "double-ended-queue": { @@ -1850,8 +1910,8 @@ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "optional": true, "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" } }, "electron-to-chromium": { @@ -1866,7 +1926,7 @@ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "es-abstract": { @@ -1875,11 +1935,11 @@ "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.3", + "is-callable": "1.1.4", + "is-regex": "1.0.4" } }, "es-to-primitive": { @@ -1888,9 +1948,9 @@ "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", "dev": true, "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" + "is-callable": "1.1.4", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" } }, "escape-string-regexp": { @@ -1905,11 +1965,11 @@ "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "dev": true, "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "esprima": "3.1.3", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.6.1" }, "dependencies": { "esprima": { @@ -1951,7 +2011,7 @@ "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==", "dev": true, "requires": { - "merge": "^1.2.0" + "merge": "1.2.1" } }, "execa": { @@ -1960,13 +2020,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "expand-brackets": { @@ -1975,7 +2035,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "expand-range": { @@ -1984,7 +2044,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "^2.1.0" + "fill-range": "2.2.4" } }, "expect": { @@ -1993,12 +2053,12 @@ "integrity": "sha1-1aKdCg4fshU1V8rvJnTUVH6RRnQ=", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "jest-diff": "^22.4.3", - "jest-get-type": "^22.4.3", - "jest-matcher-utils": "^22.4.3", - "jest-message-util": "^22.4.3", - "jest-regex-util": "^22.4.3" + "ansi-styles": "3.2.1", + "jest-diff": "22.4.3", + "jest-get-type": "22.4.3", + "jest-matcher-utils": "22.4.3", + "jest-message-util": "22.4.3", + "jest-regex-util": "22.4.3" } }, "extend": { @@ -2012,8 +2072,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -2022,7 +2082,7 @@ "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -2033,7 +2093,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "extsprintf": { @@ -2068,7 +2128,7 @@ "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", "dev": true, "requires": { - "bser": "^2.0.0" + "bser": "2.0.0" } }, "filename-regex": { @@ -2083,8 +2143,8 @@ "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", "dev": true, "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" + "glob": "7.1.2", + "minimatch": "3.0.4" } }, "fill-range": { @@ -2093,11 +2153,11 @@ "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "3.1.0", + "repeat-element": "1.1.3", + "repeat-string": "1.6.1" } }, "find-up": { @@ -2106,7 +2166,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "for-in": { @@ -2121,7 +2181,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "forever-agent": { @@ -2134,9 +2194,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { - "asynckit": "^0.4.0", + "asynckit": "0.4.0", "combined-stream": "1.0.6", - "mime-types": "^2.1.12" + "mime-types": "2.1.17" }, "dependencies": { "combined-stream": { @@ -2144,7 +2204,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } } } @@ -2155,7 +2215,18 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "^0.2.2" + "map-cache": "0.2.2" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.2" } }, "fs.realpath": { @@ -2171,8 +2242,8 @@ "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "2.10.0", + "node-pre-gyp": "0.10.0" }, "dependencies": { "abbrev": { @@ -2198,8 +2269,8 @@ "dev": true, "optional": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" } }, "balanced-match": { @@ -2212,7 +2283,7 @@ "bundled": true, "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -2276,7 +2347,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.2.4" } }, "fs.realpath": { @@ -2291,14 +2362,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" } }, "glob": { @@ -2307,12 +2378,12 @@ "dev": true, "optional": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "has-unicode": { @@ -2327,7 +2398,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "^2.1.0" + "safer-buffer": "2.1.2" } }, "ignore-walk": { @@ -2336,7 +2407,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "inflight": { @@ -2345,8 +2416,8 @@ "dev": true, "optional": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -2365,7 +2436,7 @@ "bundled": true, "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "isarray": { @@ -2379,7 +2450,7 @@ "bundled": true, "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -2392,8 +2463,8 @@ "bundled": true, "dev": true, "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" + "safe-buffer": "5.1.1", + "yallist": "3.0.2" } }, "minizlib": { @@ -2402,7 +2473,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.2.4" } }, "mkdirp": { @@ -2425,9 +2496,9 @@ "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" } }, "node-pre-gyp": { @@ -2436,16 +2507,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.0", + "nopt": "4.0.1", + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" } }, "nopt": { @@ -2454,8 +2525,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.1", + "osenv": "0.1.5" } }, "npm-bundled": { @@ -2470,8 +2541,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" } }, "npmlog": { @@ -2480,10 +2551,10 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "number-is-nan": { @@ -2502,7 +2573,7 @@ "bundled": true, "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "os-homedir": { @@ -2523,8 +2594,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "path-is-absolute": { @@ -2545,10 +2616,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -2565,13 +2636,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "rimraf": { @@ -2580,7 +2651,7 @@ "dev": true, "optional": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "safe-buffer": { @@ -2623,9 +2694,9 @@ "bundled": true, "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -2634,7 +2705,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "strip-ansi": { @@ -2642,7 +2713,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-json-comments": { @@ -2657,13 +2728,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" } }, "util-deprecate": { @@ -2678,7 +2749,7 @@ "dev": true, "optional": true, "requires": { - "string-width": "^1.0.2" + "string-width": "1.0.2" } }, "wrappy": { @@ -2728,7 +2799,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "glob": { @@ -2737,12 +2808,12 @@ "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-base": { @@ -2751,8 +2822,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "glob-parent": "2.0.0", + "is-glob": "2.0.1" } }, "glob-parent": { @@ -2761,7 +2832,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "globals": { @@ -2788,10 +2859,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" }, "dependencies": { "async": { @@ -2806,7 +2877,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -2821,8 +2892,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" + "ajv": "5.5.2", + "har-schema": "2.0.0" } }, "has": { @@ -2831,7 +2902,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "^1.1.1" + "function-bind": "1.1.1" } }, "has-ansi": { @@ -2840,7 +2911,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-flag": { @@ -2855,9 +2926,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -2874,8 +2945,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "is-number": { @@ -2884,7 +2955,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -2893,7 +2964,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -2904,11 +2975,17 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } }, + "highlight.js": { + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.14.2.tgz", + "integrity": "sha512-Nc6YNECYpxyJABGYJAyw7dBAYbXEuIzwzkqoJnwbc1nIpCiN+3ioYf0XrBnLiyyG0JLuJhpPtt2iTSbXiKLoyA==", + "dev": true + }, "hoek": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", @@ -2920,8 +2997,8 @@ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "dev": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "hosted-git-info": { @@ -2936,7 +3013,7 @@ "integrity": "sha1-5w2EuU2lOqN14R/jo1G+ZkLKRvg=", "dev": true, "requires": { - "whatwg-encoding": "^1.0.1" + "whatwg-encoding": "1.0.4" } }, "http-signature": { @@ -2944,9 +3021,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.2" } }, "iconv-lite": { @@ -2955,7 +3032,7 @@ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } }, "import-local": { @@ -2964,8 +3041,8 @@ "integrity": "sha1-Xk/9wD9P5sAJxnKb6yljHC+CJ7w=", "dev": true, "requires": { - "pkg-dir": "^2.0.0", - "resolve-cwd": "^2.0.0" + "pkg-dir": "2.0.0", + "resolve-cwd": "2.0.0" } }, "imurmurhash": { @@ -2980,8 +3057,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -2990,13 +3067,19 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=", "dev": true, "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.4.0" } }, "invert-kv": { @@ -3011,7 +3094,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-arrayish": { @@ -3032,7 +3115,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-callable": { @@ -3047,7 +3130,7 @@ "integrity": "sha512-plgvKjQtalH2P3Gytb7L61Lmz95g2DlpzFiQyRSFew8WoJKxtKRzrZMeyRN2supblm3Psc8OQGy7Xjb6XG11jw==", "dev": true, "requires": { - "ci-info": "^1.3.0" + "ci-info": "1.4.0" } }, "is-data-descriptor": { @@ -3056,7 +3139,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-date-object": { @@ -3071,9 +3154,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -3096,7 +3179,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "^2.0.0" + "is-primitive": "2.0.0" } }, "is-extendable": { @@ -3117,7 +3200,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -3138,7 +3221,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "is-number": { @@ -3147,7 +3230,7 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-plain-object": { @@ -3156,7 +3239,7 @@ "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -3185,7 +3268,7 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "^1.0.1" + "has": "1.0.3" } }, "is-stream": { @@ -3255,7 +3338,7 @@ "integrity": "sha512-eLAMkPG9FU0v5L02lIkcj/2/Zlz9OuluaXikdr5iStk8FDbSwAixTK9TkYxbF0eNnzAJTwM2fkV2A1tpsIp4Jg==", "dev": true, "requires": { - "append-transform": "^1.0.0" + "append-transform": "1.0.0" } }, "istanbul-lib-instrument": { @@ -3264,13 +3347,13 @@ "integrity": "sha1-cktLbKzrqGktPx+dByfiecQBr3s=", "dev": true, "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.0", - "semver": "^5.3.0" + "babel-generator": "6.26.1", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.2.0", + "semver": "5.5.0" } }, "istanbul-lib-report": { @@ -3279,10 +3362,10 @@ "integrity": "sha1-6IbN9QXE672OCZ5DlqkNCijirLU=", "dev": true, "requires": { - "istanbul-lib-coverage": "^1.2.0", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "path-parse": "1.0.6", + "supports-color": "3.2.3" }, "dependencies": { "has-flag": { @@ -3297,7 +3380,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -3308,7 +3391,7 @@ "integrity": "sha1-LzIugeHZUgdnWX3KPCCgzOiaNVQ=", "dev": true, "requires": { - "handlebars": "^4.0.3" + "handlebars": "4.0.11" } }, "jest": { @@ -3317,8 +3400,8 @@ "integrity": "sha1-ImH0sRfcRtmkoaZz0hUJWN7pLxY=", "dev": true, "requires": { - "import-local": "^1.0.0", - "jest-cli": "^22.4.3" + "import-local": "1.0.0", + "jest-cli": "22.4.4" }, "dependencies": { "cliui": { @@ -3327,9 +3410,9 @@ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" } }, "debug": { @@ -3347,40 +3430,40 @@ "integrity": "sha512-I9dsgkeyjVEEZj9wrGrqlH+8OlNob9Iptyl+6L5+ToOLJmHm4JwOPatin1b2Bzp5R5YRQJ+oiedx7o1H7wJzhA==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "import-local": "^1.0.0", - "is-ci": "^1.0.10", - "istanbul-api": "^1.1.14", - "istanbul-lib-coverage": "^1.1.1", - "istanbul-lib-instrument": "^1.8.0", - "istanbul-lib-source-maps": "^1.2.1", - "jest-changed-files": "^22.2.0", - "jest-config": "^22.4.4", - "jest-environment-jsdom": "^22.4.1", - "jest-get-type": "^22.1.0", - "jest-haste-map": "^22.4.2", - "jest-message-util": "^22.4.0", - "jest-regex-util": "^22.1.0", - "jest-resolve-dependencies": "^22.1.0", - "jest-runner": "^22.4.4", - "jest-runtime": "^22.4.4", - "jest-snapshot": "^22.4.0", - "jest-util": "^22.4.1", - "jest-validate": "^22.4.4", - "jest-worker": "^22.2.2", - "micromatch": "^2.3.11", - "node-notifier": "^5.2.1", - "realpath-native": "^1.0.0", - "rimraf": "^2.5.4", - "slash": "^1.0.0", - "string-length": "^2.0.0", - "strip-ansi": "^4.0.0", - "which": "^1.2.12", - "yargs": "^10.0.3" + "ansi-escapes": "3.1.0", + "chalk": "2.4.1", + "exit": "0.1.2", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "import-local": "1.0.0", + "is-ci": "1.2.0", + "istanbul-api": "1.3.1", + "istanbul-lib-coverage": "1.2.0", + "istanbul-lib-instrument": "1.10.1", + "istanbul-lib-source-maps": "1.2.3", + "jest-changed-files": "22.4.3", + "jest-config": "22.4.4", + "jest-environment-jsdom": "22.4.3", + "jest-get-type": "22.4.3", + "jest-haste-map": "22.4.3", + "jest-message-util": "22.4.3", + "jest-regex-util": "22.4.3", + "jest-resolve-dependencies": "22.4.3", + "jest-runner": "22.4.4", + "jest-runtime": "22.4.4", + "jest-snapshot": "22.4.3", + "jest-util": "22.4.3", + "jest-validate": "22.4.4", + "jest-worker": "22.4.3", + "micromatch": "2.3.11", + "node-notifier": "5.2.1", + "realpath-native": "1.0.1", + "rimraf": "2.6.2", + "slash": "1.0.0", + "string-length": "2.0.0", + "strip-ansi": "4.0.0", + "which": "1.3.1", + "yargs": "10.1.2" }, "dependencies": { "ansi-escapes": { @@ -3395,9 +3478,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "exit": { @@ -3412,12 +3495,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "graceful-fs": { @@ -3432,7 +3515,7 @@ "integrity": "sha512-plgvKjQtalH2P3Gytb7L61Lmz95g2DlpzFiQyRSFew8WoJKxtKRzrZMeyRN2supblm3Psc8OQGy7Xjb6XG11jw==", "dev": true, "requires": { - "ci-info": "^1.3.0" + "ci-info": "1.4.0" } }, "istanbul-api": { @@ -3441,18 +3524,18 @@ "integrity": "sha512-duj6AlLcsWNwUpfyfHt0nWIeRiZpuShnP40YTxOGQgtaN8fd6JYSxsvxUphTDy8V5MfDXo4s/xVCIIvVCO808g==", "dev": true, "requires": { - "async": "^2.1.4", - "compare-versions": "^3.1.0", - "fileset": "^2.0.2", - "istanbul-lib-coverage": "^1.2.0", - "istanbul-lib-hook": "^1.2.0", - "istanbul-lib-instrument": "^1.10.1", - "istanbul-lib-report": "^1.1.4", - "istanbul-lib-source-maps": "^1.2.4", - "istanbul-reports": "^1.3.0", - "js-yaml": "^3.7.0", - "mkdirp": "^0.5.1", - "once": "^1.4.0" + "async": "2.6.0", + "compare-versions": "3.3.1", + "fileset": "2.0.3", + "istanbul-lib-coverage": "1.2.0", + "istanbul-lib-hook": "1.2.1", + "istanbul-lib-instrument": "1.10.1", + "istanbul-lib-report": "1.1.4", + "istanbul-lib-source-maps": "1.2.5", + "istanbul-reports": "1.3.0", + "js-yaml": "3.12.0", + "mkdirp": "0.5.1", + "once": "1.4.0" }, "dependencies": { "istanbul-lib-source-maps": { @@ -3461,11 +3544,11 @@ "integrity": "sha512-8O2T/3VhrQHn0XcJbP1/GN7kXMiRAlPi+fj3uEHrjBD8Oz7Py0prSC25C09NuAZS6bgW1NNKAvCSHZXB0irSGA==", "dev": true, "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.2.0", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" + "debug": "3.1.0", + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.7" } } } @@ -3482,13 +3565,13 @@ "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", "dev": true, "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.0", - "semver": "^5.3.0" + "babel-generator": "6.26.1", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.2.0", + "semver": "5.5.0" } }, "istanbul-lib-source-maps": { @@ -3497,11 +3580,11 @@ "integrity": "sha512-fDa0hwU/5sDXwAklXgAoCJCOsFsBplVQ6WBldz5UwaqOzmDhUK4nfuR7/G//G2lERlblUNJB8P6e8cXq3a7MlA==", "dev": true, "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.1.2", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" + "debug": "3.1.0", + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.7" } }, "jest-changed-files": { @@ -3510,7 +3593,7 @@ "integrity": "sha512-83Dh0w1aSkUNFhy5d2dvqWxi/y6weDwVVLU6vmK0cV9VpRxPzhTeGimbsbRDSnEoszhF937M4sDLLeS7Cu/Tmw==", "dev": true, "requires": { - "throat": "^4.0.0" + "throat": "4.1.0" } }, "jest-config": { @@ -3519,17 +3602,17 @@ "integrity": "sha512-9CKfo1GC4zrXSoMLcNeDvQBfgtqGTB1uP8iDIZ97oB26RCUb886KkKWhVcpyxVDOUxbhN+uzcBCeFe7w+Iem4A==", "dev": true, "requires": { - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^22.4.1", - "jest-environment-node": "^22.4.1", - "jest-get-type": "^22.1.0", - "jest-jasmine2": "^22.4.4", - "jest-regex-util": "^22.1.0", - "jest-resolve": "^22.4.2", - "jest-util": "^22.4.1", - "jest-validate": "^22.4.4", - "pretty-format": "^22.4.0" + "chalk": "2.4.1", + "glob": "7.1.2", + "jest-environment-jsdom": "22.4.3", + "jest-environment-node": "22.4.3", + "jest-get-type": "22.4.3", + "jest-jasmine2": "22.4.4", + "jest-regex-util": "22.4.3", + "jest-resolve": "22.4.3", + "jest-util": "22.4.3", + "jest-validate": "22.4.4", + "pretty-format": "22.4.3" } }, "jest-environment-jsdom": { @@ -3538,9 +3621,9 @@ "integrity": "sha512-FviwfR+VyT3Datf13+ULjIMO5CSeajlayhhYQwpzgunswoaLIPutdbrnfUHEMyJCwvqQFaVtTmn9+Y8WCt6n1w==", "dev": true, "requires": { - "jest-mock": "^22.4.3", - "jest-util": "^22.4.3", - "jsdom": "^11.5.1" + "jest-mock": "22.4.3", + "jest-util": "22.4.3", + "jsdom": "11.12.0" } }, "jest-get-type": { @@ -3555,13 +3638,13 @@ "integrity": "sha512-4Q9fjzuPVwnaqGKDpIsCSoTSnG3cteyk2oNVjBX12HHOaF1oxql+uUiqZb5Ndu7g/vTZfdNwwy4WwYogLh29DQ==", "dev": true, "requires": { - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.1.11", - "jest-docblock": "^22.4.3", - "jest-serializer": "^22.4.3", - "jest-worker": "^22.4.3", - "micromatch": "^2.3.11", - "sane": "^2.0.0" + "fb-watchman": "2.0.0", + "graceful-fs": "4.1.11", + "jest-docblock": "22.4.3", + "jest-serializer": "22.4.3", + "jest-worker": "22.4.3", + "micromatch": "2.3.11", + "sane": "2.5.2" } }, "jest-message-util": { @@ -3570,11 +3653,11 @@ "integrity": "sha512-iAMeKxhB3Se5xkSjU0NndLLCHtP4n+GtCqV0bISKA5dmOXQfEbdEmYiu2qpnWBDCQdEafNDDU6Q+l6oBMd/+BA==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0-beta.35", - "chalk": "^2.0.1", - "micromatch": "^2.3.11", - "slash": "^1.0.0", - "stack-utils": "^1.0.1" + "@babel/code-frame": "7.0.0-rc.2", + "chalk": "2.4.1", + "micromatch": "2.3.11", + "slash": "1.0.0", + "stack-utils": "1.0.1" } }, "jest-regex-util": { @@ -3589,7 +3672,7 @@ "integrity": "sha512-06czCMVToSN8F2U4EvgSB1Bv/56gc7MpCftZ9z9fBgUQM7dzHGCMBsyfVA6dZTx8v0FDcnALf7hupeQxaBCvpA==", "dev": true, "requires": { - "jest-regex-util": "^22.4.3" + "jest-regex-util": "22.4.3" } }, "jest-runner": { @@ -3598,17 +3681,17 @@ "integrity": "sha512-5S/OpB51igQW9xnkM5Tgd/7ZjiAuIoiJAVtvVTBcEBiXBIFzWM3BAMPBM19FX68gRV0KWyFuGKj0EY3M3aceeQ==", "dev": true, "requires": { - "exit": "^0.1.2", - "jest-config": "^22.4.4", - "jest-docblock": "^22.4.0", - "jest-haste-map": "^22.4.2", - "jest-jasmine2": "^22.4.4", - "jest-leak-detector": "^22.4.0", - "jest-message-util": "^22.4.0", - "jest-runtime": "^22.4.4", - "jest-util": "^22.4.1", - "jest-worker": "^22.2.2", - "throat": "^4.0.0" + "exit": "0.1.2", + "jest-config": "22.4.4", + "jest-docblock": "22.4.3", + "jest-haste-map": "22.4.3", + "jest-jasmine2": "22.4.4", + "jest-leak-detector": "22.4.3", + "jest-message-util": "22.4.3", + "jest-runtime": "22.4.4", + "jest-util": "22.4.3", + "jest-worker": "22.4.3", + "throat": "4.1.0" } }, "jest-runtime": { @@ -3617,26 +3700,26 @@ "integrity": "sha512-WRTj9m///npte1YjuphCYX7GRY/c2YvJImU9t7qOwFcqHr4YMzmX6evP/3Sehz5DKW2Vi8ONYPCFWe36JVXxfw==", "dev": true, "requires": { - "babel-core": "^6.0.0", - "babel-jest": "^22.4.4", - "babel-plugin-istanbul": "^4.1.5", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "exit": "^0.1.2", - "graceful-fs": "^4.1.11", - "jest-config": "^22.4.4", - "jest-haste-map": "^22.4.2", - "jest-regex-util": "^22.1.0", - "jest-resolve": "^22.4.2", - "jest-util": "^22.4.1", - "jest-validate": "^22.4.4", - "json-stable-stringify": "^1.0.1", - "micromatch": "^2.3.11", - "realpath-native": "^1.0.0", - "slash": "^1.0.0", + "babel-core": "6.26.0", + "babel-jest": "22.4.4", + "babel-plugin-istanbul": "4.1.6", + "chalk": "2.4.1", + "convert-source-map": "1.5.1", + "exit": "0.1.2", + "graceful-fs": "4.1.11", + "jest-config": "22.4.4", + "jest-haste-map": "22.4.3", + "jest-regex-util": "22.4.3", + "jest-resolve": "22.4.3", + "jest-util": "22.4.3", + "jest-validate": "22.4.4", + "json-stable-stringify": "1.0.1", + "micromatch": "2.3.11", + "realpath-native": "1.0.1", + "slash": "1.0.0", "strip-bom": "3.0.0", - "write-file-atomic": "^2.1.0", - "yargs": "^10.0.3" + "write-file-atomic": "2.3.0", + "yargs": "10.1.2" }, "dependencies": { "babel-jest": { @@ -3645,8 +3728,8 @@ "integrity": "sha512-A9NB6/lZhYyypR9ATryOSDcqBaqNdzq4U+CN+/wcMsLcmKkPxQEoTKLajGfd3IkxNyVBT8NewUK2nWyGbSzHEQ==", "dev": true, "requires": { - "babel-plugin-istanbul": "^4.1.5", - "babel-preset-jest": "^22.4.4" + "babel-plugin-istanbul": "4.1.6", + "babel-preset-jest": "22.4.4" } } } @@ -3657,12 +3740,12 @@ "integrity": "sha512-JXA0gVs5YL0HtLDCGa9YxcmmV2LZbwJ+0MfyXBBc5qpgkEYITQFJP7XNhcHFbUvRiniRpRbGVfJrOoYhhGE0RQ==", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-diff": "^22.4.3", - "jest-matcher-utils": "^22.4.3", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^22.4.3" + "chalk": "2.4.1", + "jest-diff": "22.4.3", + "jest-matcher-utils": "22.4.3", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "pretty-format": "22.4.3" } }, "jest-util": { @@ -3671,13 +3754,13 @@ "integrity": "sha512-rfDfG8wyC5pDPNdcnAlZgwKnzHvZDu8Td2NJI/jAGKEGxJPYiE4F0ss/gSAkG4778Y23Hvbz+0GMrDJTeo7RjQ==", "dev": true, "requires": { - "callsites": "^2.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.11", - "is-ci": "^1.0.10", - "jest-message-util": "^22.4.3", - "mkdirp": "^0.5.1", - "source-map": "^0.6.0" + "callsites": "2.0.0", + "chalk": "2.4.1", + "graceful-fs": "4.1.11", + "is-ci": "1.2.0", + "jest-message-util": "22.4.3", + "mkdirp": "0.5.1", + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -3694,11 +3777,11 @@ "integrity": "sha512-dmlf4CIZRGvkaVg3fa0uetepcua44DHtktHm6rcoNVtYlpwe6fEJRkMFsaUVcFHLzbuBJ2cPw9Gl9TKfnzMVwg==", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-config": "^22.4.4", - "jest-get-type": "^22.1.0", - "leven": "^2.1.0", - "pretty-format": "^22.4.0" + "chalk": "2.4.1", + "jest-config": "22.4.4", + "jest-get-type": "22.4.3", + "leven": "2.1.0", + "pretty-format": "22.4.3" } }, "jest-worker": { @@ -3707,7 +3790,7 @@ "integrity": "sha512-B1ucW4fI8qVAuZmicFxI1R3kr2fNeYJyvIQ1rKcuLYnenFV5K5aMbxFj6J0i00Ju83S8jP2d7Dz14+AvbIHRYQ==", "dev": true, "requires": { - "merge-stream": "^1.0.1" + "merge-stream": "1.0.1" } }, "micromatch": { @@ -3716,19 +3799,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } }, "node-notifier": { @@ -3737,10 +3820,10 @@ "integrity": "sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg==", "dev": true, "requires": { - "growly": "^1.3.0", - "semver": "^5.4.1", - "shellwords": "^0.1.1", - "which": "^1.3.0" + "growly": "1.3.0", + "semver": "5.5.0", + "shellwords": "0.1.1", + "which": "1.3.1" } }, "realpath-native": { @@ -3749,7 +3832,7 @@ "integrity": "sha512-W14EcXuqUvKP8dkWkD7B95iMy77lpMnlFXbbk409bQtNCbeu0kvRE5reo+yIZ3JXxg6frbGsz2DLQ39lrCB40g==", "dev": true, "requires": { - "util.promisify": "^1.0.0" + "util.promisify": "1.0.0" } }, "rimraf": { @@ -3758,7 +3841,7 @@ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "slash": { @@ -3773,8 +3856,8 @@ "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", "dev": true, "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" + "astral-regex": "1.0.0", + "strip-ansi": "4.0.0" } }, "which": { @@ -3783,7 +3866,7 @@ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "yargs": { @@ -3792,18 +3875,18 @@ "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^8.1.0" + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.3", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "8.1.0" } } } @@ -3816,10 +3899,10 @@ "integrity": "sha1-4YzD/v8K7vFZ0CMQ8mhtQGU3gDA=", "dev": true, "requires": { - "chalk": "^2.0.1", - "diff": "^3.2.0", - "jest-get-type": "^22.4.3", - "pretty-format": "^22.4.3" + "chalk": "2.4.1", + "diff": "3.5.0", + "jest-get-type": "22.4.3", + "pretty-format": "22.4.3" }, "dependencies": { "chalk": { @@ -3828,9 +3911,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } } } @@ -3841,7 +3924,7 @@ "integrity": "sha1-UIhvEytCsoDJA8WSNzu26Tu2ixk=", "dev": true, "requires": { - "detect-newline": "^2.1.0" + "detect-newline": "2.1.0" } }, "jest-environment-node": { @@ -3850,8 +3933,8 @@ "integrity": "sha1-VMTqo3TIPdUqnah1m+FOvh0LkSk=", "dev": true, "requires": { - "jest-mock": "^22.4.3", - "jest-util": "^22.4.3" + "jest-mock": "22.4.3", + "jest-util": "22.4.3" } }, "jest-get-type": { @@ -3866,17 +3949,17 @@ "integrity": "sha512-nK3vdUl50MuH7vj/8at7EQVjPGWCi3d5+6aCi7Gxy/XMWdOdbH1qtO/LjKbqD8+8dUAEH+BVVh7HkjpCWC1CSw==", "dev": true, "requires": { - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^22.4.0", - "graceful-fs": "^4.1.11", - "is-generator-fn": "^1.0.0", - "jest-diff": "^22.4.0", - "jest-matcher-utils": "^22.4.0", - "jest-message-util": "^22.4.0", - "jest-snapshot": "^22.4.0", - "jest-util": "^22.4.1", - "source-map-support": "^0.5.0" + "chalk": "2.4.1", + "co": "4.6.0", + "expect": "22.4.3", + "graceful-fs": "4.1.11", + "is-generator-fn": "1.0.0", + "jest-diff": "22.4.3", + "jest-matcher-utils": "22.4.3", + "jest-message-util": "22.4.3", + "jest-snapshot": "22.4.3", + "jest-util": "22.4.3", + "source-map-support": "0.5.9" }, "dependencies": { "chalk": { @@ -3885,9 +3968,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } } } @@ -3898,9 +3981,9 @@ "integrity": "sha1-9MQ1jlKGNkpDJNwUq93VJqrfvTg=", "dev": true, "requires": { - "mkdirp": "^0.5.1", - "strip-ansi": "^4.0.0", - "xml": "^1.0.1" + "mkdirp": "0.5.1", + "strip-ansi": "4.0.0", + "xml": "1.0.1" } }, "jest-leak-detector": { @@ -3909,7 +3992,7 @@ "integrity": "sha1-K3smMQOvroxStrkSQaLeQBF+WzU=", "dev": true, "requires": { - "pretty-format": "^22.4.3" + "pretty-format": "22.4.3" } }, "jest-matcher-utils": { @@ -3918,9 +4001,9 @@ "integrity": "sha1-RjL+Qo68c+vBlNPHtl03sWH3EP8=", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-get-type": "^22.4.3", - "pretty-format": "^22.4.3" + "chalk": "2.4.1", + "jest-get-type": "22.4.3", + "pretty-format": "22.4.3" }, "dependencies": { "chalk": { @@ -3929,9 +4012,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } } } @@ -3942,11 +4025,11 @@ "integrity": "sha1-zz04qv5L792/xFXlfWXVI545nrc=", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0-beta.35", - "chalk": "^2.0.1", - "micromatch": "^2.3.11", - "slash": "^1.0.0", - "stack-utils": "^1.0.1" + "@babel/code-frame": "7.0.0-rc.2", + "chalk": "2.4.1", + "micromatch": "2.3.11", + "slash": "1.0.0", + "stack-utils": "1.0.1" }, "dependencies": { "chalk": { @@ -3955,9 +4038,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } } } @@ -3980,8 +4063,8 @@ "integrity": "sha1-DOnUOMhDgimqm5FpaOxrBcGrtOo=", "dev": true, "requires": { - "browser-resolve": "^1.11.2", - "chalk": "^2.0.1" + "browser-resolve": "1.11.3", + "chalk": "2.4.1" }, "dependencies": { "chalk": { @@ -3990,9 +4073,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } } } @@ -4009,12 +4092,12 @@ "integrity": "sha1-tcm0KEb/ufrMt2uEExW6Z4hzYtI=", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-diff": "^22.4.3", - "jest-matcher-utils": "^22.4.3", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^22.4.3" + "chalk": "2.4.1", + "jest-diff": "22.4.3", + "jest-matcher-utils": "22.4.3", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "pretty-format": "22.4.3" }, "dependencies": { "chalk": { @@ -4023,9 +4106,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } } } @@ -4036,13 +4119,13 @@ "integrity": "sha1-xw/sjuxIfDexCwgJ3AZKfs9qr6w=", "dev": true, "requires": { - "callsites": "^2.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.11", - "is-ci": "^1.0.10", - "jest-message-util": "^22.4.3", - "mkdirp": "^0.5.1", - "source-map": "^0.6.0" + "callsites": "2.0.0", + "chalk": "2.4.1", + "graceful-fs": "4.1.11", + "is-ci": "1.2.0", + "jest-message-util": "22.4.3", + "mkdirp": "0.5.1", + "source-map": "0.6.1" }, "dependencies": { "chalk": { @@ -4051,9 +4134,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "source-map": { @@ -4076,8 +4159,8 @@ "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.10", + "esprima": "4.0.1" } }, "jsbn": { @@ -4092,32 +4175,32 @@ "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", "dev": true, "requires": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", + "abab": "2.0.0", + "acorn": "5.7.1", + "acorn-globals": "4.1.0", + "array-equal": "1.0.0", + "cssom": "0.3.4", + "cssstyle": "1.1.1", + "data-urls": "1.0.1", + "domexception": "1.0.1", + "escodegen": "1.11.0", + "html-encoding-sniffer": "1.0.2", + "left-pad": "1.3.0", + "nwsapi": "2.0.8", "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", - "xml-name-validator": "^3.0.0" + "pn": "1.1.0", + "request": "2.87.0", + "request-promise-native": "1.0.5", + "sax": "1.2.4", + "symbol-tree": "3.2.2", + "tough-cookie": "2.4.3", + "w3c-hr-time": "1.0.1", + "webidl-conversions": "4.0.2", + "whatwg-encoding": "1.0.4", + "whatwg-mimetype": "2.1.0", + "whatwg-url": "6.5.0", + "ws": "5.2.2", + "xml-name-validator": "3.0.0" }, "dependencies": { "tough-cookie": { @@ -4126,8 +4209,8 @@ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "1.1.29", + "punycode": "1.4.1" } } } @@ -4154,7 +4237,7 @@ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "requires": { - "jsonify": "~0.0.0" + "jsonify": "0.0.0" } }, "json-stringify-safe": { @@ -4168,6 +4251,15 @@ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } + }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -4191,7 +4283,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "lazy-cache": { @@ -4207,7 +4299,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "left-pad": { @@ -4228,8 +4320,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "1.1.2", + "type-check": "0.3.2" } }, "load-json-file": { @@ -4238,11 +4330,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" }, "dependencies": { "strip-bom": { @@ -4251,7 +4343,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } } } @@ -4262,8 +4354,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" } }, "lodash": { @@ -4299,7 +4391,7 @@ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "js-tokens": "3.0.2" } }, "lru-cache": { @@ -4307,8 +4399,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "lrucache": { @@ -4322,7 +4414,7 @@ "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", "dev": true, "requires": { - "tmpl": "1.0.x" + "tmpl": "1.0.4" } }, "map-cache": { @@ -4337,9 +4429,15 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "^1.0.0" + "object-visit": "1.0.1" } }, + "marked": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.4.0.tgz", + "integrity": "sha512-tMsdNBgOsrUophCAFQl0XPe6Zqk/uy9gnue+jIIKhykO51hxyu6uNx7zBPy0+y/WKYVZZMspV9YeXLNdKk+iYw==", + "dev": true + }, "math-random": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", @@ -4352,7 +4450,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "merge": { @@ -4367,7 +4465,7 @@ "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", "dev": true, "requires": { - "readable-stream": "^2.0.1" + "readable-stream": "2.3.6" } }, "micromatch": { @@ -4376,19 +4474,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } }, "mime-db": { @@ -4401,7 +4499,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "requires": { - "mime-db": "~1.30.0" + "mime-db": "1.30.0" } }, "mimic-fn": { @@ -4416,7 +4514,7 @@ "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -4431,8 +4529,8 @@ "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "for-in": "1.0.2", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -4441,7 +4539,7 @@ "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -4474,17 +4572,17 @@ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "arr-diff": { @@ -4519,15 +4617,15 @@ "integrity": "sha1-OXOAh9agSX06Fl+zUmErOKL5uS8=", "dev": true, "requires": { - "chai": "^4.1.2", - "debug": "^3.1.0", - "deep-equal": "^1.0.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.5", - "mkdirp": "^0.5.0", - "propagate": "^1.0.0", - "qs": "^6.5.1", - "semver": "^5.5.0" + "chai": "4.1.2", + "debug": "3.1.0", + "deep-equal": "1.0.1", + "json-stringify-safe": "5.0.1", + "lodash": "4.17.10", + "mkdirp": "0.5.1", + "propagate": "1.0.0", + "qs": "6.5.1", + "semver": "5.5.0" }, "dependencies": { "debug": { @@ -4552,8 +4650,8 @@ "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-3.2.1.tgz", "integrity": "sha1-p5WNMqikLZEZziWYZWfqLF+WZ3M=", "requires": { - "clone": "1.0.x", - "lodash": "4.x" + "clone": "1.0.2", + "lodash": "4.17.10" } }, "node-int64": { @@ -4573,10 +4671,10 @@ "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.7.1", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.4" } }, "normalize-path": { @@ -4585,7 +4683,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } }, "npm-run-path": { @@ -4594,7 +4692,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "number-is-nan": { @@ -4626,9 +4724,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" }, "dependencies": { "define-property": { @@ -4637,7 +4735,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -4654,7 +4752,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "^3.0.0" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -4671,8 +4769,8 @@ "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "define-properties": "1.1.3", + "es-abstract": "1.12.0" } }, "object.omit": { @@ -4681,8 +4779,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "0.1.5", + "is-extendable": "0.1.1" } }, "object.pick": { @@ -4691,7 +4789,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -4708,7 +4806,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "optimist": { @@ -4717,8 +4815,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.8", + "wordwrap": "0.0.3" } }, "optionator": { @@ -4727,12 +4825,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" }, "dependencies": { "wordwrap": { @@ -4755,9 +4853,9 @@ "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "os-tmpdir": { @@ -4778,7 +4876,7 @@ "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { @@ -4787,7 +4885,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.2.0" } }, "p-try": { @@ -4802,10 +4900,10 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" } }, "parse-json": { @@ -4814,7 +4912,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.2" } }, "parse5": { @@ -4859,9 +4957,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pathval": { @@ -4893,7 +4991,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -4902,7 +5000,7 @@ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "dev": true, "requires": { - "find-up": "^2.1.0" + "find-up": "2.1.0" } }, "pn": { @@ -4935,8 +5033,8 @@ "integrity": "sha1-+HPXgIOanALpZkyKCC6e556qwW8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" + "ansi-regex": "3.0.0", + "ansi-styles": "3.2.1" }, "dependencies": { "ansi-regex": { @@ -4959,6 +5057,12 @@ "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=", "dev": true }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "propagate": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz", @@ -4992,9 +5096,9 @@ "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", "dev": true, "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" + "is-number": "4.0.0", + "kind-of": "6.0.2", + "math-random": "1.0.1" }, "dependencies": { "is-number": { @@ -5017,9 +5121,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -5028,8 +5132,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" }, "dependencies": { "find-up": { @@ -5038,8 +5142,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "path-exists": { @@ -5048,7 +5152,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } } } @@ -5059,13 +5163,22 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "1.1.7" } }, "redis": { @@ -5073,9 +5186,9 @@ "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", "integrity": "sha1-ICKI4/WMSfYHnZevehDhMDrhSwI=", "requires": { - "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" + "double-ended-queue": "2.1.0-0", + "redis-commands": "1.3.1", + "redis-parser": "2.6.0" } }, "redis-commands": { @@ -5106,9 +5219,9 @@ "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", "dev": true, "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "private": "0.1.8" } }, "regex-cache": { @@ -5117,7 +5230,7 @@ "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", "dev": true, "requires": { - "is-equal-shallow": "^0.1.3" + "is-equal-shallow": "0.1.3" } }, "regex-not": { @@ -5126,8 +5239,8 @@ "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", "dev": true, "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" } }, "regexpu-core": { @@ -5136,9 +5249,9 @@ "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "regenerate": "1.4.0", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" } }, "regjsgen": { @@ -5153,7 +5266,7 @@ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { - "jsesc": "~0.5.0" + "jsesc": "0.5.0" }, "dependencies": { "jsesc": { @@ -5188,7 +5301,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.0.2" } }, "request": { @@ -5196,26 +5309,26 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", "integrity": "sha1-MvACNc0I1IK00NaNuTqCnA7VdW4=", "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" } }, "request-etag": { @@ -5223,10 +5336,10 @@ "resolved": "https://registry.npmjs.org/request-etag/-/request-etag-2.0.3.tgz", "integrity": "sha1-E8kjdhfWxYnrcYX29FC4MTBovBc=", "requires": { - "lodash.assign": "^4.0.0", - "lodash.clonedeep": "^4.0.1", - "lru-cache": "^4.0.0", - "request": "^2.67.0" + "lodash.assign": "4.2.0", + "lodash.clonedeep": "4.5.0", + "lru-cache": "4.1.2", + "request": "2.87.0" } }, "request-promise-core": { @@ -5235,7 +5348,7 @@ "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", "dev": true, "requires": { - "lodash": "^4.13.1" + "lodash": "4.17.10" } }, "request-promise-native": { @@ -5245,8 +5358,8 @@ "dev": true, "requires": { "request-promise-core": "1.1.1", - "stealthy-require": "^1.1.0", - "tough-cookie": ">=2.3.3" + "stealthy-require": "1.1.1", + "tough-cookie": "2.3.3" } }, "require-directory": { @@ -5273,7 +5386,7 @@ "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", "dev": true, "requires": { - "resolve-from": "^3.0.0" + "resolve-from": "3.0.0" } }, "resolve-from": { @@ -5301,7 +5414,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rsvp": { @@ -5321,7 +5434,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "~0.1.10" + "ret": "0.1.15" } }, "safer-buffer": { @@ -5335,15 +5448,15 @@ "integrity": "sha1-tNwYYcIbQn6SlQej51HiosuKs/o=", "dev": true, "requires": { - "anymatch": "^2.0.0", - "capture-exit": "^1.2.0", - "exec-sh": "^0.2.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.3", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5", - "watch": "~0.18.0" + "anymatch": "2.0.0", + "capture-exit": "1.2.0", + "exec-sh": "0.2.2", + "fb-watchman": "2.0.0", + "fsevents": "1.2.4", + "micromatch": "3.1.10", + "minimist": "1.2.0", + "walker": "1.0.7", + "watch": "0.18.0" }, "dependencies": { "arr-diff": { @@ -5364,16 +5477,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.3", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -5382,7 +5495,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -5393,13 +5506,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -5408,7 +5521,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -5417,7 +5530,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -5426,7 +5539,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -5435,7 +5548,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -5446,7 +5559,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -5455,7 +5568,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -5466,9 +5579,9 @@ "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, "kind-of": { @@ -5485,14 +5598,14 @@ "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -5501,7 +5614,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -5510,7 +5623,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -5521,10 +5634,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -5533,7 +5646,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -5544,7 +5657,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -5553,7 +5666,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -5562,9 +5675,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "is-number": { @@ -5573,7 +5686,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -5582,7 +5695,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -5605,19 +5718,19 @@ "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "minimist": { @@ -5651,10 +5764,10 @@ "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" }, "dependencies": { "extend-shallow": { @@ -5663,7 +5776,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -5674,7 +5787,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -5683,6 +5796,17 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shelljs": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", + "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", + "dev": true, + "requires": { + "glob": "7.1.2", + "interpret": "1.2.0", + "rechoir": "0.6.2" + } + }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -5707,14 +5831,14 @@ "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", "dev": true, "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.2", + "use": "3.1.1" }, "dependencies": { "define-property": { @@ -5723,7 +5847,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -5732,7 +5856,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -5743,9 +5867,9 @@ "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", "dev": true, "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { "define-property": { @@ -5754,7 +5878,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -5763,7 +5887,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -5772,7 +5896,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -5781,9 +5905,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "isobject": { @@ -5806,7 +5930,7 @@ "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", "dev": true, "requires": { - "kind-of": "^3.2.0" + "kind-of": "3.2.2" } }, "source-map": { @@ -5821,11 +5945,11 @@ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "atob": "2.1.2", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" } }, "source-map-support": { @@ -5834,8 +5958,8 @@ "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "buffer-from": "1.1.1", + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -5858,8 +5982,8 @@ "integrity": "sha1-BaW01xU6GVvJLDxCW2nzsqlSTII=", "dev": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" } }, "spdx-exceptions": { @@ -5874,8 +5998,8 @@ "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", "dev": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" } }, "spdx-license-ids": { @@ -5890,7 +6014,7 @@ "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", "dev": true, "requires": { - "extend-shallow": "^3.0.0" + "extend-shallow": "3.0.2" } }, "sprintf-js": { @@ -5904,15 +6028,15 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "asn1": "0.2.4", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" } }, "stack-trace": { @@ -5932,8 +6056,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "define-property": "0.2.5", + "object-copy": "0.1.0" }, "dependencies": { "define-property": { @@ -5942,7 +6066,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -5959,8 +6083,8 @@ "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, "string_decoder": { @@ -5969,7 +6093,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } }, "strip-ansi": { @@ -5978,7 +6102,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" }, "dependencies": { "ansi-regex": { @@ -6007,7 +6131,7 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } }, "symbol-tree": { @@ -6022,11 +6146,11 @@ "integrity": "sha1-36Ii8DSAvKaSB8pyizfXS0X3JPo=", "dev": true, "requires": { - "arrify": "^1.0.1", - "micromatch": "^3.1.8", - "object-assign": "^4.1.0", - "read-pkg-up": "^1.0.1", - "require-main-filename": "^1.0.1" + "arrify": "1.0.1", + "micromatch": "3.1.10", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "require-main-filename": "1.0.1" }, "dependencies": { "arr-diff": { @@ -6047,16 +6171,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.3", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -6065,7 +6189,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -6076,13 +6200,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -6091,7 +6215,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -6100,7 +6224,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -6109,7 +6233,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -6118,7 +6242,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6129,7 +6253,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -6138,7 +6262,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6149,9 +6273,9 @@ "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, "kind-of": { @@ -6168,14 +6292,14 @@ "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -6184,7 +6308,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -6193,7 +6317,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -6204,10 +6328,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -6216,7 +6340,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -6227,7 +6351,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -6236,7 +6360,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -6245,9 +6369,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "is-number": { @@ -6256,7 +6380,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -6265,7 +6389,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6288,19 +6412,19 @@ "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } } } @@ -6317,7 +6441,7 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "os-tmpdir": "1.0.2" } }, "tmpl": { @@ -6338,7 +6462,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "to-regex": { @@ -6347,10 +6471,10 @@ "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", "dev": true, "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -6359,8 +6483,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" }, "dependencies": { "is-number": { @@ -6369,7 +6493,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } } } @@ -6379,7 +6503,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", "requires": { - "punycode": "^1.4.1" + "punycode": "1.4.1" } }, "tr46": { @@ -6388,7 +6512,7 @@ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", "dev": true, "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" }, "dependencies": { "punycode": { @@ -6415,7 +6539,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.1" } }, "tweetnacl": { @@ -6430,7 +6554,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "1.1.2" } }, "type-detect": { @@ -6439,6 +6563,45 @@ "integrity": "sha1-dkb7XxiHHPu3dJ5pvTmmOI63RQw=", "dev": true }, + "typedoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.14.2.tgz", + "integrity": "sha512-aEbgJXV8/KqaVhcedT7xG6d2r+mOvB5ep3eIz1KuB5sc4fDYXcepEEMdU7XSqLFO5hVPu0nllHi1QxX2h/QlpQ==", + "dev": true, + "requires": { + "@types/fs-extra": "5.0.4", + "@types/handlebars": "4.0.40", + "@types/highlight.js": "9.12.3", + "@types/lodash": "4.14.120", + "@types/marked": "0.4.2", + "@types/minimatch": "3.0.3", + "@types/shelljs": "0.8.2", + "fs-extra": "7.0.1", + "handlebars": "4.0.11", + "highlight.js": "9.14.2", + "lodash": "4.17.10", + "marked": "0.4.0", + "minimatch": "3.0.4", + "progress": "2.0.3", + "shelljs": "0.8.3", + "typedoc-default-themes": "0.5.0", + "typescript": "3.2.4" + }, + "dependencies": { + "typescript": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", + "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==", + "dev": true + } + } + }, + "typedoc-default-themes": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.5.0.tgz", + "integrity": "sha1-bcJDPnjti+qOiHo6zeLzF4W9Yic=", + "dev": true + }, "typescript": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.1.tgz", @@ -6452,9 +6615,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" } }, "uglify-to-browserify": { @@ -6470,10 +6633,10 @@ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" }, "dependencies": { "extend-shallow": { @@ -6482,7 +6645,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "set-value": { @@ -6491,22 +6654,28 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" } } } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "has-value": "0.3.1", + "isobject": "3.0.1" }, "dependencies": { "has-value": { @@ -6515,9 +6684,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" }, "dependencies": { "isobject": { @@ -6569,8 +6738,8 @@ "integrity": "sha1-RA9xZaRZyaFtwUXrjnLzVocJcDA=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" + "define-properties": "1.1.3", + "object.getownpropertydescriptors": "2.0.3" } }, "uuid": { @@ -6584,8 +6753,8 @@ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" } }, "verror": { @@ -6593,9 +6762,9 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, "w3c-hr-time": { @@ -6604,7 +6773,7 @@ "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", "dev": true, "requires": { - "browser-process-hrtime": "^0.1.2" + "browser-process-hrtime": "0.1.2" } }, "walker": { @@ -6613,7 +6782,7 @@ "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", "dev": true, "requires": { - "makeerror": "1.0.x" + "makeerror": "1.0.11" } }, "watch": { @@ -6622,8 +6791,8 @@ "integrity": "sha1-KAlUdsbffJDJYxOJkMClQj60uYY=", "dev": true, "requires": { - "exec-sh": "^0.2.0", - "minimist": "^1.2.0" + "exec-sh": "0.2.2", + "minimist": "1.2.0" }, "dependencies": { "minimist": { @@ -6661,9 +6830,9 @@ "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", "dev": true, "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" + "lodash.sortby": "4.7.0", + "tr46": "1.0.1", + "webidl-conversions": "4.0.2" } }, "which": { @@ -6672,7 +6841,7 @@ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -6693,12 +6862,12 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.1.tgz", "integrity": "sha1-o6kmUQVWQmPGeFtFg7jIrKJv3tY=", "requires": { - "async": "~1.0.0", - "colors": "1.0.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "stack-trace": "0.0.x" + "async": "1.0.0", + "colors": "1.0.3", + "cycle": "1.0.3", + "eyes": "0.1.8", + "isstream": "0.1.2", + "stack-trace": "0.0.10" }, "dependencies": { "async": { @@ -6720,8 +6889,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "is-fullwidth-code-point": { @@ -6730,7 +6899,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "string-width": { @@ -6739,9 +6908,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "strip-ansi": { @@ -6750,7 +6919,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } } } @@ -6767,9 +6936,9 @@ "integrity": "sha1-H/YVdcLipOjlENb6TiQ8zhg5mas=", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" } }, "ws": { @@ -6778,7 +6947,7 @@ "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", "dev": true, "requires": { - "async-limiter": "~1.0.0" + "async-limiter": "1.0.0" } }, "xml": { @@ -6816,9 +6985,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } }, @@ -6828,7 +6997,7 @@ "integrity": "sha1-8TdqM7Ziml0GN4KUTacyYx6WaVA=", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" }, "dependencies": { "camelcase": { diff --git a/package.json b/package.json index 192c8d7..59ccf5f 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "jest-junit": "3.6.0", "nock": "9.2.3", "tmp": "0.0.33", + "typedoc": "0.14.2", "typescript": "3.0.1" }, "jest": { From a9afc6a3ee5baf9d4e89bbe74ede7dc6da97a409 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 6 Feb 2019 19:27:38 -0800 Subject: [PATCH 088/330] misc doc fixes --- docs/typedoc.js | 1 + index.d.ts | 495 ++++++++++++++++++++++++++++-------------------- 2 files changed, 288 insertions(+), 208 deletions(-) diff --git a/docs/typedoc.js b/docs/typedoc.js index f00ca93..758f5f7 100644 --- a/docs/typedoc.js +++ b/docs/typedoc.js @@ -8,5 +8,6 @@ module.exports = { ], name: 'LaunchDarkly Node SDK', theme: 'default', + readme: 'none', includeDeclarations: true }; diff --git a/index.d.ts b/index.d.ts index 1fb101d..d51e90f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,9 +1,9 @@ // Type definitions for ldclient-node /** - * The LaunchDarkly Node.js client interface. + * This is the API reference for the LaunchDarkly SDK for Node.js. * - * Documentation: http://docs.launchdarkly.com/docs/node-sdk-reference + * For information, see the [SDK reference guide](http://docs.launchdarkly.com/docs/node-sdk-reference). */ declare module 'ldclient-node' { @@ -19,18 +19,45 @@ declare module 'ldclient-node' { } /** - * The LaunchDarkly static global. + * Creates an instance of the LaunchDarkly client. + * + * The client will begin attempting to connect to LaunchDarkly as soon as it is created. To + * determine when it is ready to use, call [[LDClient.waitForInitialization]], or register an + * event listener for the `"ready"` event using [[LDClient.on]]. + * + * @param key + * The SDK key. + * @param options + * Optional configuration settings. + * @return + * The new client instance. */ export function init(key: string, options?: LDOptions): LDClient; /** - * Create a feature flag store backed by a Redis instance + * Creates a feature store backed by a Redis instance. + * + * For more details about how and why you can use a persistent feature store, see + * the [SDK Reference Guide](https://docs.launchdarkly.com/v2.0/docs/using-a-persistent-feature-store). + * + * @param redisOpts + * Optional configuration parameters to be passed to the `redis` package. + * @param cacheTTL + * The amount of time, in seconds, that recently read or updated items should remain in an + * in-memory cache. If it is zero, there will be no in-memory caching. The default value is DefaultCacheTTL. + * @param prefix + * A string that should be prepended to all Redis keys used by the feature store. + * @param logger + * A custom logger for warnings and errors, if you are not using the default logger. + * + * @returns + * An object to put in the `featureStore` property for [[LDOptions]]. */ export function RedisFeatureStore( - redisOpts: ClientOpts, - cacheTTL: number, - prefix: string, - logger: LDLogger | object + redisOpts?: ClientOpts, + cacheTTL?: number, + prefix?: string, + logger?: LDLogger | object ): LDFeatureStore; /** @@ -48,8 +75,7 @@ declare module 'ldclient-node' { }; /** - * An object that contains the state of all feature flags, generated by the client's - * allFlagsState() method. + * An object that contains the state of all feature flags, generated by [[LDClient.allFlagsState]]. */ export interface LDFlagsState { /** @@ -62,25 +88,29 @@ declare module 'ldclient-node' { /** * Returns the value of an individual feature flag at the time the state was recorded. * It will be null if the flag returned the default value, or if there was no such flag. - * @param key the flag key + * + * @param key + * The flag key. */ - getFlagValue: (key: string) => LDFlagValue; + getFlagValue(key: string): LDFlagValue; /** * Returns the evaluation reason for a feature flag at the time the state was recorded. * It will be null if reasons were not recorded, or if there was no such flag. - * @param key the flag key + * + * @param key + * The flag key. */ - getFlagReason: (key: string) => LDEvaluationReason; + getFlagReason(key: string): LDEvaluationReason; /** * Returns a map of feature flag keys to values. If a flag would have evaluated to the * default value, its value will be null. * * Do not use this method if you are passing data to the front end to "bootstrap" the - * JavaScript client. Instead, use toJson(). + * JavaScript client. Instead, use [[toJSON]]. */ - allValues: () => LDFlagSet; + allValues(): LDFlagSet; /** * Returns a Javascript representation of the entire state map, in the format used by @@ -90,68 +120,67 @@ declare module 'ldclient-node' { * Do not rely on the exact shape of this data, as it may change in future to support * the needs of the JavaScript client. */ - toJSON: () => object; + toJSON(): object; } /** * Describes the reason that a flag evaluation produced a particular value. This is - * part of the LDEvaluationDetail object returned by variationDetail(). + * part of the [[LDEvaluationDetail]] object returned by [[LDClient.variationDetail]]. */ export type LDEvaluationReason = { /** * The general category of the reason: * - * 'OFF': the flag was off and therefore returned its configured off value - * - * 'FALLTHROUGH': the flag was on but the user did not match any targets or rules - * - * 'TARGET_MATCH': the user key was specifically targeted for this flag - * - * 'RULE_MATCH': the user matched one of the flag's rules - * - * 'PREREQUISITE_FAILED': the flag was considered off because it had at least one - * prerequisite flag that either was off or did not return the desired variation - * - * 'ERROR': the flag could not be evaluated, e.g. because it does not exist or due - * to an unexpected error + * - `'OFF'`: The flag was off and therefore returned its configured off value. + * - `'FALLTHROUGH'`: The flag was on but the user did not match any targets or rules. + * - `'TARGET_MATCH'`: The user key was specifically targeted for this flag. + * - `'RULE_MATCH'`: the user matched one of the flag's rules. + * - `'PREREQUISITE_FAILED'`: The flag was considered off because it had at least one + * prerequisite flag that either was off or did not return the desired variation. + * - `'ERROR'`: The flag could not be evaluated, e.g. because it does not exist or due + * to an unexpected error. */ kind: string; /** - * A further description of the error condition, if the kind was 'ERROR'. + * A further description of the error condition, if the kind was `'ERROR'`. */ errorKind?: string; /** - * The index of the matched rule (0 for the first), if the kind was 'RULE_MATCH'. + * The index of the matched rule (0 for the first), if the kind was `'RULE_MATCH'`. */ ruleIndex?: number; /** - * The unique identifier of the matched rule, if the kind was 'RULE_MATCH'. + * The unique identifier of the matched rule, if the kind was `'RULE_MATCH'`. */ ruleId?: string; /** - * The key of the failed prerequisite flag, if the kind was 'PREREQUISITE_FAILED'. + * The key of the failed prerequisite flag, if the kind was `'PREREQUISITE_FAILED'`. */ prerequisiteKey?: string; }; /** - * An object returned by LDClient.variationDetail(), combining the result of a feature flag - * evaluation with information about how it was calculated. + * An object that combines the result of a feature flag evaluation with information about + * how it was calculated. + * + * This is the result of calling [[LDClient.variationDetail]]. + * + * For more information, see the [SDK reference guide](https://docs.launchdarkly.com/docs/evaluation-reasons). */ export type LDEvaluationDetail = { /** * The result of the flag evaluation. This will be either one of the flag's variations or - * the default value that was passed to variationDetail(). + * the default value that was passed to [[LDClient.variationDetail]]. */ value: LDFlagValue; /** * The index of the returned value within the flag's list of variations, e.g. 0 for the - * first variation - or null if the default value was returned. + * first variation-- or `null` if the default value was returned. */ variationIndex?: number; @@ -164,62 +193,64 @@ declare module 'ldclient-node' { /** * LaunchDarkly initialization options. */ - export interface LDOptions { + export type LDOptions = { /** - * The base uri for the LaunchDarkly server. + * The base URI for the LaunchDarkly server. * - * This is used for enterprise customers with their own LaunchDarkly instances. * Most users should use the default value. */ baseUri?: string; /** - * The stream uri for the LaunchDarkly server. + * The base URI for the LaunchDarkly streaming server. * - * This is used for enterprise customers with their own LaunchDarkly instances. * Most users should use the default value. */ streamUri?: string; /** - * The events uri for the LaunchDarkly server. + * The base URI for the LaunchDarkly events server. * - * This is used for enterprise customers with their own LaunchDarkly instances. * Most users should use the default value. */ eventsUri?: string; /** - * In seconds, controls the request timeout to LaunchDarkly. + * The connection timeout, in seconds. */ timeout?: number; /** - * Controls the maximum size of the event buffer. LaunchDarkly sends events asynchronously, and buffers them for efficiency. + * The capacity of the analytics events queue. + * + * The client buffers up to this many events in memory before flushing. If the capacity is + * exceeded before the buffer is flushed, events will be discarded. */ capacity?: number; /** * Configures a logger for warnings and errors generated by the SDK. * - * This can be a custom logger or an instance of winston.Logger + * This can be a custom logger or an instance of `winston.Logger`. */ logger?: LDLogger | object; /** - * Feature store used by the LaunchDarkly client, defaults to in memory storage. + * A component that stores feature flags and related data received from LaunchDarkly. * - * The SDK provides an in memory feature store as well as a redis feature store. + * By default, this is an in-memory data structure. The SDK also provides a Redis + * implementation ([[RedisFeatureStore]]); other options are described in the + * [SDK reference guide](https://docs.launchdarkly.com/v2.0/docs/using-a-persistent-feature-store). */ featureStore?: LDFeatureStore; /** - * In seconds, controls how long LaunchDarkly buffers events before sending them back to our server. + * The interval in between flushes of the analytics events queue, in seconds. */ flushInterval?: number; /** - * In seconds, controls the time between polling requests. + * The time between polling requests, in seconds. Ignored in streaming mode. */ pollInterval?: number; @@ -230,13 +261,14 @@ declare module 'ldclient-node' { /** * Allows you to specify a port for an optional HTTP proxy. + * * Both the host and port must be specified to enable proxy support. */ - proxyPort?: string; + proxyPort?: number; /** * Allows you to specify basic authentication parameters for an optional HTTP proxy. - * Usually of the form username:password. + * Usually of the form `username:password`. */ proxyAuth?: string; @@ -246,33 +278,38 @@ declare module 'ldclient-node' { offline?: boolean; /** - * Whether streaming or polling should be used to receive flag updates. + * Whether streaming mode should be used to receive flag updates. + * + * This is true by default. If you set it to false, the client will use polling. + * Streaming should only be disabled on the advice of LaunchDarkly support. */ stream?: boolean; /** - * Whether to rely on LDD for feature updates. + * Whether you are using the LaunchDarkly relay proxy in daemon mode. + * + * In this configuration, the client will not connect to LaunchDarkly to get feature flags, + * but will instead get feature state from a database (Redis or another supported feature + * store integration) that is populated by the relay. By default, this is false. */ useLdd?: boolean; /** - * Whether to send events back to LaunchDarkly + * Whether to send analytics events back to LaunchDarkly. By default, this is true. */ sendEvents?: boolean; /** - * Whether all user attributes (except the user key) should be marked as - * private, and not sent to LaunchDarkly. + * Whether all user attributes (except the user key) should be marked as private, and + * not sent to LaunchDarkly. * - * Defaults to false. + * By default, this is false. */ allAttributesPrivate?: boolean; /** - * The names of user attributes that should be marked as private, and not sent + * The names of any user attributes that should be marked as private, and not sent * to LaunchDarkly. - * - * Must be a list of strings. Defaults to empty list. */ privateAttributeNames?: Array; @@ -296,7 +333,7 @@ declare module 'ldclient-node' { /** * A LaunchDarkly user object. */ - export interface LDUser { + export type LDUser = { /** * A unique string identifying a user. */ @@ -347,7 +384,7 @@ declare module 'ldclient-node' { country?: string; /** - * Whether to show the user on the Users page in LaunchDarkly. + * If true, the user will _not_ appear on the Users page in the LaunchDarkly dashboard. */ anonymous?: boolean; @@ -367,43 +404,41 @@ declare module 'ldclient-node' { * The LaunchDarkly client logger interface. * * The client will output informational debugging messages to the logger. - * Internally, this logger defaults to an instance of winston.Logger, which takes - * logs a variadic sequence of variables. - * See: https://github.com/winstonjs/winston - * + * Internally, this logger defaults to an instance of [`winston`](https://github.com/winstonjs/winston)'s + * `Logger` class. */ export interface LDLogger { /** * The error logger. * * @param args - * A sequence of any javascript variables + * A sequence of any JavaScript values. */ - error: (...args: any[]) => void; + error(...args: any[]): void; /** * The warning logger. * * @param args - * A sequence of any javascript variables + * A sequence of any JavaScript values. */ - warn: (...args: any[]) => void; + warn(...args: any[]): void; /** * The info logger. * * @param args - * A sequence of any javascript variables + * A sequence of any JavaScript values. */ - info: (...args: any[]) => void; + info(...args: any[]): void; /** * The debug logger. * * @param args - * A sequence of any javascript variables + * A sequence of any JavaScript values. */ - debug: (...args: any[]) => void; + debug(...args: any[]): void; } /** @@ -530,7 +565,7 @@ declare module 'ldclient-node' { } /** - * Optional settings that can be passed to LDClient.allFlagsState(). + * Optional settings that can be passed to [[LDClient.allFlagsState]]. */ export type LDFlagsStateOptions = { /** @@ -544,221 +579,265 @@ declare module 'ldclient-node' { */ withReasons?: boolean; /** - * True if any flag metadata that is normally only used for event generation - such as flag versions and - * evaluation reasons - should be omitted for any flag that does not have event tracking or debugging turned on. + * True if any flag metadata that is normally only used for event generation-- such as flag versions and + * evaluation reasons-- should be omitted for any flag that does not have event tracking or debugging turned on. * This reduces the size of the JSON data if you are passing the flag state to the front end. */ detailsOnlyForTrackedFlags?: boolean; }; /** - * The LaunchDarkly client's instance interface. + * The LaunchDarkly SDK client object. + * + * Applications should configure the client at startup time and continue to use it throughout the lifetime + * of the application, rather than creating instances on the fly. + * + * Note that `LDClient` inherits from `EventEmitter`, so you can use the standard `on()`, `once()`, and + * `off()` methods to receive events. The client can emit the following kinds of events: * - * @see http://docs.launchdarkly.com/docs/js-sdk-reference + * - `"ready"`: Sent only once, when the client has successfully connected to LaunchDarkly. + * - `"failed"`: Sent only once, if the client has permanently failed to connect to LaunchDarkly. + * - `"error"`: Contains an error object describing some abnormal condition that the client has detected + * (such as a network error). + * - `"update"`: The client has received a change to a feature flag. The event parameter is an object + * containing the flag configuration; its `key` property is the flag key. Note that this does not + * necessarily mean the flag's value has changed for any particular user, only that some part of the + * flag configuration was changed. + * - `"update:KEY"`: The client has received a change to the feature flag whose key is KEY. This is the + * same as `"update"` but allows you to listen for a specific flag. + * + * For more information, see the [SDK Reference Guide](http://docs.launchdarkly.com/docs/node-sdk-reference). */ export interface LDClient extends EventEmitter { /** - * @returns Whether the client library has completed initialization. + * Tests whether the client has completed initialization. + * + * If this returns false, it means that the client has not yet successfully connected to LaunchDarkly. + * It might still be in the process of starting up, or it might be attempting to reconnect after an + * unsuccessful attempt, or it might have received an unrecoverable error (such as an invalid SDK key) + * and given up. + * + * @returns + * True if the client has successfully initialized. */ - initialized: () => boolean; + initialized(): boolean; /** - * Returns a Promise that will be resolved if and when the client is successfully initialized. - * If initialization fails, the Promise will not resolve, but will not be rejected either - * (unlike waitForInitialization). + * Returns a Promise that tracks the client's initialization state. * - * This method is deprecated and will be removed in a future release. Instead, use - * waitForInitialization(), which waits for either success or failure. + * **Deprecated**: please use [[waitForInitialization]] instead. The difference between that method + * and this one is that `waitUntilReady` never rejects the Promise, even if initialization fails. * - * @returns a Promise containing the initialization state of the client + * @returns + * A Promise that will be resolved if the client initializes successfully. */ - waitUntilReady: () => Promise; + waitUntilReady(): Promise; /** - * Returns a Promise that will be resolved if the client successfully initializes, or - * rejected if client initialization has irrevocably failed (for instance, if it detects - * that the SDK key is invalid). The success and failure cases can also be detected by listening - * for the events "ready" and "failed". - * @returns a Promise containing the initialization state of the client; if successful, the resolved - * value is the same client object + * Returns a Promise that tracks the client's initialization state. + * + * The Promise will be resolved if the client successfully initializes, or rejected if client + * initialization has irrevocably failed (for instance, if it detects that the SDK key is invalid). + * + * Note that you can also use event listeners ([[on]]) for the same purpose: the event `"ready"` + * indicates success, and `"failed"` indicates failure. + * + * @returns + * A Promise that will be resolved if the client initializes successfully, or rejected if it + * fails. If successful, the result is the same client object. */ - waitForInitialization: () => Promise; + waitForInitialization(): Promise; /** - * Retrieves a flag's value. + * Determines the variation of a feature flag for a user. * * @param key - * The key of the flag for which to retrieve the corresponding value. + * The unique key of the feature flag. * @param user - * The user for the variation. - * - * The variation call will automatically create a user in LaunchDarkly if a user with that user key doesn't exist already. - * + * The end user requesting the flag. The client will generate an analytics event to register + * this user with LaunchDarkly if the user does not already exist. * @param defaultValue - * The value to use if the flag is not available (for example, if the - * user is offline or a flag is requested that does not exist). - * + * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @param callback - * The callback to receive the variation result. - * - * @returns a Promise containing the flag value + * A Node-style callback to receive the result value. If omitted, you will receive a Promise instead. + * @returns + * If you provided a callback, then nothing. Otherwise, a Promise which will be resolved + * with the result value. */ - variation: ( + variation( key: string, user: LDUser, defaultValue: LDFlagValue, callback?: (err: any, res: LDFlagValue) => void - ) => Promise; + ): Promise | void; /** - * Retrieves a flag's value, along with information about how it was calculated, in the form - * of an LDEvaluationDetail object. + * Determines the variation of a feature flag for a user, along with information about how it was + * calculated. * - * The reason property of the result will also be included in analytics events, if you are + * The `reason` property of the result will also be included in analytics events, if you are * capturing detailed event data for this flag. * + * For more information, see the [SDK reference guide](https://docs.launchdarkly.com/docs/evaluation-reasons). + * * @param key - * The key of the flag for which to retrieve the corresponding value. + * The unique key of the feature flag. * @param user - * The user for the variation. - * - * The variation call will automatically create a user in LaunchDarkly if a user with that user key doesn't exist already. - * + * The end user requesting the flag. The client will generate an analytics event to register + * this user with LaunchDarkly if the user does not already exist. * @param defaultValue - * The value to use if the flag is not available (for example, if the - * user is offline or a flag is requested that does not exist). - * + * The default value of the flag, to be used if the value is not available from LaunchDarkly. * @param callback - * The callback to receive the result. - * - * @returns a Promise containing the flag value and explanation + * A Node-style callback to receive the result (as an [[LDEvaluationDetail]]). If omitted, you + * will receive a Promise instead. + * @returns + * If you provided a callback, then nothing. Otherwise, a Promise which will be resolved + * with the result (as an [[LDEvaluationDetail]]). */ - variationDetail: ( + variationDetail( key: string, user: LDUser, defaultValue: LDFlagValue, callback?: (err: any, res: LDEvaluationDetail) => void - ) => Promise; + ): Promise | void; - toggle: ( + /** + * Synonym for [[variation]]. + * + * **Deprecated**. Please use [[variation]] instead. + */ + toggle( key: string, user: LDUser, defaultValue: LDFlagValue, callback?: (err: any, res: LDFlagValue) => void - ) => Promise; + ): Promise | void; /** * Retrieves the set of all flag values for a user. * - * This method is deprecated; use allFlagsState() instead. Current versions of the client-side - * SDK will not generate analytics events correctly if you pass the result of allFlags(). + * **Deprecated**: use [[allFlagsState]] instead. Current versions of the client-side + * SDK will not generate analytics events correctly if you pass the result of `allFlags()`. * * @param user + * The end user requesting the feature flags. * @param callback - * The node style callback to receive the variation result. - * @returns a Promise containing the set of all flag values for a user + * A Node-style callback to receive the result (as an [[LDFlagSet]]). If omitted, you + * will receive a Promise instead. + * @returns + * If you provided a callback, then nothing. Otherwise, a Promise which will be resolved + * with the result as an [[LDFlagSet]]. */ - allFlags: ( + allFlags( user: LDUser, - callback?: (err: any, res: LDFlagSet) => void - ) => Promise; + callback?: (err: Error, res: LDFlagSet) => void + ): Promise | void; /** - * Builds an object that encapsulates the state of all feature flags for a given user, - * including the flag values and also metadata that can be used on the front end. This + * Builds an object that encapsulates the state of all feature flags for a given user. + * This includes the flag values and also metadata that can be used on the front end. This * method does not send analytics events back to LaunchDarkly. * * The most common use case for this method is to bootstrap a set of client-side - * feature flags from a back-end service. Call the toJSON() method of the returned object + * feature flags from a back-end service. Call the `toJSON()` method of the returned object * to convert it to the data structure used by the client-side SDK. * - * @param user The end user requesting the feature flags. - * @param options Optional object with properties that determine how the state is computed; - * set `clientSideOnly: true` to include only client-side-enabled flags - * @param callback The node-style callback to receive the state result. - * @returns a Promise containing the state object + * @param user + * The end user requesting the feature flags. + * @param options + * Optional [[LDFlagsStateOptions]] to determine how the state is computed. + * @param callback + * A Node-style callback to receive the result (as an [[LDFlagsState]]). If omitted, you + * will receive a Promise instead. + * @returns + * If you provided a callback, then nothing. Otherwise, a Promise which will be resolved + * with the result as an [[LDFlagsState]]. */ - allFlagsState: ( + allFlagsState( user: LDUser, options?: LDFlagsStateOptions, - callback?: (err: any, res: LDFlagsState) => void - ) => Promise; + callback?: (err: Error, res: LDFlagsState) => void + ): Promise | void; /** + * Computes an HMAC signature of a user signed with the client's SDK key. * - * The secure_mode_hash method computes an HMAC signature of a user signed with the client's SDK key. - * - * If you're using our JavaScript SDK for client-side flags, this - * method generates the signature you need for secure mode. + * For more information, see the JavaScript SDK Reference Guide on + * [Secure mode](https://github.com/launchdarkly/js-client#secure-mode). * * @param user + * The user properties. * - * @returns The hash. + * @returns + * The hash string. */ - secureModeHash: (user: LDUser) => string; + secureModeHash(user: LDUser): string; /** - * Close the update processor as well as the attached feature store. + * Discards all network connections, background tasks, and other resources held by the client. + * + * Do not attempt to use the client after calling this method. */ - close: () => void; + close(): void; /** + * Tests whether the client is configured in offline mode. * - * @returns Whether the client is configured in offline mode. + * @returns + * True if the `offline` property is true in your [[LDOptions]]. */ - isOffline: () => boolean; + isOffline(): boolean; /** - * Track page events to use in goals or A/B tests. + * Tracks that a user performed an event. * - * LaunchDarkly automatically tracks pageviews and clicks that are - * specified in the Goals section of their dashboard. This can be used - * to track custom goals or other events that do not currently have - * goals. + * LaunchDarkly automatically tracks pageviews and clicks that are specified in the Goals + * section of the dashboard. This can be used to track custom goals or other events that do + * not currently have goals. * * @param key - * The event to record. + * The name of the event, which may correspond to a goal in A/B tests. * @param user * The user to track. * @param data - * Additional information to associate with the event. + * Optional additional information to associate with the event. */ - track: (key: string, user: LDUser, data?: any) => void; + track(key: string, user: LDUser, data?: any): void; /** * Identifies a user to LaunchDarkly. * - * This only needs to be called if the user changes identities because - * normally the user's identity is set during client initialization. + * This simply creates an analytics event that will transmit the given user properties to + * LaunchDarkly, so that the user will be visible on your dashboard even if you have not + * evaluated any flags for that user. It has no other effect. * * @param user - * A map of user options. Must contain at least the `key` property - * which identifies the user. + * The user properties. Must contain at least the `key` property. */ - identify: (user: LDUser) => void; + identify(user: LDUser): void; /** - * Flush the queue + * Flushes all pending analytics events. * - * Internally, the LaunchDarkly SDK keeps an event queue for track and identify calls. - * These are flushed periodically (see configuration option: flushInterval) - * and when the queue size limit (see configuration option: capacity) is reached. + * Normally, batches of events are delivered in the background at intervals determined by the + * `flushInterval` property of [[LDOptions]]. Calling `flush()` triggers an immediate delivery. * * @param callback - * A function which will be called when the flush completes; if omitted, you - * will receive a Promise instead + * A function which will be called when the flush completes. If omitted, you + * will receive a Promise instead. * - * @returns a Promise which resolves once flushing is finished, if you did not - * provide a callback; note that it will be rejected if the HTTP request fails, so be - * sure to provide a rejection handler if you are not using a callback + * @returns + * If you provided a callback, then nothing. Otherwise, a Promise which resolves once + * flushing is finished. Note that the Promise will be rejected if the HTTP request + * fails, so be sure to attach a rejection handler to it. */ - flush: (callback?: (err: any, res: boolean) => void) => Promise; + flush(callback?: (err: Error, res: boolean) => void): Promise | void; } /** - * Configuration for FileDataSource. + * Configuration for [[FileDataSource]]. */ - export interface FileDataSourceOptions { + export type FileDataSourceOptions = { /** * The path(s) of the file(s) that FileDataSource will read. */ @@ -766,14 +845,14 @@ declare module 'ldclient-node' { /** * True if FileDataSource should reload flags whenever one of the data files is modified. - * This feature uses Node's fs.watch() API, so it is subject to - * the limitations described here: https://nodejs.org/docs/latest/api/fs.html#fs_fs_watch_filename_options_listener + * This feature uses Node's `fs.watch()` API, so it is subject to + * the limitations described [here](https://nodejs.org/docs/latest/api/fs.html#fs_fs_watch_filename_options_listener). */ autoUpdate?: boolean; /** * Configures a logger for warnings and errors. This can be a custom logger or an instance of - * winston.Logger. By default, it uses the same logger as the rest of the SDK. + * `winston.Logger`. By default, it uses the same logger as the rest of the SDK. */ logger?: LDLogger | object; } @@ -781,37 +860,33 @@ declare module 'ldclient-node' { /** * Creates an object that allows you to use local files as a source of feature flag state, * instead of connecting to LaunchDarkly. This would typically be used in a test environment. - *

- * To use this component, call FileDataSource(options) and store the result in the "updateProcessor" + * + * To use this component, call `FileDataSource(options)` and store the result in the `updateProcessor` * property of your LaunchDarkly client configuration: - *

+   * 
    *     var dataSource = LaunchDarkly.FileDataSource({ paths: [ myFilePath ] });
    *     var config = { updateProcessor: dataSource };
-   * 
- *

- * Flag data files can be either JSON or YAML. They contain an object with three possible - * properties: - *

    - *
  • "flags": Full feature flag definitions. - *
  • "flagValues": Simplified feature flags, just a map of flag keys to values. - *
  • "segments": User segment definitions. - *
- *

- * The format of the data in "flags" and "segments" is defined by the LaunchDarkly application - * and is subject to change. You can query existing flags and segments from LaunchDarkly in JSON - * format by querying https://app.launchdarkly.com/sdk/latest-all and passing your SDK key in - * the Authorization header. - *

- * For more details, see the LaunchDarkly reference guide: - * https://docs.launchdarkly.com/v2.0/docs/reading-flags-from-a-file * - * @param options configuration for the data source; you should at least set the "paths" property + * This will cause the client not to connect to LaunchDarkly to get feature flags. The + * client may still make network connections to send analytics events, unless you have disabled + * this in your configuration with `send_events` or `offline`. + * + * The format of the data files is described in the SDK Reference Guide on + * [Reading flags from a file](https://docs.launchdarkly.com/v2.0/docs/reading-flags-from-a-file). + * + * @param options + * Configuration for the data source. You should at least set the `paths` property. + * @returns + * An object to put in the `updateProcessor` property for [[LDOptions]]. */ export function FileDataSource( options: FileDataSourceOptions ): object; } +/** + * @ignore + */ declare module 'ldclient-node/streaming' { import { LDOptions, @@ -826,6 +901,10 @@ declare module 'ldclient-node/streaming' { ): LDStreamProcessor; export = StreamProcessor; } + +/** + * @ignore + */ declare module 'ldclient-node/requestor' { import { LDOptions, LDFeatureRequestor } from 'ldclient-node'; From 0ef07f6f98c954255c184e1754d03499d882bcf2 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 6 Feb 2019 19:51:35 -0800 Subject: [PATCH 089/330] misc doc fixes --- index.d.ts | 123 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/index.d.ts b/index.d.ts index d51e90f..62ed2dc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -244,6 +244,14 @@ declare module 'ldclient-node' { */ featureStore?: LDFeatureStore; + /** + * A component that obtains feature flag data and puts it in the feature store. + * + * By default, this is the client's default streaming or polling component. It can be changed + * for testing purposes; see [[FileDataSource]]. + */ + updateProcessor?: object; + /** * The interval in between flushes of the analytics events queue, in seconds. */ @@ -266,6 +274,11 @@ declare module 'ldclient-node' { */ proxyPort?: number; + /** + * When using an HTTP proxy, specifies whether it is accessed via `http` or `https`. + */ + proxyScheme?: string; + /** * Allows you to specify basic authentication parameters for an optional HTTP proxy. * Usually of the form `username:password`. @@ -442,106 +455,129 @@ declare module 'ldclient-node' { } /** - * The LaunchDarkly client feature store. + * Interface for a feature store component. * - * The client uses this internally to store flag updates it - * receives from LaunchDarkly. + * The feature store is what the client uses to store feature flag data that has been received + * from LaunchDarkly. By default, it uses an in-memory implementation; there are also adapters + * for Redis and other databases (see the [SDK Reference Guide](https://docs.launchdarkly.com/v2.0/docs/using-a-persistent-feature-store)). + * You will not need to use this interface unless you are writing your own implementation. */ export interface LDFeatureStore { /** - * Get a flag's value. + * Get an entity from the store. + * + * The store should treat any entity with the property `deleted: true` as "not found". * * @param kind - * The type of data to be accessed + * The type of data to be accessed. The `namespace` property of this object indicates which + * collection of entities to use, e.g. `"features"` or `"segments"`. The store should not + * make any assumptions about the format of the data, but just return a JSON object. * * @param key - * The flag key + * The unique key of the entity within the specified collection. * * @param callback - * Will be called with the resulting flag. + * Will be called with the retrieved entity, or null if not found. */ - get: (kind: object, key: string, callback: (res: LDFlagValue) => void) => void; + get(kind: object, key: string, callback: (res: object) => void): void; /** - * Get all flags. + * Get all entities from a collection. + * + * The store should filter out any entities with the property `deleted: true`. * * @param kind - * The type of data to be accessed + * The type of data to be accessed. The `namespace` property of this object indicates which + * collection of entities to use, e.g. `"features"` or `"segments"`. The store should not + * make any assumptions about the format of the data, but just return an object in which + * each key is the `key` property of an entity and the value is the entity. * * @param callback - * Will be called with the resulting flag set. + * Will be called with the resulting map. */ - all: (kind: object, callback: (res: LDFlagSet) => void) => void; + all(kind: object, callback: (res: object) => void): void; /** - * Initialize the store. + * Initialize the store, overwriting any existing data. * - * @param flags - * Populate the store with an initial flag set. + * @param allData + * An object in which each key is the "namespace" of a collection (e.g. `"features"`) and + * the value is an object that maps keys to entities. * * @param callback - * Will be called when the store has been initialized. + * Will be called when the store has been initialized. */ - init: (flags: LDFlagSet, callback?: () => void) => void; + init(allData: object, callback: () => void): void; /** - * Delete a key from the store. + * Delete an entity from the store. + * + * Deletion should be implemented by storing a placeholder object with the property + * `deleted: true` and a `version` property equal to the provided version. In other words, + * it should be exactly the same as calling `upsert` with such an object. * * @param kind - * The type of data to be accessed + * The type of data to be accessed. The `namespace` property of this object indicates which + * collection of entities to use, e.g. `"features"` or `"segments"`. * * @param key - * The flag key. + * The unique key of the entity within the specified collection. * * @param version - * The next version to increment the flag. The store should not update - * a newer version with an older version. + * A number that must be greater than the `version` property of the existing entity in + * order for it to be deleted. If it is less than or equal to the existing version, the + * method should do nothing. * * @param callback - * Will be called when the delete operation is complete. + * Will be called when the delete operation is complete. */ - delete: (kind: object, key: string, version: string, callback?: () => void) => void; + delete(kind: object, key: string, version: string, callback: () => void): void; /** - * Upsert a flag to the store. + * Add an entity or update an existing entity. * * @param kind - * The type of data to be accessed + * The type of data to be accessed. The `namespace` property of this object indicates which + * collection of entities to use, e.g. `"features"` or `"segments"`. The store should not + * make any assumptions about the format of the data, but just return a JSON object. * * @param key - * The flag key. + * The unique key of the entity within the specified collection. * - * @param flag - * The feature flag for the corresponding key. + * @param data + * The contents of the entity, as an object that can be converted to JSON. The store + * should check the `version` property of this object, and should *not* overwrite any + * existing data if the existing `version` is greater than or equal to that value. * * @param callback - * Will be called after the upsert operation is complete. + * Will be called after the upsert operation is complete. */ - upsert: (kind: object, key: string, flag: LDFlagValue, callback?: () => void) => void; + upsert(kind: object, key: string, data: object, callback: () => void): void; /** - * Is the store initialized? + * Tests whether the store is initialized. * - * @param callback - * Will be called when the store is initialized. - * - * @returns - * Truthy if the cache is already initialized. + * "Initialized" means that the store has been populated with data, either by the client + * having called `init()` within this process, or by another process (if this is a shared + * database). * + * @param callback + * Will be called back with the boolean result. */ - initialized: (callback?: (err: any) => void) => boolean; + initialized(callback: (isInitialized: boolean) => void): void; /** - * Close the feature store. - * + * Releases any resources being used by the feature store. */ - close: () => void; + close(): void; } /** * The LaunchDarkly client stream processor * * The client uses this internally to retrieve updates from LaunchDarkly. + * + * @ignore */ export interface LDStreamProcessor { start: (fn?: (err?: any) => void) => void; @@ -552,8 +588,9 @@ declare module 'ldclient-node' { /** * The LaunchDarkly client feature flag requestor * - * The client uses this internally to retrieve feature - * flags from LaunchDarkly. + * The client uses this internally to retrieve feature flags from LaunchDarkly. + * + * @ignore */ export interface LDFeatureRequestor { requestObject: ( From 1aea408ab6ef6af6a9bd63858f0498b0a1883b6c Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 6 Feb 2019 19:55:46 -0800 Subject: [PATCH 090/330] rm unused var --- docs/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index b8a2973..8280631 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,6 +1,4 @@ -TYPEDOC_OPTIONS=--name "LaunchDarkly Node SDK" --includeDeclarations --exclude "**/node_modules/**" --exclude test-types.ts - .PHONY: html html: From 57562b8518ba3305661666ede69fc8c48b8bd6df Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 6 Feb 2019 20:32:08 -0800 Subject: [PATCH 091/330] add custom theme --- docs/theme/assets/css/main.css | 865 ++++++++++++++++++ docs/theme/assets/css/main.css.map | 7 + docs/theme/assets/images/icons.png | Bin 0 -> 9487 bytes docs/theme/assets/images/icons@2x.png | Bin 0 -> 27740 bytes docs/theme/assets/images/widgets.png | Bin 0 -> 480 bytes docs/theme/assets/images/widgets@2x.png | Bin 0 -> 855 bytes docs/theme/assets/js/main.js | 5 + docs/theme/helpers/if_eq.js | 10 + docs/theme/layouts/default.hbs | 50 + docs/theme/partials/analytics.hbs | 11 + docs/theme/partials/breadcrumb.hbs | 13 + docs/theme/partials/comment.hbs | 22 + docs/theme/partials/footer.hbs | 66 ++ docs/theme/partials/header.hbs | 71 ++ docs/theme/partials/hierarchy.hbs | 17 + docs/theme/partials/index.hbs | 19 + docs/theme/partials/member.declaration.hbs | 22 + docs/theme/partials/member.getterSetter.hbs | 37 + docs/theme/partials/member.hbs | 22 + docs/theme/partials/member.signature.body.hbs | 56 ++ .../theme/partials/member.signature.title.hbs | 28 + docs/theme/partials/member.signatures.hbs | 13 + docs/theme/partials/member.sources.hbs | 22 + docs/theme/partials/members.group.hbs | 8 + docs/theme/partials/members.hbs | 5 + docs/theme/partials/navigation.hbs | 26 + docs/theme/partials/parameter.hbs | 81 ++ docs/theme/partials/toc.hbs | 10 + docs/theme/partials/toc.root.hbs | 18 + docs/theme/partials/type.hbs | 83 ++ docs/theme/partials/typeAndParent.hbs | 42 + docs/theme/partials/typeParameters.hbs | 14 + docs/theme/templates/index.hbs | 3 + docs/theme/templates/reflection.hbs | 79 ++ docs/typedoc.js | 2 +- 35 files changed, 1726 insertions(+), 1 deletion(-) create mode 100644 docs/theme/assets/css/main.css create mode 100644 docs/theme/assets/css/main.css.map create mode 100644 docs/theme/assets/images/icons.png create mode 100644 docs/theme/assets/images/icons@2x.png create mode 100644 docs/theme/assets/images/widgets.png create mode 100644 docs/theme/assets/images/widgets@2x.png create mode 100644 docs/theme/assets/js/main.js create mode 100644 docs/theme/helpers/if_eq.js create mode 100644 docs/theme/layouts/default.hbs create mode 100644 docs/theme/partials/analytics.hbs create mode 100644 docs/theme/partials/breadcrumb.hbs create mode 100644 docs/theme/partials/comment.hbs create mode 100644 docs/theme/partials/footer.hbs create mode 100644 docs/theme/partials/header.hbs create mode 100644 docs/theme/partials/hierarchy.hbs create mode 100644 docs/theme/partials/index.hbs create mode 100644 docs/theme/partials/member.declaration.hbs create mode 100644 docs/theme/partials/member.getterSetter.hbs create mode 100644 docs/theme/partials/member.hbs create mode 100644 docs/theme/partials/member.signature.body.hbs create mode 100644 docs/theme/partials/member.signature.title.hbs create mode 100644 docs/theme/partials/member.signatures.hbs create mode 100644 docs/theme/partials/member.sources.hbs create mode 100644 docs/theme/partials/members.group.hbs create mode 100644 docs/theme/partials/members.hbs create mode 100644 docs/theme/partials/navigation.hbs create mode 100644 docs/theme/partials/parameter.hbs create mode 100644 docs/theme/partials/toc.hbs create mode 100644 docs/theme/partials/toc.root.hbs create mode 100644 docs/theme/partials/type.hbs create mode 100644 docs/theme/partials/typeAndParent.hbs create mode 100644 docs/theme/partials/typeParameters.hbs create mode 100644 docs/theme/templates/index.hbs create mode 100644 docs/theme/templates/reflection.hbs diff --git a/docs/theme/assets/css/main.css b/docs/theme/assets/css/main.css new file mode 100644 index 0000000..48b3645 --- /dev/null +++ b/docs/theme/assets/css/main.css @@ -0,0 +1,865 @@ +/*! normalize.css v1.1.3 | MIT License | git.io/normalize */ +/* ========================================================================== HTML5 display definitions ========================================================================== */ +/** Correct `block` display not defined in IE 6/7/8/9 and Firefox 3. */ +article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } + +/** Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. */ +audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } + +/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */ +audio:not([controls]) { display: none; height: 0; } + +/** Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. Known issue: no IE 6 support. */ +[hidden] { display: none; } + +/* ========================================================================== Base ========================================================================== */ +/** 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using `em` units. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */ +html { font-size: 100%; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ font-family: sans-serif; } + +/** Address `font-family` inconsistency between `textarea` and other form elements. */ +button, input, select, textarea { font-family: sans-serif; } + +/** Address margins handled incorrectly in IE 6/7. */ +body { margin: 0; } + +/* ========================================================================== Links ========================================================================== */ +/** Address `outline` inconsistency between Chrome and other browsers. */ +a:focus { outline: thin dotted; } +a:active, a:hover { outline: 0; } + +/** Improve readability when focused and also mouse hovered in all browsers. */ +/* ========================================================================== Typography ========================================================================== */ +/** Address font sizes and margins set differently in IE 6/7. Address font sizes within `section` and `article` in Firefox 4+, Safari 5, and Chrome. */ +h1 { font-size: 2em; margin: 0.67em 0; } + +h2 { font-size: 1.5em; margin: 0.83em 0; } + +h3 { font-size: 1.17em; margin: 1em 0; } + +h4, .tsd-index-panel h3 { font-size: 1em; margin: 1.33em 0; } + +h5 { font-size: 0.83em; margin: 1.67em 0; } + +h6 { font-size: 0.67em; margin: 2.33em 0; } + +/** Address styling not present in IE 7/8/9, Safari 5, and Chrome. */ +abbr[title] { border-bottom: 1px dotted; } + +/** Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. */ +b, strong { font-weight: bold; } + +blockquote { margin: 1em 40px; } + +/** Address styling not present in Safari 5 and Chrome. */ +dfn { font-style: italic; } + +/** Address differences between Firefox and other browsers. Known issue: no IE 6/7 normalization. */ +hr { box-sizing: content-box; height: 0; } + +/** Address styling not present in IE 6/7/8/9. */ +mark { background: #ff0; color: #000; } + +/** Address margins set differently in IE 6/7. */ +p, pre { margin: 1em 0; } + +/** Correct font family set oddly in IE 6, Safari 4/5, and Chrome. */ +code, kbd, pre, samp { font-family: monospace, serif; _font-family: "courier new", monospace; font-size: 1em; } + +/** Improve readability of pre-formatted text in all browsers. */ +pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } + +/** Address CSS quotes not supported in IE 6/7. */ +q { quotes: none; } +q:before, q:after { content: ""; content: none; } + +/** Address `quotes` property not supported in Safari 4. */ +/** Address inconsistent and variable font size in all browsers. */ +small { font-size: 80%; } + +/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */ +sub { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } + +sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; top: -0.5em; } + +sub { bottom: -0.25em; } + +/* ========================================================================== Lists ========================================================================== */ +/** Address margins set differently in IE 6/7. */ +dl, menu, ol, ul { margin: 1em 0; } + +dd { margin: 0 0 0 40px; } + +/** Address paddings set differently in IE 6/7. */ +menu, ol, ul { padding: 0 0 0 40px; } + +/** Correct list images handled incorrectly in IE 7. */ +nav ul, nav ol { list-style: none; list-style-image: none; } + +/* ========================================================================== Embedded content ========================================================================== */ +/** 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 2. Improve image quality when scaled in IE 7. */ +img { border: 0; /* 1 */ -ms-interpolation-mode: bicubic; } + +/* 2 */ +/** Correct overflow displayed oddly in IE 9. */ +svg:not(:root) { overflow: hidden; } + +/* ========================================================================== Figures ========================================================================== */ +/** Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. */ +figure, form { margin: 0; } + +/* ========================================================================== Forms ========================================================================== */ +/** Correct margin displayed oddly in IE 6/7. */ +/** Define consistent border, margin, and padding. */ +fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } + +/** 1. Correct color not being inherited in IE 6/7/8/9. 2. Correct text not wrapping in Firefox 3. 3. Correct alignment displayed oddly in IE 6/7. */ +legend { border: 0; /* 1 */ padding: 0; white-space: normal; /* 2 */ *margin-left: -7px; } + +/* 3 */ +/** 1. Correct font size not being inherited in all browsers. 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, and Chrome. 3. Improve appearance and consistency in all browsers. */ +button, input, select, textarea { font-size: 100%; /* 1 */ margin: 0; /* 2 */ vertical-align: baseline; /* 3 */ *vertical-align: middle; } + +/* 3 */ +/** Address Firefox 3+ setting `line-height` on `input` using `!important` in the UA stylesheet. */ +button, input { line-height: normal; } + +/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. Correct `select` style inheritance in Firefox 4+ and Opera. */ +button, select { text-transform: none; } + +/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. 4. Remove inner spacing in IE 7 without affecting normal text inputs. Known issue: inner spacing remains in IE 6. */ +button, html input[type="button"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ *overflow: visible; } + +/* 4 */ +input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ *overflow: visible; } + +/* 4 */ +/** Re-set default cursor for disabled elements. */ +button[disabled], html input[disabled] { cursor: default; } + +/** 1. Address box sizing set to content-box in IE 8/9. 2. Remove excess padding in IE 8/9. 3. Remove excess padding in IE 7. Known issue: excess padding remains in IE 6. */ +input { /* 3 */ } +input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ *height: 13px; /* 3 */ *width: 13px; } +input[type="search"] { -webkit-appearance: textfield; /* 1 */ /* 2 */ box-sizing: content-box; } +input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } + +/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */ +/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */ +/** Remove inner padding and border in Firefox 3+. */ +button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } + +/** 1. Remove default vertical scrollbar in IE 6/7/8/9. 2. Improve readability and alignment in all browsers. */ +textarea { overflow: auto; /* 1 */ vertical-align: top; } + +/* 2 */ +/* ========================================================================== Tables ========================================================================== */ +/** Remove most spacing between table cells. */ +table { border-collapse: collapse; border-spacing: 0; } + +/* Visual Studio-like style based on original C# coloring by Jason Diamond */ +.hljs { display: inline-block; padding: 0.5em; background: white; color: black; } + +.hljs-comment, .hljs-annotation, .hljs-template_comment, .diff .hljs-header, .hljs-chunk, .apache .hljs-cbracket { color: #008000; } + +.hljs-keyword, .hljs-id, .hljs-built_in, .css .smalltalk .hljs-class, .hljs-winutils, .bash .hljs-variable, .tex .hljs-command, .hljs-request, .hljs-status, .nginx .hljs-title { color: #00f; } + +.xml .hljs-tag { color: #00f; } +.xml .hljs-tag .hljs-value { color: #00f; } + +.hljs-string, .hljs-title, .hljs-parent, .hljs-tag .hljs-value, .hljs-rules .hljs-value { color: #a31515; } + +.ruby .hljs-symbol { color: #a31515; } +.ruby .hljs-symbol .hljs-string { color: #a31515; } + +.hljs-template_tag, .django .hljs-variable, .hljs-addition, .hljs-flow, .hljs-stream, .apache .hljs-tag, .hljs-date, .tex .hljs-formula, .coffeescript .hljs-attribute { color: #a31515; } + +.ruby .hljs-string, .hljs-decorator, .hljs-filter .hljs-argument, .hljs-localvars, .hljs-array, .hljs-attr_selector, .hljs-pseudo, .hljs-pi, .hljs-doctype, .hljs-deletion, .hljs-envvar, .hljs-shebang, .hljs-preprocessor, .hljs-pragma, .userType, .apache .hljs-sqbracket, .nginx .hljs-built_in, .tex .hljs-special, .hljs-prompt { color: #2b91af; } + +.hljs-phpdoc, .hljs-javadoc, .hljs-xmlDocTag { color: #808080; } + +.vhdl .hljs-typename { font-weight: bold; } +.vhdl .hljs-string { color: #666666; } +.vhdl .hljs-literal { color: #a31515; } +.vhdl .hljs-attribute { color: #00b0e8; } + +.xml .hljs-attribute { color: #f00; } + +.col > :first-child, .col-1 > :first-child, .col-2 > :first-child, .col-3 > :first-child, .col-4 > :first-child, .col-5 > :first-child, .col-6 > :first-child, .col-7 > :first-child, .col-8 > :first-child, .col-9 > :first-child, .col-10 > :first-child, .col-11 > :first-child, .tsd-panel > :first-child, ul.tsd-descriptions > li > :first-child, .col > :first-child > :first-child, .col-1 > :first-child > :first-child, .col-2 > :first-child > :first-child, .col-3 > :first-child > :first-child, .col-4 > :first-child > :first-child, .col-5 > :first-child > :first-child, .col-6 > :first-child > :first-child, .col-7 > :first-child > :first-child, .col-8 > :first-child > :first-child, .col-9 > :first-child > :first-child, .col-10 > :first-child > :first-child, .col-11 > :first-child > :first-child, .tsd-panel > :first-child > :first-child, ul.tsd-descriptions > li > :first-child > :first-child, .col > :first-child > :first-child > :first-child, .col-1 > :first-child > :first-child > :first-child, .col-2 > :first-child > :first-child > :first-child, .col-3 > :first-child > :first-child > :first-child, .col-4 > :first-child > :first-child > :first-child, .col-5 > :first-child > :first-child > :first-child, .col-6 > :first-child > :first-child > :first-child, .col-7 > :first-child > :first-child > :first-child, .col-8 > :first-child > :first-child > :first-child, .col-9 > :first-child > :first-child > :first-child, .col-10 > :first-child > :first-child > :first-child, .col-11 > :first-child > :first-child > :first-child, .tsd-panel > :first-child > :first-child > :first-child, ul.tsd-descriptions > li > :first-child > :first-child > :first-child { margin-top: 0; } +.col > :last-child, .col-1 > :last-child, .col-2 > :last-child, .col-3 > :last-child, .col-4 > :last-child, .col-5 > :last-child, .col-6 > :last-child, .col-7 > :last-child, .col-8 > :last-child, .col-9 > :last-child, .col-10 > :last-child, .col-11 > :last-child, .tsd-panel > :last-child, ul.tsd-descriptions > li > :last-child, .col > :last-child > :last-child, .col-1 > :last-child > :last-child, .col-2 > :last-child > :last-child, .col-3 > :last-child > :last-child, .col-4 > :last-child > :last-child, .col-5 > :last-child > :last-child, .col-6 > :last-child > :last-child, .col-7 > :last-child > :last-child, .col-8 > :last-child > :last-child, .col-9 > :last-child > :last-child, .col-10 > :last-child > :last-child, .col-11 > :last-child > :last-child, .tsd-panel > :last-child > :last-child, ul.tsd-descriptions > li > :last-child > :last-child, .col > :last-child > :last-child > :last-child, .col-1 > :last-child > :last-child > :last-child, .col-2 > :last-child > :last-child > :last-child, .col-3 > :last-child > :last-child > :last-child, .col-4 > :last-child > :last-child > :last-child, .col-5 > :last-child > :last-child > :last-child, .col-6 > :last-child > :last-child > :last-child, .col-7 > :last-child > :last-child > :last-child, .col-8 > :last-child > :last-child > :last-child, .col-9 > :last-child > :last-child > :last-child, .col-10 > :last-child > :last-child > :last-child, .col-11 > :last-child > :last-child > :last-child, .tsd-panel > :last-child > :last-child > :last-child, ul.tsd-descriptions > li > :last-child > :last-child > :last-child { margin-bottom: 0; } + +.container { max-width: 1200px; margin: 0 auto; padding: 0 40px; } +@media (max-width: 640px) { .container { padding: 0 20px; } } + +.container-main { padding-bottom: 200px; } + +.row { position: relative; margin: 0 -10px; } +.row:after { visibility: hidden; display: block; content: ""; clear: both; height: 0; } + +.col, .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11 { box-sizing: border-box; float: left; padding: 0 10px; } + +.col-1 { width: 8.33333%; } + +.offset-1 { margin-left: 8.33333%; } + +.col-2 { width: 16.66667%; } + +.offset-2 { margin-left: 16.66667%; } + +.col-3 { width: 25%; } + +.offset-3 { margin-left: 25%; } + +.col-4 { width: 33.33333%; } + +.offset-4 { margin-left: 33.33333%; } + +.col-5 { width: 41.66667%; } + +.offset-5 { margin-left: 41.66667%; } + +.col-6 { width: 50%; } + +.offset-6 { margin-left: 50%; } + +.col-7 { width: 58.33333%; } + +.offset-7 { margin-left: 58.33333%; } + +.col-8 { width: 66.66667%; } + +.offset-8 { margin-left: 66.66667%; } + +.col-9 { width: 75%; } + +.offset-9 { margin-left: 75%; } + +.col-10 { width: 83.33333%; } + +.offset-10 { margin-left: 83.33333%; } + +.col-11 { width: 91.66667%; } + +.offset-11 { margin-left: 91.66667%; } + +.tsd-kind-icon { display: block; position: relative; padding-left: 20px; text-indent: -20px; } +.tsd-kind-icon:before { content: ''; display: inline-block; vertical-align: middle; width: 17px; height: 17px; margin: 0 3px 2px 0; background-image: url(../images/icons.png); } +@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .tsd-kind-icon:before { background-image: url(../images/icons@2x.png); background-size: 238px 204px; } } + +.tsd-signature.tsd-kind-icon:before { background-position: 0 -153px; } + +.tsd-kind-object-literal > .tsd-kind-icon:before { background-position: 0px -17px; } +.tsd-kind-object-literal.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -17px; } +.tsd-kind-object-literal.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -17px; } + +.tsd-kind-class > .tsd-kind-icon:before { background-position: 0px -34px; } +.tsd-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -34px; } +.tsd-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -34px; } + +.tsd-kind-class.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -51px; } +.tsd-kind-class.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -51px; } +.tsd-kind-class.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -51px; } + +.tsd-kind-interface > .tsd-kind-icon:before { background-position: 0px -68px; } +.tsd-kind-interface.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -68px; } +.tsd-kind-interface.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -68px; } + +.tsd-kind-interface.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -85px; } +.tsd-kind-interface.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -85px; } +.tsd-kind-interface.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -85px; } + +.tsd-kind-module > .tsd-kind-icon:before { background-position: 0px -102px; } +.tsd-kind-module.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -102px; } +.tsd-kind-module.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -102px; } + +.tsd-kind-external-module > .tsd-kind-icon:before { background-position: 0px -102px; } +.tsd-kind-external-module.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -102px; } +.tsd-kind-external-module.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -102px; } + +.tsd-kind-enum > .tsd-kind-icon:before { background-position: 0px -119px; } +.tsd-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -119px; } +.tsd-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -119px; } + +.tsd-kind-enum-member > .tsd-kind-icon:before { background-position: 0px -136px; } +.tsd-kind-enum-member.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -136px; } +.tsd-kind-enum-member.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -136px; } + +.tsd-kind-signature > .tsd-kind-icon:before { background-position: 0px -153px; } +.tsd-kind-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -153px; } +.tsd-kind-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -153px; } + +.tsd-kind-type-alias > .tsd-kind-icon:before { background-position: 0px -170px; } +.tsd-kind-type-alias.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -170px; } +.tsd-kind-type-alias.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -170px; } + +.tsd-kind-variable > .tsd-kind-icon:before { background-position: -136px -0px; } +.tsd-kind-variable.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -0px; } +.tsd-kind-variable.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -0px; } +.tsd-kind-variable.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -0px; } + +.tsd-kind-property > .tsd-kind-icon:before { background-position: -136px -0px; } +.tsd-kind-property.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -0px; } +.tsd-kind-property.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -0px; } +.tsd-kind-property.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -0px; } +.tsd-kind-property.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -0px; } +.tsd-kind-property.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -0px; } + +.tsd-kind-get-signature > .tsd-kind-icon:before { background-position: -136px -17px; } +.tsd-kind-get-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -17px; } +.tsd-kind-get-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -17px; } + +.tsd-kind-set-signature > .tsd-kind-icon:before { background-position: -136px -34px; } +.tsd-kind-set-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -34px; } +.tsd-kind-set-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -34px; } + +.tsd-kind-accessor > .tsd-kind-icon:before { background-position: -136px -51px; } +.tsd-kind-accessor.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -51px; } +.tsd-kind-accessor.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -51px; } +.tsd-kind-accessor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -51px; } + +.tsd-kind-function > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-method > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-call-signature > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-function.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: -136px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -85px; } + +.tsd-kind-method.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: -136px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -85px; } + +.tsd-kind-constructor > .tsd-kind-icon:before { background-position: -136px -102px; } +.tsd-kind-constructor.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -102px; } +.tsd-kind-constructor.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -102px; } +.tsd-kind-constructor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -102px; } + +.tsd-kind-constructor-signature > .tsd-kind-icon:before { background-position: -136px -102px; } +.tsd-kind-constructor-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -102px; } +.tsd-kind-constructor-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -102px; } + +.tsd-kind-index-signature > .tsd-kind-icon:before { background-position: -136px -119px; } +.tsd-kind-index-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -119px; } +.tsd-kind-index-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -119px; } + +.tsd-kind-event > .tsd-kind-icon:before { background-position: -136px -136px; } +.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -136px; } +.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -136px; } +.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -136px; } +.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -136px; } +.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -136px; } + +.tsd-is-static > .tsd-kind-icon:before { background-position: -136px -153px; } +.tsd-is-static.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -153px; } +.tsd-is-static.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -153px; } +.tsd-is-static.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -153px; } +.tsd-is-static.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -153px; } +.tsd-is-static.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -153px; } + +.tsd-is-static.tsd-kind-function > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-method > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-call-signature > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-event > .tsd-kind-icon:before { background-position: -136px -187px; } +.tsd-is-static.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -187px; } +.tsd-is-static.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -187px; } + +.no-transition { transition: none !important; } + +@-webkit-keyframes fade-in { from { opacity: 0; } + to { opacity: 1; } } + +@keyframes fade-in { from { opacity: 0; } + to { opacity: 1; } } +@-webkit-keyframes fade-out { from { opacity: 1; visibility: visible; } + to { opacity: 0; } } +@keyframes fade-out { from { opacity: 1; visibility: visible; } + to { opacity: 0; } } +@-webkit-keyframes fade-in-delayed { 0% { opacity: 0; } + 33% { opacity: 0; } + 100% { opacity: 1; } } +@keyframes fade-in-delayed { 0% { opacity: 0; } + 33% { opacity: 0; } + 100% { opacity: 1; } } +@-webkit-keyframes fade-out-delayed { 0% { opacity: 1; visibility: visible; } + 66% { opacity: 0; } + 100% { opacity: 0; } } +@keyframes fade-out-delayed { 0% { opacity: 1; visibility: visible; } + 66% { opacity: 0; } + 100% { opacity: 0; } } +@-webkit-keyframes shift-to-left { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); } + to { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } } +@keyframes shift-to-left { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); } + to { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } } +@-webkit-keyframes unshift-to-left { from { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@keyframes unshift-to-left { from { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@-webkit-keyframes pop-in-from-right { from { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@keyframes pop-in-from-right { from { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@-webkit-keyframes pop-out-to-right { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); visibility: visible; } + to { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } } +@keyframes pop-out-to-right { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); visibility: visible; } + to { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } } +body { background: #fdfdfd; font-family: "Segoe UI", sans-serif; font-size: 16px; color: #222; } + +a { color: #4da6ff; text-decoration: none; } +a:hover { text-decoration: underline; } + +code, pre { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; padding: 0.2em; margin: 0; font-size: 14px; background-color: rgba(0, 0, 0, 0.04); } + +pre { padding: 10px; } +pre code { padding: 0; font-size: 100%; background-color: transparent; } + +.tsd-typography { line-height: 1.333em; } +.tsd-typography ul { list-style: square; padding: 0 0 0 20px; margin: 0; } +.tsd-typography h4, .tsd-typography .tsd-index-panel h3, .tsd-index-panel .tsd-typography h3, .tsd-typography h5, .tsd-typography h6 { font-size: 1em; margin: 0; } +.tsd-typography h5, .tsd-typography h6 { font-weight: normal; } +.tsd-typography p, .tsd-typography ul, .tsd-typography ol { margin: 1em 0; } + +@media (min-width: 901px) and (max-width: 1024px) { html.default .col-content { width: 72%; } + html.default .col-menu { width: 28%; } + html.default .tsd-navigation { padding-left: 10px; } } +@media (max-width: 900px) { html.default .col-content { float: none; width: 100%; } + html.default .col-menu { position: fixed !important; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; z-index: 1024; top: 0 !important; bottom: 0 !important; left: auto !important; right: 0 !important; width: 100%; padding: 20px 20px 0 0; max-width: 450px; visibility: hidden; background-color: #fff; -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } + html.default .col-menu > *:last-child { padding-bottom: 20px; } + html.default .overlay { content: ""; display: block; position: fixed; z-index: 1023; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.75); visibility: hidden; } + html.default.to-has-menu .overlay { -webkit-animation: fade-in 0.4s; animation: fade-in 0.4s; } + html.default.to-has-menu header, html.default.to-has-menu footer, html.default.to-has-menu .col-content { -webkit-animation: shift-to-left 0.4s; animation: shift-to-left 0.4s; } + html.default.to-has-menu .col-menu { -webkit-animation: pop-in-from-right 0.4s; animation: pop-in-from-right 0.4s; } + html.default.from-has-menu .overlay { -webkit-animation: fade-out 0.4s; animation: fade-out 0.4s; } + html.default.from-has-menu header, html.default.from-has-menu footer, html.default.from-has-menu .col-content { -webkit-animation: unshift-to-left 0.4s; animation: unshift-to-left 0.4s; } + html.default.from-has-menu .col-menu { -webkit-animation: pop-out-to-right 0.4s; animation: pop-out-to-right 0.4s; } + html.default.has-menu body { overflow: hidden; } + html.default.has-menu .overlay { visibility: visible; } + html.default.has-menu header, html.default.has-menu footer, html.default.has-menu .col-content { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } + html.default.has-menu .col-menu { visibility: visible; -webkit-transform: translate(0, 0); transform: translate(0, 0); } } + +.tsd-page-title { padding: 70px 0 20px 0; margin: 0 0 40px 0; background: #fff; box-shadow: 0 0 5px rgba(0, 0, 0, 0.35); } +.tsd-page-title h1 { margin: 0; } + +.tsd-breadcrumb { margin: 0; padding: 0; color: #808080; } +.tsd-breadcrumb a { color: #808080; text-decoration: none; } +.tsd-breadcrumb a:hover { text-decoration: underline; } +.tsd-breadcrumb li { display: inline; } +.tsd-breadcrumb li:after { content: " / "; } + +html.minimal .container { margin: 0; } +html.minimal .container-main { padding-top: 50px; padding-bottom: 0; } +html.minimal .content-wrap { padding-left: 300px; } +html.minimal .tsd-navigation { position: fixed !important; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; box-sizing: border-box; z-index: 1; left: 0; top: 40px; bottom: 0; width: 300px; padding: 20px; margin: 0; } +html.minimal .tsd-member .tsd-member { margin-left: 0; } +html.minimal .tsd-page-toolbar { position: fixed; z-index: 2; } +html.minimal #tsd-filter .tsd-filter-group { right: 0; -webkit-transform: none; transform: none; } +html.minimal footer { background-color: transparent; } +html.minimal footer .container { padding: 0; } +html.minimal .tsd-generator { padding: 0; } +@media (max-width: 900px) { html.minimal .tsd-navigation { display: none; } + html.minimal .content-wrap { padding-left: 0; } } + +dl.tsd-comment-tags { overflow: hidden; } +dl.tsd-comment-tags dt { clear: both; float: left; padding: 1px 5px; margin: 0 10px 0 0; border-radius: 4px; border: 1px solid #808080; color: #808080; font-size: 0.8em; font-weight: normal; } +dl.tsd-comment-tags dd { margin: 0 0 10px 0; } +dl.tsd-comment-tags p { margin: 0; } + +.tsd-panel.tsd-comment .lead { font-size: 1.1em; line-height: 1.333em; margin-bottom: 2em; } +.tsd-panel.tsd-comment .lead:last-child { margin-bottom: 0; } + +.toggle-protected .tsd-is-private { display: none; } + +.toggle-public .tsd-is-private, .toggle-public .tsd-is-protected, .toggle-public .tsd-is-private-protected { display: none; } + +.toggle-inherited .tsd-is-inherited { display: none; } + +.toggle-only-exported .tsd-is-not-exported { display: none; } + +.toggle-externals .tsd-is-external { display: none; } + +#tsd-filter { position: relative; display: inline-block; height: 40px; vertical-align: bottom; } +.no-filter #tsd-filter { display: none; } +#tsd-filter .tsd-filter-group { display: inline-block; height: 40px; vertical-align: bottom; white-space: nowrap; } +#tsd-filter input { display: none; } +@media (max-width: 900px) { #tsd-filter .tsd-filter-group { display: block; position: absolute; top: 40px; right: 20px; height: auto; background-color: #fff; visibility: hidden; -webkit-transform: translate(50%, 0); transform: translate(50%, 0); box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } + .has-options #tsd-filter .tsd-filter-group { visibility: visible; } + .to-has-options #tsd-filter .tsd-filter-group { -webkit-animation: fade-in 0.2s; animation: fade-in 0.2s; } + .from-has-options #tsd-filter .tsd-filter-group { -webkit-animation: fade-out 0.2s; animation: fade-out 0.2s; } + #tsd-filter label, #tsd-filter .tsd-select { display: block; padding-right: 20px; } } + +footer { border-top: 1px solid #eee; background-color: #fff; } +footer.with-border-bottom { border-bottom: 1px solid #eee; } +footer .tsd-legend-group { font-size: 0; } +footer .tsd-legend { display: inline-block; width: 25%; padding: 0; font-size: 16px; list-style: none; line-height: 1.333em; vertical-align: top; } +@media (max-width: 900px) { footer .tsd-legend { width: 50%; } } + +.tsd-hierarchy { list-style: square; padding: 0 0 0 20px; margin: 0; } +.tsd-hierarchy .target { font-weight: bold; } + +.tsd-index-panel .tsd-index-content { margin-bottom: -30px !important; } +.tsd-index-panel .tsd-index-section { margin-bottom: 30px !important; } +.tsd-index-panel h3 { margin: 0 -20px 10px -20px; padding: 0 20px 10px 20px; border-bottom: 1px solid #eee; } +.tsd-index-panel ul.tsd-index-list { -webkit-column-count: 3; -moz-column-count: 3; -ms-column-count: 3; -o-column-count: 3; column-count: 3; -webkit-column-gap: 20px; -moz-column-gap: 20px; -ms-column-gap: 20px; -o-column-gap: 20px; column-gap: 20px; padding: 0; list-style: none; line-height: 1.333em; } +@media (max-width: 900px) { .tsd-index-panel ul.tsd-index-list { -webkit-column-count: 1; -moz-column-count: 1; -ms-column-count: 1; -o-column-count: 1; column-count: 1; } } +@media (min-width: 901px) and (max-width: 1024px) { .tsd-index-panel ul.tsd-index-list { -webkit-column-count: 2; -moz-column-count: 2; -ms-column-count: 2; -o-column-count: 2; column-count: 2; } } +.tsd-index-panel ul.tsd-index-list li { -webkit-column-break-inside: avoid; -moz-column-break-inside: avoid; -ms-column-break-inside: avoid; -o-column-break-inside: avoid; column-break-inside: avoid; -webkit-page-break-inside: avoid; -moz-page-break-inside: avoid; -ms-page-break-inside: avoid; -o-page-break-inside: avoid; page-break-inside: avoid; } +.tsd-index-panel a, .tsd-index-panel .tsd-parent-kind-module a { color: #9600ff; } +.tsd-index-panel .tsd-parent-kind-interface a { color: #7da01f; } +.tsd-index-panel .tsd-parent-kind-enum a { color: #cc9900; } +.tsd-index-panel .tsd-parent-kind-class a { color: #4da6ff; } +.tsd-index-panel .tsd-kind-module a { color: #9600ff; } +.tsd-index-panel .tsd-kind-interface a { color: #7da01f; } +.tsd-index-panel .tsd-kind-enum a { color: #cc9900; } +.tsd-index-panel .tsd-kind-class a { color: #4da6ff; } +.tsd-index-panel .tsd-is-private a { color: #808080; } + +.tsd-flag { display: inline-block; padding: 1px 5px; border-radius: 4px; color: #fff; background-color: #808080; text-indent: 0; font-size: 14px; font-weight: normal; } + +.tsd-anchor { position: absolute; top: -100px; } + +.tsd-member { position: relative; } +.tsd-member .tsd-anchor + h3 { margin-top: 0; margin-bottom: 0; border-bottom: none; } + +.tsd-navigation { padding: 0 0 0 40px; } +.tsd-navigation a { display: block; padding-top: 2px; padding-bottom: 2px; border-left: 2px solid transparent; color: #222; text-decoration: none; transition: border-left-color 0.1s; } +.tsd-navigation a:hover { text-decoration: underline; } +.tsd-navigation ul { margin: 0; padding: 0; list-style: none; } +.tsd-navigation li { padding: 0; } + +.tsd-navigation.primary { padding-bottom: 40px; } +.tsd-navigation.primary a { display: block; padding-top: 6px; padding-bottom: 6px; } +.tsd-navigation.primary ul li a { padding-left: 5px; } +.tsd-navigation.primary ul li li a { padding-left: 25px; } +.tsd-navigation.primary ul li li li a { padding-left: 45px; } +.tsd-navigation.primary ul li li li li a { padding-left: 65px; } +.tsd-navigation.primary ul li li li li li a { padding-left: 85px; } +.tsd-navigation.primary ul li li li li li li a { padding-left: 105px; } +.tsd-navigation.primary > ul { border-bottom: 1px solid #eee; } +.tsd-navigation.primary li { border-top: 1px solid #eee; } +.tsd-navigation.primary li.current > a { font-weight: bold; } +.tsd-navigation.primary li.label span { display: block; padding: 20px 0 6px 5px; color: #808080; } +.tsd-navigation.primary li.globals + li > span, .tsd-navigation.primary li.globals + li > a { padding-top: 20px; } + +.tsd-navigation.secondary ul { transition: opacity 0.2s; } +.tsd-navigation.secondary ul li a { padding-left: 25px; } +.tsd-navigation.secondary ul li li a { padding-left: 45px; } +.tsd-navigation.secondary ul li li li a { padding-left: 65px; } +.tsd-navigation.secondary ul li li li li a { padding-left: 85px; } +.tsd-navigation.secondary ul li li li li li a { padding-left: 105px; } +.tsd-navigation.secondary ul li li li li li li a { padding-left: 125px; } +.tsd-navigation.secondary ul.current a { border-left-color: #eee; } +.tsd-navigation.secondary li.focus > a, .tsd-navigation.secondary ul.current li.focus > a { border-left-color: #000; } +.tsd-navigation.secondary li.current { margin-top: 20px; margin-bottom: 20px; border-left-color: #eee; } +.tsd-navigation.secondary li.current > a { font-weight: bold; } + +@media (min-width: 901px) { .menu-sticky-wrap { position: static; } + .no-csspositionsticky .menu-sticky-wrap.sticky { position: fixed; } + .no-csspositionsticky .menu-sticky-wrap.sticky-current { position: fixed; } + .no-csspositionsticky .menu-sticky-wrap.sticky-current ul.before-current, .no-csspositionsticky .menu-sticky-wrap.sticky-current ul.after-current { opacity: 0; } + .no-csspositionsticky .menu-sticky-wrap.sticky-bottom { position: absolute; top: auto !important; left: auto !important; bottom: 0; right: 0; } + .csspositionsticky .menu-sticky-wrap.sticky { position: -webkit-sticky; position: sticky; } + .csspositionsticky .menu-sticky-wrap.sticky-current { position: -webkit-sticky; position: sticky; } } + +.tsd-panel { margin: 20px 0; padding: 20px; background-color: #fff; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } +.tsd-panel:empty { display: none; } +.tsd-panel > h1, .tsd-panel > h2, .tsd-panel > h3 { margin: 1.5em -20px 10px -20px; padding: 0 20px 10px 20px; border-bottom: 1px solid #eee; } +.tsd-panel > h1.tsd-before-signature, .tsd-panel > h2.tsd-before-signature, .tsd-panel > h3.tsd-before-signature { margin-bottom: 0; border-bottom: 0; } +.tsd-panel table { display: block; width: 100%; overflow: auto; margin-top: 10px; word-break: normal; word-break: keep-all; } +.tsd-panel table th { font-weight: bold; } +.tsd-panel table th, .tsd-panel table td { padding: 6px 13px; border: 1px solid #ddd; } +.tsd-panel table tr { background-color: #fff; border-top: 1px solid #ccc; } +.tsd-panel table tr:nth-child(2n) { background-color: #f8f8f8; } + +.tsd-panel-group { margin: 60px 0; } +.tsd-panel-group > h1, .tsd-panel-group > h2, .tsd-panel-group > h3 { padding-left: 20px; padding-right: 20px; } + +#tsd-search { transition: background-color 0.2s; } +#tsd-search .title { position: relative; z-index: 2; } +#tsd-search .field { position: absolute; left: 0; top: 0; right: 40px; height: 40px; } +#tsd-search .field input { box-sizing: border-box; position: relative; top: -50px; z-index: 1; width: 100%; padding: 0 10px; opacity: 0; outline: 0; border: 0; background: transparent; color: #222; } +#tsd-search .field label { position: absolute; overflow: hidden; right: -40px; } +#tsd-search .field input, #tsd-search .title { transition: opacity 0.2s; } +#tsd-search .results { position: absolute; visibility: hidden; top: 40px; width: 100%; margin: 0; padding: 0; list-style: none; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } +#tsd-search .results li { padding: 0 10px; background-color: #fdfdfd; } +#tsd-search .results li:nth-child(even) { background-color: #fff; } +#tsd-search .results li.state { display: none; } +#tsd-search .results li.current, #tsd-search .results li:hover { background-color: #eee; } +#tsd-search .results a { display: block; } +#tsd-search .results a:before { top: 10px; } +#tsd-search .results span.parent { color: #808080; font-weight: normal; } +#tsd-search.has-focus { background-color: #eee; } +#tsd-search.has-focus .field input { top: 0; opacity: 1; } +#tsd-search.has-focus .title { z-index: 0; opacity: 0; } +#tsd-search.has-focus .results { visibility: visible; } +#tsd-search.loading .results li.state.loading { display: block; } +#tsd-search.failure .results li.state.failure { display: block; } + +.tsd-signature { margin: 0 0 1em 0; padding: 10px; border: 1px solid #eee; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; } +.tsd-signature.tsd-kind-icon { padding-left: 30px; } +.tsd-signature.tsd-kind-icon:before { top: 10px; left: 10px; } +.tsd-panel > .tsd-signature { margin-left: -20px; margin-right: -20px; border-width: 1px 0; } +.tsd-panel > .tsd-signature.tsd-kind-icon { padding-left: 40px; } +.tsd-panel > .tsd-signature.tsd-kind-icon:before { left: 20px; } + +.tsd-signature-symbol { color: #808080; font-weight: normal; } + +.tsd-signature-type { font-style: italic; font-weight: normal; } + +.tsd-signatures { padding: 0; margin: 0 0 1em 0; border: 1px solid #eee; } +.tsd-signatures .tsd-signature { margin: 0; border-width: 1px 0 0 0; transition: background-color 0.1s; } +.tsd-signatures .tsd-signature:first-child { border-top-width: 0; } +.tsd-signatures .tsd-signature.current { background-color: #eee; } +.tsd-signatures.active > .tsd-signature { cursor: pointer; } +.tsd-panel > .tsd-signatures { margin-left: -20px; margin-right: -20px; border-width: 1px 0; } +.tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon { padding-left: 40px; } +.tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon:before { left: 20px; } +.tsd-panel > a.anchor + .tsd-signatures { border-top-width: 0; margin-top: -20px; } + +ul.tsd-descriptions { position: relative; overflow: hidden; transition: height 0.3s; padding: 0; list-style: none; } +ul.tsd-descriptions.active > .tsd-description { display: none; } +ul.tsd-descriptions.active > .tsd-description.current { display: block; } +ul.tsd-descriptions.active > .tsd-description.fade-in { -webkit-animation: fade-in-delayed 0.3s; animation: fade-in-delayed 0.3s; } +ul.tsd-descriptions.active > .tsd-description.fade-out { -webkit-animation: fade-out-delayed 0.3s; animation: fade-out-delayed 0.3s; position: absolute; display: block; top: 0; left: 0; right: 0; opacity: 0; visibility: hidden; } +ul.tsd-descriptions h4, ul.tsd-descriptions .tsd-index-panel h3, .tsd-index-panel ul.tsd-descriptions h3 { font-size: 16px; margin: 1em 0 0.5em 0; } + +ul.tsd-parameters, ul.tsd-type-parameters { list-style: square; margin: 0; padding-left: 20px; } +ul.tsd-parameters > li.tsd-parameter-siganture, ul.tsd-type-parameters > li.tsd-parameter-siganture { list-style: none; margin-left: -20px; } +ul.tsd-parameters h5, ul.tsd-type-parameters h5 { font-size: 16px; margin: 1em 0 0.5em 0; } +ul.tsd-parameters .tsd-comment, ul.tsd-type-parameters .tsd-comment { margin-top: -0.5em; } + +.tsd-sources { font-size: 14px; color: #808080; margin: 0 0 1em 0; } +.tsd-sources a { color: #808080; text-decoration: underline; } +.tsd-sources ul, .tsd-sources p { margin: 0 !important; } +.tsd-sources ul { list-style: none; padding: 0; } + +.tsd-page-toolbar { position: absolute; z-index: 1; top: 0; left: 0; width: 100%; height: 40px; color: #333; background: #fff; border-bottom: 1px solid #eee; } +.tsd-page-toolbar a { color: #333; text-decoration: none; } +.tsd-page-toolbar a.title { font-weight: bold; } +.tsd-page-toolbar a.title:hover { text-decoration: underline; } +.tsd-page-toolbar .table-wrap { display: table; width: 100%; height: 40px; } +.tsd-page-toolbar .table-cell { display: table-cell; position: relative; white-space: nowrap; line-height: 40px; } +.tsd-page-toolbar .table-cell:first-child { width: 100%; } + +.tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { content: ""; display: inline-block; width: 40px; height: 40px; margin: 0 -8px 0 0; background-image: url(../images/widgets.png); background-repeat: no-repeat; text-indent: -1024px; vertical-align: bottom; } +@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { background-image: url(../images/widgets@2x.png); background-size: 320px 40px; } } + +.tsd-widget { display: inline-block; overflow: hidden; opacity: 0.6; height: 40px; transition: opacity 0.1s, background-color 0.2s; vertical-align: bottom; cursor: pointer; } +.tsd-widget:hover { opacity: 0.8; } +.tsd-widget.active { opacity: 1; background-color: #eee; } +.tsd-widget.no-caption { width: 40px; } +.tsd-widget.no-caption:before { margin: 0; } +.tsd-widget.search:before { background-position: 0 0; } +.tsd-widget.menu:before { background-position: -40px 0; } +.tsd-widget.options:before { background-position: -80px 0; } +.tsd-widget.options, .tsd-widget.menu { display: none; } +@media (max-width: 900px) { .tsd-widget.options, .tsd-widget.menu { display: inline-block; } } +input[type=checkbox] + .tsd-widget:before { background-position: -120px 0; } +input[type=checkbox]:checked + .tsd-widget:before { background-position: -160px 0; } + +.tsd-select { position: relative; display: inline-block; height: 40px; transition: opacity 0.1s, background-color 0.2s; vertical-align: bottom; cursor: pointer; } +.tsd-select .tsd-select-label { opacity: 0.6; transition: opacity 0.2s; } +.tsd-select .tsd-select-label:before { background-position: -240px 0; } +.tsd-select.active .tsd-select-label { opacity: 0.8; } +.tsd-select.active .tsd-select-list { visibility: visible; opacity: 1; transition-delay: 0s; } +.tsd-select .tsd-select-list { position: absolute; visibility: hidden; top: 40px; left: 0; margin: 0; padding: 0; opacity: 0; list-style: none; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); transition: visibility 0s 0.2s, opacity 0.2s; } +.tsd-select .tsd-select-list li { padding: 0 20px 0 0; background-color: #fdfdfd; } +.tsd-select .tsd-select-list li:before { background-position: 40px 0; } +.tsd-select .tsd-select-list li:nth-child(even) { background-color: #fff; } +.tsd-select .tsd-select-list li:hover { background-color: #eee; } +.tsd-select .tsd-select-list li.selected:before { background-position: -200px 0; } +@media (max-width: 900px) { .tsd-select .tsd-select-list { top: 0; left: auto; right: 100%; margin-right: -5px; } + .tsd-select .tsd-select-label:before { background-position: -280px 0; } } + +img { max-width: 100%; } diff --git a/docs/theme/assets/css/main.css.map b/docs/theme/assets/css/main.css.map new file mode 100644 index 0000000..bc17fe4 --- /dev/null +++ b/docs/theme/assets/css/main.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "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", +"sources": ["../../../../src/default/assets/css/vendors/_normalize.sass","../../../../src/default/assets/css/vendors/_highlight.js.sass","../../../../src/default/assets/css/setup/_mixins.sass","../../../../src/default/assets/css/setup/_grid.sass","../../../../src/default/assets/css/setup/_icons.scss","../../../../src/default/assets/css/setup/_animations.sass","../../../../src/default/assets/css/setup/_typography.sass","../../../../src/default/assets/css/_constants.sass","../../../../src/default/assets/css/layouts/_default.sass","../../../../src/default/assets/css/layouts/_minimal.sass","../../../../src/default/assets/css/elements/_comment.sass","../../../../src/default/assets/css/elements/_filter.sass","../../../../src/default/assets/css/elements/_footer.sass","../../../../src/default/assets/css/elements/_hierarchy.sass","../../../../src/default/assets/css/elements/_index.sass","../../../../src/default/assets/css/elements/_member.sass","../../../../src/default/assets/css/elements/_navigation.sass","../../../../src/default/assets/css/elements/_panel.sass","../../../../src/default/assets/css/elements/_search.sass","../../../../src/default/assets/css/elements/_signatures.sass","../../../../src/default/assets/css/elements/_sources.sass","../../../../src/default/assets/css/elements/_toolbar.sass","../../../../src/default/assets/css/elements/_images.sass"], +"names": [], +"file": "main.css" +} diff --git a/docs/theme/assets/images/icons.png b/docs/theme/assets/images/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..cb2d11573b9ade711ee30a2bd4f38e6f9ea82281 GIT binary patch literal 9487 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 JrMIm@{|8^qBMkrm literal 0 HcmV?d00001 diff --git a/docs/theme/assets/images/icons@2x.png b/docs/theme/assets/images/icons@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8932ba20ffa431194b8cebc977c731bec3ee23e0 GIT binary patch literal 27740 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 U&wLp6h6BX&boFyt=akR{0G%$)mH+?% literal 0 HcmV?d00001 diff --git a/docs/theme/assets/images/widgets@2x.png b/docs/theme/assets/images/widgets@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4bbbd57272f3b28f47527d4951ad10f950b8ad43 GIT binary patch literal 855 zcmeAS@N?(olHy`uVBq!ia0y~yU}^xe12~w0Jcmn z@(X6T|9^jgLcx21{)7exgY)a>N6m2F0<`Rqr;B4q1>>88jUdw-7W`c)zLE*mq8W2H z-<&Jl_Hco5BuC5n@AbF5GD82~-e8-v=#zCyUX0F-o}8pPfAv`!GN$ff+TL<~@kgt} z62eO?_|&+>xBmM$@p|z`tIKEdpPf8%qI>4r7@jn<=eta*{3~?g(zz{Ke9zc-G^gr? z-7foa?LcS!hmbwzru}ICvbWLlW8;+l-}!^=c32!^nV`+`C*;0-*Y%l94pC;Cb3GXz zzSf%a!{gVr{Y_lVuUj+a)*Ca+!-Hu%xmP&&X-2CuANY8^i{D7Kg6qzP zXz_ps9+lN8ESH{K4`yu&b~I>N9xGlE&;2u*b?+Go!AhN?m-bxlLvtC#MzDF2kFzfHJ1W7ybqdefSqVhbOykd*Yi%EDuhs z4wF{ft^bv2+DDnKb8gj1FuvcV`M}luS>lO<^)8x>y1#R;a=-ZKwWTQQb)ioBbi;zh zD!f5V)8581to1LL7c9!l^PSC$NBPYif!_vAZhmL4)v4U)4UsrLYiH_9rmQDd?)(e5 z^pcH>qvBg*i0dus2r*mp4;zKvu=P#s-ti;2obl`NjjwoYd>e(oo#j_uyRb<7Pv^If zzZ|mGHmV)8^tbO%^>eqMw(@7(&3g{jEp-Najo7V75xI_ZHK*FA`elF{r5}E*d7+j_R literal 0 HcmV?d00001 diff --git a/docs/theme/assets/js/main.js b/docs/theme/assets/js/main.js new file mode 100644 index 0000000..528a3b0 --- /dev/null +++ b/docs/theme/assets/js/main.js @@ -0,0 +1,5 @@ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){function s(a){var b=a.length,c=n.type(a);return"function"!==c&&!n.isWindow(a)&&(!(1!==a.nodeType||!b)||("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a))}function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}function D(a,b){for(;(a=a[b])&&1!==a.nodeType;);return a}function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),"string"==typeof(c=a.getAttribute(d))){try{c="true"===c||"false"!==c&&("null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c)}catch(e){}M.set(a,b,c)}else c=void 0;return c}function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("