diff --git a/packages/core-debugger-cli/__tests__/commands/deserialize.test.ts b/packages/core-debugger-cli/__tests__/commands/deserialize.test.ts index a8c1747379..a7b7fa572e 100644 --- a/packages/core-debugger-cli/__tests__/commands/deserialize.test.ts +++ b/packages/core-debugger-cli/__tests__/commands/deserialize.test.ts @@ -1,18 +1,13 @@ import "jest-extended"; -import { deserialize } from "../../src/commands/deserialize"; +import { DeserializeCommand } from "../../src/commands/deserialize"; describe("Commands - Deserialize", () => { const fixtureBlock = require("../__fixtures__/block.json"); const fixtureTransaction = require("../__fixtures__/transaction.json"); - it("should deserialize a block (not-full)", () => { - const actual = JSON.parse( - deserialize({ - data: fixtureBlock.serialized, - type: "block", - }), - ); + it("should deserialize a block (not-full)", async () => { + const actual = JSON.parse(await DeserializeCommand.run(["--data", fixtureBlock.serialized, "--type", "block"])); expect(actual.data.version).toBe(fixtureBlock.data.version); expect(actual.data.timestamp).toBe(fixtureBlock.data.timestamp); @@ -28,12 +23,9 @@ describe("Commands - Deserialize", () => { expect(actual.data.blockSignature).toBe(fixtureBlock.data.blockSignature); }); - it("should deserialize a block (full)", () => { + it("should deserialize a block (full)", async () => { const actual = JSON.parse( - deserialize({ - data: fixtureBlock.serializedFull, - type: "block", - }), + await DeserializeCommand.run(["--data", fixtureBlock.serializedFull, "--type", "block"]), ); expect(actual.data.version).toBe(fixtureBlock.data.version); @@ -51,12 +43,9 @@ describe("Commands - Deserialize", () => { expect(actual.transactions).toHaveLength(7); }); - it("should deserialize a transaction", () => { + it("should deserialize a transaction", async () => { const actual = JSON.parse( - deserialize({ - data: fixtureTransaction.serialized, - type: "transaction", - }), + await DeserializeCommand.run(["--data", fixtureTransaction.serialized, "--type", "transaction"]), ); expect(actual.type).toBe(fixtureTransaction.data.type); diff --git a/packages/core-debugger-cli/__tests__/commands/identity.test.ts b/packages/core-debugger-cli/__tests__/commands/identity.test.ts index 2649a2f9ac..2fff72ab91 100644 --- a/packages/core-debugger-cli/__tests__/commands/identity.test.ts +++ b/packages/core-debugger-cli/__tests__/commands/identity.test.ts @@ -1,11 +1,11 @@ import "jest-extended"; -import { identity } from "../../src/commands/identity"; +import { IdentityCommand } from "../../src/commands/identity"; -describe("Commands - Identity", () => { +describe("Commands - Identity", async () => { const fixtureIdentities = require("../__fixtures__/identities.json"); - it("should return identities from passphrase", () => { + it("should return identities from passphrase", async () => { const expected = { passphrase: "this is a top secret passphrase", publicKey: "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", @@ -13,44 +13,31 @@ describe("Commands - Identity", () => { address: "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", }; - expect( - identity({ - data: fixtureIdentities.passphrase, - type: "passphrase", - }), - ).toEqual(expected); + expect(await IdentityCommand.run(["--data", fixtureIdentities.passphrase, "--type", "passphrase"])).toEqual( + expected, + ); }); - it("should return identities from privateKey", () => { + it("should return identities from privateKey", async () => { const expected = { publicKey: "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", privateKey: "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712", address: "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", }; - expect( - identity({ - data: fixtureIdentities.privateKey, - type: "privateKey", - }), - ).toEqual(expected); + expect(await IdentityCommand.run(["--data", fixtureIdentities.privateKey, "--type", "privateKey"])).toEqual( + expected, + ); }); - it("should return identities from publicKey", () => { + it("should return identities from publicKey", async () => { const expected = { publicKey: "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", address: "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", }; - expect( - identity({ - data: fixtureIdentities.publicKey, - type: "publicKey", - }), - ).toEqual(expected); - }); - - it("should not return anything for empty input", () => { - expect(identity({})).toEqual(undefined); + expect(await IdentityCommand.run(["--data", fixtureIdentities.publicKey, "--type", "publicKey"])).toEqual( + expected, + ); }); }); diff --git a/packages/core-debugger-cli/__tests__/commands/serialize.test.ts b/packages/core-debugger-cli/__tests__/commands/serialize.test.ts index c5c62cd357..a2ddb006bb 100644 --- a/packages/core-debugger-cli/__tests__/commands/serialize.test.ts +++ b/packages/core-debugger-cli/__tests__/commands/serialize.test.ts @@ -1,37 +1,26 @@ import "jest-extended"; -import { serialize } from "../../src/commands/serialize"; +import { SerializeCommand } from "../../src/commands/serialize"; describe("Commands - Serialize", () => { const fixtureBlock = require("../__fixtures__/block.json"); const fixtureTransaction = require("../__fixtures__/transaction.json"); - it("should serialize a block (not-full)", () => { - expect( - serialize({ - data: JSON.stringify(fixtureBlock.data), - type: "block", - full: false, - }), - ).toEqual(fixtureBlock.serialized); + it("should serialize a block (not-full)", async () => { + expect(await SerializeCommand.run(["--data", JSON.stringify(fixtureBlock.data), "--type", "block"])).toEqual( + fixtureBlock.serialized, + ); }); - it("should serialize a block (full)", () => { + it("should serialize a block (full)", async () => { expect( - serialize({ - data: JSON.stringify(fixtureBlock.data), - type: "block", - full: true, - }), + await SerializeCommand.run(["--data", JSON.stringify(fixtureBlock.data), "--type", "block", "--full"]), ).toEqual(fixtureBlock.serializedFull); }); - it("should serialize a transaction", () => { + it("should serialize a transaction", async () => { expect( - serialize({ - data: JSON.stringify(fixtureTransaction.data), - type: "transaction", - }), + await SerializeCommand.run(["--data", JSON.stringify(fixtureTransaction.data), "--type", "transaction"]), ).toEqual(fixtureTransaction.serialized); }); }); diff --git a/packages/core-debugger-cli/__tests__/commands/verify-second.test.ts b/packages/core-debugger-cli/__tests__/commands/verify-second.test.ts index 0a081ec178..5cb6fb2b1a 100644 --- a/packages/core-debugger-cli/__tests__/commands/verify-second.test.ts +++ b/packages/core-debugger-cli/__tests__/commands/verify-second.test.ts @@ -1,16 +1,18 @@ import "jest-extended"; -import { verifySecondSignature } from "../../src/commands/verify-second"; +import { VerifySecondSignatureCommand } from "../../src/commands/verify-second"; describe("Commands - Verify Second", () => { const fixtureTransaction = require("../__fixtures__/transaction-second.json"); - it("should verify a second signature", () => { + it("should verify a second signature", async () => { expect( - verifySecondSignature({ - data: fixtureTransaction.serialized, - publicKey: "03699e966b2525f9088a6941d8d94f7869964a000efe65783d78ac82e1199fe609", - }), + await VerifySecondSignatureCommand.run([ + "--data", + fixtureTransaction.serialized, + "--publicKey", + "03699e966b2525f9088a6941d8d94f7869964a000efe65783d78ac82e1199fe609", + ]), ).toBeTrue(); }); }); diff --git a/packages/core-debugger-cli/__tests__/commands/verify.test.ts b/packages/core-debugger-cli/__tests__/commands/verify.test.ts index 98a8aa0755..8827d94bc0 100644 --- a/packages/core-debugger-cli/__tests__/commands/verify.test.ts +++ b/packages/core-debugger-cli/__tests__/commands/verify.test.ts @@ -1,26 +1,16 @@ import "jest-extended"; -import { verify } from "../../src/commands/verify"; +import { VerifyCommand } from "../../src/commands/verify"; describe("Commands - Verify", () => { const fixtureBlock = require("../__fixtures__/block.json"); const fixtureTransaction = require("../__fixtures__/transaction.json"); - it("should verify a block", () => { - expect( - verify({ - data: fixtureBlock.serializedFull, - type: "block", - }), - ).toBeTrue(); + it("should verify a block", async () => { + expect(await VerifyCommand.run(["--data", fixtureBlock.serializedFull, "--type", "block"])).toBeTrue(); }); - it("should verify a transaction", () => { - expect( - verify({ - data: fixtureTransaction.serialized, - type: "transaction", - }), - ).toBeTrue(); + it("should verify a transaction", async () => { + expect(await VerifyCommand.run(["--data", fixtureTransaction.serialized, "--type", "transaction"])).toBeTrue(); }); }); diff --git a/packages/core-debugger-cli/bin/debugger b/packages/core-debugger-cli/bin/debugger deleted file mode 100755 index d5d150f104..0000000000 --- a/packages/core-debugger-cli/bin/debugger +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env node - -const app = require('commander') - -const { serialize } = require('../dist/commands/serialize') -const { deserialize } = require('../dist/commands/deserialize') -const { verify } = require('../dist/commands/verify') -const { verifySecond } = require('../dist/commands/verify-second') -const { identity } = require('../dist/commands/identity') - -app.version(require('../package.json').version) - -const registerCommand = (name, description) => { - return app - .command(name) - .description(description) - .option('-l, --log', 'log the data to the console') - .option('-c, --copy', 'copy the data to the clipboard') -} - -registerCommand('ser', 'serialize the given JSON') - .option('-d, --data ', 'JSON blob to serialize') - .option('-t, --type ', 'transaction or block', 'transaction') - .action(options => serialize(options)) - -registerCommand('des', 'deserialize the given HEX') - .option('-d, --data ', 'the HEX blob to deserialize') - .option('-t, --type ', 'transaction or block', 'transaction') - .action(options => deserialize(options)) - -registerCommand('verify', 'verify the given HEX') - .option('-d, --data ', 'the HEX blob to deserialize and verify') - .option('-t, --type ', 'transaction or block', 'transaction') - .action(options => verify(options)) - -registerCommand('verify-second', 'verify a second signature of a transaction') - .option('-d, --data ', 'the transaction HEX blob to deserialize and verify') - .option('-p, --publicKey ', 'the publicKey of the second signature in HEX') - .action(options => verifySecond(options)) - -registerCommand('identity', 'get identities from the given input') - .option('-d, --data ', 'the data to get the identities from') - .option('-t, --type ', 'the input type is either of passphrase, privateKey or publicKey', 'passphrase') - .option('-n, --network ', 'the network version used for calculating the address.') - .action(options => identity(options)) - -app - .command('*') - .action(env => { - app.help() - }) - -app.parse(process.argv) - -if (app.args.length === 0) { - app.help() -} diff --git a/packages/core-debugger-cli/bin/run b/packages/core-debugger-cli/bin/run new file mode 100755 index 0000000000..30b14e1773 --- /dev/null +++ b/packages/core-debugger-cli/bin/run @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +require('@oclif/command').run() +.then(require('@oclif/command/flush')) +.catch(require('@oclif/errors/handle')) diff --git a/packages/core-debugger-cli/bin/run.cmd b/packages/core-debugger-cli/bin/run.cmd new file mode 100644 index 0000000000..968fc30758 --- /dev/null +++ b/packages/core-debugger-cli/bin/run.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\run" %* diff --git a/packages/core-debugger-cli/package.json b/packages/core-debugger-cli/package.json index 35e3489200..b7b6ba0c29 100644 --- a/packages/core-debugger-cli/package.json +++ b/packages/core-debugger-cli/package.json @@ -8,15 +8,19 @@ "license": "MIT", "main": "dist/index.js", "files": [ - "dist" + "/bin", + "/dist", + "/oclif.manifest.json" ], "bin": { - "core:debugger": "./bin/debugger" + "debugger": "./bin/run" }, "scripts": { - "start": "./bin/debugger", + "debugger": "./bin/run", "prepublishOnly": "yarn test && yarn build", "pretest": "yarn lint && yarn build", + "prepack": "oclif-dev manifest && npm shrinkwrap", + "postpack": "rm -f oclif.manifest.json", "compile": "../../node_modules/typescript/bin/tsc", "build": "yarn clean && yarn compile", "build:watch": "yarn clean && yarn compile -w", @@ -32,9 +36,12 @@ }, "dependencies": { "@arkecosystem/crypto": "^2.1.0", + "@oclif/command": "^1.5.8", + "@oclif/config": "^1.12.4", + "@oclif/plugin-help": "^2.1.6", + "@oclif/plugin-not-found": "^1.2.2", "@types/clipboardy": "^1.1.0", - "clipboardy": "^1.2.3", - "commander": "^2.19.0" + "clipboardy": "^1.2.3" }, "publishConfig": { "access": "public" @@ -44,5 +51,13 @@ }, "jest": { "preset": "../../jest-preset.json" + }, + "oclif": { + "commands": "./dist/commands", + "bin": "debugger", + "plugins": [ + "@oclif/plugin-help", + "@oclif/plugin-not-found" + ] } } diff --git a/packages/core-debugger-cli/src/commands/command.ts b/packages/core-debugger-cli/src/commands/command.ts new file mode 100644 index 0000000000..4ab327459a --- /dev/null +++ b/packages/core-debugger-cli/src/commands/command.ts @@ -0,0 +1,12 @@ +import Command, { flags } from "@oclif/command"; + +export abstract class BaseCommand extends Command { + public static flags = { + log: flags.string({ + description: "log the data to the console", + }), + copy: flags.string({ + description: "copy the data to the clipboard", + }), + }; +} diff --git a/packages/core-debugger-cli/src/commands/deserialize.ts b/packages/core-debugger-cli/src/commands/deserialize.ts index 1d6e988cec..553fe65abb 100644 --- a/packages/core-debugger-cli/src/commands/deserialize.ts +++ b/packages/core-debugger-cli/src/commands/deserialize.ts @@ -1,18 +1,31 @@ import { models } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; import { handleOutput } from "../utils"; +import { BaseCommand } from "./command"; -function deserialize(opts) { - const { Block, Transaction } = models; +export class DeserializeCommand extends BaseCommand { + public static description: string = "Deserialize the given HEX"; - let deserialized; + public static flags = { + ...BaseCommand.flags, + data: flags.string({ + description: "the HEX blob to deserialize", + required: true, + default: "transaction", + }), + type: flags.string({ + description: "transaction or block", + required: true, + }), + }; - if (opts.type === "transaction") { - deserialized = new Transaction(opts.data); - } else { - deserialized = new Block(opts.data); - } + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(DeserializeCommand); - return handleOutput(opts, JSON.stringify(deserialized, null, 4)); -} + const deserialized = + flags.type === "transaction" ? new models.Transaction(flags.data) : new models.Block(flags.data); -export { deserialize }; + return handleOutput(flags, JSON.stringify(deserialized, null, 4)); + } +} diff --git a/packages/core-debugger-cli/src/commands/identity.ts b/packages/core-debugger-cli/src/commands/identity.ts index 7147fad320..0ed91c6ba3 100644 --- a/packages/core-debugger-cli/src/commands/identity.ts +++ b/packages/core-debugger-cli/src/commands/identity.ts @@ -1,32 +1,56 @@ import { crypto } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; import { handleOutput } from "../utils"; +import { BaseCommand } from "./command"; -function identity(opts) { - let output; +export class IdentityCommand extends BaseCommand { + public static description: string = "Get identities from the given input"; - if (opts.type === "passphrase") { - const keys = crypto.getKeys(opts.data); - output = { - passphrase: opts.data, - publicKey: keys.publicKey, - privateKey: keys.privateKey, - address: crypto.getAddress(keys.publicKey, opts.network), - }; - } else if (opts.type === "privateKey") { - const keys = crypto.getKeysByPrivateKey(opts.data); - output = { - publicKey: keys.publicKey, - privateKey: keys.privateKey, - address: crypto.getAddress(keys.publicKey, opts.network), - }; - } else if (opts.type === "publicKey") { - output = { - publicKey: opts.data, - address: crypto.getAddress(opts.data, opts.network), - }; - } + public static flags = { + ...BaseCommand.flags, + data: flags.string({ + description: "the data to get the identities from", + required: true, + }), + network: flags.integer({ + description: "the network version used for calculating the address.", + required: true, + default: 30, + }), + type: flags.string({ + description: "the input type is either of passphrase, privateKey or publicKey", + required: true, + }), + }; - return handleOutput(opts, output); -} + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(IdentityCommand); + + let output; -export { identity }; + if (flags.type === "passphrase") { + const keys = crypto.getKeys(flags.data); + output = { + passphrase: flags.data, + publicKey: keys.publicKey, + privateKey: keys.privateKey, + address: crypto.getAddress(keys.publicKey, flags.network), + }; + } else if (flags.type === "privateKey") { + const keys = crypto.getKeysByPrivateKey(flags.data); + output = { + publicKey: keys.publicKey, + privateKey: keys.privateKey, + address: crypto.getAddress(keys.publicKey, flags.network), + }; + } else if (flags.type === "publicKey") { + output = { + publicKey: flags.data, + address: crypto.getAddress(flags.data, flags.network), + }; + } + + return handleOutput(flags, output); + } +} diff --git a/packages/core-debugger-cli/src/commands/serialize.ts b/packages/core-debugger-cli/src/commands/serialize.ts index da2d29336f..61c06590b6 100644 --- a/packages/core-debugger-cli/src/commands/serialize.ts +++ b/packages/core-debugger-cli/src/commands/serialize.ts @@ -1,15 +1,36 @@ import { models } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; import { handleOutput } from "../utils"; +import { BaseCommand } from "./command"; -function serialize(opts) { - const { Block, Transaction } = models; +export class SerializeCommand extends BaseCommand { + public static description: string = "Serialize the given JSON"; - const serialized: any = - opts.type === "transaction" - ? Transaction.serialize(JSON.parse(opts.data)) - : Block[opts.full ? "serializeFull" : "serialize"](JSON.parse(opts.data)); + public static flags = { + ...BaseCommand.flags, + data: flags.string({ + description: "the HEX blob to serialize", + required: true, + }), + type: flags.string({ + description: "transaction or block", + required: true, + }), + full: flags.boolean({ + description: "serialize a full block with transactions", + required: false, + }), + }; - return handleOutput(opts, serialized.toString("hex")); -} + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(SerializeCommand); + + const serialized: any = + flags.type === "transaction" + ? models.Transaction.serialize(JSON.parse(flags.data)) + : models.Block[flags.full ? "serializeFull" : "serialize"](JSON.parse(flags.data)); -export { serialize }; + return handleOutput(flags, serialized.toString("hex")); + } +} diff --git a/packages/core-debugger-cli/src/commands/verify-second.ts b/packages/core-debugger-cli/src/commands/verify-second.ts index ed1dcc7610..aeed1cadb1 100644 --- a/packages/core-debugger-cli/src/commands/verify-second.ts +++ b/packages/core-debugger-cli/src/commands/verify-second.ts @@ -1,14 +1,29 @@ import { crypto, models } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; import { handleOutput } from "../utils"; +import { BaseCommand } from "./command"; -function verifySecondSignature(opts) { - const { Transaction } = models; +export class VerifySecondSignatureCommand extends BaseCommand { + public static description: string = "Verify a second signature of a transaction"; - const transaction = new Transaction(opts.data); - const publicKey = opts.publicKey; + public static flags = { + ...BaseCommand.flags, + data: flags.string({ + description: "the HEX blob to deserialize and verify", + required: true, + }), + publicKey: flags.string({ + description: "the publicKey of the second signature in HEX", + required: true, + }), + }; - const output = crypto.verifySecondSignature(transaction, publicKey); - return handleOutput(opts, output); -} + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(VerifySecondSignatureCommand); + + const transaction = new models.Transaction(flags.data); -export { verifySecondSignature }; + return handleOutput(flags, crypto.verifySecondSignature(transaction, flags.publicKey)); + } +} diff --git a/packages/core-debugger-cli/src/commands/verify.ts b/packages/core-debugger-cli/src/commands/verify.ts index 6ca0311469..37deb12c65 100644 --- a/packages/core-debugger-cli/src/commands/verify.ts +++ b/packages/core-debugger-cli/src/commands/verify.ts @@ -1,14 +1,35 @@ import { models } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; import { handleOutput } from "../utils"; +import { BaseCommand } from "./command"; -function verify(opts) { - const { Block, Transaction } = models; +export class VerifyCommand extends BaseCommand { + public static description: string = "Verify the given HEX"; - const deserialized = - opts.type === "transaction" ? new Transaction(opts.data) : new Block(Block.deserialize(opts.data)); + public static flags = { + ...BaseCommand.flags, + data: flags.string({ + description: "the HEX blob to deserialize and verify", + required: true, + }), + type: flags.string({ + description: "transaction or block", + required: true, + }), + }; - const output = deserialized instanceof Transaction ? deserialized.verify() : deserialized.verification.verified; - return handleOutput(opts, output); -} + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(VerifyCommand); + + const deserialized = + flags.type === "transaction" + ? new models.Transaction(flags.data) + : new models.Block(models.Block.deserialize(flags.data)); -export { verify }; + const output = + deserialized instanceof models.Transaction ? deserialized.verify() : deserialized.verification.verified; + + return handleOutput(flags, output); + } +} diff --git a/packages/core-debugger-cli/src/index.ts b/packages/core-debugger-cli/src/index.ts new file mode 100644 index 0000000000..8bdb76f9a0 --- /dev/null +++ b/packages/core-debugger-cli/src/index.ts @@ -0,0 +1 @@ +export { run } from "@oclif/command"; diff --git a/packages/core-json-rpc/package.json b/packages/core-json-rpc/package.json index 60177ad943..9eea77bded 100644 --- a/packages/core-json-rpc/package.json +++ b/packages/core-json-rpc/package.json @@ -53,6 +53,7 @@ "devDependencies": { "@arkecosystem/core-p2p": "^2.1.0", "@arkecosystem/core-test-utils": "^2.1.0", + "@types/is-reachable": "^3.0.0", "@types/keyv__sqlite": "^2.0.0", "axios-mock-adapter": "^1.15.0" }, diff --git a/packages/core-logger-winston/package.json b/packages/core-logger-winston/package.json index 55a415beb4..8ff7b4e9a0 100644 --- a/packages/core-logger-winston/package.json +++ b/packages/core-logger-winston/package.json @@ -41,6 +41,7 @@ }, "devDependencies": { "@types/capture-console": "^1.0.0", + "@types/lodash.isempty": "^4.4.4", "@types/node-emoji": "^1.8.0", "capture-console": "^1.0.1" }, diff --git a/yarn.lock b/yarn.lock index 336294fc1d..7d46f00bea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1782,6 +1782,11 @@ dependencies: "@types/node" "*" +"@types/is-reachable@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/is-reachable/-/is-reachable-3.0.0.tgz#3203aa585cd5d3a4c1339bc857f4de06c4a18aff" + integrity sha512-rCl0PxFRPqwnKnWOO+6YdfnIIEsqahrEQjBfKR31C6E5oC4MY2U9W6VvXl090ZBYoCEY2wtqrBQaZn0UBGY85g== + "@types/jest@^23.3.10": version "23.3.10" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.10.tgz#4897974cc317bf99d4fe6af1efa15957fa9c94de"