diff --git a/.babelrc.js b/.babelrc.js index 438c4838..efb55191 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -12,5 +12,6 @@ module.exports = { }, }, ], + '@babel/preset-typescript', ], }; diff --git a/.circleci/config.yml b/.circleci/config.yml index 3970bd93..eb4cf294 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -25,6 +25,7 @@ jobs: paths: - ~/.cache - run: yarn lint + - run: yarn check-types - run: yarn test:browser:ci - run: yarn test:coverage --no-cache - run: ./node_modules/.bin/codecov diff --git a/.eslintignore b/.eslintignore index 5e45ae73..638dc2f8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,5 +3,6 @@ dist es example lib +types babel-browser-build.js browser-es-module-loader.js diff --git a/.eslintrc.js b/.eslintrc.js index 162a5764..03fd7b46 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,6 +3,11 @@ module.exports = { jest: true, }, extends: ['airbnb-base', 'prettier'], + parser: 'pluggable-babel-eslint', + parserOptions: { + plugins: ['typescript'], + }, + plugins: ['typescript'], rules: { 'valid-jsdoc': [ 'error', @@ -15,11 +20,43 @@ module.exports = { }, ], }, + overrides: [ + { + files: ['**/*.ts'], + rules: { + 'no-undef': 'off', + 'typescript/class-name-casing': 'error', + 'typescript/explicit-function-return-type': [ + 'error', + { allowExpressions: true }, + ], + 'typescript/interface-name-prefix': 'error', + 'typescript/no-angle-bracket-type-assertion': 'error', + 'typescript/no-empty-interface': 'error', + 'typescript/no-inferrable-types': [ + 'error', + { ignoreProperties: false, ignoreParameters: false }, + ], + 'typescript/no-namespace': 'error', + 'typescript/no-non-null-assertion': 'error', + 'typescript/no-parameter-properties': 'error', + 'typescript/no-triple-slash-reference': 'error', + 'typescript/no-unused-vars': 'error', + 'typescript/no-var-requires': 'error', + 'typescript/prefer-namespace-keyword': 'error', + 'typescript/type-annotation-spacing': 'error', + }, + }, + ], settings: { 'import/resolver': { jest: { jestConfigFile: './jest.config.js', }, + node: { + extensions: ['.js', '.ts'], + paths: ['src', 'src/__mocks__'], + }, }, }, }; diff --git a/.gitignore b/.gitignore index 3ef8688b..344a9b2b 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,7 @@ dist/ ### VSCode ### .vscode + + +### rollup-plugin-typescript2 ### +.rpt2_cache diff --git a/API.md b/API.md index 3796a7e7..3db87fb1 100644 --- a/API.md +++ b/API.md @@ -41,27 +41,27 @@ account (email address or username).

## Functions
-
breach(breachName)Promise
+
breach(breachName)Promise.<Breach> | Promise.<null>

Fetches data for a specific breach event.

-
breachedAccount(account, [options])Promise
+
breachedAccount(account, [options])Promise.<Array.<Breach>> | Promise.<null>

Fetches breach data for a specific account.

-
breaches([options])Promise
+
breaches([options])Promise.<Array.<Breach>>

Fetches all breach events in the system.

-
dataClasses()Promise
+
dataClasses()Promise.<Array.<string>> | Promise.<null>

Fetches all data classes in the system.

-
pasteAccount(email)Promise
+
pasteAccount(email)Promise.<Array.<Paste>> | Promise.<null>

Fetches paste data for a specific account (email address).

-
pwnedPassword(password)Promise
+
pwnedPassword(password)Promise.<number>

Fetches the number of times the the given password has been exposed in a breach (0 indicating no exposure). The password is given in plain text, but only the first 5 characters of its SHA-1 hash will be submitted to the API.

-
pwnedPasswordRange(prefix)Promise
+
pwnedPasswordRange(prefix)Promise.<Array.<PwnedPasswordSuffix>>

Fetches the SHA-1 hash suffixes for the given 5-character SHA-1 hash prefix.

When a password hash with the same first 5 characters is found in the Pwned Passwords repository, the API will respond with an HTTP 200 and include the @@ -69,7 +69,7 @@ suffix of every hash beginning with the specified prefix, followed by a count of how many times it appears in the data set. This function parses the response and returns a more structured format.

-
search(account, [breachOptions])Promise
+
search(account, [breachOptions])Promise.<SearchResults>

Fetches all breaches and all pastes associated with the provided account (email address or username). Note that the remote API does not support querying pastes by username (only email addresses), so in the event the @@ -80,6 +80,25 @@ convenience method is designed to mimic.

+## Typedefs + +
+
Breach : object
+

An object representing a breach.

+
+
Paste : object
+

An object representing a paste.

+
+
PwnedPasswordSuffix : object
+

An object representing an exposed password hash suffix (corresponding to a +given hash prefix) and how many times it occurred in the Pwned Passwords +repository.

+
+
SearchResults : object
+

An object representing search results.

+
+
+ ## breach @@ -91,12 +110,13 @@ import { breach } from 'hibp'; ``` -### breach(breachName) ⇒ Promise ⏏ +### breach(breachName) ⇒ [Promise.<Breach>](#breach--object) \| Promise.<null> ⏏ Fetches data for a specific breach event. **Kind**: global method of [breach](#module_breach) -**Returns**: Promise - a Promise which resolves to an object representing a -breach (or null if no breach was found), or rejects with an Error +**Returns**: [Promise.<Breach>](#breach--object) \| Promise.<null> - a Promise which resolves to an +object representing a breach (or null if no breach was found), or rejects +with an Error | Param | Type | Description | | --- | --- | --- | @@ -127,17 +147,18 @@ import { breachedAccount } from 'hibp'; ``` -### breachedAccount(account, [options]) ⇒ Promise ⏏ +### breachedAccount(account, [options]) ⇒ Promise.<Array.<Breach>> \| Promise.<null> ⏏ Fetches breach data for a specific account. **Kind**: global method of [breachedAccount](#module_breachedAccount) -**Returns**: Promise - a Promise which resolves to an array of breach objects -(or null if no breaches were found), or rejects with an Error +**Returns**: Promise.<Array.<Breach>> \| Promise.<null> - a Promise which resolves to an +array of breach objects (or null if no breaches were found), or rejects with +an Error | Param | Type | Description | | --- | --- | --- | | account | string | a username or email address | -| [options] | Object | a configuration object | +| [options] | object | a configuration object | | [options.domain] | string | a domain by which to filter the results (default: all domains) | | [options.truncate] | boolean | truncate the results to only include the name of each breach (default: false) | @@ -194,16 +215,16 @@ import { breaches } from 'hibp'; ``` -### breaches([options]) ⇒ Promise ⏏ +### breaches([options]) ⇒ Promise.<Array.<Breach>> ⏏ Fetches all breach events in the system. **Kind**: global method of [breaches](#module_breaches) -**Returns**: Promise - a Promise which resolves to an array of breach objects -(an empty array if no breaches were found), or rejects with an Error +**Returns**: Promise.<Array.<Breach>> - a Promise which resolves to an array of breach +objects (an empty array if no breaches were found), or rejects with an Error | Param | Type | Description | | --- | --- | --- | -| [options] | Object | a configuration object | +| [options] | object | a configuration object | | [options.domain] | string | a domain by which to filter the results (default: all domains) | **Example** @@ -245,12 +266,13 @@ import { dataClasses } from 'hibp'; ``` -### dataClasses() ⇒ Promise ⏏ +### dataClasses() ⇒ Promise.<Array.<string>> \| Promise.<null> ⏏ Fetches all data classes in the system. **Kind**: global method of [dataClasses](#module_dataClasses) -**Returns**: Promise - a Promise which resolves to an array of strings (or -null if no data classes were found), or rejects with an Error +**Returns**: Promise.<Array.<string>> \| Promise.<null> - a Promise which resolves to an +array of strings (or null if no data classes were found), or rejects with an +Error **Example** ```js dataClasses() @@ -276,12 +298,13 @@ import { pasteAccount } from 'hibp'; ``` -### pasteAccount(email) ⇒ Promise ⏏ +### pasteAccount(email) ⇒ Promise.<Array.<Paste>> \| Promise.<null> ⏏ Fetches paste data for a specific account (email address). **Kind**: global method of [pasteAccount](#module_pasteAccount) -**Returns**: Promise - a Promise which resolves to an array of paste objects -(or null if no pastes were found), or rejects with an Error +**Returns**: Promise.<Array.<Paste>> \| Promise.<null> - a Promise which resolves to an +array of paste objects (or null if no pastes were found), or rejects with an +Error | Param | Type | Description | | --- | --- | --- | @@ -313,14 +336,14 @@ import { pwnedPassword } from 'hibp'; ``` -### pwnedPassword(password) ⇒ Promise ⏏ +### pwnedPassword(password) ⇒ Promise.<number> ⏏ Fetches the number of times the the given password has been exposed in a breach (0 indicating no exposure). The password is given in plain text, but only the first 5 characters of its SHA-1 hash will be submitted to the API. **Kind**: global method of [pwnedPassword](#module_pwnedPassword) -**Returns**: Promise - a Promise which resolves to the number of times the -password has been exposed in a breach, or rejects with an Error +**Returns**: Promise.<number> - a Promise which resolves to the number of times +the password has been exposed in a breach, or rejects with an Error **See**: https://haveibeenpwned.com/API/v2#PwnedPasswords | Param | Type | Description | @@ -354,7 +377,7 @@ import { pwnedPasswordRange } from 'hibp'; ``` -### pwnedPasswordRange(prefix) ⇒ Promise ⏏ +### pwnedPasswordRange(prefix) ⇒ Promise.<Array.<PwnedPasswordSuffix>> ⏏ Fetches the SHA-1 hash suffixes for the given 5-character SHA-1 hash prefix. When a password hash with the same first 5 characters is found in the Pwned @@ -364,10 +387,10 @@ of how many times it appears in the data set. This function parses the response and returns a more structured format. **Kind**: global method of [pwnedPasswordRange](#module_pwnedPasswordRange) -**Returns**: Promise - a Promise which resolves to an array of objects, each -containing the `suffix` that when matched with the prefix composes the -complete hash, and a `count` of how many times it appears in the breached -password data set, or rejects with an Error +**Returns**: Promise.<Array.<PwnedPasswordSuffix>> - a Promise which resolves to an array +of objects, each containing the `suffix` that when matched with the prefix +composes the complete hash, and a `count` of how many times it appears in the +breached password data set, or rejects with an Error **See**: https://haveibeenpwned.com/API/v2#SearchingPwnedPasswordsByRange | Param | Type | Description | @@ -410,7 +433,7 @@ import { search } from 'hibp'; ``` -### search(account, [breachOptions]) ⇒ Promise ⏏ +### search(account, [breachOptions]) ⇒ [Promise.<SearchResults>](#SearchResults) ⏏ Fetches all breaches and all pastes associated with the provided account (email address or username). Note that the remote API does not support querying pastes by username (only email addresses), so in the event the @@ -420,16 +443,16 @@ exactly how searching via the current web interface behaves, which this convenience method is designed to mimic. **Kind**: global method of [search](#module_search) -**Returns**: Promise - a Promise which resolves to an object containing a -"breaches" key (which can be null or an array of breach objects) and a -"pastes" key (which can be null or an array of paste objects), or rejects -with an Error +**Returns**: [Promise.<SearchResults>](#SearchResults) - a Promise which resolves to an object +containing a "breaches" key (which can be null or an array of breach objects) +and a "pastes" key (which can be null or an array of paste objects), or +rejects with an Error **See**: https://haveibeenpwned.com/ | Param | Type | Description | | --- | --- | --- | | account | string | an email address or username | -| [breachOptions] | Object | a configuration object pertaining to breach queries | +| [breachOptions] | object | a configuration object pertaining to breach queries | | [breachOptions.domain] | string | a domain by which to filter the results (default: all domains) | | [breachOptions.truncate] | boolean | truncate the results to only include the name of each breach (default: false) | @@ -473,3 +496,73 @@ import * as hibp from 'hibp'; // Now all hibp functions are available on the hibp object: hibp.dataClasses().then(...) ``` + + +## Breach : object +An object representing a breach. + +**Kind**: global typedef +**Properties** + +| Name | Type | +| --- | --- | +| Name | string | +| Title | string | +| Domain | string | +| BreachDate | string | +| AddedDate | string | +| ModifiedDate | string | +| PwnCount | number | +| Description | string | +| LogoPath | string | +| DataClasses | Array.<string> | +| IsVerified | boolean | +| IsFabricated | boolean | +| IsSensitive | boolean | +| IsRetired | boolean | +| IsSpamList | boolean | + + + +## Paste : object +An object representing a paste. + +**Kind**: global typedef +**Properties** + +| Name | Type | +| --- | --- | +| Id | string | +| Source | string | +| Title | string | +| Date | string | +| EmailCount | number | + + + +## PwnedPasswordSuffix : object +An object representing an exposed password hash suffix (corresponding to a +given hash prefix) and how many times it occurred in the Pwned Passwords +repository. + +**Kind**: global typedef +**Properties** + +| Name | Type | +| --- | --- | +| suffix | string | +| count | number | + + + +## SearchResults : object +An object representing search results. + +**Kind**: global typedef +**Properties** + +| Name | Type | +| --- | --- | +| breaches | [Array.<Breach>](#breach--object) \| null | +| pastes | [Array.<Paste>](#Paste) \| null | + diff --git a/cypress/integration/browser.spec.js b/cypress/integration/browser.spec.ts similarity index 100% rename from cypress/integration/browser.spec.js rename to cypress/integration/browser.spec.ts diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 00000000..76d60acd --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "noEmit": true, + "strict": true, + "types": ["cypress", "jsdom"] + }, + "include": ["."] +} diff --git a/cypress/types/hibp-umd.d.ts b/cypress/types/hibp-umd.d.ts new file mode 100644 index 00000000..03f6ca97 --- /dev/null +++ b/cypress/types/hibp-umd.d.ts @@ -0,0 +1,14 @@ +// Augment the Window interface so we can use cy.window().its('hibp.breach') +// (for example) in tests. + +interface Window { + hibp: object; + 'hibp.breach': () => any; + 'hibp.breachedAccount': () => any; + 'hibp.breaches': () => any; + 'hibp.dataClasses': () => any; + 'hibp.pasteAccount': () => any; + 'hibp.pwnedPassword': () => any; + 'hibp.pwnedPasswordRange': () => any; + 'hibp.search': () => any; +} diff --git a/jest.config.js b/jest.config.js index e46b326c..0e0a3adf 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,6 +4,10 @@ module.exports = { coveragePathIgnorePatterns: ['/node_modules/', '/test/'], coverageReporters: ['html', 'json', 'lcov', 'text'], modulePaths: ['/src', '/src/__mocks__'], - testPathIgnorePatterns: ['cypress'], + moduleFileExtensions: ['js', 'json', 'ts'], testEnvironment: 'node', + testMatch: ['**/*.test.(j|t)s?(x)'], + transform: { + '^.+\\.ts$': 'babel-jest', + }, }; diff --git a/jsdoc2md.json b/jsdoc2md.json new file mode 100644 index 00000000..cf5e1bbc --- /dev/null +++ b/jsdoc2md.json @@ -0,0 +1,20 @@ +{ + "source": { + "includePattern": ".+\\.(j|t)s(doc|x)?$", + "excludePattern": ".+\\.(test|spec).ts" + }, + "plugins": ["node_modules/jsdoc-babel"], + "babel": { + "extensions": ["ts", "tsx"], + "ignore": ["**/*.(test|spec).ts"], + "babelrc": false, + "presets": [ + ["@babel/preset-env", { "targets": { "node": "current" } }], + "@babel/typescript" + ], + "plugins": [ + "@babel/proposal-class-properties", + "@babel/proposal-object-rest-spread" + ] + } +} diff --git a/lint-staged.config.js b/lint-staged.config.js index 433c00d4..507cc7aa 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -1,4 +1,4 @@ module.exports = { - '*.js': ['prettier --write', 'eslint --fix', 'git add'], + '*.{js,ts}': ['prettier --write', 'eslint --fix', 'git add'], '*.{json,md,yml}': ['prettier --write', 'git add'], }; diff --git a/package.json b/package.json index 3f28d1de..63dbaea8 100644 --- a/package.json +++ b/package.json @@ -27,32 +27,40 @@ "main": "lib/hibp.js", "module": "es/hibp.js", "unpkg": "dist/hibp.min.js", + "types": "types/hibp.d.ts", "runkitExampleFilename": "example/runkit.js", "files": [ "dist", "es", "example", "lib", + "types", "API.md", "MIGRATION.md" ], "sideEffects": false, "scripts": { - "build": "rollup --config", + "build": "npm-run-all --sequential build:bundle build:types docs", + "build:bundle": "rollup --config", + "build:types": "dts-bundle-generator --silent --umd-module-name hibp --out-file types/hibp.d.ts src/hibp.ts && prettier --loglevel silent --write types/hibp.d.ts", + "check-types": "npm-run-all --sequential --continue-on-error check-types:*", + "check-types:cypress": "tsc --project cypress", + "check-types:src": "tsc --noEmit", "clean": "rimraf dist lib es coverage", "cy:open": "cypress open", "cy:run": "cypress run", - "docs": "jsdoc2md src/*.js > API.md", - "format": "prettier --write \"**/*.{js,json,md,yml}\"", - "lint": "eslint .", - "prebuild": "npm run -s clean", - "prepare": "npm run -s build", - "prepublishOnly": "npm run lint && npm run test", + "docs": "jsdoc2md --no-cache --files src/*.ts --configure jsdoc2md.json > API.md && node scripts/fix-api-docs.js", + "format": "prettier --write \"**/*.{js,ts,json,md,yml}\"", + "lint": "eslint --ext \".js,.ts\" .", + "prebuild": "npm run --silent clean", + "prepare": "npm run --silent build", + "prepublishOnly": "npm-run-all --sequential lint check-types test", + "start-server-and-test": "start-server-and-test start-test-server 5000", "start-test-server": "serve --no-clipboard", "test": "cross-env NODE_ENV=test jest", + "test:browser": "npm-run-all --sequential build:bundle \"start-server-and-test cy:open\"", + "test:browser:ci": "npm-run-all --sequential build:bundle \"start-server-and-test cy:run\"", "test:coverage": "npm run test -- --coverage", - "test:browser": "npm run build && start-server-and-test start-test-server 5000 cy:open", - "test:browser:ci": "npm run build && start-server-and-test start-test-server 5000 cy:run", "test:watch": "npm run test -- --watch" }, "repository": { @@ -73,24 +81,38 @@ "devDependencies": { "@babel/cli": "7.2.3", "@babel/core": "7.2.2", + "@babel/parser": "7.2.3", + "@babel/plugin-proposal-class-properties": "7.2.3", + "@babel/plugin-proposal-object-rest-spread": "7.2.0", "@babel/preset-env": "7.2.3", + "@babel/preset-typescript": "7.1.0", + "@types/common-tags": "1.8.0", + "@types/jest": "23.3.11", + "@types/jsdom": "12.2.1", + "@types/jssha": "2.0.0", + "@types/node": "10.12.18", "babel-core": "7.0.0-bridge.0", "babel-jest": "23.6.0", "codecov": "3.1.0", "common-tags": "1.8.0", "cross-env": "5.2.0", - "cypress": "3.1.4", + "cypress": "3.1.3", + "dts-bundle-generator": "2.0.0", "eslint": "5.12.0", "eslint-config-airbnb-base": "13.1.0", "eslint-config-prettier": "3.3.0", "eslint-import-resolver-jest": "2.1.1", "eslint-plugin-cypress": "2.2.0", "eslint-plugin-import": "2.14.0", + "eslint-plugin-typescript": "0.14.0", "glob": "7.1.3", "husky": "1.3.1", "jest": "23.6.0", + "jsdoc-babel": "0.5.0", "jsdoc-to-markdown": "4.0.1", "lint-staged": "8.1.0", + "npm-run-all": "4.1.5", + "pluggable-babel-eslint": "0.3.0", "prettier": "1.15.3", "rimraf": "2.6.3", "rollup": "1.0.2", @@ -100,7 +122,9 @@ "rollup-plugin-node-resolve": "4.0.0", "rollup-plugin-replace": "2.1.0", "rollup-plugin-terser": "4.0.1", + "rollup-plugin-typescript2": "0.18.1", "serve": "10.1.1", - "start-server-and-test": "1.7.11" + "start-server-and-test": "1.7.11", + "typescript": "3.2.2" } } diff --git a/rollup.config.js b/rollup.config.js index 26e12888..7e50f464 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,4 +1,5 @@ import glob from 'glob'; +import typescript from 'rollup-plugin-typescript2'; import json from 'rollup-plugin-json'; import nodeResolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; @@ -8,7 +9,17 @@ import { terser } from 'rollup-plugin-terser'; const umdName = 'hibp'; const external = id => !/^(\.|\/|[a-z]:\\)/i.test(id); -const babelOpts = { exclude: 'node_modules/**' }; +const babelOpts = { exclude: 'node_modules/**', extensions: ['.js', '.ts'] }; +const typescriptOpts = { + check: false, + rollupCommonJSResolveHack: true, + tsconfigOverride: { + compilerOptions: { + noEmit: true, + declaration: false, + }, + }, +}; const nodeResolveOpts = { browser: true, jsnext: true }; const terserOpts = { compress: { @@ -21,7 +32,7 @@ const terserOpts = { export default [ // CommonJS { - input: 'src/hibp.js', + input: 'src/hibp.ts', output: { file: 'lib/hibp.js', format: 'cjs', @@ -29,13 +40,22 @@ export default [ indent: false, }, external, - plugins: [json({ preferConst: true }), babel(babelOpts)], + plugins: [ + json({ preferConst: true }), + babel(babelOpts), + typescript(typescriptOpts), + ], }, // ESM { - input: glob.sync('src/**/*.js', { - ignore: ['**/__mocks__/**', '**/__tests__/**', '**/*.test.js'], + input: glob.sync('src/**/*.ts', { + ignore: [ + '**/__mocks__/**', + '**/__tests__/**', + '**/*.test.ts', + '**/*.d.ts', + ], }), output: { dir: 'es', @@ -44,12 +64,16 @@ export default [ indent: false, }, external, - plugins: [json({ preferConst: true }), babel(babelOpts)], + plugins: [ + json({ preferConst: true }), + babel(babelOpts), + typescript(typescriptOpts), + ], }, // ESM for Browsers (development) { - input: 'src/hibp.js', + input: 'src/hibp.ts', output: { file: 'dist/hibp.mjs', format: 'esm', @@ -61,12 +85,13 @@ export default [ nodeResolve(nodeResolveOpts), commonjs(), replace({ 'process.env.NODE_ENV': JSON.stringify('development') }), + typescript(typescriptOpts), ], }, // ESM for Browsers (production) { - input: 'src/hibp.js', + input: 'src/hibp.ts', output: { file: 'dist/hibp.min.mjs', format: 'esm', @@ -78,13 +103,14 @@ export default [ nodeResolve(nodeResolveOpts), commonjs(), replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), + typescript(typescriptOpts), terser(terserOpts), ], }, // UMD (development) { - input: 'src/hibp.js', + input: 'src/hibp.ts', output: { file: 'dist/hibp.js', format: 'umd', @@ -97,12 +123,13 @@ export default [ nodeResolve(nodeResolveOpts), commonjs(), replace({ 'process.env.NODE_ENV': JSON.stringify('development') }), + typescript(typescriptOpts), ], }, // UMD (production) { - input: 'src/hibp.js', + input: 'src/hibp.ts', output: { file: 'dist/hibp.min.js', format: 'umd', @@ -116,6 +143,7 @@ export default [ nodeResolve(nodeResolveOpts), commonjs(), replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), + typescript(typescriptOpts), terser(terserOpts), ], }, diff --git a/scripts/fix-api-docs.js b/scripts/fix-api-docs.js new file mode 100644 index 00000000..1a189a1f --- /dev/null +++ b/scripts/fix-api-docs.js @@ -0,0 +1,17 @@ +const fs = require('fs'); + +const filename = 'API.md'; +const generatedApiDocs = fs.readFileSync(filename, 'utf8'); +const newApiDocs = generatedApiDocs + // Replace the generated link to the `Breach` object with a more specific + // anchor name as the generated one collides with the `breach` function + // because anchor links are case insensitive. + .replace(/#Breach/g, '#breach--object') + // Surround all the generated Promise.> strings with links to the + // corresponding typedef object as jsdoc2md seems to have an issue parsing the + // syntax for a promise that resolves to an array of custom types. + .replace( + /(Promise\.<Array\.<([A-Z].*)>>)/g, + (match, g1, g2) => `${g1}`, + ); +fs.writeFileSync(filename, newApiDocs, 'utf8'); diff --git a/src/__mocks__/AxiosError.js b/src/__mocks__/AxiosError.js deleted file mode 100644 index 65211387..00000000 --- a/src/__mocks__/AxiosError.js +++ /dev/null @@ -1,6 +0,0 @@ -export default class AxiosError extends Error { - constructor(response) { - super(); - this.response = response; - } -} diff --git a/src/__mocks__/AxiosError.ts b/src/__mocks__/AxiosError.ts new file mode 100644 index 00000000..15204004 --- /dev/null +++ b/src/__mocks__/AxiosError.ts @@ -0,0 +1,14 @@ +import { HaveIBeenPwnedApiResponse } from 'internal/haveibeenpwned/responses'; +import { PwnedPasswordsApiResponse } from 'internal/pwnedpasswords/responses'; + +type ApiResponse = HaveIBeenPwnedApiResponse | PwnedPasswordsApiResponse; + +/** @internal */ +export default class AxiosError extends Error { + response: ApiResponse; + + constructor(response: ApiResponse) { + super(); + this.response = response; + } +} diff --git a/src/__mocks__/axios.js b/src/__mocks__/axios.ts similarity index 86% rename from src/__mocks__/axios.js rename to src/__mocks__/axios.ts index 370d67f7..d6cdd8e8 100644 --- a/src/__mocks__/axios.js +++ b/src/__mocks__/axios.ts @@ -1,5 +1,6 @@ import mockAxiosInstance from './axiosInstance'; +/** @internal */ export default { create: () => mockAxiosInstance, }; diff --git a/src/__mocks__/axiosInstance.js b/src/__mocks__/axiosInstance.ts similarity index 83% rename from src/__mocks__/axiosInstance.js rename to src/__mocks__/axiosInstance.ts index fac258ad..a20f6144 100644 --- a/src/__mocks__/axiosInstance.js +++ b/src/__mocks__/axiosInstance.ts @@ -1,3 +1,4 @@ +/** @internal */ export default { get: jest.fn(() => Promise.resolve({ status: 418, data: {} })), }; diff --git a/src/__snapshots__/hibp.test.js.snap b/src/__snapshots__/hibp.test.ts.snap similarity index 100% rename from src/__snapshots__/hibp.test.js.snap rename to src/__snapshots__/hibp.test.ts.snap diff --git a/src/breach.js b/src/breach.js deleted file mode 100644 index 9665a763..00000000 --- a/src/breach.js +++ /dev/null @@ -1,33 +0,0 @@ -import fetchFromApi from './internal/haveibeenpwned/fetchFromApi'; - -/** - * Fetches data for a specific breach event. - * - * @param {string} breachName the name of a breach in the system - * @returns {Promise} a Promise which resolves to an object representing a - * breach (or null if no breach was found), or rejects with an Error - * @example - * breach('Adobe') - * .then(data => { - * if (data) { - * // ... - * } else { - * // ... - * } - * }) - * .catch(err => { - * // ... - * }); - * @alias module:breach - */ -const breach = breachName => - fetchFromApi(`/breach/${encodeURIComponent(breachName)}`); - -/** - * A module for retrieving data for a specific breach event. - * - * @module breach - * @example - * import { breach } from 'hibp'; - */ -export default breach; diff --git a/src/breach.test.js b/src/breach.test.ts similarity index 86% rename from src/breach.test.js rename to src/breach.test.ts index 9e6f87f0..6e0e46c0 100644 --- a/src/breach.test.js +++ b/src/breach.test.ts @@ -1,8 +1,10 @@ import AxiosError from 'AxiosError'; import { OK, NOT_FOUND } from './internal/haveibeenpwned/responses'; -import mockAxios from './internal/haveibeenpwned/axiosInstance'; +import axios from './internal/haveibeenpwned/axiosInstance'; import breach from './breach'; +const mockAxios = axios as jest.Mocked; + describe('breach', () => { describe('found', () => { const data = { diff --git a/src/breach.ts b/src/breach.ts new file mode 100644 index 00000000..0467f6af --- /dev/null +++ b/src/breach.ts @@ -0,0 +1,57 @@ +import fetchFromApi from './internal/haveibeenpwned/fetchFromApi'; +import { Breach } from './types/remote-api'; + +/** + * An object representing a breach. + * + * @typedef {object} Breach + * @property {string} Name + * @property {string} Title + * @property {string} Domain + * @property {string} BreachDate + * @property {string} AddedDate + * @property {string} ModifiedDate + * @property {number} PwnCount + * @property {string} Description + * @property {string} LogoPath + * @property {string[]} DataClasses + * @property {boolean} IsVerified + * @property {boolean} IsFabricated + * @property {boolean} IsSensitive + * @property {boolean} IsRetired + * @property {boolean} IsSpamList + */ +/** + * Fetches data for a specific breach event. + * + * @param {string} breachName the name of a breach in the system + * @returns {(Promise|Promise)} a Promise which resolves to an + * object representing a breach (or null if no breach was found), or rejects + * with an Error + * @example + * breach('Adobe') + * .then(data => { + * if (data) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @alias module:breach + */ +const breach = (breachName: string): Promise => + fetchFromApi( + `/breach/${encodeURIComponent(breachName)}`, + ) as Promise; + +/** + * A module for retrieving data for a specific breach event. + * + * @module breach + * @example + * import { breach } from 'hibp'; + */ +export default breach; diff --git a/src/breachedAccount.test.js b/src/breachedAccount.test.ts similarity index 94% rename from src/breachedAccount.test.js rename to src/breachedAccount.test.ts index b5f72ee7..f62c3080 100644 --- a/src/breachedAccount.test.js +++ b/src/breachedAccount.test.ts @@ -1,8 +1,10 @@ import AxiosError from 'AxiosError'; import { OK, NOT_FOUND } from './internal/haveibeenpwned/responses'; -import mockAxios from './internal/haveibeenpwned/axiosInstance'; +import axios from './internal/haveibeenpwned/axiosInstance'; import breachedAccount from './breachedAccount'; +const mockAxios = axios as jest.Mocked; + describe('breachedAccount', () => { describe('breached', () => { const data = [{ some: 'stuff' }]; diff --git a/src/breachedAccount.js b/src/breachedAccount.ts similarity index 74% rename from src/breachedAccount.js rename to src/breachedAccount.ts index b4f1835f..3fd34d84 100644 --- a/src/breachedAccount.js +++ b/src/breachedAccount.ts @@ -1,16 +1,18 @@ import fetchFromApi from './internal/haveibeenpwned/fetchFromApi'; +import { Breach } from './types/remote-api'; /** * Fetches breach data for a specific account. * * @param {string} account a username or email address - * @param {Object} [options] a configuration object + * @param {object} [options] a configuration object * @param {string} [options.domain] a domain by which to filter the results * (default: all domains) * @param {boolean} [options.truncate] truncate the results to only include * the name of each breach (default: false) - * @returns {Promise} a Promise which resolves to an array of breach objects - * (or null if no breaches were found), or rejects with an Error + * @returns {(Promise | Promise)} a Promise which resolves to an + * array of breach objects (or null if no breaches were found), or rejects with + * an Error * @example * breachedAccount('foo') * .then(data => { @@ -49,7 +51,13 @@ import fetchFromApi from './internal/haveibeenpwned/fetchFromApi'; * }); * @alias module:breachedAccount */ -const breachedAccount = (account, options = {}) => { +const breachedAccount = ( + account: string, + options: { + domain?: string; + truncate?: boolean; + } = {}, +): Promise => { const endpoint = `/breachedaccount/${encodeURIComponent(account)}?`; const params = []; if (options.domain) { @@ -58,7 +66,9 @@ const breachedAccount = (account, options = {}) => { if (options.truncate) { params.push('truncateResponse=true'); } - return fetchFromApi(`${endpoint}${params.join('&')}`); + return fetchFromApi(`${endpoint}${params.join('&')}`) as Promise< + Breach[] | null + >; }; /** diff --git a/src/breaches.test.js b/src/breaches.test.ts similarity index 83% rename from src/breaches.test.js rename to src/breaches.test.ts index 534b2ddb..dcf11d58 100644 --- a/src/breaches.test.js +++ b/src/breaches.test.ts @@ -1,7 +1,9 @@ import { OK } from './internal/haveibeenpwned/responses'; -import mockAxios from './internal/haveibeenpwned/axiosInstance'; +import axios from './internal/haveibeenpwned/axiosInstance'; import breaches from './breaches'; +const mockAxios = axios as jest.Mocked; + describe('breaches', () => { const data = [{ breach: 'info' }]; diff --git a/src/breaches.js b/src/breaches.ts similarity index 68% rename from src/breaches.js rename to src/breaches.ts index 04340a17..3edf76bf 100644 --- a/src/breaches.js +++ b/src/breaches.ts @@ -1,13 +1,14 @@ import fetchFromApi from './internal/haveibeenpwned/fetchFromApi'; +import { Breach } from './types/remote-api'; /** * Fetches all breach events in the system. * - * @param {Object} [options] a configuration object + * @param {object} [options] a configuration object * @param {string} [options.domain] a domain by which to filter the results * (default: all domains) - * @returns {Promise} a Promise which resolves to an array of breach objects - * (an empty array if no breaches were found), or rejects with an Error + * @returns {Promise} a Promise which resolves to an array of breach + * objects (an empty array if no breaches were found), or rejects with an Error * @example * breaches() * .then(data => { @@ -34,13 +35,17 @@ import fetchFromApi from './internal/haveibeenpwned/fetchFromApi'; * }); * @alias module:breaches */ -const breaches = (options = {}) => { +const breaches = ( + options: { + domain?: string; + } = {}, +): Promise => { const endpoint = '/breaches?'; const params = []; if (options.domain) { params.push(`domain=${encodeURIComponent(options.domain)}`); } - return fetchFromApi(`${endpoint}${params.join('&')}`); + return fetchFromApi(`${endpoint}${params.join('&')}`) as Promise; }; /** diff --git a/src/dataClasses.test.js b/src/dataClasses.test.ts similarity index 79% rename from src/dataClasses.test.js rename to src/dataClasses.test.ts index 0e4457a7..10b60c7e 100644 --- a/src/dataClasses.test.js +++ b/src/dataClasses.test.ts @@ -1,7 +1,9 @@ import { OK } from './internal/haveibeenpwned/responses'; -import mockAxios from './internal/haveibeenpwned/axiosInstance'; +import axios from './internal/haveibeenpwned/axiosInstance'; import dataClasses from './dataClasses'; +const mockAxios = axios as jest.Mocked; + describe('dataClasses', () => { const data = ['some', 'data', 'classes']; diff --git a/src/dataClasses.js b/src/dataClasses.ts similarity index 64% rename from src/dataClasses.js rename to src/dataClasses.ts index 49b6a26b..8a54a1b0 100644 --- a/src/dataClasses.js +++ b/src/dataClasses.ts @@ -3,8 +3,9 @@ import fetchFromApi from './internal/haveibeenpwned/fetchFromApi'; /** * Fetches all data classes in the system. * - * @returns {Promise} a Promise which resolves to an array of strings (or - * null if no data classes were found), or rejects with an Error + * @returns {(Promise | Promise)} a Promise which resolves to an + * array of strings (or null if no data classes were found), or rejects with an + * Error * @example * dataClasses() * .then(data => { @@ -19,7 +20,8 @@ import fetchFromApi from './internal/haveibeenpwned/fetchFromApi'; * }); * @alias module:dataClasses */ -const dataClasses = () => fetchFromApi('/dataclasses'); +const dataClasses = (): Promise => + fetchFromApi('/dataclasses') as Promise; /** * A module for retrieving all data classes in the system. diff --git a/src/hibp.test.js b/src/hibp.test.ts similarity index 100% rename from src/hibp.test.js rename to src/hibp.test.ts diff --git a/src/hibp.js b/src/hibp.ts similarity index 83% rename from src/hibp.js rename to src/hibp.ts index 9020f3a9..4fedd8e7 100644 --- a/src/hibp.js +++ b/src/hibp.ts @@ -34,3 +34,7 @@ export { pwnedPasswordRange, search, }; + +// https://github.com/jsdoc2md/jsdoc-to-markdown/wiki/How-to-document-TypeScript#jsdoc-comments-disappear +// eslint-disable-next-line no-unused-vars +const JSDOC2MARKDOWN_STUB = undefined; diff --git a/src/internal/haveibeenpwned/__snapshots__/fetchFromApi.test.js.snap b/src/internal/haveibeenpwned/__snapshots__/fetchFromApi.test.ts.snap similarity index 100% rename from src/internal/haveibeenpwned/__snapshots__/fetchFromApi.test.js.snap rename to src/internal/haveibeenpwned/__snapshots__/fetchFromApi.test.ts.snap diff --git a/src/internal/haveibeenpwned/axiosInstance.test.js b/src/internal/haveibeenpwned/axiosInstance.test.ts similarity index 90% rename from src/internal/haveibeenpwned/axiosInstance.test.js rename to src/internal/haveibeenpwned/axiosInstance.test.ts index eff9f924..d2364769 100644 --- a/src/internal/haveibeenpwned/axiosInstance.test.js +++ b/src/internal/haveibeenpwned/axiosInstance.test.ts @@ -15,7 +15,8 @@ describe('internal (haveibeenpwned): axiosInstance', () => { jest.resetModules(); // Browser - global.navigator = {}; + // eslint-disable-next-line no-undef + global.navigator = {} as Navigator; const axiosInstanceBrowser = require.requireActual('./axiosInstance') .default; expect(Object.keys(axiosInstanceBrowser.defaults.headers)).not.toContain( diff --git a/src/internal/haveibeenpwned/axiosInstance.js b/src/internal/haveibeenpwned/axiosInstance.ts similarity index 97% rename from src/internal/haveibeenpwned/axiosInstance.js rename to src/internal/haveibeenpwned/axiosInstance.ts index 92d53ff0..306137ca 100644 --- a/src/internal/haveibeenpwned/axiosInstance.js +++ b/src/internal/haveibeenpwned/axiosInstance.ts @@ -4,6 +4,7 @@ import { name, version } from '../../../package.json'; /** * An Axios instance used for API queries. Not meant for general use. * + * @internal * @private */ export default Axios.create({ diff --git a/src/internal/haveibeenpwned/fetchFromApi.test.js b/src/internal/haveibeenpwned/fetchFromApi.test.ts similarity index 94% rename from src/internal/haveibeenpwned/fetchFromApi.test.js rename to src/internal/haveibeenpwned/fetchFromApi.test.ts index 9bde798c..a3a0b035 100644 --- a/src/internal/haveibeenpwned/fetchFromApi.test.js +++ b/src/internal/haveibeenpwned/fetchFromApi.test.ts @@ -2,7 +2,9 @@ import AxiosError from 'AxiosError'; import breachedAccount from 'breachedAccount'; import dataClasses from 'dataClasses'; import { BAD_REQUEST, FORBIDDEN, TOO_MANY_REQUESTS } from './responses'; -import mockAxios from './axiosInstance'; +import axios from './axiosInstance'; + +const mockAxios = axios as jest.Mocked; describe('internal (haveibeenpwned): fetchFromApi', () => { describe('request failure', () => { diff --git a/src/internal/haveibeenpwned/fetchFromApi.js b/src/internal/haveibeenpwned/fetchFromApi.ts similarity index 67% rename from src/internal/haveibeenpwned/fetchFromApi.js rename to src/internal/haveibeenpwned/fetchFromApi.ts index cae743ab..4dc3de92 100644 --- a/src/internal/haveibeenpwned/fetchFromApi.js +++ b/src/internal/haveibeenpwned/fetchFromApi.ts @@ -1,3 +1,4 @@ +import { Breach, Paste } from 'types/remote-api'; import axios from './axiosInstance'; import { BAD_REQUEST, @@ -6,6 +7,13 @@ import { TOO_MANY_REQUESTS, } from './responses'; +export type ApiData = + | Breach // breach + | Breach[] // breachedaccount, breaches + | Paste[] // pasteaccount + | string[] // dataclasses + | null; // most endpoints can return an empty response + /** * Fetches data from the supplied API endpoint. * @@ -15,13 +23,15 @@ import { * HTTP status code 403 throws an Error (forbidden). * HTTP status code 429 throws an Error (too many requests). * + * @internal * @private * @param {string} endpoint the API endpoint to query - * @returns {Promise} a Promise which resolves to the data resulting from the - * query (or null for 404 Not Found responses), or rejects with an Error + * @returns {Promise} a Promise which resolves to the data resulting + * from the query (or null for 404 Not Found responses), or rejects with an + * Error */ -export default endpoint => - Promise.resolve(axios.get(endpoint)) +export default (endpoint: string): Promise => + Promise.resolve(axios.get(endpoint)) .then(res => res.data) .catch(err => { if (err.response) { diff --git a/src/internal/haveibeenpwned/responses.js b/src/internal/haveibeenpwned/responses.ts similarity index 67% rename from src/internal/haveibeenpwned/responses.js rename to src/internal/haveibeenpwned/responses.ts index bed32e47..a835ab05 100644 --- a/src/internal/haveibeenpwned/responses.js +++ b/src/internal/haveibeenpwned/responses.ts @@ -10,22 +10,33 @@ * descriptive error for the consumer. (They are also leveraged in our tests.) */ -export const OK = { +export interface HaveIBeenPwnedApiResponse { + // eslint-disable-next-line no-restricted-globals + status: number; + statusText?: string; + data?: string; +} + +/** @internal */ +export const OK: HaveIBeenPwnedApiResponse = { status: 200, }; -export const BAD_REQUEST = { +/** @internal */ +export const BAD_REQUEST: HaveIBeenPwnedApiResponse = { status: 400, statusText: 'Bad request — the account does not comply with an acceptable format.', }; -export const FORBIDDEN = { +/** @internal */ +export const FORBIDDEN: HaveIBeenPwnedApiResponse = { status: 403, statusText: 'Forbidden - no user agent has been specified in the request.', }; -export const NOT_FOUND = { +/** @internal */ +export const NOT_FOUND: HaveIBeenPwnedApiResponse = { status: 404, }; @@ -33,8 +44,10 @@ export const NOT_FOUND = { * This response has unique behavior. For some reason, the API includes a * human-readable message in the response body for this one. Manually populating * the message here purely for use in tests. + * + * @internal */ -export const TOO_MANY_REQUESTS = { +export const TOO_MANY_REQUESTS: HaveIBeenPwnedApiResponse = { status: 429, data: 'Rate limit exceeded, refer to acceptable use of the API: ' + diff --git a/src/internal/jssha.d.ts b/src/internal/jssha.d.ts new file mode 100644 index 00000000..9f029646 --- /dev/null +++ b/src/internal/jssha.d.ts @@ -0,0 +1,5 @@ +declare module 'jssha/src/sha1' { + import jssha from 'jssha'; + + export default jssha; +} diff --git a/src/internal/pwnedpasswords/__snapshots__/fetchFromApi.test.js.snap b/src/internal/pwnedpasswords/__snapshots__/fetchFromApi.test.ts.snap similarity index 100% rename from src/internal/pwnedpasswords/__snapshots__/fetchFromApi.test.js.snap rename to src/internal/pwnedpasswords/__snapshots__/fetchFromApi.test.ts.snap diff --git a/src/internal/pwnedpasswords/axiosInstance.js b/src/internal/pwnedpasswords/axiosInstance.ts similarity index 93% rename from src/internal/pwnedpasswords/axiosInstance.js rename to src/internal/pwnedpasswords/axiosInstance.ts index 787bf7af..ca500a02 100644 --- a/src/internal/pwnedpasswords/axiosInstance.js +++ b/src/internal/pwnedpasswords/axiosInstance.ts @@ -3,6 +3,7 @@ import Axios from 'axios'; /** * An Axios instance used for API queries. Not meant for general use. * + * @internal * @private */ export default Axios.create({ diff --git a/src/internal/pwnedpasswords/fetchFromApi.test.js b/src/internal/pwnedpasswords/fetchFromApi.test.ts similarity index 92% rename from src/internal/pwnedpasswords/fetchFromApi.test.js rename to src/internal/pwnedpasswords/fetchFromApi.test.ts index c637d0d2..0ed6cb4e 100644 --- a/src/internal/pwnedpasswords/fetchFromApi.test.js +++ b/src/internal/pwnedpasswords/fetchFromApi.test.ts @@ -1,7 +1,9 @@ import AxiosError from 'AxiosError'; import pwnedPasswordRange from 'pwnedPasswordRange'; import { BAD_REQUEST } from './responses'; -import mockAxios from './axiosInstance'; +import axios from './axiosInstance'; + +const mockAxios = axios as jest.Mocked; describe('internal (pwnedpassword): fetchFromApi', () => { describe('request failure', () => { diff --git a/src/internal/pwnedpasswords/fetchFromApi.js b/src/internal/pwnedpasswords/fetchFromApi.ts similarity index 73% rename from src/internal/pwnedpasswords/fetchFromApi.js rename to src/internal/pwnedpasswords/fetchFromApi.ts index 8b0c36eb..98011f62 100644 --- a/src/internal/pwnedpasswords/fetchFromApi.js +++ b/src/internal/pwnedpasswords/fetchFromApi.ts @@ -7,13 +7,14 @@ import { BAD_REQUEST } from './responses'; * HTTP status code 200 returns plain text (data found). * HTTP status code 400 throws an Error (bad request). * + * @internal * @private * @param {string} endpoint the API endpoint to query - * @returns {Promise} a Promise which resolves to the data resulting from the - * query, or rejects with an Error + * @returns {Promise} a Promise which resolves to the data resulting + * from the query, or rejects with an Error */ -export default endpoint => - Promise.resolve(axios.get(endpoint)) +export default (endpoint: string): Promise => + Promise.resolve(axios.get(endpoint)) .then(res => res.data) .catch(err => { if (err.response) { diff --git a/src/internal/pwnedpasswords/responses.js b/src/internal/pwnedpasswords/responses.ts similarity index 60% rename from src/internal/pwnedpasswords/responses.js rename to src/internal/pwnedpasswords/responses.ts index 19a75c68..1f321ad7 100644 --- a/src/internal/pwnedpasswords/responses.js +++ b/src/internal/pwnedpasswords/responses.ts @@ -5,10 +5,18 @@ * */ +export interface PwnedPasswordsApiResponse { + // eslint-disable-next-line no-restricted-globals + status: number; + data?: string; +} + +/** @internal */ export const OK = { status: 200, }; +/** @internal */ export const BAD_REQUEST = { status: 400, data: 'The hash prefix was not in a valid format', diff --git a/src/pasteAccount.test.js b/src/pasteAccount.test.ts similarity index 86% rename from src/pasteAccount.test.js rename to src/pasteAccount.test.ts index 9d356d1a..65b291c7 100644 --- a/src/pasteAccount.test.js +++ b/src/pasteAccount.test.ts @@ -1,8 +1,10 @@ import AxiosError from 'AxiosError'; import { OK, NOT_FOUND } from './internal/haveibeenpwned/responses'; -import mockAxios from './internal/haveibeenpwned/axiosInstance'; +import axios from './internal/haveibeenpwned/axiosInstance'; import pasteAccount from './pasteAccount'; +const mockAxios = axios as jest.Mocked; + describe('pasteAccount', () => { describe('pasted email', () => { const data = [{ paste: 'information' }]; diff --git a/src/pasteAccount.js b/src/pasteAccount.ts similarity index 50% rename from src/pasteAccount.js rename to src/pasteAccount.ts index a97f8d3d..9983bf45 100644 --- a/src/pasteAccount.js +++ b/src/pasteAccount.ts @@ -1,11 +1,23 @@ import fetchFromApi from './internal/haveibeenpwned/fetchFromApi'; +import { Paste } from './types/remote-api'; +/** + * An object representing a paste. + * + * @typedef {object} Paste + * @property {string} Id + * @property {string} Source + * @property {string} Title + * @property {string} Date + * @property {number} EmailCount + */ /** * Fetches paste data for a specific account (email address). * * @param {string} email the email address to query - * @returns {Promise} a Promise which resolves to an array of paste objects - * (or null if no pastes were found), or rejects with an Error + * @returns {(Promise | Promise)} a Promise which resolves to an + * array of paste objects (or null if no pastes were found), or rejects with an + * Error * @example * pasteAccount('foo@bar.com') * .then(data => { @@ -20,8 +32,10 @@ import fetchFromApi from './internal/haveibeenpwned/fetchFromApi'; * }); * @alias module:pasteAccount */ -const pasteAccount = email => - fetchFromApi(`/pasteaccount/${encodeURIComponent(email)}`); +const pasteAccount = (email: string): Promise => + fetchFromApi(`/pasteaccount/${encodeURIComponent(email)}`) as Promise< + Paste[] | null + >; /** * A module for retrieving paste data for a specific account (email address). diff --git a/src/pwnedPassword.test.js b/src/pwnedPassword.test.ts similarity index 80% rename from src/pwnedPassword.test.js rename to src/pwnedPassword.test.ts index 63f7485a..c5a2013b 100644 --- a/src/pwnedPassword.test.js +++ b/src/pwnedPassword.test.ts @@ -1,8 +1,11 @@ +// eslint-disable-next-line import/no-extraneous-dependencies import { stripIndents } from 'common-tags'; import { OK } from './internal/pwnedpasswords/responses'; -import mockAxios from './internal/pwnedpasswords/axiosInstance'; +import axios from './internal/pwnedpasswords/axiosInstance'; import pwnedPassword from './pwnedPassword'; +const mockAxios = axios as jest.Mocked; + describe('pwnedPassword', () => { describe('pwned', () => { mockAxios.get.mockResolvedValue({ diff --git a/src/pwnedPassword.js b/src/pwnedPassword.ts similarity index 82% rename from src/pwnedPassword.js rename to src/pwnedPassword.ts index 5ab316b8..6cd3d4be 100644 --- a/src/pwnedPassword.js +++ b/src/pwnedPassword.ts @@ -1,3 +1,4 @@ +// @ts-ignore: dts-bundle-generator doesn't pick up ./internal/jssha.d.ts import JSSHA from 'jssha/src/sha1'; import pwnedPasswordRange from './pwnedPasswordRange'; @@ -7,8 +8,8 @@ import pwnedPasswordRange from './pwnedPasswordRange'; * only the first 5 characters of its SHA-1 hash will be submitted to the API. * * @param {string} password a password in plain text - * @returns {Promise} a Promise which resolves to the number of times the - * password has been exposed in a breach, or rejects with an Error + * @returns {Promise} a Promise which resolves to the number of times + * the password has been exposed in a breach, or rejects with an Error * @example * pwnedPassword('f00b4r') * .then(numPwns => { @@ -25,7 +26,7 @@ import pwnedPasswordRange from './pwnedPasswordRange'; * @see https://haveibeenpwned.com/API/v2#PwnedPasswords * @alias module:pwnedPassword */ -const pwnedPassword = password => { +const pwnedPassword = (password: string): Promise => { const sha1 = new JSSHA('SHA-1', 'TEXT'); sha1.update(password); const hash = sha1.getHash('HEX', { outputUpper: true }); diff --git a/src/pwnedPasswordRange.test.js b/src/pwnedPasswordRange.test.ts similarity index 83% rename from src/pwnedPasswordRange.test.js rename to src/pwnedPasswordRange.test.ts index 0ffa367e..3809699f 100644 --- a/src/pwnedPasswordRange.test.js +++ b/src/pwnedPasswordRange.test.ts @@ -1,8 +1,11 @@ +// eslint-disable-next-line import/no-extraneous-dependencies import { stripIndents } from 'common-tags'; import { OK } from './internal/pwnedpasswords/responses'; -import mockAxios from './internal/pwnedpasswords/axiosInstance'; +import axios from './internal/pwnedpasswords/axiosInstance'; import pwnedPasswordRange from './pwnedPasswordRange'; +const mockAxios = axios as jest.Mocked; + describe('pwnedPasswordRange', () => { describe('valid range', () => { const data = stripIndents` diff --git a/src/pwnedPasswordRange.js b/src/pwnedPasswordRange.ts similarity index 73% rename from src/pwnedPasswordRange.js rename to src/pwnedPasswordRange.ts index bbf017f3..9800a6a9 100644 --- a/src/pwnedPasswordRange.js +++ b/src/pwnedPasswordRange.ts @@ -1,5 +1,19 @@ import fetchFromApi from './internal/pwnedpasswords/fetchFromApi'; +export interface PwnedPasswordSuffix { + suffix: string; + count: number; +} + +/** + * An object representing an exposed password hash suffix (corresponding to a + * given hash prefix) and how many times it occurred in the Pwned Passwords + * repository. + * + * @typedef {object} PwnedPasswordSuffix + * @property {string} suffix + * @property {number} count + */ /** * Fetches the SHA-1 hash suffixes for the given 5-character SHA-1 hash prefix. * @@ -11,10 +25,10 @@ import fetchFromApi from './internal/pwnedpasswords/fetchFromApi'; * * @param {string} prefix the first 5 characters of a SHA-1 password hash (case * insensitive) - * @returns {Promise} a Promise which resolves to an array of objects, each - * containing the `suffix` that when matched with the prefix composes the - * complete hash, and a `count` of how many times it appears in the breached - * password data set, or rejects with an Error + * @returns {Promise} a Promise which resolves to an array + * of objects, each containing the `suffix` that when matched with the prefix + * composes the complete hash, and a `count` of how many times it appears in the + * breached password data set, or rejects with an Error * * @example * pwnedPasswordRange('5BAA6') @@ -39,7 +53,7 @@ import fetchFromApi from './internal/pwnedpasswords/fetchFromApi'; * @see https://haveibeenpwned.com/API/v2#SearchingPwnedPasswordsByRange * @alias module:pwnedPasswordRange */ -const pwnedPasswordRange = prefix => +const pwnedPasswordRange = (prefix: string): Promise => fetchFromApi(`/range/${encodeURIComponent(prefix)}`) // create array from lines of text in response body .then(data => data.split('\n')) diff --git a/src/search.test.js b/src/search.test.ts similarity index 88% rename from src/search.test.js rename to src/search.test.ts index 672cb6ab..3f8d26fc 100644 --- a/src/search.test.js +++ b/src/search.test.ts @@ -1,7 +1,9 @@ import { OK } from './internal/haveibeenpwned/responses'; -import mockAxios from './internal/haveibeenpwned/axiosInstance'; +import axios from './internal/haveibeenpwned/axiosInstance'; import search from './search'; +const mockAxios = axios as jest.Mocked; + describe('search', () => { it('searches breaches by username', () => { const breaches = [{ stuff: 'about', a: 'breach' }]; diff --git a/src/search.js b/src/search.ts similarity index 70% rename from src/search.js rename to src/search.ts index 08d9d72c..d7b463f4 100644 --- a/src/search.js +++ b/src/search.ts @@ -1,6 +1,19 @@ +import { Breach, Paste } from './types/remote-api'; import breachedAccount from './breachedAccount'; import pasteAccount from './pasteAccount'; +export interface SearchResults { + breaches: Breach[] | null; + pastes: Paste[] | null; +} + +/** + * An object representing search results. + * + * @typedef {object} SearchResults + * @property {(Breach[] | null)} breaches + * @property {(Paste[] | null)} pastes + */ /** * Fetches all breaches and all pastes associated with the provided account * (email address or username). Note that the remote API does not support @@ -11,16 +24,16 @@ import pasteAccount from './pasteAccount'; * convenience method is designed to mimic. * * @param {string} account an email address or username - * @param {Object} [breachOptions] a configuration object pertaining to - * breach queries + * @param {object} [breachOptions] a configuration object + * pertaining to breach queries * @param {string} [breachOptions.domain] a domain by which to filter the * results (default: all domains) * @param {boolean} [breachOptions.truncate] truncate the results to only * include the name of each breach (default: false) - * @returns {Promise} a Promise which resolves to an object containing a - * "breaches" key (which can be null or an array of breach objects) and a - * "pastes" key (which can be null or an array of paste objects), or rejects - * with an Error + * @returns {Promise} a Promise which resolves to an object + * containing a "breaches" key (which can be null or an array of breach objects) + * and a "pastes" key (which can be null or an array of paste objects), or + * rejects with an Error * @example * search('foo') * .then(data => { @@ -49,7 +62,13 @@ import pasteAccount from './pasteAccount'; * @see https://haveibeenpwned.com/ * @alias module:search */ -const search = (account, breachOptions = {}) => +const search = ( + account: string, + breachOptions: { + domain?: string; + truncate?: boolean; + } = {}, +): Promise => Promise.all([ breachedAccount(account, breachOptions), // This email regex is garbage but it seems to be what the API uses: diff --git a/src/types/remote-api.ts b/src/types/remote-api.ts new file mode 100644 index 00000000..40920b14 --- /dev/null +++ b/src/types/remote-api.ts @@ -0,0 +1,25 @@ +export interface Breach { + Name: string; + Title: string; + Domain: string; + BreachDate: string; + AddedDate: string; + ModifiedDate: string; + PwnCount: number; + Description: string; + LogoPath: string; + DataClasses: string[]; + IsVerified: boolean; + IsFabricated: boolean; + IsSensitive: boolean; + IsRetired: boolean; + IsSpamList: boolean; +} + +export interface Paste { + Id: string; + Source: string; + Title: string; + Date: string; + EmailCount: number; +} diff --git a/test/types/node.d.ts b/test/types/node.d.ts new file mode 100644 index 00000000..b34e28c6 --- /dev/null +++ b/test/types/node.d.ts @@ -0,0 +1,9 @@ +// Add faux `navigator` to the `global` object as the jest testEnvironment is +// set to 'node' and therefore doesn't have `document`, `navigator`, or `window` +/** @internal */ +declare namespace NodeJS { + interface Global { + // optional to allow for removing it to test absence + navigator?: Navigator; + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..af53d803 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "*": ["node_modules/*", "src/*", "src/__mocks__/*"] + }, + "lib": ["esnext"], + "target": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "esModuleInterop": true, + "declaration": true, + "strict": true, + "stripInternal": true, + "types": ["node", "jsdom", "jest"] + }, + "exclude": ["cypress"] +} diff --git a/types/hibp.d.ts b/types/hibp.d.ts new file mode 100644 index 00000000..e8dc1c74 --- /dev/null +++ b/types/hibp.d.ts @@ -0,0 +1,363 @@ +export interface Breach { + Name: string; + Title: string; + Domain: string; + BreachDate: string; + AddedDate: string; + ModifiedDate: string; + PwnCount: number; + Description: string; + LogoPath: string; + DataClasses: string[]; + IsVerified: boolean; + IsFabricated: boolean; + IsSensitive: boolean; + IsRetired: boolean; + IsSpamList: boolean; +} +export interface Paste { + Id: string; + Source: string; + Title: string; + Date: string; + EmailCount: number; +} +/** + * An object representing a breach. + * + * @typedef {object} Breach + * @property {string} Name + * @property {string} Title + * @property {string} Domain + * @property {string} BreachDate + * @property {string} AddedDate + * @property {string} ModifiedDate + * @property {number} PwnCount + * @property {string} Description + * @property {string} LogoPath + * @property {string[]} DataClasses + * @property {boolean} IsVerified + * @property {boolean} IsFabricated + * @property {boolean} IsSensitive + * @property {boolean} IsRetired + * @property {boolean} IsSpamList + */ +/** + * Fetches data for a specific breach event. + * + * @param {string} breachName the name of a breach in the system + * @returns {(Promise|Promise)} a Promise which resolves to an + * object representing a breach (or null if no breach was found), or rejects + * with an Error + * @example + * breach('Adobe') + * .then(data => { + * if (data) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @alias module:breach + */ +export declare const breach: (breachName: string) => Promise; +/** + * Fetches breach data for a specific account. + * + * @param {string} account a username or email address + * @param {object} [options] a configuration object + * @param {string} [options.domain] a domain by which to filter the results + * (default: all domains) + * @param {boolean} [options.truncate] truncate the results to only include + * the name of each breach (default: false) + * @returns {(Promise | Promise)} a Promise which resolves to an + * array of breach objects (or null if no breaches were found), or rejects with + * an Error + * @example + * breachedAccount('foo') + * .then(data => { + * if (data) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @example + * breachedAccount('bar', { truncate: true }) + * .then(data => { + * if (data) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @example + * breachedAccount('baz', { domain: 'adobe.com', truncate: true }) + * .then(data => { + * if (data) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @alias module:breachedAccount + */ +export declare const breachedAccount: ( + account: string, + options?: { + domain?: string | undefined; + truncate?: boolean | undefined; + }, +) => Promise; +/** + * Fetches all breach events in the system. + * + * @param {object} [options] a configuration object + * @param {string} [options.domain] a domain by which to filter the results + * (default: all domains) + * @returns {Promise} a Promise which resolves to an array of breach + * objects (an empty array if no breaches were found), or rejects with an Error + * @example + * breaches() + * .then(data => { + * if (data) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @example + * breaches({ domain: 'adobe.com' }) + * .then(data => { + * if (data) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @alias module:breaches + */ +export declare const breaches: ( + options?: { + domain?: string | undefined; + }, +) => Promise; +/** + * Fetches all data classes in the system. + * + * @returns {(Promise | Promise)} a Promise which resolves to an + * array of strings (or null if no data classes were found), or rejects with an + * Error + * @example + * dataClasses() + * .then(data => { + * if (data) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @alias module:dataClasses + */ +export declare const dataClasses: () => Promise; +/** + * An object representing a paste. + * + * @typedef {object} Paste + * @property {string} Id + * @property {string} Source + * @property {string} Title + * @property {string} Date + * @property {number} EmailCount + */ +/** + * Fetches paste data for a specific account (email address). + * + * @param {string} email the email address to query + * @returns {(Promise | Promise)} a Promise which resolves to an + * array of paste objects (or null if no pastes were found), or rejects with an + * Error + * @example + * pasteAccount('foo@bar.com') + * .then(data => { + * if (data) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @alias module:pasteAccount + */ +export declare const pasteAccount: (email: string) => Promise; +/** + * Fetches the number of times the the given password has been exposed in a + * breach (0 indicating no exposure). The password is given in plain text, but + * only the first 5 characters of its SHA-1 hash will be submitted to the API. + * + * @param {string} password a password in plain text + * @returns {Promise} a Promise which resolves to the number of times + * the password has been exposed in a breach, or rejects with an Error + * @example + * pwnedPassword('f00b4r') + * .then(numPwns => { + * // truthy check or numeric condition + * if (numPwns) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @see https://haveibeenpwned.com/API/v2#PwnedPasswords + * @alias module:pwnedPassword + */ +export declare const pwnedPassword: (password: string) => Promise; +export interface PwnedPasswordSuffix { + suffix: string; + count: number; +} +/** + * An object representing an exposed password hash suffix (corresponding to a + * given hash prefix) and how many times it occurred in the Pwned Passwords + * repository. + * + * @typedef {object} PwnedPasswordSuffix + * @property {string} suffix + * @property {number} count + */ +/** + * Fetches the SHA-1 hash suffixes for the given 5-character SHA-1 hash prefix. + * + * When a password hash with the same first 5 characters is found in the Pwned + * Passwords repository, the API will respond with an HTTP 200 and include the + * suffix of every hash beginning with the specified prefix, followed by a count + * of how many times it appears in the data set. This function parses the + * response and returns a more structured format. + * + * @param {string} prefix the first 5 characters of a SHA-1 password hash (case + * insensitive) + * @returns {Promise} a Promise which resolves to an array + * of objects, each containing the `suffix` that when matched with the prefix + * composes the complete hash, and a `count` of how many times it appears in the + * breached password data set, or rejects with an Error + * + * @example + * pwnedPasswordRange('5BAA6') + * .then(results => { + * // results will have the following shape: + * // [ + * // { suffix: "003D68EB55068C33ACE09247EE4C639306B", count: 3 }, + * // { suffix: "012C192B2F16F82EA0EB9EF18D9D539B0DD", count: 1 }, + * // ... + * // ] + * }) + * @example + * const suffix = '1E4C9B93F3F0682250B6CF8331B7EE68FD8'; + * pwnedPasswordRange('5BAA6') + * // filter to matching suffix + * .then(results => results.filter(row => row.suffix === suffix)) + * // return count if match, 0 if not + * .then(results => (results[0] ? results[0].count : 0)) + * .catch(err => { + * // ... + * }); + * @see https://haveibeenpwned.com/API/v2#SearchingPwnedPasswordsByRange + * @alias module:pwnedPasswordRange + */ +export declare const pwnedPasswordRange: ( + prefix: string, +) => Promise; +export interface SearchResults { + breaches: Breach[] | null; + pastes: Paste[] | null; +} +/** + * An object representing search results. + * + * @typedef {object} SearchResults + * @property {(Breach[] | null)} breaches + * @property {(Paste[] | null)} pastes + */ +/** + * Fetches all breaches and all pastes associated with the provided account + * (email address or username). Note that the remote API does not support + * querying pastes by username (only email addresses), so in the event the + * provided account is not a valid email address, only breach data is queried + * and the "pastes" field of the resulting object will always be null. This is + * exactly how searching via the current web interface behaves, which this + * convenience method is designed to mimic. + * + * @param {string} account an email address or username + * @param {object} [breachOptions] a configuration object + * pertaining to breach queries + * @param {string} [breachOptions.domain] a domain by which to filter the + * results (default: all domains) + * @param {boolean} [breachOptions.truncate] truncate the results to only + * include the name of each breach (default: false) + * @returns {Promise} a Promise which resolves to an object + * containing a "breaches" key (which can be null or an array of breach objects) + * and a "pastes" key (which can be null or an array of paste objects), or + * rejects with an Error + * @example + * search('foo') + * .then(data => { + * if (data.breaches || data.pastes) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * @example + * search('nobody@nowhere.com', { truncate: true }) + * .then(data => { + * if (data.breaches || data.pastes) { + * // ... + * } else { + * // ... + * } + * }) + * .catch(err => { + * // ... + * }); + * + * @see https://haveibeenpwned.com/ + * @alias module:search + */ +export declare const search: ( + account: string, + breachOptions?: { + domain?: string | undefined; + truncate?: boolean | undefined; + }, +) => Promise; + +export as namespace hibp; diff --git a/yarn.lock b/yarn.lock index bec511c0..04f9a95f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -81,6 +81,17 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" +"@babel/helper-create-class-features-plugin@^7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.2.3.tgz#f6e719abb90cb7f4a69591e35fd5eb89047c4a7c" + integrity sha512-xO/3Gn+2C7/eOUeb0VRnSP1+yvWHNxlpAot1eMhtoKDCN7POsyQP5excuT5UsV5daHxMWBeIIOeI5cmB8vMRgQ== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-member-expression-to-functions" "^7.0.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.2.3" + "@babel/helper-define-map@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz#3b74caec329b3c80c116290887c0dd9ae468c20c" @@ -177,7 +188,7 @@ "@babel/traverse" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-replace-supers@^7.1.0": +"@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.2.3": version "7.2.3" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.2.3.tgz#19970020cf22677d62b3a689561dbd9644d8c5e5" integrity sha512-GyieIznGUfPXPWu0yLS6U55Mz67AZD9cUk0BfirOWlPrXlBcan9Gz+vHGz+cPfuoweZSnPzPIm67VtQM0OWZbA== @@ -230,7 +241,7 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.2.2", "@babel/parser@^7.2.3": +"@babel/parser@7.2.3", "@babel/parser@^7.0.0", "@babel/parser@^7.2.2", "@babel/parser@^7.2.3": version "7.2.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.3.tgz#32f5df65744b70888d17872ec106b02434ba1489" integrity sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA== @@ -244,6 +255,14 @@ "@babel/helper-remap-async-to-generator" "^7.1.0" "@babel/plugin-syntax-async-generators" "^7.2.0" +"@babel/plugin-proposal-class-properties@7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.2.3.tgz#c9e1294363b346cff333007a92080f3203698461" + integrity sha512-FVuQngLoN2iDrpW7LmhPZ2sO4DJxf35FOcwidwB9Ru9tMvI5URthnkVHuG14IStV+TzkMTyLMoOUlSTtrdVwqw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.2.3" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-json-strings@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" @@ -252,7 +271,7 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-json-strings" "^7.2.0" -"@babel/plugin-proposal-object-rest-spread@^7.2.0": +"@babel/plugin-proposal-object-rest-spread@7.2.0", "@babel/plugin-proposal-object-rest-spread@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.2.0.tgz#88f5fec3e7ad019014c97f7ee3c992f0adbf7fb8" integrity sha512-1L5mWLSvR76XYUQJXkd/EEQgjq8HHRP6lQuZTTg0VA4tTGPpGemmCdAfQIz1rzEuWAm+ecP8PyyEm30jC1eQCg== @@ -305,6 +324,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-typescript@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.2.0.tgz#55d240536bd314dcbbec70fd949c5cabaed1de29" + integrity sha512-WhKr6yu6yGpGcNMVgIBuI9MkredpVc7Y3YR4UzEZmDztHoL6wV56YBHLhWnjO1EvId1B32HrD3DRFc+zSoKI1g== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-transform-arrow-functions@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550" @@ -511,6 +537,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-transform-typescript@^7.1.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.2.0.tgz#bce7c06300434de6a860ae8acf6a442ef74a99d1" + integrity sha512-EnI7i2/gJ7ZNr2MuyvN2Hu+BHJENlxWte5XygPvfj/MbvtOkWor9zcnHpMMQL2YYaaCcqtIvJUyJ7QVfoGs7ew== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-typescript" "^7.2.0" + "@babel/plugin-transform-unicode-regex@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.2.0.tgz#4eb8db16f972f8abb5062c161b8b115546ade08b" @@ -567,6 +601,14 @@ js-levenshtein "^1.1.3" semver "^5.3.0" +"@babel/preset-typescript@7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.1.0.tgz#49ad6e2084ff0bfb5f1f7fb3b5e76c434d442c7f" + integrity sha512-LYveByuF9AOM8WrsNne5+N79k1YxjNB6gmpCQsnuSBAcV8QUeB+ZUxQzL7Rz7HksPbahymKkq2qBR+o36ggFZA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-transform-typescript" "^7.1.0" + "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2": version "7.2.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907" @@ -576,7 +618,7 @@ "@babel/parser" "^7.2.2" "@babel/types" "^7.2.2" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.2.2", "@babel/traverse@^7.2.3": +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.2.2", "@babel/traverse@^7.2.3": version "7.2.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8" integrity sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw== @@ -667,11 +709,21 @@ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.8.tgz#d27600e9ba2f371e08695d90a0fe0408d89c7be7" integrity sha512-m812CONwdZn/dMzkIJEY0yAs4apyTkTORgfB2UsMOxgkUbC205AHnm4T8I0I5gPg9MHrFc1dJ35iS75c0CJkjg== +"@types/common-tags@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@types/common-tags/-/common-tags-1.8.0.tgz#79d55e748d730b997be5b7fce4b74488d8b26a6b" + integrity sha512-htRqZr5qn8EzMelhX/Xmx142z218lLyGaeZ3YR8jlze4TATRU9huKKvuBmAJEW4LCC4pnY1N6JAm6p85fMHjhg== + "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== +"@types/jest@23.3.11": + version "23.3.11" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.11.tgz#d3a936ae753d9e484965f5cbd19027c2b8af2551" + integrity sha512-eroF85PoG87XjCwzxey7yBsQNkIY/TV5myKKSG/022A0FW25afdu/uub6JDMS5eT68zBBt82S+w/MFOTjeLM3Q== + "@types/jquery@*": version "3.3.29" resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd" @@ -684,6 +736,20 @@ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.6.tgz#5932ead926307ca21e5b36808257f7c926b06565" integrity sha512-403D4wN95Mtzt2EoQHARf5oe/jEPhzBOBNrunk+ydQGW8WmkQ/E8rViRAEB1qEt/vssfGfNVD6ujP4FVeegrLg== +"@types/jsdom@12.2.1": + version "12.2.1" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-12.2.1.tgz#0525dc8621cfbf235bca298b0f9c96bd0c6b2e5a" + integrity sha512-VnLP1qW70OkzpMVuFsJPhxeIzEW1y+t91Fa2rE+b3UZ3ZiTwB28pYrdNj58wa0AQ+dV7eIBcdMFl3ql9C+cc9g== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^4.0.0" + +"@types/jssha@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/jssha/-/jssha-2.0.0.tgz#15027ef6480c4d2c8c42762e227b23b34276e68f" + integrity sha512-oBnY3csYnXfqZXDRBJwP1nDDJCW/+VMJ88UHT4DCy0deSXpJIQvMCwYlnmdW4M+u7PiSfQc44LmiFcUbJ8hLEw== + "@types/lodash@4.14.87": version "4.14.87" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.87.tgz#55f92183b048c2c64402afe472f8333f4e319a6b" @@ -699,7 +765,7 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e" integrity sha512-k2tWTQU8G4+iSMvqKi0Q9IIsWAp/n8xzdZS4Q4YVIltApoMA00wFBFdlJnmoaK1/z7B0Cy0yPe6GgXteSmdUNw== -"@types/node@*": +"@types/node@*", "@types/node@10.12.18": version "10.12.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== @@ -717,16 +783,21 @@ resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.0.3.tgz#f8647e883d873962130f906a6114a4e187755696" integrity sha512-cjmJQLx2B5Hp9SzO7rdSivipo3kBqRqeYkTW17nLST1tn5YLWBjTdnzdmeTJXA1+KrrBLsEuvKQ0fUPGrfazQg== -"@types/sinon@7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.0.0.tgz#84e707e157ec17d3e4c2a137f41fc3f416c0551e" - integrity sha512-kcYoPw0uKioFVC/oOqafk2yizSceIQXCYnkYts9vJIwQklFRsMubTObTDrjQamUyBRd47332s85074cd/hCwxg== +"@types/sinon@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.0.0.tgz#9a93ffa4ee1329e85166278a5ed99f81dc4c8362" + integrity sha512-cuK4xM8Lg2wd8cxshcQa8RG4IK/xfyB6TNE6tNVvkrShR4xdrYgsV04q6Dp6v1Lp6biEFdzD8k8zg/ujQeiw+A== "@types/sizzle@*": version "2.3.2" resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== +"@types/tough-cookie@*": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.4.tgz#821878b81bfab971b93a265a561d54ea61f9059f" + integrity sha512-Set5ZdrAaKI/qHdFlVMgm/GsAv/wkXhSTuZFkJ+JI7HK+wIkIlOaUXSXieIvJ0+OvGIqtREFoE+NHJtEq0gtEw== + "@zeit/schemas@2.6.0": version "2.6.0" resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3" @@ -963,6 +1034,21 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= + +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -1103,6 +1189,18 @@ babel-core@^6.0.0, babel-core@^6.26.0: slash "^1.0.0" source-map "^0.5.7" +babel-eslint@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed" + integrity sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + eslint-scope "3.7.1" + eslint-visitor-keys "^1.0.0" + babel-generator@^6.18.0, babel-generator@^6.26.0: version "6.26.1" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" @@ -1854,10 +1952,10 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" -cypress@3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.1.4.tgz#2af04da05e09f9d3871d05713b364472744c4216" - integrity sha512-8VJYtCAFqHXMnRDo4vdomR2CqfmhtReoplmbkXVspeKhKxU8WsZl0Nh5yeil8txxhq+YQwDrInItUqIm35Vw+g== +cypress@3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.1.3.tgz#f6253e2428c9f76e0541440b959b6282d1757467" + integrity sha512-ZusTQffKBVrLDvcxEinymTH0iCUL7hM1m6q9X+557wDtpd6S4et330QQE1IW10Pnyp+vYIHpkWxDm43B9G14nA== dependencies: "@cypress/listr-verbose-renderer" "0.4.1" "@cypress/xvfb" "1.2.3" @@ -1869,7 +1967,7 @@ cypress@3.1.4: "@types/lodash" "4.14.87" "@types/minimatch" "3.0.3" "@types/mocha" "2.2.44" - "@types/sinon" "7.0.0" + "@types/sinon" "4.0.0" "@types/sinon-chai" "2.7.29" bluebird "3.5.0" cachedir "1.3.0" @@ -1888,7 +1986,7 @@ cypress@3.1.4: is-installed-globally "0.1.0" lazy-ass "1.6.0" listr "0.12.0" - lodash "4.17.11" + lodash "4.17.10" log-symbols "2.2.0" minimist "1.2.0" moment "2.22.2" @@ -2094,6 +2192,14 @@ domexception@^1.0.1: dependencies: webidl-conversions "^4.0.2" +dts-bundle-generator@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dts-bundle-generator/-/dts-bundle-generator-2.0.0.tgz#faa40da625f650b1aefbd398c180726d3089ecba" + integrity sha512-qOT4mMHVFf+sqncgv0IGQ5yCIKPOS28+R7rRAihkDDd0Bb6mOtNxbFz89XnhzBUtZQ7g/MLQ8KyVPUtK1WDI9w== + dependencies: + typescript ">=2.6.1" + yargs "~11.0.0" + duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -2131,7 +2237,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.5.1, es-abstract@^1.6.1: +es-abstract@^1.4.3, es-abstract@^1.5.1, es-abstract@^1.6.1: version "1.12.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" integrity sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA== @@ -2232,11 +2338,26 @@ eslint-plugin-import@2.14.0: read-pkg-up "^2.0.0" resolve "^1.6.0" +eslint-plugin-typescript@0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-typescript/-/eslint-plugin-typescript-0.14.0.tgz#068549c3f4c7f3f85d88d398c29fa96bf500884c" + integrity sha512-2u1WnnDF2mkWWgU1lFQ2RjypUlmRoBEvQN02y9u+IL12mjWlkKFGEBnVsjs9Y8190bfPQCvWly1c2rYYUSOxWw== + dependencies: + requireindex "~1.1.0" + eslint-restricted-globals@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" integrity sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc= +eslint-scope@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" + integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug= + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + eslint-scope@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" @@ -2767,6 +2888,15 @@ fs-extra@4.0.1: jsonfile "^3.0.0" universalify "^0.1.0" +fs-extra@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.0.tgz#8cc3f47ce07ef7b3593a11b9fb245f7e34c041d6" + integrity sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -2797,7 +2927,7 @@ fsevents@^1.2.2, fsevents@^1.2.3: nan "^2.9.2" node-pre-gyp "^0.10.0" -function-bind@^1.1.0, function-bind@^1.1.1: +function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== @@ -4021,6 +4151,14 @@ jsdoc-api@^4.0.1: temp-path "^1.0.0" walk-back "^3.0.0" +jsdoc-babel@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsdoc-babel/-/jsdoc-babel-0.5.0.tgz#7217b8820469fe600dccfdee895648c6a0dd4a2e" + integrity sha512-PYfTbc3LNTeR8TpZs2M94NLDWqARq0r9gx3SvuziJfmJS7/AeMKvtj0xjzOX0R/4MOVA7/FqQQK7d6U0iEoztQ== + dependencies: + jsdoc-regex "^1.0.1" + lodash "^4.17.10" + jsdoc-parse@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/jsdoc-parse/-/jsdoc-parse-3.0.1.tgz#1194d6a16a2dfbe5fb8cccfeb5058ea808759893" @@ -4033,6 +4171,11 @@ jsdoc-parse@^3.0.1: sort-array "^2.0.0" test-value "^3.0.0" +jsdoc-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/jsdoc-regex/-/jsdoc-regex-1.0.1.tgz#8424428d5b563ad8c5c7fbec079b9a8b09c8dcfa" + integrity sha1-hCRCjVtWOtjFx/vsB5uaiwnI3Po= + jsdoc-to-markdown@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/jsdoc-to-markdown/-/jsdoc-to-markdown-4.0.1.tgz#247f7d977ecc209428972ec92ca14bd4e610355d" @@ -4160,6 +4303,18 @@ jsonfile@^3.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -4388,6 +4543,16 @@ load-json-file@^2.0.0: pify "^2.0.0" strip-bom "^3.0.0" +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -4439,7 +4604,12 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@4.17.11, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: +lodash@4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== + +lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== @@ -4545,6 +4715,11 @@ mem@^1.1.0: dependencies: mimic-fn "^1.0.0" +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + merge-stream@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" @@ -4837,6 +5012,21 @@ npm-path@^2.0.2: dependencies: which "^1.2.10" +npm-run-all@4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" + integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== + dependencies: + ansi-styles "^3.2.1" + chalk "^2.4.1" + cross-spawn "^6.0.5" + memorystream "^0.3.1" + minimatch "^3.0.4" + pidtree "^0.3.0" + read-pkg "^3.0.0" + shell-quote "^1.6.1" + string.prototype.padend "^3.0.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -5137,7 +5327,7 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse5@4.0.0: +parse5@4.0.0, parse5@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== @@ -5205,6 +5395,13 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" @@ -5222,6 +5419,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +pidtree@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.0.tgz#f6fada10fccc9f99bf50e90d0b23d72c9ebc2e6b" + integrity sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg== + pify@^2.0.0, pify@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -5272,6 +5474,14 @@ please-upgrade-node@^3.0.2, please-upgrade-node@^3.1.1: dependencies: semver-compare "^1.0.0" +pluggable-babel-eslint@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/pluggable-babel-eslint/-/pluggable-babel-eslint-0.3.0.tgz#f1b4e74888099e8b5f6319de555d166321575408" + integrity sha512-VjYHTosuKaRk/9rZVqHnbsy1UT7KGVFqFqsSdiv4pmKkUKEidF7gXQtUaILvcnSbSzYJpl9befv1y5OKzvUmrw== + dependencies: + "@babel/traverse" "^7.1.0" + babel-eslint "^10.0.1" + pluralize@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" @@ -5446,6 +5656,15 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + read-pkg@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" @@ -5698,6 +5917,11 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= +requireindex@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162" + integrity sha1-5UBLgVV+91225JxacgBIk/4D4WI= + requizzle@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.1.tgz#6943c3530c4d9a7e46f1cddd51c158fc670cdbde" @@ -5732,6 +5956,13 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= +resolve@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== + dependencies: + path-parse "^1.0.5" + resolve@^1.3.2, resolve@^1.5.0, resolve@^1.6.0, resolve@^1.8.1: version "1.9.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06" @@ -5828,7 +6059,17 @@ rollup-plugin-terser@4.0.1: lave "^1.1.10" terser "^3.14.0" -rollup-pluginutils@^2.0.1, rollup-pluginutils@^2.3.0, rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.3.3: +rollup-plugin-typescript2@0.18.1: + version "0.18.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.18.1.tgz#921865828080a254c088c6bc181ca654e5ef73c6" + integrity sha512-aR2m5NCCAUV/KpcKgCWX6Giy8rTko9z92b5t0NX9eZyjOftCvcdDFa1C9Ze/9yp590hnRymr5hG0O9SAXi1oUg== + dependencies: + fs-extra "7.0.0" + resolve "1.8.1" + rollup-pluginutils "2.3.3" + tslib "1.9.3" + +rollup-pluginutils@2.3.3, rollup-pluginutils@^2.0.1, rollup-pluginutils@^2.3.0, rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz#3aad9b1eb3e7fe8262820818840bf091e5ae6794" integrity sha512-2XZwja7b6P5q4RZ5FhyX1+f46xi1Z3qBKigLRZ6VTZjwbN0K1IFGMlwm06Uu0Emcre2Z63l77nq/pzn+KxIEoA== @@ -5995,6 +6236,16 @@ shebang-regex@^1.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shell-quote@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -6276,6 +6527,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string.prototype.padend@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz#f3aaef7c1719f170c5eab1c32bf780d96e21f2f0" + integrity sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.4.3" + function-bind "^1.0.2" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -6587,7 +6847,7 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -tslib@^1.9.0: +tslib@1.9.3, tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== @@ -6616,6 +6876,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@3.2.2, typescript@>=2.6.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5" + integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg== + typical@^2.4.2, typical@^2.6.0, typical@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d" @@ -6991,6 +7256,24 @@ yargs@^11.0.0: y18n "^3.2.1" yargs-parser "^9.0.2" +yargs@~11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.0.0.tgz#c052931006c5eee74610e5fc0354bedfd08a201b" + integrity sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw== + 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 "^9.0.2" + yauzl@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"