diff --git a/.eslintrc b/.eslintrc index 4b4184023..b50213f01 100644 --- a/.eslintrc +++ b/.eslintrc @@ -31,6 +31,8 @@ }, "rules": { "no-useless-escape": 2, + "react/no-deprecated": 1, + "react/jsx-no-target-blank": 1, "handle-callback-err": 2, "no-fallthrough": 2, "no-new-require": 2, diff --git a/Dockerfile b/Dockerfile index 58f406b5a..5a1800ddc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,10 +21,9 @@ ENV NODE_ENV=production RUN npm config set registry http://registry.npmjs.org/ && \ yarn global add -s flow-bin@0.69.0 && \ yarn install --production=false && \ - yarn run lint && \ - yarn run code:docker-build && \ - yarn run build:webui && \ - yarn run test:unit -- --silent true --coverage false --bail && \ + yarn lint && \ + yarn code:docker-build && \ + yarn build:webui && \ yarn cache clean && \ yarn install --production=true --pure-lockfile diff --git a/flow-typed/npm/jest_v22.x.x.js b/flow-typed/npm/jest_v23.x.x.js similarity index 53% rename from flow-typed/npm/jest_v22.x.x.js rename to flow-typed/npm/jest_v23.x.x.js index b10789020..a6ac47578 100644 --- a/flow-typed/npm/jest_v22.x.x.js +++ b/flow-typed/npm/jest_v23.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 6e1fc0a644aa956f79029fec0709e597 -// flow-typed version: 07ebad4796/jest_v22.x.x/flow_>=v0.39.x +// flow-typed signature: 4cacceffd326bb118e4a3c1b4d629e98 +// flow-typed version: e737b9832f/jest_v23.x.x/flow_>=v0.39.x type JestMockFn, TReturn> = { (...args: TArguments): TReturn, @@ -55,6 +55,11 @@ type JestMockFn, TReturn> = { mockImplementationOnce( fn: (...args: TArguments) => TReturn ): JestMockFn, + /** + * Accepts a string to use in test result output in place of "jest.fn()" to + * indicate which mock function is being referenced. + */ + mockName(name: string): JestMockFn, /** * Just a simple sugar function for returning `this` */ @@ -66,7 +71,23 @@ type JestMockFn, TReturn> = { /** * Sugar for only returning a value once inside your mock */ - mockReturnValueOnce(value: TReturn): JestMockFn + mockReturnValueOnce(value: TReturn): JestMockFn, + /** + * Sugar for jest.fn().mockImplementation(() => Promise.resolve(value)) + */ + mockResolvedValue(value: TReturn): JestMockFn>, + /** + * Sugar for jest.fn().mockImplementationOnce(() => Promise.resolve(value)) + */ + mockResolvedValueOnce(value: TReturn): JestMockFn>, + /** + * Sugar for jest.fn().mockImplementation(() => Promise.reject(value)) + */ + mockRejectedValue(value: TReturn): JestMockFn>, + /** + * Sugar for jest.fn().mockImplementationOnce(() => Promise.reject(value)) + */ + mockRejectedValueOnce(value: TReturn): JestMockFn> }; type JestAsymmetricEqualityType = { @@ -113,6 +134,12 @@ type JestPromiseType = { resolves: JestExpectType }; +/** + * Jest allows functions and classes to be used as test names in test() and + * describe() + */ +type JestTestName = string | Function; + /** * Plugin: jest-enzyme */ @@ -120,14 +147,16 @@ type EnzymeMatchersType = { toBeChecked(): void, toBeDisabled(): void, toBeEmpty(): void, + toBeEmptyRender(): void, toBePresent(): void, toContainReact(element: React$Element): void, + toExist(): void, toHaveClassName(className: string): void, toHaveHTML(html: string): void, - toHaveProp(propKey: string, propValue?: any): void, + toHaveProp: ((propKey: string, propValue?: any) => void) & ((props: Object) => void), toHaveRef(refName: string): void, - toHaveState(stateKey: string, stateValue?: any): void, - toHaveStyle(styleKey: string, styleValue?: any): void, + toHaveState: ((stateKey: string, stateValue?: any) => void) & ((state: Object) => void), + toHaveStyle: ((styleKey: string, styleValue?: any) => void) & ((style: Object) => void), toHaveTagName(tagName: string): void, toHaveText(text: string): void, toIncludeText(text: string): void, @@ -136,8 +165,342 @@ type EnzymeMatchersType = { toMatchSelector(selector: string): void }; -type JestExpectType = { - not: JestExpectType & EnzymeMatchersType, +// DOM testing library extensions https://github.com/kentcdodds/dom-testing-library#custom-jest-matchers +type DomTestingLibraryType = { + toBeInTheDOM(): void, + toHaveTextContent(content: string): void, + toHaveAttribute(name: string, expectedValue?: string): void +}; + +// Jest JQuery Matchers: https://github.com/unindented/custom-jquery-matchers +type JestJQueryMatchersType = { + toExist(): void, + toHaveLength(len: number): void, + toHaveId(id: string): void, + toHaveClass(className: string): void, + toHaveTag(tag: string): void, + toHaveAttr(key: string, val?: any): void, + toHaveProp(key: string, val?: any): void, + toHaveText(text: string | RegExp): void, + toHaveData(key: string, val?: any): void, + toHaveValue(val: any): void, + toHaveCss(css: {[key: string]: any}): void, + toBeChecked(): void, + toBeDisabled(): void, + toBeEmpty(): void, + toBeHidden(): void, + toBeSelected(): void, + toBeVisible(): void, + toBeFocused(): void, + toBeInDom(): void, + toBeMatchedBy(sel: string): void, + toHaveDescendant(sel: string): void, + toHaveDescendantWithText(sel: string, text: string | RegExp): void +}; + + +// Jest Extended Matchers: https://github.com/jest-community/jest-extended +type JestExtendedMatchersType = { + /** + * Note: Currently unimplemented + * Passing assertion + * + * @param {String} message + */ + // pass(message: string): void; + + /** + * Note: Currently unimplemented + * Failing assertion + * + * @param {String} message + */ + // fail(message: string): void; + + /** + * Use .toBeEmpty when checking if a String '', Array [] or Object {} is empty. + */ + toBeEmpty(): void; + + /** + * Use .toBeOneOf when checking if a value is a member of a given Array. + * @param {Array.<*>} members + */ + toBeOneOf(members: any[]): void; + + /** + * Use `.toBeNil` when checking a value is `null` or `undefined`. + */ + toBeNil(): void; + + /** + * Use `.toSatisfy` when you want to use a custom matcher by supplying a predicate function that returns a `Boolean`. + * @param {Function} predicate + */ + toSatisfy(predicate: (n: any) => boolean): void; + + /** + * Use `.toBeArray` when checking if a value is an `Array`. + */ + toBeArray(): void; + + /** + * Use `.toBeArrayOfSize` when checking if a value is an `Array` of size x. + * @param {Number} x + */ + toBeArrayOfSize(x: number): void; + + /** + * Use `.toIncludeAllMembers` when checking if an `Array` contains all of the same members of a given set. + * @param {Array.<*>} members + */ + toIncludeAllMembers(members: any[]): void; + + /** + * Use `.toIncludeAnyMembers` when checking if an `Array` contains any of the members of a given set. + * @param {Array.<*>} members + */ + toIncludeAnyMembers(members: any[]): void; + + /** + * Use `.toSatisfyAll` when you want to use a custom matcher by supplying a predicate function that returns a `Boolean` for all values in an array. + * @param {Function} predicate + */ + toSatisfyAll(predicate: (n: any) => boolean): void; + + /** + * Use `.toBeBoolean` when checking if a value is a `Boolean`. + */ + toBeBoolean(): void; + + /** + * Use `.toBeTrue` when checking a value is equal (===) to `true`. + */ + toBeTrue(): void; + + /** + * Use `.toBeFalse` when checking a value is equal (===) to `false`. + */ + toBeFalse(): void; + + /** + * Use .toBeDate when checking if a value is a Date. + */ + toBeDate(): void; + + /** + * Use `.toBeFunction` when checking if a value is a `Function`. + */ + toBeFunction(): void; + + /** + * Use `.toHaveBeenCalledBefore` when checking if a `Mock` was called before another `Mock`. + * + * Note: Required Jest version >22 + * Note: Your mock functions will have to be asynchronous to cause the timestamps inside of Jest to occur in a differentJS event loop, otherwise the mock timestamps will all be the same + * + * @param {Mock} mock + */ + toHaveBeenCalledBefore(mock: JestMockFn): void; + + /** + * Use `.toBeNumber` when checking if a value is a `Number`. + */ + toBeNumber(): void; + + /** + * Use `.toBeNaN` when checking a value is `NaN`. + */ + toBeNaN(): void; + + /** + * Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`. + */ + toBeFinite(): void; + + /** + * Use `.toBePositive` when checking if a value is a positive `Number`. + */ + toBePositive(): void; + + /** + * Use `.toBeNegative` when checking if a value is a negative `Number`. + */ + toBeNegative(): void; + + /** + * Use `.toBeEven` when checking if a value is an even `Number`. + */ + toBeEven(): void; + + /** + * Use `.toBeOdd` when checking if a value is an odd `Number`. + */ + toBeOdd(): void; + + /** + * Use `.toBeWithin` when checking if a number is in between the given bounds of: start (inclusive) and end (exclusive). + * + * @param {Number} start + * @param {Number} end + */ + toBeWithin(start: number, end: number): void; + + /** + * Use `.toBeObject` when checking if a value is an `Object`. + */ + toBeObject(): void; + + /** + * Use `.toContainKey` when checking if an object contains the provided key. + * + * @param {String} key + */ + toContainKey(key: string): void; + + /** + * Use `.toContainKeys` when checking if an object has all of the provided keys. + * + * @param {Array.} keys + */ + toContainKeys(keys: string[]): void; + + /** + * Use `.toContainAllKeys` when checking if an object only contains all of the provided keys. + * + * @param {Array.} keys + */ + toContainAllKeys(keys: string[]): void; + + /** + * Use `.toContainAnyKeys` when checking if an object contains at least one of the provided keys. + * + * @param {Array.} keys + */ + toContainAnyKeys(keys: string[]): void; + + /** + * Use `.toContainValue` when checking if an object contains the provided value. + * + * @param {*} value + */ + toContainValue(value: any): void; + + /** + * Use `.toContainValues` when checking if an object contains all of the provided values. + * + * @param {Array.<*>} values + */ + toContainValues(values: any[]): void; + + /** + * Use `.toContainAllValues` when checking if an object only contains all of the provided values. + * + * @param {Array.<*>} values + */ + toContainAllValues(values: any[]): void; + + /** + * Use `.toContainAnyValues` when checking if an object contains at least one of the provided values. + * + * @param {Array.<*>} values + */ + toContainAnyValues(values: any[]): void; + + /** + * Use `.toContainEntry` when checking if an object contains the provided entry. + * + * @param {Array.} entry + */ + toContainEntry(entry: [string, string]): void; + + /** + * Use `.toContainEntries` when checking if an object contains all of the provided entries. + * + * @param {Array.>} entries + */ + toContainEntries(entries: [string, string][]): void; + + /** + * Use `.toContainAllEntries` when checking if an object only contains all of the provided entries. + * + * @param {Array.>} entries + */ + toContainAllEntries(entries: [string, string][]): void; + + /** + * Use `.toContainAnyEntries` when checking if an object contains at least one of the provided entries. + * + * @param {Array.>} entries + */ + toContainAnyEntries(entries: [string, string][]): void; + + /** + * Use `.toBeExtensible` when checking if an object is extensible. + */ + toBeExtensible(): void; + + /** + * Use `.toBeFrozen` when checking if an object is frozen. + */ + toBeFrozen(): void; + + /** + * Use `.toBeSealed` when checking if an object is sealed. + */ + toBeSealed(): void; + + /** + * Use `.toBeString` when checking if a value is a `String`. + */ + toBeString(): void; + + /** + * Use `.toEqualCaseInsensitive` when checking if a string is equal (===) to another ignoring the casing of both strings. + * + * @param {String} string + */ + toEqualCaseInsensitive(string: string): void; + + /** + * Use `.toStartWith` when checking if a `String` starts with a given `String` prefix. + * + * @param {String} prefix + */ + toStartWith(prefix: string): void; + + /** + * Use `.toEndWith` when checking if a `String` ends with a given `String` suffix. + * + * @param {String} suffix + */ + toEndWith(suffix: string): void; + + /** + * Use `.toInclude` when checking if a `String` includes the given `String` substring. + * + * @param {String} substring + */ + toInclude(substring: string): void; + + /** + * Use `.toIncludeRepeated` when checking if a `String` includes the given `String` substring the correct number of times. + * + * @param {String} substring + * @param {Number} times + */ + toIncludeRepeated(substring: string, times: number): void; + + /** + * Use `.toIncludeMultiple` when checking if a `String` includes all of the given substrings. + * + * @param {Array.} substring + */ + toIncludeMultiple(substring: string[]): void; +}; + +interface JestExpectType { + not: JestExpectType & EnzymeMatchersType & DomTestingLibraryType & JestJQueryMatchersType & JestExtendedMatchersType, /** * If you have a mock function, you can use .lastCalledWith to test what * arguments it was last called with. @@ -148,10 +511,6 @@ type JestExpectType = { * strict equality. */ toBe(value: any): void, - /** - * Use .toHaveBeenCalled to ensure that a mock function got called. - */ - toBeCalled(): void, /** * Use .toBeCalledWith to ensure that a mock function was called with * specific arguments. @@ -227,21 +586,55 @@ type JestExpectType = { * Use .toHaveBeenCalled to ensure that a mock function got called. */ toHaveBeenCalled(): void, + toBeCalled(): void; /** * Use .toHaveBeenCalledTimes to ensure that a mock function got called exact * number of times. */ toHaveBeenCalledTimes(number: number): void, + toBeCalledTimes(number: number): void; + /** + * + */ + toHaveBeenNthCalledWith(nthCall: number, ...args: Array): void; + nthCalledWith(nthCall: number, ...args: Array): void; + /** + * + */ + toHaveReturned(): void; + toReturn(): void; + /** + * + */ + toHaveReturnedTimes(number: number): void; + toReturnTimes(number: number): void; + /** + * + */ + toHaveReturnedWith(value: any): void; + toReturnWith(value: any): void; + /** + * + */ + toHaveLastReturnedWith(value: any): void; + lastReturnedWith(value: any): void; + /** + * + */ + toHaveNthReturnedWith(nthCall: number, value: any): void; + nthReturnedWith(nthCall: number, value: any): void; /** * Use .toHaveBeenCalledWith to ensure that a mock function was called with * specific arguments. */ toHaveBeenCalledWith(...args: Array): void, + toBeCalledWith(...args: Array): void, /** * Use .toHaveBeenLastCalledWith to ensure that a mock function was last called * with specific arguments. */ toHaveBeenLastCalledWith(...args: Array): void, + lastCalledWith(...args: Array): void, /** * Check that an object has a .length property and it is set to a certain * numeric value. @@ -260,9 +653,17 @@ type JestExpectType = { */ toMatchObject(object: Object | Array): void, /** - * This ensures that a React component matches the most recent snapshot. + * Use .toStrictEqual to check that a javascript object matches a subset of the properties of an object. */ - toMatchSnapshot(name?: string): void, + toStrictEqual(value: any): void, + /** + * This ensures that an Object matches the most recent snapshot. + */ + toMatchSnapshot(propertyMatchers?: {[key: string]: JestAsymmetricEqualityType}, name?: string): void, + /** + * This ensures that an Object matches the most recent snapshot. + */ + toMatchSnapshot(name: string): void, /** * Use .toThrow to test that a function throws when it is called. * If you want to test that a specific error gets thrown, you can provide an @@ -278,7 +679,7 @@ type JestExpectType = { * matching the most recent snapshot when it is called. */ toThrowErrorMatchingSnapshot(): void -}; +} type JestObjectType = { /** @@ -391,6 +792,13 @@ type JestObjectType = { * Executes only the macro task queue (i.e. all tasks queued by setTimeout() * or setInterval() and setImmediate()). */ + advanceTimersByTime(msToRun: number): void, + /** + * Executes only the macro task queue (i.e. all tasks queued by setTimeout() + * or setInterval() and setImmediate()). + * + * Renamed to `advanceTimersByTime`. + */ runTimersToTime(msToRun: number): void, /** * Executes only the macro-tasks that are currently pending (i.e., only the @@ -424,7 +832,7 @@ type JestObjectType = { * Creates a mock function similar to jest.fn but also tracks calls to * object[methodName]. */ - spyOn(object: Object, methodName: string): JestMockFn, + spyOn(object: Object, methodName: string, accessType?: "get" | "set"): JestMockFn, /** * Set the default timeout interval for tests and before/after hooks in milliseconds. * Note: The default timeout interval is 5 seconds if this method is not called. @@ -462,17 +870,17 @@ declare var describe: { /** * Creates a block that groups together several related tests in one "test suite" */ - (name: string, fn: () => void): void, + (name: JestTestName, fn: () => void): void, /** * Only run this describe block */ - only(name: string, fn: () => void): void, + only(name: JestTestName, fn: () => void): void, /** * Skip running this describe block */ - skip(name: string, fn: () => void): void + skip(name: JestTestName, fn: () => void): void }; /** An individual test unit */ @@ -480,54 +888,54 @@ declare var it: { /** * An individual test unit * - * @param {string} Name of Test + * @param {JestTestName} Name of Test * @param {Function} Test * @param {number} Timeout for the test, in milliseconds. */ ( - name: string, + name: JestTestName, fn?: (done: () => void) => ?Promise, timeout?: number ): void, /** * Only run this test * - * @param {string} Name of Test + * @param {JestTestName} Name of Test * @param {Function} Test * @param {number} Timeout for the test, in milliseconds. */ only( - name: string, + name: JestTestName, fn?: (done: () => void) => ?Promise, timeout?: number ): void, /** * Skip running this test * - * @param {string} Name of Test + * @param {JestTestName} Name of Test * @param {Function} Test * @param {number} Timeout for the test, in milliseconds. */ skip( - name: string, + name: JestTestName, fn?: (done: () => void) => ?Promise, timeout?: number ): void, /** * Run the test concurrently * - * @param {string} Name of Test + * @param {JestTestName} Name of Test * @param {Function} Test * @param {number} Timeout for the test, in milliseconds. */ concurrent( - name: string, + name: JestTestName, fn?: (done: () => void) => ?Promise, timeout?: number ): void }; declare function fit( - name: string, + name: JestTestName, fn: (done: () => void) => ?Promise, timeout?: number ): void; @@ -542,23 +950,75 @@ declare var xit: typeof it; /** A disabled individual test */ declare var xtest: typeof it; +type JestPrettyFormatColors = { + comment: { close: string, open: string }, + content: { close: string, open: string }, + prop: { close: string, open: string }, + tag: { close: string, open: string }, + value: { close: string, open: string }, +}; + +type JestPrettyFormatIndent = string => string; +type JestPrettyFormatRefs = Array; +type JestPrettyFormatPrint = any => string; +type JestPrettyFormatStringOrNull = string | null; + +type JestPrettyFormatOptions = {| + callToJSON: boolean, + edgeSpacing: string, + escapeRegex: boolean, + highlight: boolean, + indent: number, + maxDepth: number, + min: boolean, + plugins: JestPrettyFormatPlugins, + printFunctionName: boolean, + spacing: string, + theme: {| + comment: string, + content: string, + prop: string, + tag: string, + value: string, + |}, +|}; + +type JestPrettyFormatPlugin = { + print: ( + val: any, + serialize: JestPrettyFormatPrint, + indent: JestPrettyFormatIndent, + opts: JestPrettyFormatOptions, + colors: JestPrettyFormatColors, + ) => string, + test: any => boolean, +}; + +type JestPrettyFormatPlugins = Array; + /** The expect function is used every time you want to test a value */ declare var expect: { /** The object that you want to make assertions against */ - (value: any): JestExpectType & JestPromiseType & EnzymeMatchersType, + (value: any): JestExpectType & JestPromiseType & EnzymeMatchersType & DomTestingLibraryType & JestJQueryMatchersType & JestExtendedMatchersType, /** Add additional Jasmine matchers to Jest's roster */ extend(matchers: { [name: string]: JestMatcher }): void, /** Add a module that formats application-specific data structures. */ - addSnapshotSerializer(serializer: (input: Object) => string): void, + addSnapshotSerializer(pluginModule: JestPrettyFormatPlugin): void, assertions(expectedAssertions: number): void, hasAssertions(): void, any(value: mixed): JestAsymmetricEqualityType, - anything(): void, - arrayContaining(value: Array): void, - objectContaining(value: Object): void, + anything(): any, + arrayContaining(value: Array): Array, + objectContaining(value: Object): Object, /** Matches any received string that contains the exact expected string. */ - stringContaining(value: string): void, - stringMatching(value: string | RegExp): void + stringContaining(value: string): string, + stringMatching(value: string | RegExp): string, + not: { + arrayContaining: (value: $ReadOnlyArray) => Array, + objectContaining: (value: {}) => Object, + stringContaining: (value: string) => string, + stringMatching: (value: string | RegExp) => string, + }, }; // TODO handle return type @@ -575,14 +1035,14 @@ declare var jest: JestObjectType; declare var jasmine: { DEFAULT_TIMEOUT_INTERVAL: number, any(value: mixed): JestAsymmetricEqualityType, - anything(): void, - arrayContaining(value: Array): void, + anything(): any, + arrayContaining(value: Array): Array, clock(): JestClockType, createSpy(name: string): JestSpyType, createSpyObj( baseName: string, methodNames: Array ): { [methodName: string]: JestSpyType }, - objectContaining(value: Object): void, - stringMatching(value: string): void + objectContaining(value: Object): Object, + stringMatching(value: string): string }; diff --git a/jest.config.unit.js b/jest.config.js similarity index 100% rename from jest.config.unit.js rename to jest.config.js diff --git a/package.json b/package.json index 005352639..d24ad47ac 100644 --- a/package.json +++ b/package.json @@ -52,11 +52,11 @@ "devDependencies": { "@commitlint/cli": "6.1.3", "@commitlint/config-conventional": "6.1.3", - "@verdaccio/types": "3.0.0", + "@verdaccio/types": "3.0.1", "babel-cli": "6.26.0", "babel-core": "6.26.0", "babel-eslint": "8.2.2", - "babel-jest": "22.4.3", + "babel-jest": "23.2.0", "babel-loader": "7.1.4", "babel-plugin-flow-runtime": "0.17.0", "babel-plugin-syntax-dynamic-import": "6.18.0", @@ -82,14 +82,14 @@ "element-theme-default": "1.4.13", "enzyme": "3.3.0", "enzyme-adapter-react-16": "1.1.1", - "eslint": "4.18.2", + "eslint": "5.0.1", "eslint-config-google": "0.9.1", "eslint-loader": "2.0.0", "eslint-plugin-babel": "4.1.2", - "eslint-plugin-flowtype": "2.46.1", - "eslint-plugin-import": "2.9.0", - "eslint-plugin-jest": "21.14.0", - "eslint-plugin-react": "7.7.0", + "eslint-plugin-flowtype": "2.49.3", + "eslint-plugin-import": "2.13.0", + "eslint-plugin-jest": "21.17.0", + "eslint-plugin-react": "7.10.0", "file-loader": "1.1.11", "flow-bin": "0.69.0", "flow-runtime": "0.17.0", @@ -99,10 +99,10 @@ "husky": "0.15.0-rc.8", "identity-obj-proxy": "3.0.0", "in-publish": "2.0.0", - "jest": "22.4.3", - "jest-environment-jsdom": "22.4.3", - "jest-environment-jsdom-global": "1.0.3", - "jest-environment-node": "22.4.3", + "jest": "23.2.0", + "jest-environment-jsdom": "23.2.0", + "jest-environment-jsdom-global": "1.1.0", + "jest-environment-node": "23.2.0", "localstorage-memory": "1.0.2", "mini-css-extract-plugin": "0.4.0", "node-mocks-http": "1.6.7", @@ -154,10 +154,10 @@ "flow": "flow", "pretest": "npm run code:build", "test": "npm run test:unit", - "test:unit": "cross-env NODE_ENV=test BABEL_ENV=test TZ=UTC jest --config ./jest.config.unit.js --maxWorkers 2", + "test:unit": "cross-env NODE_ENV=test BABEL_ENV=test TZ=UTC jest --config ./jest.config.js --maxWorkers 2", "test:functional": "cross-env NODE_ENV=testOldEnv jest --config ./test/jest.config.functional.js --testPathPattern ./test/functional/index*", "test:e2e": "cross-env BABEL_ENV=testOldEnv jest --config ./test/jest.config.e2e.js", - "test:all": "npm run test && npm run test:functional && npm run test:e2e", + "test:all": "npm run build:webui && npm run test && npm run test:functional && npm run test:e2e", "pre:ci": "npm run lint && npm run build:webui", "commitmsg": "commitlint -e $GIT_PARAMS", "coverage:publish": "codecov", diff --git a/src/api/index.js b/src/api/index.js index 5b384a0b5..d69802a2f 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -15,13 +15,13 @@ import type {$ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler, import type {Config as IConfig} from '@verdaccio/types'; import {ErrorCode} from '../lib/utils'; import {API_ERROR, HTTP_STATUS} from '../lib/constants'; +import AppConfig from '../lib/config'; const LoggerApp = require('../lib/logger'); -const Config = require('../lib/config'); const Middleware = require('./middleware'); const Cats = require('../lib/status-cats'); -const defineAPI = function(config: Config, storage: IStorageHandler) { +const defineAPI = function(config: IConfig, storage: IStorageHandler) { const auth: IAuth = new Auth(config); const app: $Application = express(); // run in production mode by default, just in case @@ -103,7 +103,7 @@ const defineAPI = function(config: Config, storage: IStorageHandler) { export default async function(configHash: any) { LoggerApp.setup(configHash.logs); - const config: IConfig = new Config(configHash); + const config: IConfig = new AppConfig(configHash); const storage: IStorageHandler = new Storage(config); // waits until init calls have been intialized await storage.init(config); diff --git a/src/lib/auth-utils.js b/src/lib/auth-utils.js new file mode 100644 index 000000000..67e7142dc --- /dev/null +++ b/src/lib/auth-utils.js @@ -0,0 +1,34 @@ +import {ErrorCode} from './utils'; +import {API_ERROR} from './constants'; + +export function allow_action(action) { + return function(user, pkg, callback) { + const {name, groups} = user; + const hasPermission = pkg[action].some((group) => name === group || groups.includes(group)); + + if (hasPermission) { + return callback(null, true); + } + + if (name) { + callback(ErrorCode.getForbidden(`user ${name} is not allowed to ${action} package ${pkg.name}`)); + } else { + callback(ErrorCode.getForbidden(`unregistered users are not allowed to ${action} package ${pkg.name}`)); + } + }; +} + +export function getDefaultPlugins() { + return { + authenticate(user, password, cb) { + cb(ErrorCode.getForbidden(API_ERROR.BAD_USERNAME_PASSWORD)); + }, + + add_user(user, password, cb) { + return cb(ErrorCode.getConflict(API_ERROR.BAD_USERNAME_PASSWORD)); + }, + + allow_access: allow_action('access'), + allow_publish: allow_action('publish'), + }; +} diff --git a/src/lib/auth.js b/src/lib/auth.js index c53af7ebc..4daa9b6f6 100644 --- a/src/lib/auth.js +++ b/src/lib/auth.js @@ -2,20 +2,20 @@ import _ from 'lodash'; import {loadPlugin} from '../lib/plugin-loader'; -import {ErrorCode} from './utils'; +import {buildBase64Buffer, ErrorCode} from './utils'; import {aesDecrypt, aesEncrypt, signPayload, verifyPayload} from './crypto-utils'; import type {Config, Logger, Callback} from '@verdaccio/types'; import type {$Response, NextFunction} from 'express'; import type {$RequestExtend, JWTPayload} from '../../types'; -import {ROLES} from './constants'; - +import {API_ERROR, HTTP_STATUS, ROLES, TOKEN_BASIC, TOKEN_BEARER} from './constants'; +import {getMatchedPackagesSpec} from './config-utils'; +import type {IAuth} from '../../types'; +import {getDefaultPlugins} from './auth-utils'; const LoggerApi = require('./logger'); -/** - * Handles the authentification, load auth plugins. - */ -class Auth { + +class Auth implements IAuth { config: Config; logger: Logger; secret: string; @@ -31,48 +31,20 @@ class Auth { } _loadPlugin(config: Config) { - const plugin_params = { + const pluginOptions = { config, logger: this.logger, }; - return loadPlugin(config, config.auth, plugin_params, function(p) { - return p.authenticate || p.allow_access || p.allow_publish; + return loadPlugin(config, config.auth, pluginOptions, (plugin) => { + const {authenticate, allow_access, allow_publish} = plugin; + + return authenticate || allow_access || allow_publish; }); } _applyDefaultPlugins() { - const allow_action = function(action) { - return function(user, pkg, cb) { - const ok = pkg[action].reduce(function(prev, curr) { - if (user.name === curr || user.groups.indexOf(curr) !== -1) return true; - return prev; - }, false); - - if (ok) { - return cb(null, true); - } - - if (user.name) { - cb(ErrorCode.getForbidden(`user ${user.name} is not allowed to ${action} package ${pkg.name}`)); - } else { - cb(ErrorCode.getForbidden(`unregistered users are not allowed to ${action} package ${pkg.name}`)); - } - }; - }; - - this.plugins.push({ - authenticate: function(user, password, cb) { - cb(ErrorCode.getForbidden('bad username/password, access denied')); - }, - - add_user: function(user, password, cb) { - return cb(ErrorCode.getConflict('bad username/password, access denied')); - }, - - allow_access: allow_action('access'), - allow_publish: allow_action('publish'), - }); + this.plugins.push(getDefaultPlugins()); } authenticate(user: string, password: string, cb: Callback) { @@ -80,7 +52,7 @@ class Auth { (function next() { const plugin = plugins.shift(); - if (typeof(plugin.authenticate) !== 'function') { + if (_.isFunction(plugin.authenticate) === false) { return next(); } @@ -98,12 +70,12 @@ class Auth { // Info: Cannot use `== false to check falsey values` if (!!groups && groups.length !== 0) { // TODO: create a better understanding of expectations - if (typeof groups === 'string') { + if (_.isString(groups)) { throw new TypeError('invalid type for function'); } const isGroupValid: boolean = _.isArray(groups); if (!isGroupValid) { - throw new TypeError('user groups is different than an array'); + throw new TypeError(API_ERROR.BAD_FORMAT_USER_GROUP); } return cb(err, authenticatedUser(user, groups)); @@ -115,19 +87,19 @@ class Auth { add_user(user: string, password: string, cb: Callback) { let self = this; - let plugins = this.plugins.slice(0) + let plugins = this.plugins.slice(0); - ;(function next() { - let p = plugins.shift(); - let n = 'adduser'; - if (typeof(p[n]) !== 'function') { - n = 'add_user'; + (function next() { + let plugin = plugins.shift(); + let method = 'adduser'; + if (_.isFunction(plugin[method]) === false) { + method = 'add_user'; } - if (typeof(p[n]) !== 'function') { + if (_.isFunction[method] === false) { next(); } else { // p.add_user() execution - p[n](user, password, function(err, ok) { + plugin[method](user, password, function(err, ok) { if (err) { return cb(err); } @@ -146,7 +118,7 @@ class Auth { allow_access(packageName: string, user: string, callback: Callback) { let plugins = this.plugins.slice(0); // $FlowFixMe - let pkg = Object.assign({name: packageName}, this.config.getMatchedPackagesSpec(packageName)); + let pkg = Object.assign({name: packageName}, getMatchedPackagesSpec(packageName, this.config.packages)); (function next() { const plugin = plugins.shift(); @@ -175,7 +147,7 @@ class Auth { allow_publish(packageName: string, user: string, callback: Callback) { let plugins = this.plugins.slice(0); // $FlowFixMe - let pkg = Object.assign({name: packageName}, this.config.getMatchedPackagesSpec(packageName)); + let pkg = Object.assign({name: packageName}, getMatchedPackagesSpec(packageName, this.config.packages)); (function next() { const plugin = plugins.shift(); @@ -184,7 +156,7 @@ class Auth { return next(); } - plugin.allow_publish(user, pkg, function(err, ok) { + plugin.allow_publish(user, pkg, (err, ok) => { if (err) { return callback(err); } @@ -219,13 +191,13 @@ class Auth { req.remote_user = buildAnonymousUser(); const authorization = req.headers.authorization; - if (authorization == null) { + if (_.isNil(authorization)) { return next(); } const parts = authorization.split(' '); if (parts.length !== 2) { - return next( ErrorCode.getBadRequest('bad authorization header') ); + return next( ErrorCode.getBadRequest(API_ERROR.BAD_AUTH_HEADER) ); } const credentials = this._parseCredentials(parts); @@ -257,12 +229,12 @@ class Auth { _parseCredentials(parts: Array) { let credentials; const scheme = parts[0]; - if (scheme.toUpperCase() === 'BASIC') { - credentials = new Buffer(parts[1], 'base64').toString(); - this.logger.info('basic authentication is deprecated, please use JWT instead'); + if (scheme.toUpperCase() === TOKEN_BASIC.toUpperCase()) { + credentials = buildBase64Buffer(parts[1]).toString(); + this.logger.info(API_ERROR.DEPRECATED_BASIC_HEADER); return credentials; - } else if (scheme.toUpperCase() === 'BEARER') { - const token = new Buffer(parts[1], 'base64'); + } else if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) { + const token = buildBase64Buffer(parts[1]); credentials = aesDecrypt(token, this.secret).toString('utf8'); return credentials; @@ -276,17 +248,17 @@ class Auth { */ webUIJWTmiddleware() { return (req: $RequestExtend, res: $Response, _next: NextFunction) => { - if (req.remote_user !== null && req.remote_user.name !== undefined) { + if (_.isNull(req.remote_user) === false && _.isNil(req.remote_user.name) === false) { return _next(); } req.pause(); - const next = function(_err) { + const next = () => { req.resume(); return _next(); }; - const token = (req.headers.authorization || '').replace('Bearer ', ''); + const token = (req.headers.authorization || '').replace(`${TOKEN_BEARER} `, ''); if (!token) { return next(); } @@ -328,7 +300,7 @@ class Auth { try { decoded = verifyPayload(token, this.secret); } catch (err) { - throw ErrorCode.getCode(401, err.message); + throw ErrorCode.getCode(HTTP_STATUS.UNAUTHORIZED, err.message); } return decoded; @@ -350,7 +322,7 @@ function buildAnonymousUser() { return { name: undefined, // groups without '$' are going to be deprecated eventually - groups: ['$all', '$anonymous', '@all', '@anonymous'], + groups: [ROLES.$ALL, ROLES.$ANONYMOUS, ROLES.DEPRECATED_ALL, ROLES.DEPRECATED_ANONUMOUS], real_groups: [], }; } @@ -361,7 +333,12 @@ function buildAnonymousUser() { */ function authenticatedUser(name: string, pluginGroups: Array) { const isGroupValid: boolean = _.isArray(pluginGroups); - const groups = (isGroupValid ? pluginGroups : []).concat([ROLES.$ALL, ROLES.$AUTH, ROLES.DEPRECATED_ALL, ROLES.DEPRECATED_AUTH, ROLES.ALL]); + const groups = (isGroupValid ? pluginGroups : []).concat([ + ROLES.$ALL, + ROLES.$AUTH, + ROLES.DEPRECATED_ALL, + ROLES.DEPRECATED_AUTH, + ROLES.ALL]); return { name, diff --git a/src/lib/bootstrap.js b/src/lib/bootstrap.js index 358177a19..76784e803 100644 --- a/src/lib/bootstrap.js +++ b/src/lib/bootstrap.js @@ -64,9 +64,12 @@ export function getListListenAddresses(argListen: string, configListen: mixed) { * @param {String} pkgVersion * @param {String} pkgName */ -function startVerdaccio(config: any, cliListen: string, - configPath: string, pkgVersion: string, - pkgName: string, callback: Callback) { +function startVerdaccio(config: any, + cliListen: string, + configPath: string, + pkgVersion: string, + pkgName: string, + callback: Callback) { if (isObject(config) === false) { throw new Error('config file must be an object'); } diff --git a/src/lib/config-utils.js b/src/lib/config-utils.js new file mode 100644 index 000000000..2b712402d --- /dev/null +++ b/src/lib/config-utils.js @@ -0,0 +1,128 @@ +// @flow +import _ from 'lodash'; +import assert from 'assert'; +import minimatch from 'minimatch'; + +import {ErrorCode} from './utils'; + +import type {PackageList, UpLinksConfList} from '@verdaccio/types'; +import type {MatchedPackage} from '../../types'; + +const BLACKLIST = { + all: true, + anonymous: true, + undefined: true, + owner: true, + none: true, +}; + +/** + * Normalise user list. + * @return {Array} + */ +export function normalizeUserlist(oldFormat: any, newFormat: any) { + const result = []; + /* eslint prefer-rest-params: "off" */ + + for (let i=0; i < arguments.length; i++) { + if (arguments[i] == null) { + continue; + } + + // if it's a string, split it to array + if (_.isString(arguments[i])) { + result.push(arguments[i].split(/\s+/)); + } else if (Array.isArray(arguments[i])) { + result.push(arguments[i]); + } else { + throw ErrorCode.getInternalError('CONFIG: bad package acl (array or string expected): ' + JSON.stringify(arguments[i])); + } + } + return _.flatten(result); +} + +export function uplinkSanityCheck(uplinks: UpLinksConfList, users: any = BLACKLIST) { + const newUplinks = _.clone(uplinks); + let newUsers = _.clone(users); + + for (let uplink in newUplinks) { + if (Object.prototype.hasOwnProperty.call(newUplinks, uplink)) { + if (_.isNil(newUplinks[uplink].cache)) { + newUplinks[uplink].cache = true; + } + newUsers = sanityCheckNames(uplink, newUsers); + } + } + + return newUplinks; +} + +export function sanityCheckNames(item: string, users: any) { + assert(item !== 'all' && item !== 'owner' && item !== 'anonymous' && item !== 'undefined' && item !== 'none', 'CONFIG: reserved uplink name: ' + item); + assert(!item.match(/\s/), 'CONFIG: invalid uplink name: ' + item); + assert(_.isNil(users[item]), 'CONFIG: duplicate uplink name: ' + item); + users[item] = true; + + return users; +} + +export function sanityCheckUplinksProps(configUpLinks: any) { + const uplinks = _.clone(configUpLinks); + + for (let uplink in uplinks) { + if (Object.prototype.hasOwnProperty.call(uplinks, uplink)) { + assert(uplinks[uplink].url, 'CONFIG: no url for uplink: ' + uplink); + assert( _.isString(uplinks[uplink].url), 'CONFIG: wrong url format for uplink: ' + uplink); + uplinks[uplink].url = uplinks[uplink].url.replace(/\/$/, ''); + } + } + + return uplinks; +} + +/** + * Check whether an uplink can proxy + */ +export function hasProxyTo(pkg: string, upLink: string, packages: PackageList): boolean { + const matchedPkg: MatchedPackage = (getMatchedPackagesSpec(pkg, packages): MatchedPackage); + const proxyList = typeof matchedPkg !== 'undefined' ? matchedPkg.proxy : []; + if (proxyList) { + return proxyList.some((curr) => upLink === curr); + } + + return false; +} + +export function getMatchedPackagesSpec(pkg: string, packages: PackageList): MatchedPackage { + for (let i in packages) { + // $FlowFixMe + if (minimatch.makeRe(i).exec(pkg)) { + return packages[i]; + } + } + return; +} + +export function normalisePackageAccess(packages: PackageList): PackageList { + const normalizedPkgs: PackageList = {...packages}; + // add a default rule for all packages to make writing plugins easier + if (_.isNil(normalizedPkgs['**'])) { + normalizedPkgs['**'] = {}; + } + + for (let pkg in packages) { + if (Object.prototype.hasOwnProperty.call(packages, pkg)) { + assert(_.isObject(packages[pkg]) && _.isArray(packages[pkg]) === false, + `CONFIG: bad "'${pkg}'" package description (object expected)`); + normalizedPkgs[pkg].access = normalizeUserlist(packages[pkg].allow_access, packages[pkg].access); + delete normalizedPkgs[pkg].allow_access; + normalizedPkgs[pkg].publish = normalizeUserlist(packages[pkg].allow_publish, packages[pkg].publish); + delete normalizedPkgs[pkg].allow_publish; + normalizedPkgs[pkg].proxy = normalizeUserlist(packages[pkg].proxy_access, packages[pkg].proxy); + delete normalizedPkgs[pkg].proxy_access; + } + } + + return normalizedPkgs; +} + diff --git a/src/lib/config.js b/src/lib/config.js index 4fcbe4388..c28673380 100644 --- a/src/lib/config.js +++ b/src/lib/config.js @@ -1,210 +1,107 @@ +// @flow + +import _ from 'lodash'; +import assert from 'assert'; + import {generateRandomHexString} from './crypto-utils'; +import { + getMatchedPackagesSpec, + normalisePackageAccess, + sanityCheckUplinksProps, + uplinkSanityCheck} from './config-utils'; +import {getUserAgent, isObject} from './utils'; +import {APP_ERROR} from './constants'; -const assert = require('assert'); -const _ = require('lodash'); -const Error = require('http-errors'); -const minimatch = require('minimatch'); +import type { + PackageList, + Config as AppConfig, + Logger, + } from '@verdaccio/types'; -const Utils = require('./utils'); -const pkginfo = require('pkginfo')(module); // eslint-disable-line no-unused-vars -const pkgVersion = module.exports.version; -const pkgName = module.exports.name; -const strategicConfigProps = ['users', 'uplinks', 'packages']; +import type {MatchedPackage, StartUpConfig} from '../../types'; + +const LoggerApi = require('./logger'); +const strategicConfigProps = ['uplinks', 'packages']; const allowedEnvConfig = ['http_proxy', 'https_proxy', 'no_proxy']; -/** - * [[a, [b, c]], d] -> [a, b, c, d] - * @param {*} array - * @return {Array} - */ -function flatten(array) { - let result = []; - for (let i=0; i < array.length; i++) { - if (Array.isArray(array[i])) { - /* eslint prefer-spread: "off" */ - result.push.apply(result, flatten(array[i])); - } else { - result.push(array[i]); - } - } - return result; -} - -function checkUserOrUplink(item, users) { - assert(item !== 'all' && item !== 'owner' - && item !== 'anonymous' && item !== 'undefined' && item !== 'none', 'CONFIG: reserved user/uplink name: ' + item); - assert(!item.match(/\s/), 'CONFIG: invalid user name: ' + item); - assert(users[item] == null, 'CONFIG: duplicate user/uplink name: ' + item); - users[item] = true; -} - -/** - * Normalise user list. - * @return {Array} - */ -function normalizeUserlist() { - let result = []; - /* eslint prefer-rest-params: "off" */ - - for (let i=0; i < arguments.length; i++) { - if (arguments[i] == null) { - continue; - } - - // if it's a string, split it to array - if (typeof(arguments[i]) === 'string') { - result.push(arguments[i].split(/\s+/)); - } else if (Array.isArray(arguments[i])) { - result.push(arguments[i]); - } else { - throw Error('CONFIG: bad package acl (array or string expected): ' + JSON.stringify(arguments[i])); - } - } - return flatten(result); -} - /** * Coordinates the application configuration */ -class Config { - /** - * @param {*} config config the content - */ - constructor(config) { +class Config implements AppConfig { + logger: Logger; + user_agent: string; + secret: string; + uplinks: any; + packages: PackageList; + users: any; + server_id: string; + self_path: string; + storage: string | void; + $key: any; + $value: any; + + constructor(config: StartUpConfig) { const self = this; - const users = { - all: true, - anonymous: true, - undefined: true, - owner: true, - none: true, - }; + this.logger = LoggerApi.logger; + this.self_path = config.self_path; + this.storage = config.storage; for (let configProp in config) { if (self[configProp] == null) { self[configProp] = config[configProp]; } } - if (!self.user_agent) { - self.user_agent = `${pkgName}/${pkgVersion}`; + + if (_.isNil(this.user_agent)) { + this.user_agent = getUserAgent(); } // some weird shell scripts are valid yaml files parsed as string - assert.equal(typeof(config), 'object', 'CONFIG: it doesn\'t look like a valid config file'); + assert(_.isObject(config), APP_ERROR.CONFIG_NOT_VALID); // sanity check for strategic config properties strategicConfigProps.forEach(function(x) { - if (self[x] == null) self[x] = {}; - assert(Utils.isObject(self[x]), `CONFIG: bad "${x}" value (object expected)`); + if (self[x] == null) { + self[x] = {}; + } + + assert(isObject(self[x]), `CONFIG: bad "${x}" value (object expected)`); }); - // sanity check for users - for (let i in self.users) { - if (Object.prototype.hasOwnProperty.call(self.users, i)) { - checkUserOrUplink(i, users); - } - } - // sanity check for uplinks - /* eslint guard-for-in: 0 */ - for (let i in self.uplinks) { - if (self.uplinks[i].cache == null) { - self.uplinks[i].cache = true; - } - if (Object.prototype.hasOwnProperty.call(self.uplinks, i)) { - checkUserOrUplink(i, users); - } + this.uplinks = sanityCheckUplinksProps(uplinkSanityCheck(this.uplinks)); + + if (_.isNil(this.users) === false) { + this.logger.warn(`[users]: property on configuration file + is not longer supported, property being ignored`); } - for (let user in self.users) { - if (Object.prototype.hasOwnProperty.call(self.users, user)) { - assert(self.users[user].password, 'CONFIG: no password for user: ' + user); - assert(typeof(self.users[user].password) === 'string' && - self.users[user].password.match(/^[a-f0-9]{40}$/) - , 'CONFIG: wrong password format for user: ' + user + ', sha1 expected'); - } - } - - for (let uplink in self.uplinks) { - if (Object.prototype.hasOwnProperty.call(self.uplinks, uplink)) { - assert(self.uplinks[uplink].url, 'CONFIG: no url for uplink: ' + uplink); - assert( typeof(self.uplinks[uplink].url) === 'string' - , 'CONFIG: wrong url format for uplink: ' + uplink); - self.uplinks[uplink].url = self.uplinks[uplink].url.replace(/\/$/, ''); - } - } - - // add a default rule for all packages to make writing plugins easier - if (self.packages['**'] == null) { - self.packages['**'] = {}; - } - - for (let pkg in self.packages) { - if (Object.prototype.hasOwnProperty.call(self.packages, pkg)) { - assert( - typeof(self.packages[pkg]) === 'object' && - !Array.isArray(self.packages[pkg]) - , 'CONFIG: bad "'+pkg+'" package description (object expected)'); - - self.packages[pkg].access = normalizeUserlist(self.packages[pkg].allow_access, self.packages[pkg].access); - delete self.packages[pkg].allow_access; - - self.packages[pkg].publish = normalizeUserlist(self.packages[pkg].allow_publish, self.packages[pkg].publish); - delete self.packages[pkg].allow_publish; - - self.packages[pkg].proxy = normalizeUserlist(self.packages[pkg].proxy_access, self.packages[pkg].proxy); - delete self.packages[pkg].proxy_access; - } - } + this.packages = normalisePackageAccess(self.packages); // loading these from ENV if aren't in config - allowedEnvConfig.forEach((function(v) { - if (!(v in self)) { - self[v] = process.env[v] || process.env[v.toUpperCase()]; + allowedEnvConfig.forEach((envConf) => { + if (!(envConf in self)) { + self[envConf] = process.env[envConf] || process.env[envConf.toUpperCase()]; } - })); + }); // unique identifier of self server (or a cluster), used to avoid loops - if (!self.server_id) { - self.server_id = generateRandomHexString(6); + if (!this.server_id) { + this.server_id = generateRandomHexString(6); } } - /** - * Check whether an uplink can proxy - * @param {String} pkg package anem - * @param {*} upLink - * @return {Boolean} - */ - hasProxyTo(pkg, upLink) { - return (this.getMatchedPackagesSpec(pkg).proxy || []).reduce(function(prev, curr) { - if (upLink === curr) { - return true; - } - return prev; - }, false); - } - /** * Check for package spec - * @param {String} pkg package name - * @return {Object} */ - getMatchedPackagesSpec(pkg) { - for (let i in this.packages) { - if (minimatch.makeRe(i).exec(pkg)) { - return this.packages[i]; - } - } - return {}; + getMatchedPackagesSpec(pkg: string): MatchedPackage { + return getMatchedPackagesSpec(pkg, this.packages); } /** * Store or create whether recieve a secret key - * @param {String} secret - * @return {String} */ - checkSecretKey(secret) { - if (_.isString(secret) && secret !== '') { + checkSecretKey(secret: string): string { + if (_.isString(secret) && _.isEmpty(secret) === false) { this.secret = secret; return secret; } @@ -215,4 +112,4 @@ class Config { } } -module.exports = Config; +export default Config; diff --git a/src/lib/constants.js b/src/lib/constants.js index c4281a2c3..79f3b6ff3 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -29,10 +29,12 @@ export const DEFAULT_UPLINK = 'npmjs'; export const ROLES = { $ALL: '$all', + ALL: 'all', $AUTH: '$authenticated', + $ANONYMOUS: '$anonymous', DEPRECATED_ALL: '@all', DEPRECATED_AUTH: '@authenticated', - ALL: 'all', + DEPRECATED_ANONUMOUS: '@anonymous', }; export const HTTP_STATUS = { @@ -65,21 +67,37 @@ export const API_MESSAGE = { }; export const API_ERROR = { + BAD_USERNAME_PASSWORD: 'bad username/password, access denied {APP}', NO_PACKAGE: 'no such package available', NOT_ALLOWED: 'not allowed to access package', INTERNAL_SERVER_ERROR: 'internal server error', UNKNOWN_ERROR: 'unknown error', NOT_PACKAGE_UPLINK: 'package does not exist on uplink', + UPLINK_OFFLINE_PUBLISH: 'one of the uplinks is down, refuse to publish', + UPLINK_OFFLINE: 'uplink is offline', CONTENT_MISMATCH: 'content length mismatch', NOT_FILE_UPLINK: 'file doesn\'t exist on uplink', MAX_USERS_REACHED: 'maximum amount of users reached', VERSION_NOT_EXIST: 'this version doesn\'t exist', FILE_NOT_FOUND: 'File not found', BAD_STATUS_CODE: 'bad status code', + PACKAGE_EXIST: 'this package is already present', + BAD_AUTH_HEADER: 'bad authorization header', WEB_DISABLED: 'Web interface is disabled in the config file', + DEPRECATED_BASIC_HEADER: 'basic authentication is deprecated, please use JWT instead', + BAD_FORMAT_USER_GROUP: 'user groups is different than an array', +}; + +export const APP_ERROR = { + CONFIG_NOT_VALID: 'CONFIG: it does not look like a valid config file', }; export const DEFAULT_NO_README = 'ERROR: No README data found!'; export const WEB_TITLE = 'Verdaccio'; + +export const PACKAGE_ACCESS = { + SCOPE: '@*/*', + ALL: '**', +}; diff --git a/src/lib/storage-utils.js b/src/lib/storage-utils.js index f4790ebc6..4d20a3a65 100644 --- a/src/lib/storage-utils.js +++ b/src/lib/storage-utils.js @@ -7,6 +7,7 @@ import {generateRandomHexString} from '../lib/crypto-utils'; import type {Package, Version} from '@verdaccio/types'; import type {IStorage} from '../../types'; +import {API_ERROR, HTTP_STATUS} from './constants'; const pkgFileName = 'package.json'; const fileExist: string = 'EEXISTS'; @@ -123,11 +124,11 @@ export function cleanUpLinksRef(keepUpLinkData: boolean, result: Package): Packa export function checkPackageLocal(name: string, localStorage: IStorage): Promise { return new Promise((resolve, reject) => { localStorage.getPackageMetadata(name, (err, results) => { - if (!_.isNil(err) && err.status !== 404) { + if (!_.isNil(err) && err.status !== HTTP_STATUS.NOT_FOUND) { return reject(err); } if (results) { - return reject(ErrorCode.getConflict('this package is already present')); + return reject(ErrorCode.getConflict(API_ERROR.PACKAGE_EXIST)); } return resolve(); }); @@ -152,25 +153,25 @@ export function checkPackageRemote(name: string, isAllowPublishOffline: boolean, // $FlowFixMe syncMetadata(name, null, {}, (err, packageJsonLocal, upLinksErrors) => { // something weird - if (err && err.status !== 404) { + if (err && err.status !== HTTP_STATUS.NOT_FOUND) { return reject(err); } // checking package exist already if (_.isNil(packageJsonLocal) === false) { - return reject(ErrorCode.getConflict('this package is already present')); + return reject(ErrorCode.getConflict(API_ERROR.PACKAGE_EXIST)); } for (let errorItem = 0; errorItem < upLinksErrors.length; errorItem++) { // checking error // if uplink fails with a status other than 404, we report failure if (_.isNil(upLinksErrors[errorItem][0]) === false) { - if (upLinksErrors[errorItem][0].status !== 404) { + if (upLinksErrors[errorItem][0].status !== HTTP_STATUS.NOT_FOUND) { if (isAllowPublishOffline) { return resolve(); } - return reject(ErrorCode.getServiceUnavailable('one of the uplinks is down, refuse to publish')); + return reject(ErrorCode.getServiceUnavailable(API_ERROR.UPLINK_OFFLINE_PUBLISH)); } } } diff --git a/src/lib/storage.js b/src/lib/storage.js index e0745aad7..e66173941 100644 --- a/src/lib/storage.js +++ b/src/lib/storage.js @@ -26,6 +26,7 @@ Callback, Logger, } from '@verdaccio/types'; import type {IReadTarball, IUploadTarball} from '@verdaccio/streams'; +import {hasProxyTo} from './config-utils'; const LoggerApi = require('../lib/logger'); @@ -412,9 +413,9 @@ class Storage implements IStorageHandler { packageInfo = generatePackageTemplate(name); } - for (let up in this.uplinks) { - if (this.config.hasProxyTo(name, up)) { - upLinks.push(this.uplinks[up]); + for (let uplink in this.uplinks) { + if (hasProxyTo(name, uplink, this.config.packages)) { + upLinks.push(this.uplinks[uplink]); } } diff --git a/src/lib/up-storage.js b/src/lib/up-storage.js index 520486a4c..a789deb54 100644 --- a/src/lib/up-storage.js +++ b/src/lib/up-storage.js @@ -105,10 +105,10 @@ class ProxyStorage implements IProxy { process.nextTick(function() { if (cb) { - cb(ErrorCode.getInternalError('uplink is offline')); + cb(ErrorCode.getInternalError(API_ERROR.UPLINK_OFFLINE)); } // $FlowFixMe - streamRead.emit('error', ErrorCode.getInternalError('uplink is offline')); + streamRead.emit('error', ErrorCode.getInternalError(API_ERROR.UPLINK_OFFLINE)); }); // $FlowFixMe streamRead._read = function() {}; diff --git a/src/lib/utils.js b/src/lib/utils.js index 18669b2ae..2b70b0837 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -17,9 +17,22 @@ import type {$Request} from 'express'; import type {StringValue} from '../../types'; const Logger = require('./logger'); +const pkginfo = require('pkginfo')(module); // eslint-disable-line no-unused-vars +const pkgVersion = module.exports.version; +const pkgName = module.exports.name; export const DIST_TAGS = 'dist-tags'; +export function getUserAgent(): string { + assert(_.isString(pkgName)); + assert(_.isString(pkgVersion)); + return `${pkgName}/${pkgVersion}`; +} + +export function buildBase64Buffer(payload: string): Buffer { + return new Buffer(payload, 'base64'); +} + /** * Validate a package. * @return {Boolean} whether the package is valid or not diff --git a/test/integration/config_nocache.yaml b/test/integration/config_nocache.yaml index 2d69d18e4..3916fb6ba 100644 --- a/test/integration/config_nocache.yaml +++ b/test/integration/config_nocache.yaml @@ -1,9 +1,5 @@ storage: ./.verdaccio_test_env/test-storage -users: - test: - password: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 - uplinks: npmjs: url: https://registry.npmjs.org/ diff --git a/test/unit/api/api.pkg.access.spec.js b/test/unit/api/api.pkg.access.spec.js index 60f630265..6ddcad22b 100644 --- a/test/unit/api/api.pkg.access.spec.js +++ b/test/unit/api/api.pkg.access.spec.js @@ -4,17 +4,21 @@ import path from 'path'; import rimraf from 'rimraf'; import {HEADERS} from '../../../src/lib/constants'; -import configDefault from '../partials/config/access'; +import configDefault from '../partials/config/config_access'; import Config from '../../../src/lib/config'; import endPointAPI from '../../../src/api/index'; +import {mockServer} from './mock'; +import {DOMAIN_SERVERS} from '../../functional/config.functional'; require('../../../src/lib/logger').setup([]); describe('api with no limited access configuration', () => { let config; let app; + let mockRegistry; beforeAll(function(done) { + const mockServerPort = 55530; const store = path.join(__dirname, './partials/store/access-storage'); rimraf(store, async () => { const configForTest = _.clone(configDefault); @@ -24,8 +28,14 @@ describe('api with no limited access configuration', () => { } }; configForTest.self_path = store; + configForTest.uplinks = { + npmjs: { + url: `http://${DOMAIN_SERVERS}:${mockServerPort}` + } + }; config = new Config(configForTest); app = await endPointAPI(config); + mockRegistry = await mockServer(mockServerPort).init(); done(); }); }); @@ -34,9 +44,11 @@ describe('api with no limited access configuration', () => { const store = path.join(__dirname, './partials/store/access-storage'); rimraf(store, (err) => { if (err) { + mockRegistry[0].stop(); return done(err); } + mockRegistry[0].stop(); return done(); }); }); diff --git a/test/unit/api/api.spec.js b/test/unit/api/api.spec.js index e8fd817e0..f21526cc4 100644 --- a/test/unit/api/api.spec.js +++ b/test/unit/api/api.spec.js @@ -29,7 +29,7 @@ describe('endpoint unit test', () => { const configForTest = _.clone(configDefault); configForTest.auth = { htpasswd: { - file: './test-storage/htpasswd-test' + file: './test-storage/.htpasswd' } }; configForTest.uplinks = { diff --git a/test/unit/api/config-utils.spec.js b/test/unit/api/config-utils.spec.js new file mode 100644 index 000000000..667db85b5 --- /dev/null +++ b/test/unit/api/config-utils.spec.js @@ -0,0 +1,226 @@ +// @flow + +import path from 'path'; +import {spliceURL} from '../../../src/utils/string'; +import {parseConfigFile} from '../../../src/lib/utils'; +import { + getMatchedPackagesSpec, + hasProxyTo, + normalisePackageAccess, sanityCheckUplinksProps, + uplinkSanityCheck +} from '../../../src/lib/config-utils'; +import {PACKAGE_ACCESS, ROLES} from '../../../src/lib/constants'; + +describe('Config Utilities', () => { + + const parsePartial = (name) => { + return path.join(__dirname, `../partials/config/yaml/${name}.yaml`); + }; + + describe('uplinkSanityCheck', () => { + test('should test basic conversion', ()=> { + const uplinks = uplinkSanityCheck(parseConfigFile(parsePartial('uplink-basic')).uplinks); + expect(Object.keys(uplinks)).toContain('server1'); + expect(Object.keys(uplinks)).toContain('server2'); + }); + + test('should throw error on blacklisted uplink name', ()=> { + const {uplinks} = parseConfigFile(parsePartial('uplink-wrong')); + + expect(() => { + uplinkSanityCheck(uplinks) + }).toThrow('CONFIG: reserved uplink name: anonymous'); + }); + }); + + describe('sanityCheckUplinksProps', () => { + test('should fails if url prop is missing', ()=> { + const {uplinks} = parseConfigFile(parsePartial('uplink-wrong')); + expect(() => { + sanityCheckUplinksProps(uplinks) + }).toThrow('CONFIG: no url for uplink: none-url'); + }); + + test('should bypass an empty uplink list', ()=> { + expect(sanityCheckUplinksProps([])).toHaveLength(0); + }); + }); + + describe('normalisePackageAccess', () => { + test('should test basic conversion', ()=> { + const {packages} = parseConfigFile(parsePartial('pkgs-basic')); + const access = normalisePackageAccess(packages); + + expect(access).toBeDefined(); + const scoped = access[`${PACKAGE_ACCESS.SCOPE}`]; + const all = access[`${PACKAGE_ACCESS.ALL}`]; + + expect(scoped).toBeDefined(); + expect(all).toBeDefined(); + }); + + test('should test multi group', ()=> { + const {packages} = parseConfigFile(parsePartial('pkgs-multi-group')); + const access = normalisePackageAccess(packages); + + expect(access).toBeDefined(); + const scoped = access[`${PACKAGE_ACCESS.SCOPE}`]; + + const all = access[`${PACKAGE_ACCESS.ALL}`]; + + expect(scoped).toBeDefined(); + expect(scoped.access).toContain('$all'); + expect(scoped.publish).toHaveLength(2); + expect(scoped.publish).toContain('admin'); + expect(scoped.publish).toContain('superadmin'); + + expect(all).toBeDefined(); + expect(all.access).toHaveLength(3); + expect(all.access).toContain('$all'); + expect(all.publish).toHaveLength(1); + expect(all.publish).toContain('admin'); + + }); + + + test('should deprecated packages props', ()=> { + const {packages} = parseConfigFile(parsePartial('deprecated-pkgs-basic')); + const access = normalisePackageAccess(packages); + + expect(access).toBeDefined(); + const scoped = access[`${PACKAGE_ACCESS.SCOPE}`]; + const all = access[`${PACKAGE_ACCESS.ALL}`]; + const react = access['react-*']; + + expect(react).toBeDefined(); + expect(react.access).toBeDefined(); + // $FlowFixMe + expect(react.access[0]).toBe(ROLES.$ALL); + expect(react.publish).toBeDefined(); + // $FlowFixMe); + expect(react.publish[0]).toBe('admin'); + expect(react.proxy).toBeDefined(); + // $FlowFixMe + expect(react.proxy[0]).toBe('uplink2'); + expect(react.storage).toBeDefined(); + + expect(react.storage).toBe('react-storage'); + expect(scoped).toBeDefined(); + expect(scoped.storage).not.toBeDefined(); + expect(all).toBeDefined(); + expect(all.access).toBeDefined(); + expect(all.storage).not.toBeDefined(); + expect(all.publish).toBeDefined(); + expect(all.proxy).toBeDefined(); + expect(all.allow_access).toBeUndefined(); + expect(all.allow_publish).toBeUndefined(); + expect(all.proxy_access).toBeUndefined(); + }); + + test('should check not default packages access', ()=> { + const {packages} = parseConfigFile(parsePartial('pkgs-empty')); + const access = normalisePackageAccess(packages); + expect(access).toBeDefined(); + + const scoped = access[`${PACKAGE_ACCESS.SCOPE}`]; + expect(scoped).toBeUndefined(); + + // ** should be added by default + const all = access[`${PACKAGE_ACCESS.ALL}`]; + expect(all).toBeDefined(); + + expect(all.access).toBeUndefined(); + expect(all.publish).toBeUndefined(); + + }); + }); + + describe('getMatchedPackagesSpec', () => { + test('should test basic config', () => { + const {packages} = parseConfigFile(parsePartial('pkgs-custom')); + // $FlowFixMe + expect(getMatchedPackagesSpec('react', packages).proxy).toMatch('facebook'); + // $FlowFixMe + expect(getMatchedPackagesSpec('angular', packages).proxy).toMatch('google'); + // $FlowFixMe + expect(getMatchedPackagesSpec('vue', packages).proxy).toMatch('npmjs'); + // $FlowFixMe + expect(getMatchedPackagesSpec('@scope/vue', packages).proxy).toMatch('npmjs'); + }); + + test('should test no ** wildcard on config', () => { + const {packages} = parseConfigFile(parsePartial('pkgs-nosuper-wildcard-custom')); + // $FlowFixMe + expect(getMatchedPackagesSpec('react', packages).proxy).toMatch('facebook'); + // $FlowFixMe + expect(getMatchedPackagesSpec('angular', packages).proxy).toMatch('google'); + // $FlowFixMe + expect(getMatchedPackagesSpec('@fake/angular', packages).proxy).toMatch('npmjs'); + expect(getMatchedPackagesSpec('vue', packages)).toBeUndefined(); + expect(getMatchedPackagesSpec('@scope/vue', packages)).toBeUndefined(); + }); + }); + + describe('hasProxyTo', () => { + test('should test basic config', () => { + const packages = normalisePackageAccess(parseConfigFile(parsePartial('pkgs-basic')).packages); + // react + expect(hasProxyTo('react', 'facebook', packages)).toBeFalsy(); + expect(hasProxyTo('react', 'google', packages)).toBeFalsy(); + // vue + expect(hasProxyTo('vue', 'google', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'fake', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'npmjs', packages)).toBeTruthy(); + // angular + expect(hasProxyTo('angular', 'google', packages)).toBeFalsy(); + expect(hasProxyTo('angular', 'facebook', packages)).toBeFalsy(); + expect(hasProxyTo('angular', 'npmjs', packages)).toBeTruthy(); + }); + + test('should test resolve based on custom package access', () => { + const packages = normalisePackageAccess(parseConfigFile(parsePartial('pkgs-custom')).packages); + // react + expect(hasProxyTo('react', 'facebook', packages)).toBeTruthy(); + expect(hasProxyTo('react', 'google', packages)).toBeFalsy(); + // vue + expect(hasProxyTo('vue', 'google', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'fake', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'npmjs', packages)).toBeTruthy(); + // angular + expect(hasProxyTo('angular', 'google', packages)).toBeTruthy(); + expect(hasProxyTo('angular', 'facebook', packages)).toBeFalsy(); + expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); + }); + + test('should not resolve any proxy', () => { + const packages = normalisePackageAccess(parseConfigFile(parsePartial('pkgs-empty')).packages); + // react + expect(hasProxyTo('react', 'npmjs', packages)).toBeFalsy(); + expect(hasProxyTo('react', 'npmjs', packages)).toBeFalsy(); + // vue + expect(hasProxyTo('vue', 'npmjs', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'npmjs', packages)).toBeFalsy(); + expect(hasProxyTo('vue', 'npmjs', packages)).toBeFalsy(); + // angular + expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); + expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); + expect(hasProxyTo('angular', 'npmjs', packages)).toBeFalsy(); + // private + expect(hasProxyTo('private', 'fake', packages)).toBeFalsy(); + }); + }); + + describe('spliceURL', () => { + test('should splice two strings and generate a url', () => { + const url: string = spliceURL('http://domain.com', '/-/static/logo.png'); + + expect(url).toMatch('http://domain.com/-/static/logo.png'); + }); + + test('should splice a empty strings and generate a url', () => { + const url: string = spliceURL('', '/-/static/logo.png'); + + expect(url).toMatch('/-/static/logo.png'); + }); + }); +}); diff --git a/test/unit/api/config.spec.js b/test/unit/api/config.spec.js index 074c5e396..e19015257 100644 --- a/test/unit/api/config.spec.js +++ b/test/unit/api/config.spec.js @@ -6,6 +6,7 @@ import {parseConfigFile} from '../../../src/lib/utils'; import {DEFAULT_REGISTRY, DEFAULT_UPLINK, ROLES, WEB_TITLE} from '../../../src/lib/constants'; const resolveConf = (conf) => path.join(__dirname, `../../../conf/${conf}.yaml`); +require('../../../src/lib/logger').setup([]); const checkDefaultUplink = (config) => { expect(_.isObject(config.uplinks[DEFAULT_UPLINK])).toBeTruthy(); diff --git a/test/unit/api/search.spec.js b/test/unit/api/search.spec.js index 34e66a199..43cc61cb9 100644 --- a/test/unit/api/search.spec.js +++ b/test/unit/api/search.spec.js @@ -1,10 +1,10 @@ - - import assert from 'assert'; import Search from '../../../src/lib/search'; +import Config from '../../../src/lib/config'; import Storage from '../../../src/lib/storage'; + let config_hash = require('../partials/config/index'); -let Config = require('../../../src/lib/config'); + require('../../../src/lib/logger').setup([]); diff --git a/test/unit/api/up-storage.spec.js b/test/unit/api/up-storage.spec.js index 56cdc99c7..66d6231a8 100644 --- a/test/unit/api/up-storage.spec.js +++ b/test/unit/api/up-storage.spec.js @@ -8,7 +8,7 @@ import {setup} from '../../../src/lib/logger'; import type {Config, UpLinkConf} from '@verdaccio/types'; import type {IProxy} from '../../../types/index'; -import {API_ERROR} from "../../../src/lib/constants"; +import {API_ERROR, HTTP_STATUS} from "../../../src/lib/constants"; import {mockServer} from './mock'; import {DOMAIN_SERVERS} from '../../functional/config.functional'; @@ -102,8 +102,8 @@ describe('UpStorge', () => { stream.on('error', function(err) { expect(err).not.toBeNull(); - expect(err.statusCode).toBe(404); - expect(err.message).toMatch(/file doesn't exist on uplink/); + expect(err.statusCode).toBe(HTTP_STATUS.NOT_FOUND); + expect(err.message).toMatch(API_ERROR.NOT_FILE_UPLINK); done(); }); @@ -141,9 +141,9 @@ describe('UpStorge', () => { const streamThirdTry = proxy.fetchTarball(tarball); streamThirdTry.on('error', function(err) { expect(err).not.toBeNull(); - expect(err.statusCode).toBe(500); + expect(err.statusCode).toBe(HTTP_STATUS.INTERNAL_ERROR); expect(proxy.failed_requests).toBe(2); - expect(err.message).toMatch(/uplink is offline/); + expect(err.message).toMatch(API_ERROR.UPLINK_OFFLINE); done(); }); }); diff --git a/test/unit/partials/config/access.js b/test/unit/partials/config/config_access.js similarity index 82% rename from test/unit/partials/config/access.js rename to test/unit/partials/config/config_access.js index 480e5bdb2..d9a863465 100644 --- a/test/unit/partials/config/access.js +++ b/test/unit/partials/config/config_access.js @@ -1,11 +1,10 @@ import path from 'path'; -import {DEFAULT_REGISTRY} from '../../../../src/lib/constants'; const config = { storage: path.join(__dirname, '../store/access-storage'), uplinks: { 'npmjs': { - 'url': DEFAULT_REGISTRY + 'url': 'http://never_use:0000/' } }, packages: { diff --git a/test/unit/partials/config/yaml/deprecated-pkgs-basic.yaml b/test/unit/partials/config/yaml/deprecated-pkgs-basic.yaml new file mode 100644 index 000000000..9faed10e5 --- /dev/null +++ b/test/unit/partials/config/yaml/deprecated-pkgs-basic.yaml @@ -0,0 +1,14 @@ +packages: + '@*/*': + access: $all + publish: $authenticated + proxy: npmjs + 'react-*': + allow_access: $all + publish: admin + proxy_access: uplink2 + storage: 'react-storage' + '**': + allow_access: $all + allow_publish: $authenticated + proxy_access: npmjs diff --git a/test/unit/partials/config/yaml/pkgs-basic.yaml b/test/unit/partials/config/yaml/pkgs-basic.yaml new file mode 100644 index 000000000..b16483bd7 --- /dev/null +++ b/test/unit/partials/config/yaml/pkgs-basic.yaml @@ -0,0 +1,9 @@ +packages: + '@*/*': + access: $all + publish: $authenticated + proxy: npmjs + '**': + access: $all + publish: $authenticated + proxy: npmjs diff --git a/test/unit/partials/config/yaml/pkgs-custom.yaml b/test/unit/partials/config/yaml/pkgs-custom.yaml new file mode 100644 index 000000000..4ee938acf --- /dev/null +++ b/test/unit/partials/config/yaml/pkgs-custom.yaml @@ -0,0 +1,17 @@ +packages: + 'react': + access: admin + publish: admin + proxy: facebook + 'angular': + access: admin + publish: admin + proxy: google + '@*/*': + access: $all + publish: $authenticated + proxy: npmjs + '**': + access: $all + publish: $authenticated + proxy: npmjs diff --git a/test/unit/partials/config/yaml/pkgs-empty.yaml b/test/unit/partials/config/yaml/pkgs-empty.yaml new file mode 100644 index 000000000..43b16d090 --- /dev/null +++ b/test/unit/partials/config/yaml/pkgs-empty.yaml @@ -0,0 +1,4 @@ +packages: + 'private': + access: admin + publish: admin diff --git a/test/unit/partials/config/yaml/pkgs-multi-group.yaml b/test/unit/partials/config/yaml/pkgs-multi-group.yaml new file mode 100644 index 000000000..499bf954d --- /dev/null +++ b/test/unit/partials/config/yaml/pkgs-multi-group.yaml @@ -0,0 +1,9 @@ +packages: + '@*/*': + access: $all + publish: admin superadmin + proxy: npmjs + '**': + access: $all user1 user2 + publish: admin + proxy: npmjs diff --git a/test/unit/partials/config/yaml/pkgs-nosuper-wildcard-custom.yaml b/test/unit/partials/config/yaml/pkgs-nosuper-wildcard-custom.yaml new file mode 100644 index 000000000..d86a53f56 --- /dev/null +++ b/test/unit/partials/config/yaml/pkgs-nosuper-wildcard-custom.yaml @@ -0,0 +1,13 @@ +packages: + 'react': + access: admin + publish: admin + proxy: facebook + 'angular': + access: admin + publish: admin + proxy: google + '@fake/*': + access: $all + publish: $authenticated + proxy: npmjs diff --git a/test/unit/partials/config/yaml/uplink-basic.yaml b/test/unit/partials/config/yaml/uplink-basic.yaml new file mode 100644 index 000000000..934eca3ef --- /dev/null +++ b/test/unit/partials/config/yaml/uplink-basic.yaml @@ -0,0 +1,7 @@ +uplinks: + server1: + url: http://localhost:55551/ + maxage: 0 + server2: + url: http://localhost:55551/ + maxage: 0 diff --git a/test/unit/partials/config/yaml/uplink-wrong.yaml b/test/unit/partials/config/yaml/uplink-wrong.yaml new file mode 100644 index 000000000..ac5435aed --- /dev/null +++ b/test/unit/partials/config/yaml/uplink-wrong.yaml @@ -0,0 +1,9 @@ +uplinks: + facebook: + url: http://localhost:55551/ + maxage: 0 + anonymous: + url: http://localhost:55551/ + maxage: 0 + none-url: + maxage: 0 diff --git a/types/index.js b/types/index.js index 091088430..451a624c3 100644 --- a/types/index.js +++ b/types/index.js @@ -8,6 +8,7 @@ import type { MergeTags, Config, Logger, + PackageAccess, Package} from '@verdaccio/types'; import type { IUploadTarball, @@ -95,6 +96,13 @@ export interface IStorageHandler { _updateVersionsHiddenUpLink(versions: Versions, upLink: IProxy): void; } +export type StartUpConfig = { + storage: string; + self_path: string; +} + +export type MatchedPackage = PackageAccess | void; + export interface IStorage { config: Config; localData: ILocalData; diff --git a/yarn.lock b/yarn.lock index 687249e56..adfe7ae1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -234,9 +234,9 @@ version "1.0.0" resolved "https://registry.npmjs.org/@verdaccio/streams/-/streams-1.0.0.tgz#d5d24c6747208728b9fd16b908e3932c3fb1f864" -"@verdaccio/types@3.0.0": - version "3.0.0" - resolved "https://registry.npmjs.org/@verdaccio/types/-/types-3.0.0.tgz#1f5ad5c959955b320181ac9968dd894f209985bc" +"@verdaccio/types@3.0.1": + version "3.0.1" + resolved "https://registry.npmjs.org/@verdaccio/types/-/types-3.0.1.tgz#8cdfd74a8a070ad797a056652f257f9ead3717f9" "@webassemblyjs/ast@1.5.9": version "1.5.9" @@ -400,35 +400,31 @@ acorn-globals@^4.1.0: dependencies: acorn "^5.0.0" -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" +acorn-jsx@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz#e8e41e48ea2fe0c896740610ab6a4ffd8add225e" dependencies: - acorn "^3.0.4" + acorn "^5.0.3" -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - -acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0: +acorn@^5.0.0, acorn@^5.3.0: version "5.5.3" resolved "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9" +acorn@^5.0.3, acorn@^5.6.0: + version "5.7.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" + agent-base@^4.1.0: version "4.2.0" resolved "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce" dependencies: es6-promisify "^5.0.0" -ajv-keywords@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" - ajv-keywords@^3.0.0, ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" -ajv@^5.0.0, ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0: +ajv@^5.0.0, ajv@^5.1.0: version "5.5.2" resolved "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: @@ -446,6 +442,15 @@ ajv@^6.0.1, ajv@^6.1.0: json-schema-traverse "^0.3.0" uri-js "^4.2.1" +ajv@^6.5.0: + version "6.5.1" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.5.1.tgz#88ebc1263c7133937d108b80c5572e64e1d9322d" + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.1" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -745,7 +750,7 @@ babel-cli@6.26.0: optionalDependencies: chokidar "^1.6.1" -babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: +babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: @@ -951,12 +956,12 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-jest@22.4.3, babel-jest@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.3.tgz#4b7a0b6041691bbd422ab49b3b73654a49a6627a" +babel-jest@23.2.0, babel-jest@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-23.2.0.tgz#14a9d6a3f4122dfea6069d37085adf26a53a4dba" dependencies: - babel-plugin-istanbul "^4.1.5" - babel-preset-jest "^22.4.3" + babel-plugin-istanbul "^4.1.6" + babel-preset-jest "^23.2.0" babel-loader@7.1.4: version "7.1.4" @@ -989,7 +994,7 @@ babel-plugin-flow-runtime@0.17.0: camelcase "^3.0.0" flow-config-parser "^0.3.0" -babel-plugin-istanbul@^4.1.5: +babel-plugin-istanbul@^4.1.6: version "4.1.6" resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45" dependencies: @@ -998,9 +1003,9 @@ babel-plugin-istanbul@^4.1.5: istanbul-lib-instrument "^1.10.1" test-exclude "^4.2.1" -babel-plugin-jest-hoist@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.3.tgz#7d8bcccadc2667f96a0dcc6afe1891875ee6c14a" +babel-plugin-jest-hoist@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz#e61fae05a1ca8801aadee57a6d66b8cefaf44167" babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" @@ -1413,11 +1418,11 @@ babel-preset-flow@6.23.0, babel-preset-flow@^6.23.0: dependencies: babel-plugin-transform-flow-strip-types "^6.22.0" -babel-preset-jest@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-22.4.3.tgz#e92eef9813b7026ab4ca675799f37419b5a44156" +babel-preset-jest@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz#8ec7a03a138f001a1a8fb1e8113652bf1a55da46" dependencies: - babel-plugin-jest-hoist "^22.4.3" + babel-plugin-jest-hoist "^23.2.0" babel-plugin-syntax-object-rest-spread "^6.13.0" babel-preset-react@6.24.1: @@ -1683,9 +1688,9 @@ browser-process-hrtime@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz#425d68a58d3447f02a04aa894187fce8af8b7b8e" -browser-resolve@^1.11.2: - version "1.11.2" - resolved "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" +browser-resolve@^1.11.3: + version "1.11.3" + resolved "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" dependencies: resolve "1.1.7" @@ -1792,7 +1797,7 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -builtin-modules@^1.0.0, builtin-modules@^1.1.1: +builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -2139,6 +2144,10 @@ clone@^1.0.2: version "1.0.4" resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" +clorox@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/clorox/-/clorox-1.0.3.tgz#6fa63653f280c33d69f548fb14d239ddcfa1590d" + co@^4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -2279,7 +2288,7 @@ concat-stream@1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@^1.4.10, concat-stream@^1.5.0, concat-stream@^1.6.0: +concat-stream@^1.4.10, concat-stream@^1.5.0: version "1.6.2" resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" dependencies: @@ -2952,7 +2961,7 @@ doctrine@1.5.0: esutils "^2.0.2" isarray "^1.0.0" -doctrine@^2.0.2, doctrine@^2.1.0: +doctrine@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" dependencies: @@ -3205,6 +3214,16 @@ error-stack-parser@^2.0.0: dependencies: stackframe "^1.0.3" +es-abstract@^1.10.0: + version "1.12.0" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" + dependencies: + 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-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0: version "1.11.0" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681" @@ -3296,7 +3315,7 @@ eslint-loader@2.0.0: object-hash "^1.1.4" rimraf "^2.6.1" -eslint-module-utils@^2.1.1: +eslint-module-utils@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746" dependencies: @@ -3307,39 +3326,39 @@ eslint-plugin-babel@4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/eslint-plugin-babel/-/eslint-plugin-babel-4.1.2.tgz#79202a0e35757dd92780919b2336f1fa2fe53c1e" -eslint-plugin-flowtype@2.46.1: - version "2.46.1" - resolved "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.46.1.tgz#c4f81d580cd89c82bc3a85a1ccf4ae3a915143a4" +eslint-plugin-flowtype@2.49.3: + version "2.49.3" + resolved "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.49.3.tgz#ccca6ee5ba2027eb3ed36bc2ec8c9a842feee841" dependencies: - lodash "^4.15.0" + lodash "^4.17.10" -eslint-plugin-import@2.9.0: - version "2.9.0" - resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.9.0.tgz#26002efbfca5989b7288ac047508bd24f217b169" +eslint-plugin-import@2.13.0: + version "2.13.0" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.13.0.tgz#df24f241175e312d91662dc91ca84064caec14ed" dependencies: - builtin-modules "^1.1.1" contains-path "^0.1.0" debug "^2.6.8" doctrine "1.5.0" eslint-import-resolver-node "^0.3.1" - eslint-module-utils "^2.1.1" + eslint-module-utils "^2.2.0" has "^1.0.1" lodash "^4.17.4" minimatch "^3.0.3" read-pkg-up "^2.0.0" + resolve "^1.6.0" -eslint-plugin-jest@21.14.0: - version "21.14.0" - resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-21.14.0.tgz#ae1c2f9f8cc4ba389c824a5ba9414036b6d5ea5a" +eslint-plugin-jest@21.17.0: + version "21.17.0" + resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-21.17.0.tgz#fdb00e2f9ff16987d6ebcf2c75c7add105760bbb" -eslint-plugin-react@7.7.0: - version "7.7.0" - resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz#f606c719dbd8a1a2b3d25c16299813878cca0160" +eslint-plugin-react@7.10.0: + version "7.10.0" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.10.0.tgz#af5c1fef31c4704db02098f9be18202993828b50" dependencies: - doctrine "^2.0.2" - has "^1.0.1" + doctrine "^2.1.0" + has "^1.0.3" jsx-ast-utils "^2.0.1" - prop-types "^15.6.0" + prop-types "^15.6.2" eslint-scope@^3.7.1, eslint-scope@~3.7.1: version "3.7.1" @@ -3348,58 +3367,66 @@ eslint-scope@^3.7.1, eslint-scope@~3.7.1: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-scope@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" -eslint@4.18.2: - version "4.18.2" - resolved "https://registry.npmjs.org/eslint/-/eslint-4.18.2.tgz#0f81267ad1012e7d2051e186a9004cc2267b8d45" +eslint@5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eslint/-/eslint-5.0.1.tgz#109b90ab7f7a736f54e0f341c8bb9d09777494c3" dependencies: - ajv "^5.3.0" - babel-code-frame "^6.22.0" + ajv "^6.5.0" + babel-code-frame "^6.26.0" chalk "^2.1.0" - concat-stream "^1.6.0" - cross-spawn "^5.1.0" + cross-spawn "^6.0.5" debug "^3.1.0" doctrine "^2.1.0" - eslint-scope "^3.7.1" + eslint-scope "^4.0.0" eslint-visitor-keys "^1.0.0" - espree "^3.5.2" - esquery "^1.0.0" + espree "^4.0.0" + esquery "^1.0.1" esutils "^2.0.2" file-entry-cache "^2.0.0" functional-red-black-tree "^1.0.1" glob "^7.1.2" - globals "^11.0.1" + globals "^11.5.0" ignore "^3.3.3" imurmurhash "^0.1.4" - inquirer "^3.0.6" - is-resolvable "^1.0.0" - js-yaml "^3.9.1" + inquirer "^5.2.0" + is-resolvable "^1.1.0" + js-yaml "^3.11.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.3.0" - lodash "^4.17.4" - minimatch "^3.0.2" + lodash "^4.17.5" + minimatch "^3.0.4" mkdirp "^0.5.1" natural-compare "^1.4.0" optionator "^0.8.2" path-is-inside "^1.0.2" pluralize "^7.0.0" progress "^2.0.0" + regexpp "^1.1.0" require-uncached "^1.0.3" - semver "^5.3.0" + semver "^5.5.0" + string.prototype.matchall "^2.0.0" strip-ansi "^4.0.0" - strip-json-comments "~2.0.1" - table "4.0.2" - text-table "~0.2.0" + strip-json-comments "^2.0.1" + table "^4.0.3" + text-table "^0.2.0" -espree@^3.5.2: - version "3.5.4" - resolved "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" +espree@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz#253998f20a0f82db5d866385799d912a83a36634" dependencies: - acorn "^5.5.0" - acorn-jsx "^3.0.0" + acorn "^5.6.0" + acorn-jsx "^4.1.1" esprima@^2.6.0: version "2.7.3" @@ -3413,7 +3440,7 @@ esprima@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" -esquery@^1.0.0: +esquery@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" dependencies: @@ -3522,16 +3549,16 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -expect@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/expect/-/expect-22.4.3.tgz#d5a29d0a0e1fb2153557caef2674d4547e914674" +expect@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/expect/-/expect-23.2.0.tgz#53a7e135e36fe27e75867b1178ff08aaacc2b0dd" dependencies: 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" + jest-diff "^23.2.0" + jest-get-type "^22.1.0" + jest-matcher-utils "^23.2.0" + jest-message-util "^23.2.0" + jest-regex-util "^23.0.0" express@4.16.3, express@^4.16.2: version "4.16.3" @@ -3585,7 +3612,7 @@ extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" -external-editor@^2.0.4, external-editor@^2.1.0: +external-editor@^2.1.0: version "2.2.0" resolved "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" dependencies: @@ -4130,10 +4157,14 @@ global@4.3.2, global@^4.3.0: min-document "^2.19.0" process "~0.5.1" -globals@^11.0.1, globals@^11.1.0: +globals@^11.1.0: version "11.5.0" resolved "https://registry.npmjs.org/globals/-/globals-11.5.0.tgz#6bc840de6771173b191f13d3a9c94d441ee92642" +globals@^11.5.0: + version "11.7.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" + globals@^9.18.0: version "9.18.0" resolved "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -4296,6 +4327,12 @@ has@^1.0.1: dependencies: function-bind "^1.0.2" +has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + dependencies: + function-bind "^1.1.1" + hash-base@^3.0.0: version "3.0.4" resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" @@ -4627,25 +4664,6 @@ ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: version "1.3.5" resolved "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" -inquirer@^3.0.6: - version "3.3.0" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - inquirer@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz#db350c2b73daca77ff1243962e9f22f099685726" @@ -4963,7 +4981,7 @@ is-regexp@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" -is-resolvable@^1.0.0: +is-resolvable@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" @@ -5056,7 +5074,7 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -istanbul-api@^1.1.14: +istanbul-api@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.1.tgz#4c3b05d18c0016d1022e079b98dc82c40f488954" dependencies: @@ -5073,7 +5091,7 @@ istanbul-api@^1.1.14: mkdirp "^0.5.1" once "^1.4.0" -istanbul-lib-coverage@^1.1.1, istanbul-lib-coverage@^1.1.2, istanbul-lib-coverage@^1.2.0: +istanbul-lib-coverage@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341" @@ -5083,7 +5101,7 @@ istanbul-lib-hook@^1.2.0: dependencies: append-transform "^0.4.0" -istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.8.0: +istanbul-lib-instrument@^1.10.1: version "1.10.1" resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz#724b4b6caceba8692d3f1f9d0727e279c401af7b" dependencies: @@ -5104,16 +5122,6 @@ istanbul-lib-report@^1.1.4: path-parse "^1.0.5" supports-color "^3.1.2" -istanbul-lib-source-maps@^1.2.1: - version "1.2.3" - resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz#20fb54b14e14b3fb6edb6aca3571fd2143db44e6" - dependencies: - debug "^3.1.0" - istanbul-lib-coverage "^1.1.2" - mkdirp "^0.5.1" - rimraf "^2.6.1" - source-map "^0.5.3" - istanbul-lib-source-maps@^1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.4.tgz#cc7ccad61629f4efff8e2f78adb8c522c9976ec7" @@ -5130,15 +5138,15 @@ istanbul-reports@^1.3.0: dependencies: handlebars "^4.0.3" -jest-changed-files@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-22.4.3.tgz#8882181e022c38bd46a2e4d18d44d19d90a90fb2" +jest-changed-files@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-23.2.0.tgz#a145a6e4b66d0129fc7c99cee134dc937a643d9c" dependencies: throat "^4.0.0" -jest-cli@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-22.4.3.tgz#bf16c4a5fb7edc3fa5b9bb7819e34139e88a72c7" +jest-cli@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-23.2.0.tgz#3b543a3da5145dd8937931017282379fc696c45b" dependencies: ansi-escapes "^3.0.0" chalk "^2.0.1" @@ -5147,251 +5155,275 @@ jest-cli@^22.4.3: 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.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" + istanbul-api "^1.3.1" + istanbul-lib-coverage "^1.2.0" + istanbul-lib-instrument "^1.10.1" + istanbul-lib-source-maps "^1.2.4" + jest-changed-files "^23.2.0" + jest-config "^23.2.0" + jest-environment-jsdom "^23.2.0" + jest-get-type "^22.1.0" + jest-haste-map "^23.2.0" + jest-message-util "^23.2.0" + jest-regex-util "^23.0.0" + jest-resolve-dependencies "^23.2.0" + jest-runner "^23.2.0" + jest-runtime "^23.2.0" + jest-snapshot "^23.2.0" + jest-util "^23.2.0" + jest-validate "^23.2.0" + jest-watcher "^23.2.0" + jest-worker "^23.2.0" + micromatch "^3.1.10" node-notifier "^5.2.1" + prompts "^0.1.9" 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" + yargs "^11.0.0" -jest-config@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-22.4.3.tgz#0e9d57db267839ea31309119b41dc2fa31b76403" +jest-config@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-23.2.0.tgz#d2fb556fd5a2a19c39eb56d139dcca5dad2a1c88" dependencies: + babel-core "^6.0.0" + babel-jest "^23.2.0" chalk "^2.0.1" glob "^7.1.1" - 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-environment-jsdom "^23.2.0" + jest-environment-node "^23.2.0" + jest-get-type "^22.1.0" + jest-jasmine2 "^23.2.0" + jest-regex-util "^23.0.0" + jest-resolve "^23.2.0" + jest-util "^23.2.0" + jest-validate "^23.2.0" + pretty-format "^23.2.0" -jest-diff@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz#e18cc3feff0aeef159d02310f2686d4065378030" +jest-diff@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-23.2.0.tgz#9f2cf4b51e12c791550200abc16b47130af1062a" dependencies: chalk "^2.0.1" diff "^3.2.0" - jest-get-type "^22.4.3" - pretty-format "^22.4.3" + jest-get-type "^22.1.0" + pretty-format "^23.2.0" -jest-docblock@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-22.4.3.tgz#50886f132b42b280c903c592373bb6e93bb68b19" +jest-docblock@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-23.2.0.tgz#f085e1f18548d99fdd69b20207e6fd55d91383a7" dependencies: detect-newline "^2.1.0" -jest-environment-jsdom-global@1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/jest-environment-jsdom-global/-/jest-environment-jsdom-global-1.0.3.tgz#e0616c235709e6919f33f9b5e5c3854cd8edba39" - -jest-environment-jsdom@22.4.3, jest-environment-jsdom@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz#d67daa4155e33516aecdd35afd82d4abf0fa8a1e" +jest-each@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-23.2.0.tgz#a400f81c857083f50c4f53399b109f12023fb19d" dependencies: - jest-mock "^22.4.3" - jest-util "^22.4.3" + chalk "^2.0.1" + pretty-format "^23.2.0" + +jest-environment-jsdom-global@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/jest-environment-jsdom-global/-/jest-environment-jsdom-global-1.1.0.tgz#dea8a3991aa9a6e638dce8933957fdbc42c97bf6" + +jest-environment-jsdom@23.2.0, jest-environment-jsdom@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-23.2.0.tgz#3634603a08a975b0ca8a658320f56a54a8e04558" + dependencies: + jest-mock "^23.2.0" + jest-util "^23.2.0" jsdom "^11.5.1" -jest-environment-node@22.4.3, jest-environment-node@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-22.4.3.tgz#54c4eaa374c83dd52a9da8759be14ebe1d0b9129" +jest-environment-node@23.2.0, jest-environment-node@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-23.2.0.tgz#b6fe41372e382093bb6f3d9bdf6c1c4ec0a50f18" dependencies: - jest-mock "^22.4.3" - jest-util "^22.4.3" + jest-mock "^23.2.0" + jest-util "^23.2.0" -jest-get-type@^22.4.3: +jest-get-type@^22.1.0: version "22.4.3" resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" -jest-haste-map@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-22.4.3.tgz#25842fa2ba350200767ac27f658d58b9d5c2e20b" +jest-haste-map@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-23.2.0.tgz#d10cbac007c695948c8ef1821a2b2ed2d4f2d4d8" dependencies: 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" + jest-docblock "^23.2.0" + jest-serializer "^23.0.1" + jest-worker "^23.2.0" + micromatch "^3.1.10" sane "^2.0.0" -jest-jasmine2@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-22.4.3.tgz#4daf64cd14c793da9db34a7c7b8dcfe52a745965" +jest-jasmine2@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-23.2.0.tgz#aa670cdb1e4d5f8ec774c94dda5e105fe33d8bb4" dependencies: chalk "^2.0.1" co "^4.6.0" - expect "^22.4.3" - graceful-fs "^4.1.11" + expect "^23.2.0" 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.0" + jest-diff "^23.2.0" + jest-each "^23.2.0" + jest-matcher-utils "^23.2.0" + jest-message-util "^23.2.0" + jest-snapshot "^23.2.0" + jest-util "^23.2.0" + pretty-format "^23.2.0" -jest-leak-detector@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-22.4.3.tgz#2b7b263103afae8c52b6b91241a2de40117e5b35" +jest-leak-detector@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-23.2.0.tgz#c289d961dc638f14357d4ef96e0431ecc1aa377d" dependencies: - pretty-format "^22.4.3" + pretty-format "^23.2.0" -jest-matcher-utils@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz#4632fe428ebc73ebc194d3c7b65d37b161f710ff" +jest-matcher-utils@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-23.2.0.tgz#4d4981f23213e939e3cedf23dc34c747b5ae1913" dependencies: chalk "^2.0.1" - jest-get-type "^22.4.3" - pretty-format "^22.4.3" + jest-get-type "^22.1.0" + pretty-format "^23.2.0" -jest-message-util@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz#cf3d38aafe4befddbfc455e57d65d5239e399eb7" +jest-message-util@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.2.0.tgz#591e8148fff69cf89b0414809c721756ebefe744" dependencies: "@babel/code-frame" "^7.0.0-beta.35" chalk "^2.0.1" - micromatch "^2.3.11" + micromatch "^3.1.10" slash "^1.0.0" stack-utils "^1.0.1" -jest-mock@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-22.4.3.tgz#f63ba2f07a1511772cdc7979733397df770aabc7" +jest-mock@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-23.2.0.tgz#ad1c60f29e8719d47c26e1138098b6d18b261134" -jest-regex-util@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-22.4.3.tgz#a826eb191cdf22502198c5401a1fc04de9cef5af" +jest-regex-util@^23.0.0: + version "23.0.0" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-23.0.0.tgz#dd5c1fde0c46f4371314cf10f7a751a23f4e8f76" -jest-resolve-dependencies@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-22.4.3.tgz#e2256a5a846732dc3969cb72f3c9ad7725a8195e" +jest-resolve-dependencies@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-23.2.0.tgz#6df8d5709c6406639cd07f54bff074e01b5c0458" dependencies: - jest-regex-util "^22.4.3" + jest-regex-util "^23.0.0" + jest-snapshot "^23.2.0" -jest-resolve@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-22.4.3.tgz#0ce9d438c8438229aa9b916968ec6b05c1abb4ea" +jest-resolve@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-23.2.0.tgz#a0790ad5a3b99002ab4dbfcbf8d9e2d6a69b3d99" dependencies: - browser-resolve "^1.11.2" + browser-resolve "^1.11.3" chalk "^2.0.1" + realpath-native "^1.0.0" -jest-runner@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-22.4.3.tgz#298ddd6a22b992c64401b4667702b325e50610c3" +jest-runner@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-23.2.0.tgz#0d91967ea82f72b0c705910926086d2055ce75af" dependencies: 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" + graceful-fs "^4.1.11" + jest-config "^23.2.0" + jest-docblock "^23.2.0" + jest-haste-map "^23.2.0" + jest-jasmine2 "^23.2.0" + jest-leak-detector "^23.2.0" + jest-message-util "^23.2.0" + jest-runtime "^23.2.0" + jest-util "^23.2.0" + jest-worker "^23.2.0" + source-map-support "^0.5.6" throat "^4.0.0" -jest-runtime@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-22.4.3.tgz#b69926c34b851b920f666c93e86ba2912087e3d0" +jest-runtime@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-23.2.0.tgz#62dcb01766a1c4c64696dc090209e76ce1aadcbc" dependencies: babel-core "^6.0.0" - babel-jest "^22.4.3" - babel-plugin-istanbul "^4.1.5" + babel-plugin-istanbul "^4.1.6" chalk "^2.0.1" convert-source-map "^1.4.0" exit "^0.1.2" + fast-json-stable-stringify "^2.0.0" 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" + jest-config "^23.2.0" + jest-haste-map "^23.2.0" + jest-message-util "^23.2.0" + jest-regex-util "^23.0.0" + jest-resolve "^23.2.0" + jest-snapshot "^23.2.0" + jest-util "^23.2.0" + jest-validate "^23.2.0" + micromatch "^3.1.10" realpath-native "^1.0.0" slash "^1.0.0" strip-bom "3.0.0" write-file-atomic "^2.1.0" - yargs "^10.0.3" + yargs "^11.0.0" -jest-serializer@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-22.4.3.tgz#a679b81a7f111e4766235f4f0c46d230ee0f7436" +jest-serializer@^23.0.1: + version "23.0.1" + resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-23.0.1.tgz#a3776aeb311e90fe83fab9e533e85102bd164165" -jest-snapshot@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-22.4.3.tgz#b5c9b42846ffb9faccb76b841315ba67887362d2" +jest-snapshot@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-23.2.0.tgz#c7a3d017177bbad60c8a595869cf90a8782e6a7e" dependencies: chalk "^2.0.1" - jest-diff "^22.4.3" - jest-matcher-utils "^22.4.3" + jest-diff "^23.2.0" + jest-matcher-utils "^23.2.0" mkdirp "^0.5.1" natural-compare "^1.4.0" - pretty-format "^22.4.3" + pretty-format "^23.2.0" -jest-util@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-22.4.3.tgz#c70fec8eec487c37b10b0809dc064a7ecf6aafac" +jest-util@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-23.2.0.tgz#62b770757696d96e094a04b8f1c373ca50a5ab2e" dependencies: callsites "^2.0.0" chalk "^2.0.1" graceful-fs "^4.1.11" is-ci "^1.0.10" - jest-message-util "^22.4.3" + jest-message-util "^23.2.0" mkdirp "^0.5.1" + slash "^1.0.0" source-map "^0.6.0" -jest-validate@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-22.4.3.tgz#0780954a5a7daaeec8d3c10834b9280865976b30" +jest-validate@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-23.2.0.tgz#67c8b909e11af1701765238894c67ac3291b195e" dependencies: chalk "^2.0.1" - jest-config "^22.4.3" - jest-get-type "^22.4.3" + jest-get-type "^22.1.0" leven "^2.1.0" - pretty-format "^22.4.3" + pretty-format "^23.2.0" -jest-worker@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-22.4.3.tgz#5c421417cba1c0abf64bf56bd5fb7968d79dd40b" +jest-watcher@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-23.2.0.tgz#678e852896e919e9d9a0eb4b8baf1ae279620ea9" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.1" + string-length "^2.0.0" + +jest-worker@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-23.2.0.tgz#faf706a8da36fae60eb26957257fa7b5d8ea02b9" dependencies: merge-stream "^1.0.1" -jest@22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/jest/-/jest-22.4.3.tgz#2261f4b117dc46d9a4a1a673d2150958dee92f16" +jest@23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/jest/-/jest-23.2.0.tgz#828bf31a096d45dcf06824d1ea03013af7bcfc20" dependencies: import-local "^1.0.0" - jest-cli "^22.4.3" + jest-cli "^23.2.0" js-base64@^2.1.8, js-base64@^2.1.9: version "2.4.3" @@ -5405,13 +5437,20 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" -js-yaml@3.11.0, js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1: +js-yaml@3.11.0, js-yaml@^3.7.0, js-yaml@^3.9.0: version "3.11.0" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef" dependencies: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.11.0: + version "3.12.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@~3.7.0: version "3.7.0" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" @@ -5474,6 +5513,10 @@ json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + json-schema@0.2.3: version "0.2.3" resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -5482,12 +5525,6 @@ json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - dependencies: - jsonify "~0.0.0" - json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -5500,10 +5537,6 @@ json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -6035,7 +6068,7 @@ micromatch@^2.1.5, micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" -micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9: +micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9: version "3.1.10" resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" dependencies: @@ -7319,9 +7352,9 @@ pretty-error@^2.0.2: renderkid "^2.0.1" utila "~0.4" -pretty-format@^22.4.3: - version "22.4.3" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f" +pretty-format@^23.2.0: + version "23.2.0" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-23.2.0.tgz#3b0aaa63c018a53583373c1cb3a5d96cc5e83017" dependencies: ansi-regex "^3.0.0" ansi-styles "^3.2.0" @@ -7356,6 +7389,13 @@ promise@^7.1.1: dependencies: asap "~2.0.3" +prompts@^0.1.9: + version "0.1.10" + resolved "https://registry.npmjs.org/prompts/-/prompts-0.1.10.tgz#832cbf6116ecb121d6884e84643bb2cf92b3ed2c" + dependencies: + clorox "^1.0.3" + sisteransi "^0.1.1" + prop-types@15.6.1, prop-types@^15.5.4, prop-types@^15.6.0, prop-types@^15.6.1: version "15.6.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" @@ -7364,6 +7404,13 @@ prop-types@15.6.1, prop-types@^15.5.4, prop-types@^15.6.0, prop-types@^15.6.1: loose-envify "^1.3.1" object-assign "^4.1.1" +prop-types@^15.6.2: + version "15.6.2" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" + dependencies: + loose-envify "^1.3.1" + object-assign "^4.1.1" + proxy-addr@~2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" @@ -7787,6 +7834,16 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp.prototype.flags@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz#6b30724e306a27833eeb171b66ac8890ba37e41c" + dependencies: + define-properties "^1.1.2" + +regexpp@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" + regexpu-core@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" @@ -8052,6 +8109,12 @@ resolve@^1.5.0: dependencies: path-parse "^1.0.5" +resolve@^1.6.0: + version "1.8.1" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + dependencies: + path-parse "^1.0.5" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -8115,16 +8178,6 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" - dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" - rxjs@^5.5.2: version "5.5.10" resolved "https://registry.npmjs.org/rxjs/-/rxjs-5.5.10.tgz#fde02d7a614f6c8683d0d1957827f492e09db045" @@ -8347,6 +8400,10 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" +sisteransi@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce" + slash@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -8448,7 +8505,7 @@ source-map-support@^0.4.15: dependencies: source-map "^0.5.6" -source-map-support@^0.5.0: +source-map-support@^0.5.6: version "0.5.6" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz#4435cee46b1aab62b8e8610ce60f788091c51c13" dependencies: @@ -8668,6 +8725,16 @@ string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string.prototype.matchall@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz#2af8fe3d2d6dc53ca2a59bd376b089c3c152b3c8" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.10.0" + function-bind "^1.1.1" + has-symbols "^1.0.0" + regexp.prototype.flags "^1.2.0" + string_decoder@^1.0.0, string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -8727,7 +8794,7 @@ strip-indent@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" -strip-json-comments@~2.0.1: +strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -8884,18 +8951,7 @@ symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" -table@4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" - dependencies: - ajv "^5.2.3" - ajv-keywords "^2.1.0" - chalk "^2.1.0" - lodash "^4.17.4" - slice-ansi "1.0.0" - string-width "^2.1.1" - -table@^4.0.1: +table@^4.0.1, table@^4.0.3: version "4.0.3" resolved "https://registry.npmjs.org/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc" dependencies: @@ -8944,7 +9000,7 @@ text-extensions@^1.0.0: version "1.7.0" resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.7.0.tgz#faaaba2625ed746d568a23e4d0aacd9bf08a8b39" -text-table@~0.2.0: +text-table@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -9791,12 +9847,6 @@ yargs-parser@^7.0.0: dependencies: camelcase "^4.1.0" -yargs-parser@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" - dependencies: - camelcase "^4.1.0" - yargs-parser@^9.0.2: version "9.0.2" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" @@ -9820,24 +9870,7 @@ yargs@11.0.0: y18n "^3.2.1" yargs-parser "^9.0.2" -yargs@^10.0.3: - version "10.1.2" - resolved "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5" - dependencies: - 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" - -yargs@^11.1.0: +yargs@^11.0.0, yargs@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" dependencies: