diff --git a/.changeset/serious-laws-judge.md b/.changeset/serious-laws-judge.md new file mode 100644 index 00000000000..23babd84b11 --- /dev/null +++ b/.changeset/serious-laws-judge.md @@ -0,0 +1,6 @@ +--- +"@fuel-ts/providers": minor +"@fuel-ts/errors": patch +--- + +Implemented GraphQL subscriptions diff --git a/.eslintrc.js b/.eslintrc.js index 370c57d37cf..6e46310f855 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,6 +21,12 @@ module.exports = { }, }, rules: { + 'no-restricted-syntax': [ + 'off', + { + selector: 'ForOfStatement', + }, + ], '@typescript-eslint/no-non-null-assertion': 1, // Disable error on devDependencies importing since this isn't a TS library 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], @@ -56,7 +62,6 @@ module.exports = { 'warn', { argsIgnorePattern: '^_', - varsIgnorePattern: '^_', }, ], '@typescript-eslint/no-explicit-any': 'error', diff --git a/.github/actions/ci-setup/action.yaml b/.github/actions/ci-setup/action.yaml index d7238b669aa..21a82c1362b 100644 --- a/.github/actions/ci-setup/action.yaml +++ b/.github/actions/ci-setup/action.yaml @@ -2,7 +2,7 @@ name: "CI Setup" inputs: node-version: description: "Node version" - default: 18.14.1 + default: 18.18.2 pnpm-version: description: "PNPM version" default: 8.9.0 diff --git a/.github/actions/test-setup/action.yaml b/.github/actions/test-setup/action.yaml index b37f3e33084..225f040e5e2 100644 --- a/.github/actions/test-setup/action.yaml +++ b/.github/actions/test-setup/action.yaml @@ -2,7 +2,7 @@ name: "Test Setup" inputs: node-version: description: "Node version" - default: 18.14.1 + default: 18.18.2 pnpm-version: description: "PNPM version" default: 8.9.0 @@ -31,4 +31,4 @@ runs: - name: Build run: pnpm build - shell: bash \ No newline at end of file + shell: bash diff --git a/.nvmrc b/.nvmrc index 617bcf916bf..87ec8842b15 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.14.1 +18.18.2 diff --git a/package.json b/package.json index 8befa5f1582..cace51ca569 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "author": "Fuel Labs (https://fuel.network/)", "private": true, "engines": { - "node": "^18.14.1", + "node": "^18.18.2", "pnpm": "^8.9.0" }, "packageManager": "pnpm@8.9.0", @@ -91,10 +91,5 @@ "tsx": "^3.12.7", "turbo": "^1.8.8", "typescript": "~5.2.2" - }, - "pnpm": { - "overrides": { - "cross-fetch": "4.0.0" - } } } diff --git a/packages/abi-coder/package.json b/packages/abi-coder/package.json index 626ff838602..a547a3e6e7c 100644 --- a/packages/abi-coder/package.json +++ b/packages/abi-coder/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/abi-typegen/package.json b/packages/abi-typegen/package.json index e462ab76d9f..6f92c568928 100644 --- a/packages/abi-typegen/package.json +++ b/packages/abi-typegen/package.json @@ -10,7 +10,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/address/package.json b/packages/address/package.json index 1b282a5ce99..b67ecfca20b 100644 --- a/packages/address/package.json +++ b/packages/address/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/contract/package.json b/packages/contract/package.json index e2e57f63530..8cc3f04939b 100644 --- a/packages/contract/package.json +++ b/packages/contract/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/crypto/package.json b/packages/crypto/package.json index 1314a683cae..21c9ef2e135 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "browser": { "./dist/index.mjs": "./dist/index.browser.mjs" diff --git a/packages/errors/package.json b/packages/errors/package.json index 81910f1096e..0416827c788 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/errors/src/error-codes.ts b/packages/errors/src/error-codes.ts index 244c1d463f3..e02c67180eb 100644 --- a/packages/errors/src/error-codes.ts +++ b/packages/errors/src/error-codes.ts @@ -49,6 +49,7 @@ export enum ErrorCode { CONVERTING_FAILED = 'converting-error', ELEMENT_NOT_FOUND = 'element-not-found', MISSING_REQUIRED_PARAMETER = 'missing-required-parameter', + INVALID_REQUEST = 'invalid-request', UNEXPECTED_HEX_VALUE = 'unexpected-hex-value', // transaction diff --git a/packages/fuel-gauge/src/transaction-response.test.ts b/packages/fuel-gauge/src/transaction-response.test.ts index e205ba6d1c7..edc795bc1fa 100644 --- a/packages/fuel-gauge/src/transaction-response.test.ts +++ b/packages/fuel-gauge/src/transaction-response.test.ts @@ -1,6 +1,14 @@ -import { generateTestWallet } from '@fuel-ts/wallet/test-utils'; -import type { BN, WalletUnlocked } from 'fuels'; -import { BaseAssetId, FUEL_NETWORK_URL, Provider, TransactionResponse, Wallet } from 'fuels'; +import { generateTestWallet, launchNode } from '@fuel-ts/wallet/test-utils'; +import type { BN } from 'fuels'; +import { + BaseAssetId, + FUEL_NETWORK_URL, + Provider, + TransactionResponse, + Wallet, + randomBytes, + WalletUnlocked, +} from 'fuels'; describe('TransactionSummary', () => { let provider: Provider; @@ -75,25 +83,33 @@ describe('TransactionSummary', () => { }); it('should ensure waitForResult always waits for the transaction to be processed', async () => { - const destination = Wallet.generate({ - provider, + const { cleanup, ip, port } = await launchNode({ + args: ['--poa-interval-period', '750ms'], }); + const nodeProvider = await Provider.create(`http://${ip}:${port}/graphql`); - const { id: transactionId } = await adminWallet.transfer( + const genesisWallet = new WalletUnlocked( + process.env.GENESIS_SECRET || randomBytes(32), + nodeProvider + ); + + const destination = Wallet.generate({ provider: nodeProvider }); + + const { id: transactionId } = await genesisWallet.transfer( destination.address, 100, BaseAssetId, { gasPrice, gasLimit: 10_000 } ); + const response = await TransactionResponse.create(transactionId, nodeProvider); - const response = new TransactionResponse(transactionId, provider); - - expect(response.gqlTransaction).toBeUndefined(); + expect(response.gqlTransaction?.status?.type).toBe('SubmittedStatus'); await response.waitForResult(); - expect(response.gqlTransaction?.status?.type).toBeDefined(); - expect(response.gqlTransaction?.status?.type).not.toEqual('SubmittedStatus'); + expect(response.gqlTransaction?.status?.type).toEqual('SuccessStatus'); expect(response.gqlTransaction?.id).toBe(transactionId); + + cleanup(); }); }); diff --git a/packages/fuels/package.json b/packages/fuels/package.json index 238c6190621..042b35ed1cc 100644 --- a/packages/fuels/package.json +++ b/packages/fuels/package.json @@ -10,7 +10,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/hasher/package.json b/packages/hasher/package.json index a0b3ff5083e..d1a56341248 100644 --- a/packages/hasher/package.json +++ b/packages/hasher/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/hdwallet/package.json b/packages/hdwallet/package.json index 51933ba4693..5587c38c3a9 100644 --- a/packages/hdwallet/package.json +++ b/packages/hdwallet/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/interfaces/package.json b/packages/interfaces/package.json index e3abd030952..74bbfeeed8f 100644 --- a/packages/interfaces/package.json +++ b/packages/interfaces/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/math/package.json b/packages/math/package.json index 550275b17af..11401804b34 100644 --- a/packages/math/package.json +++ b/packages/math/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/merkle/package.json b/packages/merkle/package.json index 587b7743378..9ee4e9a46d2 100644 --- a/packages/merkle/package.json +++ b/packages/merkle/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/mnemonic/package.json b/packages/mnemonic/package.json index 0bf3e0015ba..b98fd1bd9fb 100644 --- a/packages/mnemonic/package.json +++ b/packages/mnemonic/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/predicate/package.json b/packages/predicate/package.json index a45badd0f87..98684a0d215 100644 --- a/packages/predicate/package.json +++ b/packages/predicate/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/program/package.json b/packages/program/package.json index 8952ef55787..53de16be776 100644 --- a/packages/program/package.json +++ b/packages/program/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/providers/codegen.json b/packages/providers/codegen.json index 0f48ea80586..d21c27157c3 100644 --- a/packages/providers/codegen.json +++ b/packages/providers/codegen.json @@ -7,7 +7,7 @@ "plugins": [ { "typescript": {} }, { "typescript-operations": {} }, - { "typescript-graphql-request": {} } + { "typescript-generic-sdk": {} } ], "config": { "scalars": { diff --git a/packages/providers/package.json b/packages/providers/package.json index e1b0359e227..cd2ba227e1b 100644 --- a/packages/providers/package.json +++ b/packages/providers/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { @@ -48,9 +48,9 @@ "@fuel-ts/utils": "workspace:*", "@graphql-codegen/cli": "^2.13.7", "@graphql-codegen/typescript": "^2.8.0", - "@graphql-codegen/typescript-graphql-request": "^4.5.7", "@graphql-codegen/typescript-operations": "^2.5.5", - "@types/ramda": "^0.29.3", - "get-graphql-schema": "^2.1.2" + "@graphql-codegen/typescript-generic-sdk": "^3.1.0", + "get-graphql-schema": "^2.1.2", + "@types/ramda": "^0.29.3" } } diff --git a/packages/providers/src/fuel-graphql-subscriber.ts b/packages/providers/src/fuel-graphql-subscriber.ts new file mode 100644 index 00000000000..fb1f80f9a18 --- /dev/null +++ b/packages/providers/src/fuel-graphql-subscriber.ts @@ -0,0 +1,81 @@ +import { FuelError } from '@fuel-ts/errors'; +import type { DocumentNode } from 'graphql'; +import { print } from 'graphql'; + +type FuelGraphQLSubscriberOptions = { + url: string; + query: DocumentNode; + variables?: Record; + fetchFn: typeof fetch; + abortController?: AbortController; +}; + +class FuelSubscriptionStream implements TransformStream { + readable: ReadableStream>; + writable: WritableStream; + private readableStreamController!: ReadableStreamController>; + private static textDecoder = new TextDecoder(); + + constructor() { + this.readable = new ReadableStream({ + start: (controller) => { + this.readableStreamController = controller; + }, + }); + + this.writable = new WritableStream({ + write: (bytes) => { + const text = FuelSubscriptionStream.textDecoder.decode(bytes); + // the fuel node sends keep-alive messages that should be ignored + if (text.startsWith('data:')) { + const { data, errors } = JSON.parse(text.split('data:')[1]); + if (Array.isArray(errors)) { + this.readableStreamController.enqueue( + new FuelError( + FuelError.CODES.INVALID_REQUEST, + errors.map((err) => err.message).join('\n\n') + ) + ); + } else { + this.readableStreamController.enqueue(data); + } + } + }, + }); + } +} + +export async function* fuelGraphQLSubscriber({ + url, + variables, + query, + fetchFn, +}: FuelGraphQLSubscriberOptions) { + const response = await fetchFn(`${url}-sub`, { + method: 'POST', + body: JSON.stringify({ + query: print(query), + variables, + }), + headers: { + 'Content-Type': 'application/json', + Accept: 'text/event-stream', + }, + }); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const subscriptionStreamReader = response + .body!.pipeThrough(new FuelSubscriptionStream()) + .getReader(); + + while (true) { + const { value, done } = await subscriptionStreamReader.read(); + if (value instanceof FuelError) { + throw value; + } + yield value; + if (done) { + break; + } + } +} diff --git a/packages/providers/src/operations.graphql b/packages/providers/src/operations.graphql index f154d7fb844..a17615a0684 100644 --- a/packages/providers/src/operations.graphql +++ b/packages/providers/src/operations.graphql @@ -4,6 +4,31 @@ # generate `operations.ts` from this file. # Fragments + +fragment transactionStatusFragment on TransactionStatus { + type: __typename + ... on SubmittedStatus { + time + } + ... on SuccessStatus { + block { + id + } + time + programState { + returnType + data + } + } + ... on FailureStatus { + block { + id + } + time + reason + } +} + fragment transactionFragment on Transaction { id rawPayload @@ -12,27 +37,7 @@ fragment transactionFragment on Transaction { ...receiptFragment } status { - type: __typename - ... on SubmittedStatus { - time - } - ... on SuccessStatus { - block { - id - } - time - programState { - returnType - data - } - } - ... on FailureStatus { - block { - id - } - time - reason - } + ...transactionStatusFragment } } @@ -675,3 +680,15 @@ mutation produceBlocks( startTimestamp: $startTimestamp ) } + +subscription submitAndAwait($encodedTransaction: HexString!) { + submitAndAwait(tx: $encodedTransaction) { + ...transactionStatusFragment + } +} + +subscription statusChange($transactionId: TransactionId!) { + statusChange(id: $transactionId) { + ...transactionStatusFragment + } +} diff --git a/packages/providers/src/provider.ts b/packages/providers/src/provider.ts index eb0ac793da7..5057588ef55 100644 --- a/packages/providers/src/provider.ts +++ b/packages/providers/src/provider.ts @@ -13,6 +13,7 @@ import { import { checkFuelCoreVersionCompatibility } from '@fuel-ts/versions'; import type { BytesLike } from 'ethers'; import { getBytesCopy, hexlify, Network } from 'ethers'; +import type { DocumentNode } from 'graphql'; import { GraphQLClient } from 'graphql-request'; import { clone } from 'ramda'; @@ -26,6 +27,7 @@ import type { import type { Coin } from './coin'; import type { CoinQuantity, CoinQuantityLike } from './coin-quantity'; import { coinQuantityfy } from './coin-quantity'; +import { fuelGraphQLSubscriber } from './fuel-graphql-subscriber'; import { MemoryCache } from './memory-cache'; import type { Message, MessageCoin, MessageProof, MessageStatus } from './message'; import type { ExcludeResourcesOption, Resource } from './resource'; @@ -209,7 +211,12 @@ export type FetchRequestOptions = { * Provider initialization options */ export type ProviderOptions = { - fetch?: (url: string, options: FetchRequestOptions) => Promise; + fetch?: ( + url: string, + options: FetchRequestOptions, + providerOptions: Omit + ) => Promise; + timeout?: number; cacheUtxo?: number; }; @@ -268,6 +275,23 @@ export default class Provider { private static chainInfoCache: ChainInfoCache = {}; private static nodeInfoCache: NodeInfoCache = {}; + options: ProviderOptions = { + timeout: undefined, + cacheUtxo: undefined, + fetch: undefined, + }; + + private static getFetchFn(options: ProviderOptions) { + return options.fetch !== undefined + ? options.fetch + : (url: string, request: FetchRequestOptions) => + fetch(url, { + ...request, + signal: + options.timeout !== undefined ? AbortSignal.timeout(options.timeout) : undefined, + }); + } + /** * Constructor to initialize a Provider. * @@ -279,9 +303,12 @@ export default class Provider { protected constructor( /** GraphQL endpoint of the Fuel node */ public url: string, - public options: ProviderOptions = {} + options: ProviderOptions = {} ) { - this.operations = this.createOperations(url, options); + this.options = { ...this.options, ...options }; + this.url = url; + + this.operations = this.createOperations(); this.cache = options.cacheUtxo ? new MemoryCache(options.cacheUtxo) : undefined; } @@ -346,7 +373,8 @@ export default class Provider { */ async connect(url: string, options?: ProviderOptions) { this.url = url; - this.operations = this.createOperations(url, options ?? this.options); + this.options = options ?? this.options; + this.operations = this.createOperations(); await this.fetchChainAndNodeInfo(); } @@ -382,14 +410,35 @@ export default class Provider { /** * Create GraphQL client and set operations. * - * @param url - The URL of the Fuel node - * @param options - Additional options for the provider * @returns The operation SDK object */ - private createOperations(url: string, options: ProviderOptions = {}) { - this.url = url; - const gqlClient = new GraphQLClient(url, options.fetch ? { fetch: options.fetch } : undefined); - return getOperationsSdk(gqlClient); + private createOperations() { + const fetchFn = Provider.getFetchFn(this.options); + const gqlClient = new GraphQLClient(this.url, { + fetch: (url: string, requestInit: FetchRequestOptions) => + fetchFn(url, requestInit, this.options), + }); + + const executeQuery = (query: DocumentNode, vars: Record) => { + const opDefinition = query.definitions.find((x) => x.kind === 'OperationDefinition') as { + operation: string; + }; + const isSubscription = opDefinition?.operation === 'subscription'; + + if (isSubscription) { + return fuelGraphQLSubscriber({ + url: this.url, + query, + fetchFn: (url, requestInit) => + fetchFn(url as string, requestInit as FetchRequestOptions, this.options), + variables: vars, + }); + } + return gqlClient.request(query, vars); + }; + + // @ts-expect-error This is due to this function being generic. Its type is specified when calling a specific operation via provider.operations.xyz. + return getOperationsSdk(executeQuery); } /** diff --git a/packages/providers/src/transaction-response/transaction-response.ts b/packages/providers/src/transaction-response/transaction-response.ts index d356b3e88f7..ac5676e50aa 100644 --- a/packages/providers/src/transaction-response/transaction-response.ts +++ b/packages/providers/src/transaction-response/transaction-response.ts @@ -29,7 +29,6 @@ import type { GqlTransaction, AbiMap, } from '../transaction-summary/types'; -import { sleep } from '../utils'; /** @hidden */ export type TransactionResultCallReceipt = ReceiptCall; @@ -72,9 +71,6 @@ export type TransactionResultReceipt = | TransactionResultMintReceipt | TransactionResultBurnReceipt; -const STATUS_POLLING_INTERVAL_MAX_MS = 5000; -const STATUS_POLLING_INTERVAL_MIN_MS = 1000; - /** @hidden */ export type TransactionResult = TransactionSummary & { gqlTransaction: GqlTransaction; @@ -90,10 +86,6 @@ export class TransactionResponse { provider: Provider; /** Gas used on the transaction */ gasUsed: BN = bn(0); - /** Number of attempts made to fetch the transaction */ - fetchAttempts: number = 0; - /** Number of attempts made to retrieve a processed transaction. */ - resultAttempts: number = 0; /** The graphql Transaction with receipts object. */ gqlTransaction?: GqlTransaction; @@ -133,7 +125,16 @@ export class TransactionResponse { }); if (!response.transaction) { - await this.sleepBasedOnAttempts(++this.fetchAttempts); + const subscription = this.provider.operations.statusChange({ + transactionId: this.id, + }); + + for await (const { statusChange } of subscription) { + if (statusChange) { + break; + } + } + return this.fetch(); } @@ -204,14 +205,18 @@ export class TransactionResponse { async waitForResult( contractsAbiMap?: AbiMap ): Promise> { - await this.fetch(); - - if (this.gqlTransaction?.status?.type === 'SubmittedStatus') { - await this.sleepBasedOnAttempts(++this.resultAttempts); + const subscription = this.provider.operations.statusChange({ + transactionId: this.id, + }); - return this.waitForResult(contractsAbiMap); + for await (const { statusChange } of subscription) { + if (statusChange.type !== 'SubmittedStatus') { + break; + } } + await this.fetch(); + const transactionSummary = await this.getTransactionSummary(contractsAbiMap); const transactionResult: TransactionResult = { @@ -241,18 +246,4 @@ export class TransactionResponse { return result; } - - /** - * Introduces a delay based on the number of previous attempts made. - * - * @param attempts - The number of attempts. - */ - private async sleepBasedOnAttempts(attempts: number): Promise { - // TODO: Consider adding `maxTimeout` or `maxAttempts` parameter. - // The aim is to avoid perpetual execution; when the limit - // is reached, we can throw accordingly. - await sleep( - Math.min(STATUS_POLLING_INTERVAL_MIN_MS * attempts, STATUS_POLLING_INTERVAL_MAX_MS) - ); - } } diff --git a/packages/providers/test/provider.test.ts b/packages/providers/test/provider.test.ts index 24e3ac66c04..8a2f21967b3 100644 --- a/packages/providers/test/provider.test.ts +++ b/packages/providers/test/provider.test.ts @@ -10,21 +10,19 @@ import { versions } from '@fuel-ts/versions'; import * as fuelTsVersionsMod from '@fuel-ts/versions'; import { getBytesCopy, hexlify } from 'ethers'; import type { BytesLike } from 'ethers'; -import * as GraphQL from 'graphql-request'; -import type { TransactionCost } from '../src/provider'; +import type { TransactionCost, FetchRequestOptions } from '../src/provider'; import Provider from '../src/provider'; import type { CoinTransactionRequestInput, MessageTransactionRequestInput, } from '../src/transaction-request'; -import { CreateTransactionRequest, ScriptTransactionRequest } from '../src/transaction-request'; -import { fromTai64ToUnix, fromUnixToTai64 } from '../src/utils'; +import { ScriptTransactionRequest, CreateTransactionRequest } from '../src/transaction-request'; +import { TransactionResponse } from '../src/transaction-response'; +import { fromTai64ToUnix, fromUnixToTai64, sleep } from '../src/utils'; import * as gasMod from '../src/utils/gas'; import { messageProofResponse, messageStatusResponse } from './fixtures'; -import { MOCK_CHAIN } from './fixtures/chain'; -import { MOCK_NODE_INFO } from './fixtures/nodeInfo'; // https://stackoverflow.com/a/72885576 jest.mock('@fuel-ts/versions', () => ({ @@ -209,32 +207,24 @@ describe('Provider', () => { const providerUrl1 = FUEL_NETWORK_URL; const providerUrl2 = 'http://127.0.0.1:8080/graphql'; - const provider = await Provider.create(providerUrl1); + const provider = await Provider.create(providerUrl1, { + fetch: (url: string, options: FetchRequestOptions) => + getCustomFetch('getVersion', { nodeInfo: { nodeVersion: url } })(url, options), + }); expect(provider.url).toBe(providerUrl1); + expect(await provider.getVersion()).toEqual(providerUrl1); - const spyGraphQLClient = jest.spyOn(GraphQL, 'GraphQLClient').mockImplementation( - () => - ({ - request: () => - Promise.resolve({ - chain: MOCK_CHAIN, - nodeInfo: MOCK_NODE_INFO, - }), - }) as unknown as GraphQL.GraphQLClient - ); - - const spyFetchChainAndNodeInfo = jest.spyOn(Provider.prototype, 'fetchChainAndNodeInfo'); - const spyFetchChain = jest.spyOn(Provider.prototype, 'fetchChain'); - const spyFetchNode = jest.spyOn(Provider.prototype, 'fetchNode'); + const spyFetchChainAndNodeInfo = jest + .spyOn(Provider.prototype, 'fetchChainAndNodeInfo') + .mockImplementation(); await provider.connect(providerUrl2); expect(provider.url).toBe(providerUrl2); - expect(spyGraphQLClient).toBeCalledWith(providerUrl2, undefined); + + expect(await provider.getVersion()).toEqual(providerUrl2); expect(spyFetchChainAndNodeInfo).toHaveBeenCalledTimes(1); - expect(spyFetchChain).toHaveBeenCalledTimes(1); - expect(spyFetchNode).toHaveBeenCalledTimes(1); }); it('can accept a custom fetch function', async () => { @@ -510,8 +500,9 @@ describe('Provider', () => { expect(EXCLUDED.map((value) => hexlify(value))).toStrictEqual(EXPECTED); const owner = Address.fromRandom(); - const resourcesToSpendMock = jest.fn(() => Promise.resolve({ coinsToSpend: [] })); - // @ts-expect-error mock + const resourcesToSpendMock = jest.fn(() => + Promise.resolve({ coinsToSpend: [] }) + ) as unknown as typeof provider.operations.getCoinsToSpend; provider.operations.getCoinsToSpend = resourcesToSpendMock; await provider.getResourcesToSpend(owner, []); @@ -643,8 +634,9 @@ describe('Provider', () => { expect(EXCLUDED.map((value) => hexlify(value))).toStrictEqual(EXPECTED); const owner = Address.fromRandom(); - const resourcesToSpendMock = jest.fn(() => Promise.resolve({ coinsToSpend: [] })); - // @ts-expect-error mock + const resourcesToSpendMock = jest.fn(() => + Promise.resolve({ coinsToSpend: [] }) + ) as unknown as typeof provider.operations.getCoinsToSpend; provider.operations.getCoinsToSpend = resourcesToSpendMock; await provider.getResourcesToSpend(owner, [], { utxos: [ @@ -939,6 +931,82 @@ describe('Provider', () => { expect(estimateTxSpy).toHaveBeenCalled(); }); + it('An invalid subscription request throws a FuelError and does not hold the test runner (closes all handles)', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + + await expectToThrowFuelError( + async () => { + for await (const value of provider.operations.statusChange({ + transactionId: 'invalid transaction id', + })) { + // shouldn't be reached and should fail if reached + expect(value).toBeFalsy(); + } + }, + + { code: FuelError.CODES.INVALID_REQUEST } + ); + + const response = new TransactionResponse('invalid transaction id', provider); + + await expectToThrowFuelError(() => response.waitForResult(), { + code: FuelError.CODES.INVALID_REQUEST, + }); + }); + + it('default timeout is undefined', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + expect(provider.options.timeout).toBeUndefined(); + }); + + it('throws TimeoutError on timeout when calling an operation', async () => { + const timeout = 500; + const provider = await Provider.create(FUEL_NETWORK_URL, { timeout }); + jest + .spyOn(global, 'fetch') + .mockImplementationOnce((...args: unknown[]) => + sleep(timeout).then(() => + fetch(args[0] as RequestInfo | URL, args[1] as RequestInit | undefined) + ) + ); + + const { error } = await safeExec(async () => { + await provider.getBlocks({}); + }); + + expect(error).toMatchObject({ + code: 23, + name: 'TimeoutError', + message: 'The operation was aborted due to timeout', + }); + }); + + it('throws TimeoutError on timeout when calling a subscription', async () => { + const timeout = 500; + const provider = await Provider.create(FUEL_NETWORK_URL, { timeout }); + + jest + .spyOn(global, 'fetch') + .mockImplementationOnce((...args: unknown[]) => + sleep(timeout).then(() => + fetch(args[0] as RequestInfo | URL, args[1] as RequestInit | undefined) + ) + ); + + const { error } = await safeExec(async () => { + for await (const iterator of provider.operations.statusChange({ + transactionId: 'doesnt matter, will be aborted', + })) { + // shouldn't be reached and should fail if reached + expect(iterator).toBeFalsy(); + } + }); + expect(error).toMatchObject({ + code: 23, + name: 'TimeoutError', + message: 'The operation was aborted due to timeout', + }); + }); it('should ensure calculateMaxgas considers gasLimit for ScriptTransactionRequest', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); const { gasPerByte } = provider.getGasConfig(); diff --git a/packages/script/package.json b/packages/script/package.json index 9c7bc010a9f..d65dc96bdba 100644 --- a/packages/script/package.json +++ b/packages/script/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/signer/package.json b/packages/signer/package.json index 0af03f26718..4db014ad5e2 100644 --- a/packages/signer/package.json +++ b/packages/signer/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/transactions/package.json b/packages/transactions/package.json index 928401b4302..dedcf94e788 100644 --- a/packages/transactions/package.json +++ b/packages/transactions/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/utils/package.json b/packages/utils/package.json index 8af9e860007..cdd14a80fd2 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/versions/package.json b/packages/versions/package.json index ba0bd711e4b..d2139b03903 100644 --- a/packages/versions/package.json +++ b/packages/versions/package.json @@ -10,7 +10,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/wallet-manager/package.json b/packages/wallet-manager/package.json index 3ffccc5557b..c09af55e076 100644 --- a/packages/wallet-manager/package.json +++ b/packages/wallet-manager/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/wallet/package.json b/packages/wallet/package.json index ce44481c1c0..63f3ae6dd2f 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/packages/wordlists/package.json b/packages/wordlists/package.json index ce3a20753d5..85927b0a431 100644 --- a/packages/wordlists/package.json +++ b/packages/wordlists/package.json @@ -8,7 +8,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.14.1" + "node": "^18.18.2" }, "exports": { ".": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 932be44eacd..2399a919841 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,9 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - cross-fetch: 4.0.0 - importers: .: @@ -995,9 +992,9 @@ importers: '@graphql-codegen/typescript': specifier: ^2.8.0 version: 2.8.0(graphql@16.6.0) - '@graphql-codegen/typescript-graphql-request': - specifier: ^4.5.7 - version: 4.5.7(graphql-request@5.0.0)(graphql-tag@2.12.6)(graphql@16.6.0) + '@graphql-codegen/typescript-generic-sdk': + specifier: ^3.1.0 + version: 3.1.0(graphql-tag@2.12.6)(graphql@16.6.0) '@graphql-codegen/typescript-operations': specifier: ^2.5.5 version: 2.5.5(graphql@16.6.0) @@ -4192,18 +4189,16 @@ packages: tslib: 2.4.1 dev: true - /@graphql-codegen/typescript-graphql-request@4.5.7(graphql-request@5.0.0)(graphql-tag@2.12.6)(graphql@16.6.0): - resolution: {integrity: sha512-1YPaCO+0q5z0Um6Om+5LMWdB8+WQxda8eXRXwy0dqSGRy9X5HTZz/pxqaTgy76yMtPBxq1UNa7lruBTzszHhJg==} + /@graphql-codegen/typescript-generic-sdk@3.1.0(graphql-tag@2.12.6)(graphql@16.6.0): + resolution: {integrity: sha512-nQZi/YGRI1+qCZZsh0V5nz6+hCHSN4OU9tKyOTDsEPyDFnGEukDuRdCH2IZasGn22a3Iu5TUDkgp5w9wEQwGmg==} peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - graphql-request: ^3.4.0 || ^4.0.0 || ^5.0.0 graphql-tag: ^2.0.0 dependencies: - '@graphql-codegen/plugin-helpers': 2.7.2(graphql@16.6.0) - '@graphql-codegen/visitor-plugin-common': 2.13.0(graphql@16.6.0) + '@graphql-codegen/plugin-helpers': 3.1.2(graphql@16.6.0) + '@graphql-codegen/visitor-plugin-common': 2.13.1(graphql@16.6.0) auto-bind: 4.0.0 graphql: 16.6.0 - graphql-request: 5.0.0(graphql@16.6.0) graphql-tag: 2.12.6(graphql@16.6.0) tslib: 2.4.1 transitivePeerDependencies: @@ -4264,6 +4259,27 @@ packages: - supports-color dev: true + /@graphql-codegen/visitor-plugin-common@2.13.1(graphql@16.6.0): + resolution: {integrity: sha512-mD9ufZhDGhyrSaWQGrU1Q1c5f01TeWtSWy/cDwXYjJcHIj1Y/DG2x0tOflEfCvh5WcnmHNIw4lzDsg1W7iFJEg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 2.7.2(graphql@16.6.0) + '@graphql-tools/optimize': 1.4.0(graphql@16.6.0) + '@graphql-tools/relay-operation-optimizer': 6.5.18(graphql@16.6.0) + '@graphql-tools/utils': 8.13.1(graphql@16.6.0) + auto-bind: 4.0.0 + change-case-all: 1.0.14 + dependency-graph: 0.11.0 + graphql: 16.6.0 + graphql-tag: 2.12.6(graphql@16.6.0) + parse-filepath: 1.0.2 + tslib: 2.4.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@graphql-tools/apollo-engine-loader@7.3.26(graphql@16.6.0): resolution: {integrity: sha512-h1vfhdJFjnCYn9b5EY1Z91JTF0KB3hHVJNQIsiUV2mpQXZdeOXQoaWeYEKaiI5R6kwBw5PP9B0fv3jfUIG8LyQ==} peerDependencies: @@ -4964,7 +4980,7 @@ packages: graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.0 istanbul-lib-instrument: 6.0.1 - istanbul-lib-report: 3.0.0 + istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.5 jest-message-util: 29.7.0 @@ -4973,7 +4989,7 @@ packages: slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 - v8-to-istanbul: 9.1.0 + v8-to-istanbul: 9.1.3 transitivePeerDependencies: - supports-color dev: true @@ -4996,7 +5012,6 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@sinclair/typebox': 0.27.8 - dev: true /@jest/source-map@27.5.1: resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==} @@ -5141,7 +5156,7 @@ packages: resolution: {integrity: sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 29.4.3 + '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 '@types/node': 16.18.34 @@ -5549,7 +5564,6 @@ packages: /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - dev: true /@sinonjs/commons@1.8.6: resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} @@ -6119,7 +6133,7 @@ packages: resolution: {integrity: sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==} dependencies: expect: 29.5.0 - pretty-format: 29.5.0 + pretty-format: 29.7.0 /@types/js-yaml@4.0.5: resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==} @@ -8645,8 +8659,8 @@ packages: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true - /cross-fetch@4.0.0: - resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + /cross-fetch@3.1.8: + resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} dependencies: node-fetch: 2.7.0 transitivePeerDependencies: @@ -10557,6 +10571,7 @@ packages: /extract-files@9.0.0: resolution: {integrity: sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==} engines: {node: ^10.17.0 || ^12.0.0 || >= 13.7.0} + dev: false /fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -10592,7 +10607,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 - dev: true /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -10642,7 +10656,7 @@ packages: /fbjs@3.0.5: resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} dependencies: - cross-fetch: 4.0.0 + cross-fetch: 3.1.8 fbjs-css-vars: 1.0.2 loose-envify: 1.4.0 object-assign: 4.1.1 @@ -11179,12 +11193,13 @@ packages: graphql: 14 - 16 dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.6.0) - cross-fetch: 4.0.0 + cross-fetch: 3.1.8 extract-files: 9.0.0 form-data: 3.0.1 graphql: 16.6.0 transitivePeerDependencies: - encoding + dev: false /graphql-request@6.1.0(graphql@16.6.0): resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} @@ -11192,7 +11207,7 @@ packages: graphql: 14 - 16 dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.6.0) - cross-fetch: 4.0.0 + cross-fetch: 3.1.8 graphql: 16.6.0 transitivePeerDependencies: - encoding @@ -12052,7 +12067,7 @@ packages: '@babel/parser': 7.22.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 - semver: 6.3.0 + semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -12076,6 +12091,15 @@ packages: istanbul-lib-coverage: 3.2.0 make-dir: 3.1.0 supports-color: 7.2.0 + dev: false + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.0 + make-dir: 4.0.0 + supports-color: 7.2.0 /istanbul-lib-source-maps@4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} @@ -12092,7 +12116,7 @@ packages: engines: {node: '>=8'} dependencies: html-escaper: 2.0.2 - istanbul-lib-report: 3.0.0 + istanbul-lib-report: 3.0.1 /iterall@1.3.0: resolution: {integrity: sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==} @@ -12193,7 +12217,7 @@ packages: jest-util: 29.7.0 p-limit: 3.1.0 pretty-format: 29.7.0 - pure-rand: 6.0.2 + pure-rand: 6.0.4 slash: 3.0.0 stack-utils: 2.0.6 transitivePeerDependencies: @@ -12771,7 +12795,7 @@ packages: jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) jest-util: 29.7.0 jest-validate: 29.7.0 - resolve: 1.22.2 + resolve: 1.22.8 resolve.exports: 2.0.2 slash: 3.0.0 dev: true @@ -13640,7 +13664,14 @@ packages: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} dependencies: - semver: 6.3.0 + semver: 6.3.1 + dev: false + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -15785,7 +15816,6 @@ packages: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.2.0 - dev: true /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -15849,8 +15879,8 @@ packages: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} - /pure-rand@6.0.2: - resolution: {integrity: sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==} + /pure-rand@6.0.4: + resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} dev: true /pvtsutils@1.3.2: @@ -17470,7 +17500,7 @@ packages: chokidar: 3.5.3 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.2.12 + fast-glob: 3.3.1 glob-parent: 6.0.2 is-glob: 4.0.3 jiti: 1.18.2 @@ -18416,13 +18446,13 @@ packages: source-map: 0.7.4 dev: false - /v8-to-istanbul@9.1.0: - resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + /v8-to-istanbul@9.1.3: + resolution: {integrity: sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==} engines: {node: '>=10.12.0'} dependencies: '@jridgewell/trace-mapping': 0.3.18 '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 + convert-source-map: 2.0.0 dev: true /validate-npm-package-license@3.0.4: