From 98dbfbb36c9a1cfff00f7eba9f8a0d9311dabfcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nedim=20Salki=C4=87?= Date: Thu, 11 Jul 2024 11:54:41 +0200 Subject: [PATCH] fix!: `launchNode.cleanup` not killing node in last test of test group (#2718) --- .changeset/breezy-tables-jam.md | 6 + .knip.json | 1 - packages/account/package.json | 1 - .../launchNode-singular-test.test.ts | 22 +++ .../account/src/test-utils/launchNode.test.ts | 154 +++++++++++------- packages/account/src/test-utils/launchNode.ts | 93 ++++++----- packages/fuels/package.json | 1 - .../src/cli/commands/dev/autoStartFuelCore.ts | 9 - pnpm-lock.yaml | 107 +++++++----- 9 files changed, 237 insertions(+), 157 deletions(-) create mode 100644 .changeset/breezy-tables-jam.md create mode 100644 packages/account/src/test-utils/launchNode-singular-test.test.ts diff --git a/.changeset/breezy-tables-jam.md b/.changeset/breezy-tables-jam.md new file mode 100644 index 0000000000..d3ac919ea4 --- /dev/null +++ b/.changeset/breezy-tables-jam.md @@ -0,0 +1,6 @@ +--- +"@fuel-ts/account": minor +"fuels": minor +--- + +fix!: `launchNode.cleanup` not killing node in last test of test group diff --git a/.knip.json b/.knip.json index 5270e577cb..488634beb2 100644 --- a/.knip.json +++ b/.knip.json @@ -29,7 +29,6 @@ "textlint", "textlint-rule-no-dead-link", "@elasticpath/textlint-rule-no-dead-relative-link", - "tree-kill", "ts-generator", "webdriverio" ] diff --git a/packages/account/package.json b/packages/account/package.json index d50d2ec832..50e83b0358 100644 --- a/packages/account/package.json +++ b/packages/account/package.json @@ -67,7 +67,6 @@ "graphql-tag": "^2.12.6", "portfinder": "^1.0.32", "ramda": "^0.29.0", - "tree-kill": "^1.2.2", "uuid": "^10.0.0" }, "devDependencies": { diff --git a/packages/account/src/test-utils/launchNode-singular-test.test.ts b/packages/account/src/test-utils/launchNode-singular-test.test.ts new file mode 100644 index 0000000000..17237e0069 --- /dev/null +++ b/packages/account/src/test-utils/launchNode-singular-test.test.ts @@ -0,0 +1,22 @@ +import { launchNode } from './launchNode'; + +/** + * The test runner creates a test environment per file, + * which we can use to isolate the faulty behavior. + */ +/** + * @group node + */ +describe('launchNode-singular-test', () => { + const killedNode = { + url: '', + }; + afterEach(async () => { + await expect(fetch(killedNode.url)).rejects.toThrow('fetch failed'); + }); + test('synchronous cleanup kills node before test runner exits', async () => { + const { cleanup, url } = await launchNode({ loggingEnabled: false }); + killedNode.url = url; + cleanup(); + }); +}); diff --git a/packages/account/src/test-utils/launchNode.test.ts b/packages/account/src/test-utils/launchNode.test.ts index 07564cc721..8353078094 100644 --- a/packages/account/src/test-utils/launchNode.test.ts +++ b/packages/account/src/test-utils/launchNode.test.ts @@ -1,14 +1,13 @@ import { ErrorCode } from '@fuel-ts/errors'; import { safeExec, expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; -import { defaultSnapshotConfigs } from '@fuel-ts/utils'; +import { defaultSnapshotConfigs, sleep } from '@fuel-ts/utils'; import { waitUntilUnreachable } from '@fuel-ts/utils/test-utils'; import * as childProcessMod from 'child_process'; +import * as fsMod from 'fs'; import { Provider } from '../providers'; -import { killNode, launchNode } from './launchNode'; - -type ChildProcessWithoutNullStreams = childProcessMod.ChildProcessWithoutNullStreams; +import { launchNode } from './launchNode'; vi.mock('child_process', async () => { const mod = await vi.importActual('child_process'); @@ -18,6 +17,14 @@ vi.mock('child_process', async () => { }; }); +vi.mock('fs', async () => { + const mod = await vi.importActual('fs'); + return { + __esModule: true, + ...mod, + }; +}); + /** * @group node */ @@ -130,63 +137,96 @@ describe('launchNode', () => { cleanup(); }); - test('should kill process only if PID exists and node is alive', () => { - const killFn = vi.fn(); - const state = { isDead: true }; - - // should not kill - let child = { - pid: undefined, - stdout: { - removeAllListeners: () => {}, - }, - stderr: { - removeAllListeners: () => {}, - }, - } as ChildProcessWithoutNullStreams; - - killNode({ - child, - configPath: '', - killFn, - state, - }); + test('cleanup removes temporary directory', async () => { + const mkdirSyncSpy = vi.spyOn(fsMod, 'mkdirSync'); + const { cleanup } = await launchNode(); - expect(killFn).toHaveBeenCalledTimes(0); - expect(state.isDead).toEqual(true); - - // should not kill - child = { - pid: 1, - stdout: { - removeAllListeners: () => {}, - }, - stderr: { - removeAllListeners: () => {}, - }, - } as ChildProcessWithoutNullStreams; - - killNode({ - child, - configPath: '', - killFn, - state, - }); + expect(mkdirSyncSpy).toHaveBeenCalledTimes(1); + const tempDirPath = mkdirSyncSpy.mock.calls[0][0]; + cleanup(); - expect(killFn).toHaveBeenCalledTimes(0); - expect(state.isDead).toEqual(true); + // wait until cleanup finishes (done via events) + await sleep(1500); + expect(fsMod.existsSync(tempDirPath)).toBeFalsy(); + }); - // should kill - state.isDead = false; + test('temporary directory gets removed on error', async () => { + const mkdirSyncSpy = vi.spyOn(fsMod, 'mkdirSync'); - killNode({ - child, - configPath: '', - killFn, - state, - }); + const invalidCoin = { + asset_id: 'whatever', + tx_id: '', + output_index: 0, + tx_pointer_block_height: 0, + tx_pointer_tx_idx: 0, + owner: '', + amount: 0, + }; + + const { error } = await safeExec(async () => + launchNode({ + loggingEnabled: false, + snapshotConfig: { + ...defaultSnapshotConfigs, + stateConfig: { + coins: [invalidCoin], + messages: [], + }, + }, + }) + ); + expect(error).toBeDefined(); - expect(killFn).toHaveBeenCalledTimes(1); - expect(state.isDead).toEqual(true); + expect(mkdirSyncSpy).toHaveBeenCalledTimes(1); + const tempDirPath = mkdirSyncSpy.mock.calls[0][0]; + + // wait until cleanup finishes (done via events) + await sleep(1500); + expect(fsMod.existsSync(tempDirPath)).toBeFalsy(); + }); + + test('calling cleanup multiple times does not retry process killing', async () => { + const killSpy = vi.spyOn(process, 'kill'); + + const { cleanup } = await launchNode({ loggingEnabled: false }); + + cleanup(); + + expect(killSpy).toHaveBeenCalledTimes(1); + + cleanup(); + + expect(killSpy).toHaveBeenCalledTimes(1); + }); + + test('external killing of node runs side-effect cleanup', async () => { + const mkdirSyncSpy = vi.spyOn(fsMod, 'mkdirSync'); + + const { pid } = await launchNode({ loggingEnabled: false }); + + expect(mkdirSyncSpy).toHaveBeenCalledTimes(1); + const tempDirPath = mkdirSyncSpy.mock.calls[0][0]; + + childProcessMod.execSync(`kill -- -${pid}`); + // wait until cleanup finishes (done via events) + await sleep(1500); + expect(fsMod.existsSync(tempDirPath)).toBeFalsy(); + }); + + test('calling cleanup on externally killed node does not throw', async () => { + const mkdirSyncSpy = vi.spyOn(fsMod, 'mkdirSync'); + const logSpy = vi.spyOn(console, 'log'); + + const { pid, cleanup } = await launchNode({ loggingEnabled: false }); + expect(mkdirSyncSpy).toHaveBeenCalledTimes(1); + + childProcessMod.execSync(`kill -- -${pid}`); + // wait until cleanup finishes (done via events) + await sleep(1500); + cleanup(); + + expect(logSpy).toHaveBeenCalledWith( + `fuel-core node under pid ${pid} does not exist. The node might have been killed before cleanup was called. Exiting cleanly.` + ); }); }); diff --git a/packages/account/src/test-utils/launchNode.ts b/packages/account/src/test-utils/launchNode.ts index 3fd1ceddb1..0554acca8f 100644 --- a/packages/account/src/test-utils/launchNode.ts +++ b/packages/account/src/test-utils/launchNode.ts @@ -3,13 +3,11 @@ import { randomBytes } from '@fuel-ts/crypto'; import { FuelError } from '@fuel-ts/errors'; import type { SnapshotConfigs } from '@fuel-ts/utils'; import { defaultConsensusKey, hexlify, defaultSnapshotConfigs } from '@fuel-ts/utils'; -import type { ChildProcessWithoutNullStreams } from 'child_process'; import { randomUUID } from 'crypto'; import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs'; import os from 'os'; import path from 'path'; import { getPortPromise } from 'portfinder'; -import treeKill from 'tree-kill'; import { Provider } from '../providers'; import { Signer } from '../signer'; @@ -56,35 +54,9 @@ export type LaunchNodeResult = Promise<{ port: string; url: string; snapshotDir: string; + pid: number; }>; -export type KillNodeParams = { - child: ChildProcessWithoutNullStreams; - configPath: string; - killFn: (pid: number) => void; - state: { - isDead: boolean; - }; -}; - -export const killNode = (params: KillNodeParams) => { - const { child, configPath, state, killFn } = params; - if (!state.isDead) { - if (child.pid) { - state.isDead = true; - killFn(Number(child.pid)); - } - - // Remove all the listeners we've added. - child.stderr.removeAllListeners(); - - // Remove the temporary folder and all its contents. - if (existsSync(configPath)) { - rmSync(configPath, { recursive: true }); - } - } -}; - function getFinalStateConfigJSON({ stateConfig, chainConfig }: SnapshotConfigs) { const defaultCoins = defaultSnapshotConfigs.stateConfig.coins.map((coin) => ({ ...coin, @@ -232,7 +204,7 @@ export const launchNode = async ({ '--debug', ...remainingArgs, ].flat(), - { stdio: 'pipe' } + { stdio: 'pipe', detached: true } ); if (loggingEnabled) { @@ -242,13 +214,45 @@ export const launchNode = async ({ }); } - const cleanupConfig: KillNodeParams = { - child, - configPath: tempDir, - killFn: treeKill, - state: { - isDead: false, - }, + const removeSideffects = () => { + child.stderr.removeAllListeners(); + if (existsSync(tempDir)) { + rmSync(tempDir, { recursive: true }); + } + }; + + child.on('error', removeSideffects); + child.on('exit', removeSideffects); + + const childState = { + isDead: false, + }; + + const cleanup = () => { + if (childState.isDead) { + return; + } + childState.isDead = true; + + removeSideffects(); + if (child.pid !== undefined) { + try { + process.kill(-child.pid); + } catch (e) { + const error = e as Error & { code: string }; + if (error.code === 'ESRCH') { + // eslint-disable-next-line no-console + console.log( + `fuel-core node under pid ${child.pid} does not exist. The node might have been killed before cleanup was called. Exiting cleanly.` + ); + } else { + throw e; + } + } + } else { + // eslint-disable-next-line no-console + console.error('No PID available for the child process, unable to kill launched node'); + } }; // Look for a specific graphql start point in the output. @@ -264,11 +268,12 @@ export const launchNode = async ({ // Resolve with the cleanup method. resolve({ - cleanup: () => killNode(cleanupConfig), + cleanup, ip: realIp, port: realPort, url: `http://${realIp}:${realPort}/v1/graphql`, snapshotDir: snapshotDirToUse as string, + pid: child.pid as number, }); } if (/error/i.test(text)) { @@ -279,18 +284,18 @@ export const launchNode = async ({ }); // Process exit. - process.on('exit', () => killNode(cleanupConfig)); + process.on('exit', cleanup); // Catches ctrl+c event. - process.on('SIGINT', () => killNode(cleanupConfig)); + process.on('SIGINT', cleanup); // Catches "kill pid" (for example: nodemon restart). - process.on('SIGUSR1', () => killNode(cleanupConfig)); - process.on('SIGUSR2', () => killNode(cleanupConfig)); + process.on('SIGUSR1', cleanup); + process.on('SIGUSR2', cleanup); // Catches uncaught exceptions. - process.on('beforeExit', () => killNode(cleanupConfig)); - process.on('uncaughtException', () => killNode(cleanupConfig)); + process.on('beforeExit', cleanup); + process.on('uncaughtException', cleanup); child.on('error', reject); }); diff --git a/packages/fuels/package.json b/packages/fuels/package.json index 022db338f6..159adea5b0 100644 --- a/packages/fuels/package.json +++ b/packages/fuels/package.json @@ -84,7 +84,6 @@ "lodash.camelcase": "^4.3.0", "portfinder": "^1.0.32", "toml": "^3.0.0", - "tree-kill": "^1.2.2", "yup": "^1.4.0" }, "devDependencies": { diff --git a/packages/fuels/src/cli/commands/dev/autoStartFuelCore.ts b/packages/fuels/src/cli/commands/dev/autoStartFuelCore.ts index b82496f56b..303cf60004 100644 --- a/packages/fuels/src/cli/commands/dev/autoStartFuelCore.ts +++ b/packages/fuels/src/cli/commands/dev/autoStartFuelCore.ts @@ -1,5 +1,4 @@ import { defaultConsensusKey } from '@fuel-ts/utils'; -import type { ChildProcessWithoutNullStreams } from 'child_process'; import { getPortPromise } from 'portfinder'; import { launchNode } from '../../../test-utils'; @@ -15,14 +14,6 @@ export type FuelCoreNode = { killChildProcess: () => void; }; -export type KillNodeParams = { - core: ChildProcessWithoutNullStreams; - killFn: (pid: number) => void; - state: { - isDead: boolean; - }; -}; - export const autoStartFuelCore = async (config: FuelsConfig) => { let fuelCore: FuelCoreNode | undefined; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f179bfd6fe..cc7ee71d0d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,7 +118,7 @@ importers: version: 10.2.6 knip: specifier: ^5.24.1 - version: 5.24.2(@types/node@18.15.3)(typescript@5.4.5) + version: 5.24.1(@types/node@18.15.3)(typescript@5.4.5) memfs: specifier: ^4.9.3 version: 4.9.3 @@ -725,9 +725,6 @@ importers: ramda: specifier: ^0.29.0 version: 0.29.0 - tree-kill: - specifier: ^1.2.2 - version: 1.2.2 uuid: specifier: ^10.0.0 version: 10.0.0 @@ -981,9 +978,6 @@ importers: toml: specifier: ^3.0.0 version: 3.0.0 - tree-kill: - specifier: ^1.2.2 - version: 1.2.2 yup: specifier: ^1.4.0 version: 1.4.0 @@ -5754,8 +5748,8 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - ansi-sequence-parser@1.1.0: - resolution: {integrity: sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==} + ansi-sequence-parser@1.1.1: + resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==} ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} @@ -6216,6 +6210,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + browserslist@4.23.0: resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -7274,6 +7273,9 @@ packages: electron-to-chromium@1.4.442: resolution: {integrity: sha512-RkrZF//Ya+0aJq2NM3OdisNh5ZodZq1rdXOS96G8DdDgpDKqKE81yTbbQ3F/4CKm1JBPsGu1Lp/akkna2xO06Q==} + electron-to-chromium@1.4.589: + resolution: {integrity: sha512-zF6y5v/YfoFIgwf2dDfAqVlPPsyQeWNpEWXbAlDUS8Ax4Z2VoiiZpAPC0Jm9hXEkJm2vIZpwB6rc4KnLTQffbQ==} + electron-to-chromium@1.4.747: resolution: {integrity: sha512-+FnSWZIAvFHbsNVmUxhEqWiaOiPMcfum1GQzlWCg/wLigVtshOsjXHyEFfmt6cFK6+HkS3QOJBv6/3OPumbBfw==} @@ -9468,8 +9470,8 @@ packages: resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} engines: {node: '>= 8'} - knip@5.24.2: - resolution: {integrity: sha512-Y8YyqkheQ4IlVw1s5nOBrT4LZUl4g++gKmX+d/J9lxoR8pz6UV0Otn3GBbuc9JYYMjKVW94GLVtB7hKlzJSmrQ==} + knip@5.24.1: + resolution: {integrity: sha512-YgVtCxu3lpsGHrZJUozBr7oAlQvwkxaExZr0OwaiDK92em/Gp0ZfzQC3h8ZOfFVGxgNqNX7AV61xWA/Lusg46A==} engines: {node: '>=18.6.0'} hasBin: true peerDependencies: @@ -10059,6 +10061,10 @@ packages: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -10257,6 +10263,9 @@ packages: node-releases@2.0.12: resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==} + node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} @@ -13956,6 +13965,11 @@ packages: resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} engines: {node: '>= 14'} + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -14322,7 +14336,7 @@ snapshots: '@babel/compat-data': 7.22.5 '@babel/core': 7.22.5 '@babel/helper-validator-option': 7.22.5 - browserslist: 4.23.0 + browserslist: 4.22.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -14331,7 +14345,7 @@ snapshots: '@babel/compat-data': 7.22.5 '@babel/core': 7.24.4 '@babel/helper-validator-option': 7.22.5 - browserslist: 4.23.0 + browserslist: 4.22.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -14340,7 +14354,7 @@ snapshots: '@babel/compat-data': 7.22.5 '@babel/core': 7.24.7 '@babel/helper-validator-option': 7.22.5 - browserslist: 4.23.0 + browserslist: 4.22.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -18768,8 +18782,6 @@ snapshots: chalk: 4.1.2 execa: 5.1.1 fast-glob: 3.3.2 - transitivePeerDependencies: - - encoding '@react-native-community/cli-config@13.6.6': dependencies: @@ -18779,8 +18791,6 @@ snapshots: deepmerge: 4.3.1 fast-glob: 3.3.2 joi: 17.13.1 - transitivePeerDependencies: - - encoding '@react-native-community/cli-debugger-ui@13.6.6': dependencies: @@ -18806,9 +18816,7 @@ snapshots: semver: 7.5.4 strip-ansi: 5.2.0 wcwidth: 1.0.1 - yaml: 2.3.1 - transitivePeerDependencies: - - encoding + yaml: 2.4.5 '@react-native-community/cli-hermes@13.6.6': dependencies: @@ -18816,8 +18824,6 @@ snapshots: '@react-native-community/cli-tools': 13.6.6 chalk: 4.1.2 hermes-profile-transformer: 0.0.6 - transitivePeerDependencies: - - encoding '@react-native-community/cli-platform-android@13.6.6': dependencies: @@ -18827,8 +18833,6 @@ snapshots: fast-glob: 3.3.2 fast-xml-parser: 4.4.0 logkitty: 0.7.1 - transitivePeerDependencies: - - encoding '@react-native-community/cli-platform-apple@13.6.6': dependencies: @@ -18838,14 +18842,10 @@ snapshots: fast-glob: 3.3.2 fast-xml-parser: 4.4.0 ora: 5.4.1 - transitivePeerDependencies: - - encoding '@react-native-community/cli-platform-ios@13.6.6': dependencies: '@react-native-community/cli-platform-apple': 13.6.6 - transitivePeerDependencies: - - encoding '@react-native-community/cli-server-api@13.6.6(bufferutil@4.0.8)(utf-8-validate@6.0.4)': dependencies: @@ -18860,7 +18860,6 @@ snapshots: ws: 6.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.4) transitivePeerDependencies: - bufferutil - - encoding - supports-color - utf-8-validate @@ -18871,14 +18870,12 @@ snapshots: execa: 5.1.1 find-up: 5.0.0 mime: 2.6.0 - node-fetch: 2.7.0 + node-fetch: 3.3.2 open: 6.4.0 ora: 5.4.1 semver: 7.5.4 shell-quote: 1.8.1 sudo-prompt: 9.2.1 - transitivePeerDependencies: - - encoding '@react-native-community/cli-types@13.6.6': dependencies: @@ -18905,7 +18902,6 @@ snapshots: semver: 7.5.4 transitivePeerDependencies: - bufferutil - - encoding - supports-color - utf-8-validate @@ -21104,7 +21100,7 @@ snapshots: ansi-regex@6.0.1: {} - ansi-sequence-parser@1.1.0: {} + ansi-sequence-parser@1.1.1: {} ansi-styles@3.2.1: dependencies: @@ -21814,6 +21810,13 @@ snapshots: node-releases: 2.0.12 update-browserslist-db: 1.0.11(browserslist@4.21.9) + browserslist@4.22.1: + dependencies: + caniuse-lite: 1.0.30001593 + electron-to-chromium: 1.4.589 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.22.1) + browserslist@4.23.0: dependencies: caniuse-lite: 1.0.30001640 @@ -22312,7 +22315,7 @@ snapshots: core-js-compat@3.31.0: dependencies: - browserslist: 4.23.0 + browserslist: 4.22.1 core-js-pure@3.31.0: {} @@ -22927,6 +22930,8 @@ snapshots: electron-to-chromium@1.4.442: {} + electron-to-chromium@1.4.589: {} + electron-to-chromium@1.4.747: {} elliptic@6.5.4: @@ -24487,7 +24492,7 @@ snapshots: dependencies: foreground-child: 3.1.1 jackspeak: 2.3.6 - minimatch: 9.0.3 + minimatch: 9.0.5 minipass: 7.0.4 path-scurry: 1.11.1 @@ -25328,7 +25333,7 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.24.4 + '@babel/core': 7.24.7 '@babel/parser': 7.24.7 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 @@ -26095,7 +26100,7 @@ snapshots: klona@2.0.6: {} - knip@5.24.2(@types/node@18.15.3)(typescript@5.4.5): + knip@5.24.1(@types/node@18.15.3)(typescript@5.4.5): dependencies: '@ericcornelissen/bash-parser': 0.5.3 '@nodelib/fs.walk': 2.0.0 @@ -26888,6 +26893,10 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + minimist@1.2.8: {} minipass@6.0.2: {} @@ -27120,6 +27129,8 @@ snapshots: node-releases@2.0.12: {} + node-releases@2.0.13: {} + node-releases@2.0.14: {} node-stdlib-browser@1.2.0: @@ -28141,7 +28152,7 @@ snapshots: '@csstools/postcss-trigonometric-functions': 1.0.2(postcss@8.4.33) '@csstools/postcss-unset-value': 1.0.2(postcss@8.4.33) autoprefixer: 10.4.19(postcss@8.4.33) - browserslist: 4.23.0 + browserslist: 4.22.1 css-blank-pseudo: 3.0.3(postcss@8.4.33) css-has-pseudo: 3.0.4(postcss@8.4.33) css-prefers-color-scheme: 6.0.3(postcss@8.4.33) @@ -28527,7 +28538,7 @@ snapshots: dependencies: '@babel/code-frame': 7.22.5 address: 1.2.2 - browserslist: 4.23.0 + browserslist: 4.22.1 chalk: 4.1.2 cross-spawn: 7.0.3 detect-port-alt: 1.1.6 @@ -29413,7 +29424,7 @@ snapshots: shiki@0.14.7: dependencies: - ansi-sequence-parser: 1.1.0 + ansi-sequence-parser: 1.1.1 jsonc-parser: 3.2.0 vscode-oniguruma: 1.7.0 vscode-textmate: 8.0.0 @@ -30595,7 +30606,7 @@ snapshots: dependencies: lunr: 2.3.9 marked: 4.3.0 - minimatch: 9.0.3 + minimatch: 9.0.5 shiki: 0.14.7 typescript: 5.4.5 @@ -30766,6 +30777,12 @@ snapshots: escalade: 3.1.1 picocolors: 1.0.0 + update-browserslist-db@1.0.13(browserslist@4.22.1): + dependencies: + browserslist: 4.22.1 + escalade: 3.1.1 + picocolors: 1.0.0 + update-browserslist-db@1.0.13(browserslist@4.23.0): dependencies: browserslist: 4.23.0 @@ -31344,7 +31361,7 @@ snapshots: '@webassemblyjs/wasm-parser': 1.11.6 acorn: 8.9.0 acorn-import-assertions: 1.9.0(acorn@8.9.0) - browserslist: 4.23.0 + browserslist: 4.22.1 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 es-module-lexer: 1.3.0 @@ -31693,6 +31710,8 @@ snapshots: yaml@2.3.1: {} + yaml@2.4.5: {} + yargs-parser@18.1.3: dependencies: camelcase: 5.3.1