diff --git a/CHANGELOG.md b/CHANGELOG.md index 108cb6a7dc..35c227d729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# Web3API 0.0.1-prealpha.43 +## Features +* `@web3api/client-js`: Added the `client.subscribe(...)` method, enabling users to easily send queries at a specified frequency. + +## Bugs +* `@web3api/tracing-js`: Replaced the `util-inspect` dependency with a browser compatible one. + # Web3API 0.0.1-prealpha.42 ## Bugs * `@web3api/schema-parse`: Removed unnecessary sanitization for imported methods without any arguments. diff --git a/VERSION b/VERSION index 79e7320abe..1ba059a174 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.1-prealpha.42 \ No newline at end of file +0.0.1-prealpha.43 \ No newline at end of file diff --git a/packages/apis/uniswapv2/src/__tests__/e2e/fetch_e2e.spec.ts b/packages/apis/uniswapv2/src/__tests__/e2e/fetch_e2e.spec.ts index 2edf89acb1..4f2a1ec4ab 100644 --- a/packages/apis/uniswapv2/src/__tests__/e2e/fetch_e2e.spec.ts +++ b/packages/apis/uniswapv2/src/__tests__/e2e/fetch_e2e.spec.ts @@ -1,8 +1,8 @@ import { buildAndDeployApi, initTestEnvironment, stopTestEnvironment } from "@web3api/test-env-js"; -import { UriRedirect, Web3ApiClient } from "@web3api/client-js"; +import { ClientConfig, Web3ApiClient } from "@web3api/client-js"; import { ChainId, Pair, Token, TokenAmount } from "./types"; import path from "path"; -import { getRedirects, getTokenList } from "../testUtils"; +import { getPlugins, getTokenList } from "../testUtils"; import * as uni from "@uniswap/sdk"; import * as ethers from "ethers"; @@ -20,8 +20,8 @@ describe("Fetch", () => { beforeAll(async () => { const { ethereum: testEnvEtherem, ensAddress, ipfs } = await initTestEnvironment(); // get client - const redirects: UriRedirect[] = getRedirects(testEnvEtherem, ipfs, ensAddress); - client = new Web3ApiClient({ redirects }); + const config: ClientConfig = getPlugins(testEnvEtherem, ipfs, ensAddress); + client = new Web3ApiClient(config); // deploy api const apiPath: string = path.resolve(__dirname + "/../../../"); const api = await buildAndDeployApi(apiPath, ipfs, ensAddress); diff --git a/packages/apis/uniswapv2/src/__tests__/e2e/pair_e2e.spec.ts b/packages/apis/uniswapv2/src/__tests__/e2e/pair_e2e.spec.ts index 938db4d0b2..5a09793f8f 100644 --- a/packages/apis/uniswapv2/src/__tests__/e2e/pair_e2e.spec.ts +++ b/packages/apis/uniswapv2/src/__tests__/e2e/pair_e2e.spec.ts @@ -1,8 +1,8 @@ -import { UriRedirect, Web3ApiClient } from "@web3api/client-js"; +import { ClientConfig, Web3ApiClient } from "@web3api/client-js"; import { buildAndDeployApi, initTestEnvironment, stopTestEnvironment } from "@web3api/test-env-js"; import * as path from "path"; import { Pair, Token, TokenAmount } from "./types"; -import { getPairData, getRedirects, getTokenList, getUniPairs } from "../testUtils"; +import { getPairData, getPlugins, getTokenList, getUniPairs } from "../testUtils"; import * as uni from "@uniswap/sdk"; jest.setTimeout(150000); @@ -17,8 +17,8 @@ describe('Pair', () => { beforeAll(async () => { const { ethereum: testEnvEtherem, ensAddress, ipfs } = await initTestEnvironment(); // get client - const redirects: UriRedirect[] = getRedirects(testEnvEtherem, ipfs, ensAddress); - client = new Web3ApiClient({ redirects }); + const config: ClientConfig = getPlugins(testEnvEtherem, ipfs, ensAddress); + client = new Web3ApiClient(config); // deploy api const apiPath: string = path.resolve(__dirname + "/../../../"); const api = await buildAndDeployApi(apiPath, ipfs, ensAddress); diff --git a/packages/apis/uniswapv2/src/__tests__/e2e/route_e2e.spec.ts b/packages/apis/uniswapv2/src/__tests__/e2e/route_e2e.spec.ts index 22d3bce2ec..e0a45e4880 100644 --- a/packages/apis/uniswapv2/src/__tests__/e2e/route_e2e.spec.ts +++ b/packages/apis/uniswapv2/src/__tests__/e2e/route_e2e.spec.ts @@ -1,8 +1,8 @@ -import { UriRedirect, Web3ApiClient } from "@web3api/client-js"; +import { ClientConfig, Web3ApiClient } from "@web3api/client-js"; import { buildAndDeployApi, initTestEnvironment, stopTestEnvironment } from "@web3api/test-env-js"; import * as path from "path"; import { Pair, Route, Token } from "./types"; -import { getPairData, getRedirects, getTokenList, getUniPairs } from "../testUtils"; +import { getPairData, getPlugins, getTokenList, getUniPairs } from "../testUtils"; import * as uni from "@uniswap/sdk"; jest.setTimeout(360000); @@ -19,8 +19,8 @@ describe('Route', () => { beforeAll(async () => { const { ethereum: testEnvEtherem, ensAddress, ipfs } = await initTestEnvironment(); // get client - const redirects: UriRedirect[] = getRedirects(testEnvEtherem, ipfs, ensAddress); - client = new Web3ApiClient({ redirects }); + const config: ClientConfig = getPlugins(testEnvEtherem, ipfs, ensAddress); + client = new Web3ApiClient(config); // deploy api const apiPath: string = path.resolve(__dirname + "/../../../"); const api = await buildAndDeployApi(apiPath, ipfs, ensAddress); diff --git a/packages/apis/uniswapv2/src/__tests__/e2e/router_e2e.spec.ts b/packages/apis/uniswapv2/src/__tests__/e2e/router_e2e.spec.ts index e6b58e3565..099777f3cf 100644 --- a/packages/apis/uniswapv2/src/__tests__/e2e/router_e2e.spec.ts +++ b/packages/apis/uniswapv2/src/__tests__/e2e/router_e2e.spec.ts @@ -1,5 +1,5 @@ import { buildAndDeployApi, initTestEnvironment, stopTestEnvironment } from "@web3api/test-env-js"; -import { UriRedirect, Web3ApiClient } from "@web3api/client-js"; +import { ClientConfig, Web3ApiClient } from "@web3api/client-js"; import { ChainId, Pair, @@ -16,7 +16,7 @@ import { getBestTradeExactIn, getBestTradeExactOut, getPairData, - getRedirects, + getPlugins, getTokenList, getUniPairs } from "../testUtils"; @@ -40,8 +40,8 @@ describe("Router", () => { beforeAll(async () => { const { ethereum: testEnvEtherem, ensAddress, ipfs } = await initTestEnvironment(); // get client - const redirects: UriRedirect[] = getRedirects(testEnvEtherem, ipfs, ensAddress); - client = new Web3ApiClient({ redirects: redirects, tracingEnabled: true }); + const config: ClientConfig = getPlugins(testEnvEtherem, ipfs, ensAddress); + client = new Web3ApiClient(config); // deploy api const apiPath: string = path.resolve(__dirname + "../../../../"); diff --git a/packages/apis/uniswapv2/src/__tests__/e2e/swap_e2e.spec.ts b/packages/apis/uniswapv2/src/__tests__/e2e/swap_e2e.spec.ts index 7e80a5726c..8feafc8ae2 100644 --- a/packages/apis/uniswapv2/src/__tests__/e2e/swap_e2e.spec.ts +++ b/packages/apis/uniswapv2/src/__tests__/e2e/swap_e2e.spec.ts @@ -1,12 +1,12 @@ import { buildAndDeployApi, initTestEnvironment, stopTestEnvironment } from "@web3api/test-env-js"; -import { UriRedirect, Web3ApiClient } from "@web3api/client-js"; +import { ClientConfig, Web3ApiClient } from "@web3api/client-js"; import { Currency, Pair, Token, Trade, TxResponse } from "./types"; import path from "path"; -import { getRedirects, getTokenList } from "../testUtils"; +import { getPlugins, getTokenList } from "../testUtils"; import { Contract, ethers, providers } from "ethers"; import erc20ABI from "./testData/erc20ABI.json"; -jest.setTimeout(120000); +jest.setTimeout(360000); describe("Swap", () => { @@ -23,8 +23,8 @@ describe("Swap", () => { beforeAll(async () => { const { ethereum: testEnvEtherem, ensAddress, ipfs } = await initTestEnvironment(); // get client - const redirects: UriRedirect[] = getRedirects(testEnvEtherem, ipfs, ensAddress); - client = new Web3ApiClient({ redirects: redirects, tracingEnabled: true }); + const config: ClientConfig = getPlugins(testEnvEtherem, ipfs, ensAddress); + client = new Web3ApiClient(config); // deploy api const apiPath: string = path.resolve(__dirname + "../../../../"); diff --git a/packages/apis/uniswapv2/src/__tests__/e2e/trade_e2e.spec.ts b/packages/apis/uniswapv2/src/__tests__/e2e/trade_e2e.spec.ts index 54febdb770..0ab7df7d09 100644 --- a/packages/apis/uniswapv2/src/__tests__/e2e/trade_e2e.spec.ts +++ b/packages/apis/uniswapv2/src/__tests__/e2e/trade_e2e.spec.ts @@ -1,8 +1,8 @@ -import { UriRedirect, Web3ApiClient } from "@web3api/client-js"; +import { ClientConfig, Web3ApiClient } from "@web3api/client-js"; import { buildAndDeployApi, initTestEnvironment, stopTestEnvironment } from "@web3api/test-env-js"; import * as path from "path"; import { ChainId, Pair, Route, Token, TokenAmount, Trade } from "./types"; -import { getPairData, getRedirects, getTokenList, getUniPairs } from "../testUtils"; +import { getPairData, getPlugins, getTokenList, getUniPairs } from "../testUtils"; import * as uni from "@uniswap/sdk"; jest.setTimeout(120000); @@ -19,8 +19,8 @@ describe('trade e2e', () => { beforeAll(async () => { const { ethereum: testEnvEtherem, ensAddress, ipfs } = await initTestEnvironment(); // get client - const redirects: UriRedirect[] = getRedirects(testEnvEtherem, ipfs, ensAddress); - client = new Web3ApiClient({ redirects }); + const config: ClientConfig = getPlugins(testEnvEtherem, ipfs, ensAddress); + client = new Web3ApiClient(config); // deploy api const apiPath: string = path.resolve(__dirname + "/../../../"); const api = await buildAndDeployApi(apiPath, ipfs, ensAddress); diff --git a/packages/apis/uniswapv2/src/__tests__/query/router.spec.ts b/packages/apis/uniswapv2/src/__tests__/query/router.spec.ts index ea6e1e8bc7..8e95feaa43 100644 --- a/packages/apis/uniswapv2/src/__tests__/query/router.spec.ts +++ b/packages/apis/uniswapv2/src/__tests__/query/router.spec.ts @@ -82,7 +82,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromNull() @@ -96,7 +96,7 @@ describe("swapCallParameters", () => { "0x0000000000000000000000000000000000000004" ]); expect(result.value).toStrictEqual("0x64"); - const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; + const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; expect(deadlineDifference < 5).toBe(true); }); @@ -126,7 +126,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromNull(), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromValue(50), feeOnTransfer: Nullable.fromNull() @@ -164,7 +164,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromNull() @@ -179,7 +179,7 @@ describe("swapCallParameters", () => { "0x0000000000000000000000000000000000000004" ]); expect(result.value).toStrictEqual("0x0"); - const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; + const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; expect(deadlineDifference < 5).toBe(true); }); @@ -201,7 +201,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromNull() @@ -216,7 +216,7 @@ describe("swapCallParameters", () => { "0x0000000000000000000000000000000000000004" ]); expect(result.value).toStrictEqual("0x0"); - const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; + const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; expect(deadlineDifference < 5).toBe(true); }); }); @@ -244,7 +244,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromNull() @@ -258,7 +258,7 @@ describe("swapCallParameters", () => { "0x0000000000000000000000000000000000000004" ]); expect(result.value).toStrictEqual("0x80"); - const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; + const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; expect(deadlineDifference < 5).toBe(true); }); @@ -288,7 +288,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromNull() @@ -303,7 +303,7 @@ describe("swapCallParameters", () => { "0x0000000000000000000000000000000000000004" ]); expect(result.value).toStrictEqual("0x0"); - const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; + const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; expect(deadlineDifference < 5).toBe(true); }); @@ -325,7 +325,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromNull() @@ -340,7 +340,7 @@ describe("swapCallParameters", () => { "0x0000000000000000000000000000000000000004" ]); expect(result.value).toStrictEqual("0x0"); - const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; + const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; expect(deadlineDifference < 5).toBe(true); }); }); @@ -373,7 +373,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromValue(true) @@ -387,7 +387,7 @@ describe("swapCallParameters", () => { "0x0000000000000000000000000000000000000004" ]); expect(result.value).toStrictEqual("0x64"); - const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; + const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; expect(deadlineDifference < 5).toBe(true); }); @@ -413,7 +413,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromValue(true) @@ -428,7 +428,7 @@ describe("swapCallParameters", () => { "0x0000000000000000000000000000000000000004" ]); expect(result.value).toStrictEqual("0x0"); - const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; + const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; expect(deadlineDifference < 5).toBe(true); }); @@ -450,7 +450,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromValue(true) @@ -465,7 +465,7 @@ describe("swapCallParameters", () => { "0x0000000000000000000000000000000000000004" ]); expect(result.value).toStrictEqual("0x0"); - const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; + const deadlineDifference = (Date.now() / 1000) as number - parseInt(result.args[result.args.length - 1]) as number; expect(deadlineDifference < 5).toBe(true); }); }); @@ -494,7 +494,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromValue(true) @@ -530,7 +530,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromValue(true) @@ -558,7 +558,7 @@ describe("swapCallParameters", () => { tradeOptions: { ttl: Nullable.fromValue(50), recipient: "0x0000000000000000000000000000000000000004", - unixTimestamp: Date.now() / 1000, + unixTimestamp: Date.now() / 1000, allowedSlippage: "0.01", deadline: Nullable.fromNull(), feeOnTransfer: Nullable.fromValue(true) diff --git a/packages/apis/uniswapv2/src/__tests__/testUtils.ts b/packages/apis/uniswapv2/src/__tests__/testUtils.ts index 337af10dcb..6f7a1ee2ba 100644 --- a/packages/apis/uniswapv2/src/__tests__/testUtils.ts +++ b/packages/apis/uniswapv2/src/__tests__/testUtils.ts @@ -1,5 +1,5 @@ import { BestTradeOptions, ChainId, Pair, Token, TokenAmount, Trade } from "./e2e/types"; -import { UriRedirect, Web3ApiClient } from "@web3api/client-js"; +import { ClientConfig, coreInterfaceUris, Web3ApiClient } from "@web3api/client-js"; import { ethereumPlugin } from "@web3api/ethereum-plugin-js"; import { ipfsPlugin } from "@web3api/ipfs-plugin-js"; import { ensPlugin } from "@web3api/ens-plugin-js"; @@ -13,7 +13,7 @@ interface TestEnvironment { ipfs: string; ethereum: string; ensAddress: string; - redirects: UriRedirect[]; + clientConfig: ClientConfig; } export async function getEnsUri(): Promise { @@ -26,15 +26,25 @@ export async function getEnsUri(): Promise { export async function getProviders(): Promise { const { data: { ipfs, ethereum }, } = await axios.get("http://localhost:4040/providers"); const { data } = await axios.get("http://localhost:4040/deploy-ens"); - const redirects: UriRedirect[] = getRedirects(ethereum, ipfs, data.ensAddress); - return { ipfs, ethereum, ensAddress: data.ensAddress, redirects }; + const clientConfig: ClientConfig = getPlugins(ethereum, ipfs, data.ensAddress); + return { ipfs, ethereum, ensAddress: data.ensAddress, clientConfig, }; } -export function getRedirects(ethereum: string, ipfs: string, ensAddress: string): UriRedirect[] { - return [ - { - from: "ens/ethereum.web3api.eth", - to: ethereumPlugin({ +export function getPlugins(ethereum: string, ipfs: string, ensAddress: string): ClientConfig { + return { + redirects: [], + plugins: [ + { + uri: "w3://ens/ipfs.web3api.eth", + plugin: ipfsPlugin({ provider: ipfs }), + }, + { + uri: "w3://ens/ens.web3api.eth", + plugin: ensPlugin({ addresses: { testnet: ensAddress } }), + }, + { + uri: "w3://ens/ethereum.web3api.eth", + plugin: ethereumPlugin({ networks: { testnet: { provider: ethereum @@ -44,17 +54,23 @@ export function getRedirects(ethereum: string, ipfs: string, ensAddress: string) }, }, defaultNetwork: "testnet" - }) + }), }, + ], + interfaces: [ { - from: "w3://ens/ipfs.web3api.eth", - to: ipfsPlugin({ provider: ipfs }), + interface: coreInterfaceUris.uriResolver.uri, + implementations: [ + "w3://ens/ipfs.web3api.eth", + "w3://ens/ens.web3api.eth", + ], }, { - from: "w3://ens/ens.web3api.eth", - to: ensPlugin({ addresses: { testnet: ensAddress } }), - } - ]; + interface: coreInterfaceUris.logger.uri, + implementations: ["w3://ens/js-logger.web3api.eth"], + }, + ], + }; } export async function getTokenList(): Promise { diff --git a/packages/js/client/package.json b/packages/js/client/package.json index f0dd9c7c17..4243060086 100644 --- a/packages/js/client/package.json +++ b/packages/js/client/package.json @@ -15,9 +15,9 @@ "build": "rimraf ./build && tsc --project tsconfig.build.json", "prebuild": "ts-node ./scripts/extractPluginConfigs.ts", "lint": "eslint --color --ext .ts src/", - "test": "jest --passWithNoTests --runInBand --verbose=true", - "test:ci": "jest --passWithNoTests --runInBand --verbose", - "test:watch": "jest --watch --passWithNoTests --verbose" + "test": "jest --passWithNoTests --runInBand --verbose=true --detectOpenHandles", + "test:ci": "jest --passWithNoTests --runInBand --verbose --detectOpenHandles", + "test:watch": "jest --watch --passWithNoTests --verbose --detectOpenHandles" }, "dependencies": { "@msgpack/msgpack": "2.3.0", diff --git a/packages/js/client/src/Web3ApiClient.ts b/packages/js/client/src/Web3ApiClient.ts index cd268d1833..e1a14bd465 100644 --- a/packages/js/client/src/Web3ApiClient.ts +++ b/packages/js/client/src/Web3ApiClient.ts @@ -16,6 +16,8 @@ import { InterfaceImplementations, PluginRegistration, Web3ApiManifest, + Subscription, + SubscribeOptions, parseQuery, resolveUri, AnyManifest, @@ -43,7 +45,7 @@ export interface ClientConfig { export class Web3ApiClient implements Client { // TODO: the API cache needs to be more like a routing table. // It should help us keep track of what URI's map to what APIs, - // and handle cases where the are multiple jumps. For exmaple, if + // and handle cases where the are multiple jumps. For example, if // A => B => C, then the cache should have A => C, and B => C. private _apiCache: ApiCache = new Map(); private _config: Required> = { @@ -129,16 +131,10 @@ export class Web3ApiClient implements Client { >( options: QueryApiOptions ): Promise> { - let typedOptions: QueryApiOptions; - - if (typeof options.uri === "string") { - typedOptions = { - ...options, - uri: new Uri(options.uri), - }; - } else { - typedOptions = options as QueryApiOptions; - } + const typedOptions: QueryApiOptions = { + ...options, + uri: this._toUri(options.uri), + }; const run = Tracer.traceFunc( "Web3ApiClient: query", @@ -196,8 +192,8 @@ export class Web3ApiClient implements Client { } ); - return await run(typedOptions).catch((error) => { - if (error.length) { + return await run(typedOptions).catch((error: Error | Error[]) => { + if (Array.isArray(error)) { return { errors: error }; } else { return { errors: [error] }; @@ -229,6 +225,96 @@ export class Web3ApiClient implements Client { return run(typedOptions); } + public subscribe< + TData extends Record = Record, + TVariables extends Record = Record, + TUri extends Uri | string = string + >(options: SubscribeOptions): Subscription { + const typedOptions: SubscribeOptions = { + ...options, + uri: this._toUri(options.uri), + }; + + const run = Tracer.traceFunc( + "Web3ApiClient: subscribe", + (options: SubscribeOptions): Subscription => { + const { uri, query, variables, frequency: freq } = options; + // eslint-disable-next-line @typescript-eslint/no-this-alias + const client: Web3ApiClient = this; + + // calculate interval between queries, in milliseconds, 1 min default value + /* eslint-disable prettier/prettier */ + let frequency: number; + if (freq && (freq.ms || freq.sec || freq.min || freq.hours)) { + frequency = (freq.ms ?? 0) + (( + (freq.hours ?? 0) * 3600 + + (freq.min ?? 0) * 60 + + (freq.sec ?? 0) + ) * 1000); + } else { + frequency = 60000; + } + /* eslint-enable prettier/prettier */ + + const subscription: Subscription = { + frequency: frequency, + isActive: false, + stop(): void { + subscription.isActive = false; + }, + async *[Symbol.asyncIterator](): AsyncGenerator< + QueryApiResult + > { + let timeout: NodeJS.Timeout | undefined = undefined; + subscription.isActive = true; + + try { + let readyVals = 0; + let sleep: ((value?: unknown) => void) | undefined; + + timeout = setInterval(() => { + readyVals++; + if (sleep) { + sleep(); + sleep = undefined; + } + }, frequency); + + while (subscription.isActive) { + if (readyVals === 0) { + await new Promise((r) => (sleep = r)); + } + + for (; readyVals > 0; readyVals--) { + if (!subscription.isActive) { + break; + } + + const result: QueryApiResult = await client.query({ + uri: uri, + query: query, + variables: variables, + }); + + yield result; + } + } + } finally { + if (timeout) { + clearInterval(timeout); + } + subscription.isActive = false; + } + }, + }; + + return subscription; + } + ); + + return run(typedOptions); + } + public async getSchema( uri: TUri ): Promise { @@ -271,7 +357,7 @@ export class Web3ApiClient implements Client { this._toUri(uri), this.interfaces(), applyRedirects ? this.redirects() : undefined - ).map((x) => x.uri) as TUri[]) + ).map((x: Uri) => x.uri) as TUri[]) : (getImplementations( this._toUri(uri), this.interfaces(), diff --git a/packages/js/client/src/__tests__/Web3ApiClient.spec.ts b/packages/js/client/src/__tests__/Web3ApiClient.spec.ts index 20bd803175..f180ce8bac 100644 --- a/packages/js/client/src/__tests__/Web3ApiClient.spec.ts +++ b/packages/js/client/src/__tests__/Web3ApiClient.spec.ts @@ -13,6 +13,7 @@ import { getDefaultClientConfig } from "../default-client-config"; import { Uri, Plugin, + Subscription, Web3ApiManifest, BuildManifest, MetaManifest, @@ -1823,6 +1824,7 @@ scalar Int16 scalar Int32 scalar Bytes scalar BigInt +scalar JSON directive @imported( uri: String! @@ -1920,5 +1922,201 @@ enum Logger_LogLevel @imported( path: "./index.js", })).rejects.toThrow("client.getFile(...) is not implemented for Plugins."); }); + + it("simple-storage: subscribe", async () => { + const api = await buildAndDeployApi( + `${GetPathToTestApis()}/simple-storage`, + ipfsProvider, + ensAddress + ); + const client = await getClient(); + const ensUri = `ens/testnet/${api.ensDomain}`; + const ipfsUri = `ipfs/${api.ipfsCid}`; + + const deploy = await client.query<{ + deployContract: string; + }>({ + uri: ensUri, + query: ` + mutation { + deployContract( + connection: { + networkNameOrChainId: "testnet" + } + ) + } + `, + }); + + expect(deploy.errors).toBeFalsy(); + expect(deploy.data).toBeTruthy(); + expect(deploy.data?.deployContract.indexOf("0x")).toBeGreaterThan(-1); + + const address = deploy.data?.deployContract; + + // test subscription + let results: number[] = []; + let value = 0; + + const setter = setInterval(async() => { + await client.query<{ + setData: string; + }>({ + uri: ipfsUri, + query: ` + mutation { + setData( + address: $address + value: $value + connection: { + networkNameOrChainId: "testnet" + } + ) + } + `, + variables: { + address: address, + value: value++, + }, + }); + }, 4000); + + const getSubscription: Subscription<{ + getData: number; + }> = client.subscribe<{ + getData: number; + }>({ + uri: ensUri, + query: ` + query { + getData( + address: $address + connection: { + networkNameOrChainId: "testnet" + } + ) + } + `, + variables: { + address + }, + frequency: { ms: 4500 } + }); + + for await (let query of getSubscription) { + expect(query.errors).toBeFalsy(); + const val = query.data?.getData; + if (val !== undefined) { + results.push(val); + if (val >= 2) { + break; + } + } + } + clearInterval(setter); + + expect(results).toStrictEqual([0, 1, 2]); + }); + + it("simple-storage: subscription early stop", async () => { + const api = await buildAndDeployApi( + `${GetPathToTestApis()}/simple-storage`, + ipfsProvider, + ensAddress + ); + const client = await getClient(); + const ensUri = `ens/testnet/${api.ensDomain}`; + const ipfsUri = `ipfs/${api.ipfsCid}`; + + const deploy = await client.query<{ + deployContract: string; + }>({ + uri: ensUri, + query: ` + mutation { + deployContract( + connection: { + networkNameOrChainId: "testnet" + } + ) + } + `, + }); + + expect(deploy.errors).toBeFalsy(); + expect(deploy.data).toBeTruthy(); + expect(deploy.data?.deployContract.indexOf("0x")).toBeGreaterThan(-1); + + const address = deploy.data?.deployContract; + + // test subscription + let results: number[] = []; + let value = 0; + + const setter = setInterval(async() => { + await client.query<{ + setData: string; + }>({ + uri: ipfsUri, + query: ` + mutation { + setData( + address: $address + value: $value + connection: { + networkNameOrChainId: "testnet" + } + ) + } + `, + variables: { + address: address, + value: value++, + }, + }); + }, 4000); + + const getSubscription: Subscription<{ + getData: number; + }> = client.subscribe<{ + getData: number; + }>({ + uri: ensUri, + query: ` + query { + getData( + address: $address + connection: { + networkNameOrChainId: "testnet" + } + ) + } + `, + variables: { + address + }, + frequency: { ms: 4500 } + }); + + new Promise(async () => { + for await (let query of getSubscription) { + expect(query.errors).toBeFalsy(); + const val = query.data?.getData; + if (val !== undefined) { + results.push(val); + if (val >= 2) { + break; + } + } + } + } + ); + await new Promise(r => setTimeout(r, 8000)); + getSubscription.stop(); + clearInterval(setter); + + expect(results).toContain(0); + expect(results).not.toContain(2); + }); }); diff --git a/packages/js/client/src/plugin/PluginWeb3Api.ts b/packages/js/client/src/plugin/PluginWeb3Api.ts index dbf4fd9a79..05ea54374e 100644 --- a/packages/js/client/src/plugin/PluginWeb3Api.ts +++ b/packages/js/client/src/plugin/PluginWeb3Api.ts @@ -31,13 +31,13 @@ export class PluginWeb3Api extends Api { } public async invoke( - options: InvokeApiOptions, + options: InvokeApiOptions, client: Client ): Promise> { const run = Tracer.traceFunc( "PluginWeb3Api: invoke", async ( - options: InvokeApiOptions, + options: InvokeApiOptions, client: Client ): Promise> => { const { module, method, input, resultFilter } = options; diff --git a/packages/js/client/src/wasm/WasmWeb3Api.ts b/packages/js/client/src/wasm/WasmWeb3Api.ts index 0494cb4e80..6f0c6a566b 100644 --- a/packages/js/client/src/wasm/WasmWeb3Api.ts +++ b/packages/js/client/src/wasm/WasmWeb3Api.ts @@ -70,13 +70,13 @@ export class WasmWeb3Api extends Api { } public async invoke( - options: InvokeApiOptions, + options: InvokeApiOptions, client: Client ): Promise> { const run = Tracer.traceFunc( "WasmWeb3Api: invoke", async ( - options: InvokeApiOptions, + options: InvokeApiOptions, client: Client ): Promise> => { const { module: invokableModule, method, input, decode } = options; diff --git a/packages/js/core/src/algorithms/parse-query.ts b/packages/js/core/src/algorithms/parse-query.ts index f6dd20d8a1..7b3f0fc5a9 100644 --- a/packages/js/core/src/algorithms/parse-query.ts +++ b/packages/js/core/src/algorithms/parse-query.ts @@ -9,12 +9,12 @@ export const parseQuery = Tracer.traceFunc( uri: Uri, doc: QueryDocument, variables?: Record - ): QueryApiInvocations => { + ): QueryApiInvocations => { if (doc.definitions.length === 0) { throw Error("Empty query document found."); } - const queryInvocations: QueryApiInvocations = {}; + const queryInvocations: QueryApiInvocations = {}; for (const def of doc.definitions) { if (def.kind !== "OperationDefinition") { diff --git a/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.1_to_0.0.1-prealpha.5.ts b/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.1_to_0.0.1-prealpha.5.ts index c946c5800b..910340d870 100644 --- a/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.1_to_0.0.1-prealpha.5.ts +++ b/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.1_to_0.0.1-prealpha.5.ts @@ -4,6 +4,7 @@ import { Web3ApiManifest as OldManifest } from "../0.0.1-prealpha.1"; import { Web3ApiManifest as NewManifest } from "../0.0.1-prealpha.5"; export function migrate(old: OldManifest): NewManifest { + delete old.repository; const module = old.mutation || old.query; if (!module) { diff --git a/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.2_to_0.0.1-prealpha.5.ts b/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.2_to_0.0.1-prealpha.5.ts index a9025251c8..95c59c4516 100644 --- a/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.2_to_0.0.1-prealpha.5.ts +++ b/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.2_to_0.0.1-prealpha.5.ts @@ -4,6 +4,7 @@ import { Web3ApiManifest as OldManifest } from "../0.0.1-prealpha.2"; import { Web3ApiManifest as NewManifest } from "../0.0.1-prealpha.5"; export function migrate(old: OldManifest): NewManifest { + delete old.repository; return { ...old, __type: "Web3ApiManifest", diff --git a/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.3_to_0.0.1-prealpha.5.ts b/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.3_to_0.0.1-prealpha.5.ts index 863a08926e..3cf5f2e411 100644 --- a/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.3_to_0.0.1-prealpha.5.ts +++ b/packages/js/core/src/manifest/formats/web3api/migrators/0.0.1-prealpha.3_to_0.0.1-prealpha.5.ts @@ -4,6 +4,7 @@ import { Web3ApiManifest as OldManifest } from "../0.0.1-prealpha.3"; import { Web3ApiManifest as NewManifest } from "../0.0.1-prealpha.5"; export function migrate(old: OldManifest): NewManifest { + delete old.repository; let language = old.language; if (!language) { diff --git a/packages/js/core/src/types/Api.ts b/packages/js/core/src/types/Api.ts index 85e8919b30..2aef1bc52c 100644 --- a/packages/js/core/src/types/Api.ts +++ b/packages/js/core/src/types/Api.ts @@ -1,4 +1,5 @@ import { + Uri, Client, GetFileOptions, GetManifestOptions, @@ -22,7 +23,7 @@ export abstract class Api { * This client will be used for any sub-queries that occur. */ public abstract async invoke( - options: InvokeApiOptions, + options: InvokeApiOptions, client: Client ): Promise>; diff --git a/packages/js/core/src/types/Client.ts b/packages/js/core/src/types/Client.ts index ed90879f8a..865e3ebc2c 100644 --- a/packages/js/core/src/types/Client.ts +++ b/packages/js/core/src/types/Client.ts @@ -1,4 +1,4 @@ -import { QueryHandler, InvokeHandler, Uri } from "./"; +import { QueryHandler, InvokeHandler, SubscriptionHandler, Uri } from "./"; import { ManifestType, AnyManifest } from "../manifest"; export interface GetManifestOptions { @@ -14,7 +14,10 @@ export interface GetImplementationsOptions { applyRedirects?: boolean; } -export interface Client extends QueryHandler, InvokeHandler { +export interface Client + extends QueryHandler, + SubscriptionHandler, + InvokeHandler { getSchema(uri: TUri): Promise; getManifest( diff --git a/packages/js/core/src/types/Invoke.ts b/packages/js/core/src/types/Invoke.ts index 54f055b851..811715fcf8 100644 --- a/packages/js/core/src/types/Invoke.ts +++ b/packages/js/core/src/types/Invoke.ts @@ -3,7 +3,7 @@ import { Uri } from "."; export type InvokableModules = "query" | "mutation"; /** Options required for an API invocation. */ -export interface InvokeApiOptions { +export interface InvokeApiOptions { /** The API's URI */ uri: TUri; diff --git a/packages/js/core/src/types/Query.ts b/packages/js/core/src/types/Query.ts index 8a6d969d2f..e6f8af8042 100644 --- a/packages/js/core/src/types/Query.ts +++ b/packages/js/core/src/types/Query.ts @@ -60,8 +60,8 @@ export interface QueryApiResult< errors?: Error[]; } -export interface QueryApiInvocations { - [methodOrAlias: string]: InvokeApiOptions; +export interface QueryApiInvocations { + [methodOrAlias: string]: InvokeApiOptions; } /** A type that can parse & execute a given query */ diff --git a/packages/js/core/src/types/Subscription.ts b/packages/js/core/src/types/Subscription.ts new file mode 100644 index 0000000000..fd4857abac --- /dev/null +++ b/packages/js/core/src/types/Subscription.ts @@ -0,0 +1,59 @@ +import { Uri } from "./Uri"; +import { QueryApiOptions, QueryApiResult } from "./Query"; + +/** Defines the frequency of API invocations for an API subscription */ +export interface SubscriptionFrequency { + ms?: number; + sec?: number; + min?: number; + hours?: number; +} + +/** Options required for an API subscription. */ +export interface SubscribeOptions< + TVariables extends Record = Record, + TUri extends Uri | string = string +> extends QueryApiOptions { + /** + * The frequency of API invocations. Defaults to one query per minute. + */ + frequency?: SubscriptionFrequency; +} + +/** + * An API subscription, which implements the AsyncIterator protocol, is an + * AsyncIterable that yields query results at a specified frequency. + * @template TData Type of the query result. + */ +export interface Subscription< + TData extends Record = Record +> { + /** + * The frequency of API invocations. + */ + frequency: number; + /** + * Indicates whether the subscription is currently active. + */ + isActive: boolean; + /** + * Stops subscription. If a query has been called but has not yet returned, + * that query will be completed and its result will be yielded. + */ + stop(): void; + /** + * Implementation of AsyncIterator protocol makes the Subscription an + * AsyncIterable, allowing use in for await...of loops. + */ + [Symbol.asyncIterator](): AsyncGenerator>; +} + +export interface SubscriptionHandler { + subscribe< + TData extends Record = Record, + TVariables extends Record = Record, + TUri extends Uri | string = string + >( + options: SubscribeOptions + ): Subscription; +} diff --git a/packages/js/core/src/types/index.ts b/packages/js/core/src/types/index.ts index 9f5fec5dac..2e544d323b 100644 --- a/packages/js/core/src/types/index.ts +++ b/packages/js/core/src/types/index.ts @@ -1,9 +1,10 @@ export * from "./Api"; export * from "./Client"; +export * from "./Invoke"; export * from "./MaybeAsync"; export * from "./Plugin"; export * from "./Query"; -export * from "./Invoke"; +export * from "./Subscription"; export * from "./Uri"; export * from "./UriRedirect"; export * from "./InterfaceImplementations"; diff --git a/packages/js/tracing/package.json b/packages/js/tracing/package.json index 98165132a0..d10f107d37 100644 --- a/packages/js/tracing/package.json +++ b/packages/js/tracing/package.json @@ -29,7 +29,7 @@ "@opentelemetry/propagator-b3": "0.20.0", "@opentelemetry/tracing": "0.20.0", "@opentelemetry/web": "0.20.0", - "util-inspect": "0.1.8" + "browser-util-inspect": "0.2.0" }, "devDependencies": { "@types/jest": "26.0.8", diff --git a/packages/js/tracing/src/declarations.d.ts b/packages/js/tracing/src/declarations.d.ts new file mode 100644 index 0000000000..337dc43f99 --- /dev/null +++ b/packages/js/tracing/src/declarations.d.ts @@ -0,0 +1,4 @@ +declare module "browser-util-inspect" { + function inspect(obj: unknown): unknown; + export = inspect; +} diff --git a/packages/js/tracing/src/index.ts b/packages/js/tracing/src/index.ts index bd6b3cbe30..875e8d2f38 100644 --- a/packages/js/tracing/src/index.ts +++ b/packages/js/tracing/src/index.ts @@ -6,9 +6,7 @@ import { import { ZipkinExporter } from "@opentelemetry/exporter-zipkin"; import { WebTracerProvider } from "@opentelemetry/web"; import * as api from "@opentelemetry/api"; - -// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports -const inspect = require("util-inspect"); +import inspect from "browser-util-inspect"; type MaybeAsync = Promise | T; diff --git a/packages/js/tracing/tsconfig.json b/packages/js/tracing/tsconfig.json index 5d37204c00..fd4ebb1a2c 100644 --- a/packages/js/tracing/tsconfig.json +++ b/packages/js/tracing/tsconfig.json @@ -1,7 +1,10 @@ { "extends": "../../../tsconfig", "compilerOptions": { - "outDir": "build" + "outDir": "build", + "typeRoots": [ + "./src/**/*.d.ts" + ] }, "include": [ "./src/**/*.ts" diff --git a/yarn.lock b/yarn.lock index 93dbc76aec..e60ad09b66 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4927,16 +4927,6 @@ array-includes@^3.0.3, array-includes@^3.1.1: get-intrinsic "^1.1.1" is-string "^1.0.5" -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, array-union@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -6121,6 +6111,11 @@ browser-resolve@^1.11.3: dependencies: resolve "1.1.7" +browser-util-inspect@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browser-util-inspect/-/browser-util-inspect-0.2.0.tgz#cdda8ce1a4a07a4386035168a228c8777bff459c" + integrity sha1-zdqM4aSgekOGA1FooijId3v/RZw= + browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" @@ -9720,11 +9715,6 @@ for-own@^0.1.3: dependencies: for-in "^1.0.1" -foreach@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.4.tgz#cc5d0d8ae1d46cc9a555c2682f910977859935df" - integrity sha1-zF0NiuHUbMmlVcJoL5EJd4WZNd8= - foreach@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" @@ -10941,11 +10931,6 @@ indexes-of@^1.0.1: resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= - infer-owner@^1.0.3, infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -13403,11 +13388,6 @@ json-text-sequence@~0.1.0: dependencies: delimit-stream "0.1.0" -json3@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.0.tgz#0e9e7f6c5d270b758929af4d6fefdc84bd66e259" - integrity sha1-Dp5/bF0nC3WJKa9Nb+/chL1m4lk= - json3@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" @@ -15254,11 +15234,6 @@ object-is@^1.0.1: call-bind "^1.0.2" define-properties "^1.1.3" -object-keys@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.5.0.tgz#09e211f3e00318afc4f592e36e7cdc10d9ad7293" - integrity sha1-CeIR8+ADGK/E9ZLjbnzcENmtcpM= - object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -19999,19 +19974,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util-inspect@0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/util-inspect/-/util-inspect-0.1.8.tgz#2b39dbcd2d921f2d8430923caff40f4b5cea5db1" - integrity sha1-KznbzS2SHy2EMJI8r/QPS1zqXbE= - dependencies: - array-map "0.0.0" - array-reduce "0.0.0" - foreach "2.0.4" - indexof "0.0.1" - isarray "0.0.1" - json3 "3.3.0" - object-keys "0.5.0" - util-promisify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53"