diff --git a/cashtab/src/components/Agora/__tests__/index.test.js b/cashtab/src/components/Agora/__tests__/index.test.js index 882d50e7a..132f5b348 100644 --- a/cashtab/src/components/Agora/__tests__/index.test.js +++ b/cashtab/src/components/Agora/__tests__/index.test.js @@ -1,986 +1,982 @@ // Copyright (c) 2024 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import userEvent from '@testing-library/user-event'; import { when } from 'jest-when'; import { initializeCashtabStateForTests, clearLocalForage, } from 'components/App/fixtures/helpers'; import CashtabTestWrapper from 'components/App/fixtures/CashtabTestWrapper'; import appConfig from 'config/app'; import 'fake-indexeddb/auto'; import localforage from 'localforage'; import { agoraPartialAlphaWallet, agoraOfferCachetAlphaOne, agoraOfferCachetAlphaTwo, agoraOfferCachetBetaOne, agoraOfferBullAlphaOne, scamAgoraOffer, cachetCacheMocks, bullCacheMocks, scamCacheMocks, agoraPartialBetaWallet, agoraPartialAlphaKeypair, agoraPartialBetaKeypair, } from 'components/Agora/fixtures/mocks'; import { Ecc, initWasm, toHex } from 'ecash-lib'; import { MockAgora } from '../../../../../modules/mock-chronik-client'; import { token as tokenConfig } from 'config/token'; describe('<Agora />', () => { let ecc; const CACHET_TOKEN_ID = cachetCacheMocks.token.tokenId; const BULL_TOKEN_ID = bullCacheMocks.token.tokenId; const SCAM_TOKEN_ID = scamCacheMocks.token.tokenId; beforeAll(async () => { await initWasm(); ecc = new Ecc(); }); let mockedChronik; beforeEach(async () => { const mockedDate = new Date('2022-01-01T12:00:00.000Z'); jest.spyOn(global, 'Date').mockImplementation(() => mockedDate); // Mock the app with context at the Token Action screen mockedChronik = await initializeCashtabStateForTests( [agoraPartialAlphaWallet, agoraPartialBetaWallet], localforage, ); - // We need to give mockedChronik a plugin function - // This is required for creating a new Agora(mockedChronik) - mockedChronik.plugin = () => 'dummy plugin'; - // Mock chronik calls used to build token cache to show // the user can load a page without having the token info cached for (const tokenCacheMock of [ cachetCacheMocks, bullCacheMocks, scamCacheMocks, ]) { mockedChronik.setMock('token', { input: tokenCacheMock.token.tokenId, output: tokenCacheMock.token, }); mockedChronik.setMock('tx', { input: tokenCacheMock.token.tokenId, output: tokenCacheMock.tx, }); } // Mock the fetch call to Cashtab's price API global.fetch = jest.fn(); const fiatCode = 'usd'; // Use usd until you mock getting settings from localforage const cryptoId = appConfig.coingeckoId; // Keep this in the code, because different URLs will have different outputs requiring different parsing const priceApiUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${cryptoId}&vs_currencies=${fiatCode}&include_last_updated_at=true`; const xecPrice = 0.00003; const priceResponse = { ecash: { usd: xecPrice, last_updated_at: 1706644626, }, }; when(fetch) .calledWith(priceApiUrl) .mockResolvedValue({ json: () => Promise.resolve(priceResponse), }); }); afterEach(async () => { jest.clearAllMocks(); await clearLocalForage(localforage); }); it('Screen loads as expected if there are no agora partial listings', async () => { // Need to mock agora API endpoints const mockedAgora = new MockAgora(); // mock await agora.offeredFungibleTokenIds(); mockedAgora.setOfferedFungibleTokenIds([]); // also mock await agora.activeOffersByPubKey(toHex(activePk)) mockedAgora.setActiveOffersByPubKey( toHex(agoraPartialAlphaKeypair.pk), [], ); render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} agora={mockedAgora} route={`/agora/`} />, ); // Wait for the screen to load await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for agora offers to load await waitFor(() => expect( screen.queryByTitle('Loading active offers'), ).not.toBeInTheDocument(), ); // Wait for element to get token info and load expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); // We see the Token Offers section expect(screen.getByText('Token Offers')).toBeInTheDocument(); // But we have no offers expect( screen.getByText('No tokens are currently listed for sale'), ).toBeInTheDocument(); // We switch to see our created offers await userEvent.click(screen.getByTitle('Toggle Active Offers')); // We have made no offers // This is always empty if active offers is empty, as for partials, active offers will render both // public offers and offers created by your wallet // Your offers you can only cancel, not buy expect( screen.getByText('You do not have any listed tokens'), ).toBeInTheDocument(); }); it('A chronik error notice is rendered if there is some error in querying listings', async () => { // Need to mock agora API endpoints const mockedAgora = new MockAgora(); // mock await agora.offeredFungibleTokenIds(); mockedAgora.setOfferedFungibleTokenIds(new Error('some chronik error')); // also mock await agora.activeOffersByPubKey(toHex(activePk)) mockedAgora.setActiveOffersByPubKey( toHex(agoraPartialAlphaKeypair.pk), [], ); render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} agora={mockedAgora} route={`/agora/`} />, ); // Wait for the screen to load await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for agora offers to load await waitFor(() => expect( screen.queryByTitle('Loading active offers'), ).not.toBeInTheDocument(), ); // A chronik error notice is rendered expect( await screen.findByText( 'Error querying listed tokens. Please try again later.', ), ).toBeInTheDocument(); }); it('We can see a rendered offer', async () => { // Need to mock agora API endpoints const mockedAgora = new MockAgora(); // mock await agora.offeredFungibleTokenIds(); mockedAgora.setOfferedFungibleTokenIds([CACHET_TOKEN_ID]); // then mock for each one agora.activeOffersByTokenId(offeredTokenId) mockedAgora.setActiveOffersByTokenId(CACHET_TOKEN_ID, [ agoraOfferCachetAlphaOne, ]); // also mock await agora.activeOffersByPubKey(toHex(activePk)) mockedAgora.setActiveOffersByPubKey( toHex(agoraPartialAlphaKeypair.pk), [agoraOfferCachetAlphaOne], ); render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} agora={mockedAgora} route={`/agora/`} />, ); // Wait for the screen to load await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for agora offers to load await waitFor(() => expect( screen.queryByTitle('Loading active offers'), ).not.toBeInTheDocument(), ); // Wait for element to get token info and load expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); // We have an offer expect(screen.getByText('Token Offers')).toBeInTheDocument(); // We see the token name and ticker above its PartialOffer after OrderBooks load expect(await screen.findByText('Cachet (CACHET)')).toBeInTheDocument(); // Because this offer was created by this wallet, we have the option to cancel it expect( await screen.findByRole('button', { name: 'Cancel your offer' }), ).toBeInTheDocument(); }); it('We can fetch and use the blacklist from token server', async () => { when(fetch) .calledWith(`${tokenConfig.blacklistServerUrl}/blacklist`) .mockResolvedValue({ json: () => Promise.resolve({ tokenIds: [SCAM_TOKEN_ID] }), }); // Need to mock agora API endpoints const mockedAgora = new MockAgora(); // mock await agora.offeredFungibleTokenIds(); mockedAgora.setOfferedFungibleTokenIds([SCAM_TOKEN_ID]); // then mock for each one agora.activeOffersByTokenId(offeredTokenId) mockedAgora.setActiveOffersByTokenId(SCAM_TOKEN_ID, [scamAgoraOffer]); // also mock await agora.activeOffersByPubKey(toHex(activePk)) mockedAgora.setActiveOffersByPubKey( toHex(agoraPartialAlphaKeypair.pk), [scamAgoraOffer], ); render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} agora={mockedAgora} route={`/agora/`} />, ); // Wait for the screen to load await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for agora offers to load await waitFor(() => expect( screen.queryByTitle('Loading active offers'), ).not.toBeInTheDocument(), ); // Wait for element to get token info and load expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); // We see the Token Offers section expect(screen.getByText('Token Offers')).toBeInTheDocument(); // But we have no offers expect( screen.getByText('No tokens are currently listed for sale'), ).toBeInTheDocument(); // We switch to see our created offers await userEvent.click(screen.getByTitle('Toggle Active Offers')); expect(screen.getByText('Manage your listings')).toBeInTheDocument(); // We see the token name and ticker above its PartialOffer expect( screen.getByText('Badger Universal Token (BUX)'), ).toBeInTheDocument(); // Because this offer was created by this wallet, we have the option to cancel it expect( screen.getByRole('button', { name: 'Cancel your offer' }), ).toBeInTheDocument(); }); it('On token server API fail, we fall back to locally maintained blacklist. A blacklisted offer does not render in all offers, but will render in My offers', async () => { when(fetch) .calledWith(`${tokenConfig.blacklistServerUrl}/blacklist`) .mockResolvedValue({ json: () => Promise.resolve(new Error('some token server api error')), }); // Need to mock agora API endpoints const mockedAgora = new MockAgora(); // mock await agora.offeredFungibleTokenIds(); mockedAgora.setOfferedFungibleTokenIds([SCAM_TOKEN_ID]); // then mock for each one agora.activeOffersByTokenId(offeredTokenId) mockedAgora.setActiveOffersByTokenId(SCAM_TOKEN_ID, [scamAgoraOffer]); // also mock await agora.activeOffersByPubKey(toHex(activePk)) mockedAgora.setActiveOffersByPubKey( toHex(agoraPartialAlphaKeypair.pk), [scamAgoraOffer], ); render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} agora={mockedAgora} route={`/agora/`} />, ); // Wait for the screen to load await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for agora offers to load await waitFor(() => expect( screen.queryByTitle('Loading active offers'), ).not.toBeInTheDocument(), ); // Wait for element to get token info and load expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); // We see the Token Offers section expect(screen.getByText('Token Offers')).toBeInTheDocument(); // But we have no offers expect( screen.getByText('No tokens are currently listed for sale'), ).toBeInTheDocument(); // We switch to see our created offers await userEvent.click(screen.getByTitle('Toggle Active Offers')); expect(screen.getByText('Manage your listings')).toBeInTheDocument(); // We see the token name and ticker above its PartialOffer expect( screen.getByText('Badger Universal Token (BUX)'), ).toBeInTheDocument(); // Because this offer was created by this wallet, we have the option to cancel it expect( screen.getByRole('button', { name: 'Cancel your offer' }), ).toBeInTheDocument(); }); it('We can see multiple offers, some we made, others we did not, and we can cancel an offer', async () => { // We do not want the date mocked here, it interferes with changing wallets somehow jest.spyOn(global, 'Date').mockRestore(); // Need to mock agora API endpoints const mockedAgora = new MockAgora(); // mock await agora.offeredFungibleTokenIds() to return offers for both tokens mockedAgora.setOfferedFungibleTokenIds([ CACHET_TOKEN_ID, BULL_TOKEN_ID, ]); // then mock for each one agora.activeOffersByTokenId(offeredTokenId) mockedAgora.setActiveOffersByTokenId(CACHET_TOKEN_ID, [ agoraOfferCachetAlphaOne, agoraOfferCachetAlphaTwo, agoraOfferCachetBetaOne, ]); mockedAgora.setActiveOffersByTokenId(BULL_TOKEN_ID, [ agoraOfferBullAlphaOne, ]); // also mock await agora.activeOffersByPubKey(toHex(activePk)), for both walletse mockedAgora.setActiveOffersByPubKey( toHex(agoraPartialAlphaKeypair.pk), [ agoraOfferCachetAlphaOne, agoraOfferCachetAlphaTwo, agoraOfferBullAlphaOne, ], ); mockedAgora.setActiveOffersByPubKey(toHex(agoraPartialBetaKeypair.pk), [ agoraOfferCachetBetaOne, ]); // Set mocks for tx that cancels a listing const cancelHex = '0200000002f7bb552354b6f5076eb2664a8bcbbedc87b42f2ebfcb1480ee0a9141bbae635900000000644159fdead8c105622e86245089e53c93b61aca17ed1d2407faa40a595ed0d232217578ce586ca14c88cc851dc17c3fe21689dffe572c89d725abddd04b3d289dc141210233f09cd4dc3381162f09975f90866f085350a5ec890d7fba5f6739c9c0ac2afdffffffff4c48dd93dcc794ee5c9df7a7bc5637e6c78419842b8b9459727db116aed4c42501000000fdad010441475230075041525449414c413e20e3f9d86a991e416d956a1db69e0a2be9cf3cd02f2392f2d4f063d9af49dd1fe3c351ef684eafbae5399ed161e2b21895ae84c364daa9cc06846063abaa9f41004d5a014c766a04534c500001010453454e442001d63c4f4cb496829a6743f7b1805d086ea3877a1dd34b3f92ffba2c9c99f89608000000000000000000027de6240000000000d17b000000000000e833270100000000a16f7e500233f09cd4dc3381162f09975f90866f085350a5ec890d7fba5f6739c9c0ac2afd089881ff7f00000000ab7b63817b6ea2697604e8332701a26976037de6249700887d94527901377f75789263587e78037de624965880bc007e7e68587e5279037de624965880bc007e7e825980bc7c7e0200007e7b02d07b9302d17b9656807e041976a914707501557f77a97e0288ac7e7e6b7d02220258800317a9147e024c7672587d807e7e7e01ab7e537901257f7702d8007f5c7f7701207f547f7504a16f7e50886b7ea97e01877e7c92647500687b8292697e6c6c7b7eaa88520144807c7ea86f7bbb7501c17e7c677501557f7768ad075041525449414c88044147523087ffffffff030000000000000000376a04534c500001010453454e442001d63c4f4cb496829a6743f7b1805d086ea3877a1dd34b3f92ffba2c9c99f89608000000000000037822020000000000001976a91403b830e4b9dce347f3495431e1f9d1005f4b420488acb2620600000000001976a91403b830e4b9dce347f3495431e1f9d1005f4b420488ac00000000'; const cancelTxid = 'de8f638c5b11592825ff74f2ec59892f721bc1151486efe86d99a44bf05865bf'; mockedChronik.setMock('broadcastTx', { input: cancelHex, output: { txid: cancelTxid }, }); render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} agora={mockedAgora} route={`/agora/`} />, ); // Wait for the screen to load await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for agora offers to load await waitFor(() => expect( screen.queryByTitle('Loading active offers'), ).not.toBeInTheDocument(), ); // Wait for element to get token info and load expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); // We have an offer expect(screen.getByText('Token Offers')).toBeInTheDocument(); // We see all token names and tickers above their PartialOffers expect(await screen.findByText('Cachet (CACHET)')).toBeInTheDocument(); expect(await screen.findByText('Bull (BULL)')).toBeInTheDocument(); // For BULL, there is only one offer, so that offer is the spot price const BULL_SPOT_MIN_QTY = '8'; const BULL_SPOT_PRICE_MIN_BUY = '400.42k XEC'; const BULL_SPOT_PRICE_FIAT_MIN_BUY = '$12.01 USD'; // We await this as the component will load and render token info before // the offers have finished loading expect( await screen.findByText(`${BULL_SPOT_MIN_QTY} BULL`), ).toBeInTheDocument(); expect(screen.getByText(BULL_SPOT_PRICE_MIN_BUY)).toBeInTheDocument(); expect( screen.getByText(BULL_SPOT_PRICE_FIAT_MIN_BUY), ).toBeInTheDocument(); // For tokens with multiple partial offers available, the lowest-priced // offer is selected by default ("spot price") const CACHET_SPOT_MIN_QTY = '.20'; const CACHET_SPOT_PRICE_MIN_BUY = '240.64 XEC'; const CACHET_SPOT_PRICE_FIAT_MIN_BUY = '$0.0072 USD'; // Quantities are not displayed until they load, so we await expect( await screen.findByText(`${CACHET_SPOT_MIN_QTY} CACHET`), ).toBeInTheDocument(); expect(screen.getByText(CACHET_SPOT_PRICE_MIN_BUY)).toBeInTheDocument(); expect( screen.getByText(CACHET_SPOT_PRICE_FIAT_MIN_BUY), ).toBeInTheDocument(); // Because both spot offers were created by the active Alpha wallet, // we see two cancel buttons expect( screen.getAllByRole('button', { name: 'Cancel your offer' })[1], ).toBeInTheDocument(); // Change wallets using the dropdown menu at the top of the screen // NB you cannot have the Date() function mocked if you want to test changing wallets await userEvent.selectOptions( screen.getByTestId('wallet-select'), screen.getByText('Agora Partial Beta'), ); expect(await screen.findByText('42.00 XEC')).toBeInTheDocument(); // Wait for tokens to re-load (triggered by wallet change) await waitFor(() => expect( screen.queryByTitle('Loading active offers'), ).not.toBeInTheDocument(), ); // Wait for the wallet to load await waitFor(() => expect(screen.queryByTitle('Loading')).not.toBeInTheDocument(), ); // Wait for active offers to load expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); // Switching wallets triggers a refresh of the offers // Now that we are using the other wallet, we see two Buy buttons expect( await screen.findByRole('button', { name: 'Buy Cachet (CACHET)' }), ).toBeInTheDocument(); expect( await screen.findByRole('button', { name: 'Buy Bull (BULL)' }), ).toBeInTheDocument(); // Hit the switch to show listings created by the active wallet (now Beta) const toggleAllVsMyOffersSwitch = screen.getByTitle( 'Toggle Active Offers', ); await userEvent.click(toggleAllVsMyOffersSwitch); // we see only the beta-created Cachet offer expect(screen.getByText('Cachet (CACHET)')).toBeInTheDocument(); // We do not see any offers for Bull, this was created by alpha expect(screen.queryByText('Bull (BULL)')).not.toBeInTheDocument(); // Note that we only see orderbooks that we have offers for // But we see all offers for these orderbooks // The spot price offer is rendered by default // This happens to not be our offer expect( await screen.findByText(`${CACHET_SPOT_MIN_QTY} CACHET`), ).toBeInTheDocument(); // We can buy this offer from the Manage screen expect( screen.getByRole('button', { name: 'Buy Cachet (CACHET)' }), ).toBeInTheDocument(); // Select our offer await userEvent.click(screen.getByText('$0.36 USD')); // Now we can only cancel our offer expect( screen.getByRole('button', { name: 'Cancel your offer' }), ).toBeInTheDocument(); expect( screen.queryByRole('button', { name: 'Buy Cachet (CACHET)' }), ).not.toBeInTheDocument(); // OK go back to all offers await userEvent.click(toggleAllVsMyOffersSwitch); // Nice but let's go back to the first wallet // Change wallets using the dropdown menu at the top of the screen // NB you cannot have the Date() function mocked if you want to test changing wallets await userEvent.selectOptions( screen.getByTestId('wallet-select'), screen.getByText('Agora Partial Alpha'), ); expect(await screen.findByText('4,200.00 XEC')).toBeInTheDocument(); // Wait for tokens to re-load (triggered by wallet change) await waitFor(() => expect( screen.queryByTitle('Loading active offers'), ).not.toBeInTheDocument(), ); // Wait for the wallet to load await waitFor(() => expect(screen.queryByTitle('Loading')).not.toBeInTheDocument(), ); expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); // Now we see cancel buttons again expect( ( await screen.findAllByRole('button', { name: 'Cancel your offer', }) )[1], ).toBeInTheDocument(); // If we select the offer created by the Beta wallet, we see a buy button await userEvent.click(screen.getByText('$0.36 USD')); // We also see updates to the rendered spot details const UPDATED_CACHET_SPOT_MIN_QTY = '.30'; const UPDATED_CACHET_SPOT_PRICE_MIN_BUY = '3.6k XEC'; const UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY = '$0.11 USD'; expect( screen.getByText(`${UPDATED_CACHET_SPOT_MIN_QTY} CACHET`), ).toBeInTheDocument(); expect( screen.getByText(UPDATED_CACHET_SPOT_PRICE_MIN_BUY), ).toBeInTheDocument(); expect( screen.getByText(UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY), ).toBeInTheDocument(); expect( screen.getByRole('button', { name: 'Buy Cachet (CACHET)' }), ).toBeInTheDocument(); // Let's cancel the BULL offer await userEvent.click( screen.getByRole('button', { name: 'Cancel your offer' }), ); // We see a confirmation modal expect( screen.getByText( 'Cancel your offer to sell 888 Bull (BULL) for 400,424.96 XEC ($12.01 USD)?', ), ).toBeInTheDocument(); // We cancel await userEvent.click(screen.getByText('OK')); // Notification on successful cancel expect(await screen.findByText(`Canceled listing`)).toBeInTheDocument(); // Note we can't test that offers are refreshed as we cannot dynamically adjust chronik mocks // Would need regtest integration to do this }); it('We can buy an offer', async () => { // Need to mock agora API endpoints const mockedAgora = new MockAgora(); // mock await agora.offeredFungibleTokenIds() to return offers for both tokens mockedAgora.setOfferedFungibleTokenIds([ CACHET_TOKEN_ID, BULL_TOKEN_ID, ]); // then mock for each one agora.activeOffersByTokenId(offeredTokenId) mockedAgora.setActiveOffersByTokenId(CACHET_TOKEN_ID, [ agoraOfferCachetAlphaOne, agoraOfferCachetAlphaTwo, agoraOfferCachetBetaOne, ]); mockedAgora.setActiveOffersByTokenId(BULL_TOKEN_ID, [ agoraOfferBullAlphaOne, ]); // also mock await agora.activeOffersByPubKey(toHex(activePk)), for both walletse mockedAgora.setActiveOffersByPubKey( toHex(agoraPartialAlphaKeypair.pk), [ agoraOfferCachetAlphaOne, agoraOfferCachetAlphaTwo, agoraOfferBullAlphaOne, ], ); mockedAgora.setActiveOffersByPubKey(toHex(agoraPartialBetaKeypair.pk), [ agoraOfferCachetBetaOne, ]); // Set mocks for tx that buys a listing const buyHex = '02000000023f091a214fdf5ff45e1cae5f7830800a73740cbd3b752f3694090c' + 'c962b59c8101000000fd47030441475230075041525449414c21023c72addb4f' + 'df09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b140727d4804' + '0c07efcd104ceb7b7aef07834dbd094f93d1728f584859383c476e5c2c369a51' + 'b2900ac670eebea381a1db8811609b5b19c22d886c808d6ecd31cc8344220200' + '00000000001976a91403b830e4b9dce347f3495431e1f9d1005f4b420488acaf' + 'dd0000000000001976a91403b830e4b9dce347f3495431e1f9d1005f4b420488' + 'ac4d2f013f091a214fdf5ff45e1cae5f7830800a73740cbd3b752f3694090cc9' + '62b59c8101000000d67b63817b6ea269760384c420a26976039e17019700887d' + '94527901377f75789263587e78039e1701965880bc007e7e68587e5279039e17' + '01965880bc007e7e825980bc7c7e01007e7b02f6059302f7059657807e041976' + 'a914707501557f77a97e0288ac7e7e6b7d02220258800317a9147e024c767258' + '7d807e7e7e01ab7e537901257f7702d6007f5c7f7701207f547f7504ce731f40' + '886b7ea97e01877e7c92647500687b8292697e6c6c7b7eaa88520144807c7ea8' + '6f7bbb7501c17e7c677501557f7768ad075041525449414c8804414752308722' + '02000000000000ffffffff7388db19d999ee9eb8b07c726d4fb078a003c9ccea' + 'fbdb5b89b56b15be464908ce731f40c10000000384c420514d58014c766a0453' + '4c500001010453454e4420aed861a31b96934b88c0252ede135cb9700d7649f6' + '9191235087a3030e553cb108000000000000000000019e17010000000000f705' + '00000000000084c4200000000000ce731f40021e75febb8ae57a8805e80df937' + '32ab7d5d8606377cb30c0f02444809cc085f3908a0a3ff7f00000000ab7b6381' + '7b6ea269760384c420a26976039e17019700887d94527901377f75789263587e' + '78039e1701965880bc007e7e68587e5279039e1701965880bc007e7e825980bc' + '7c7e01007e7b02f6059302f7059657807e041976a914707501557f77a97e0288' + 'ac7e7e6b7d02220258800317a9147e024c7672587d807e7e7e01ab7e53790125' + '7f7702d6007f5c7f7701207f547f7504ce731f40886b7ea97e01877e7c926475' + '00687b8292697e6c6c7b7eaa88520144807c7ea86f7bbb7501c17e7c67750155' + '7f7768ad075041525449414c88044147523087fffffffff7bb552354b6f5076e' + 'b2664a8bcbbedc87b42f2ebfcb1480ee0a9141bbae63590000000064412ea299' + '62ae7585308ea45ac92adb7aaa13920e333de700fda95852726016c25a9c25b6' + '2e18621d9c834d1369985bd9fe91c8fdac3783a00eea94cd8e1fa629d7412102' + '33f09cd4dc3381162f09975f90866f085350a5ec890d7fba5f6739c9c0ac2afd' + 'ffffffff050000000000000000496a04534c500001010453454e4420aed861a3' + '1b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb108000000' + '000000000008000000000000751208000000000000001e007f05000000000019' + '76a914f208ef75eb0dd778ea4540cbd966a830c7b94bb088ac22020000000000' + '0017a914211be508fb7608c0a3b3d7a36279894d0450e7378722020000000000' + '001976a91403b830e4b9dce347f3495431e1f9d1005f4b420488acafdd000000' + '0000001976a91403b830e4b9dce347f3495431e1f9d1005f4b420488acce731f' + '40'; const buyTxid = '6fbee4e0460e3730f000e2927d69d881b8a536b80fd43b839d32e34c3490ff00'; mockedChronik.setMock('broadcastTx', { input: buyHex, output: { txid: buyTxid }, }); render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} agora={mockedAgora} route={`/agora/`} />, ); // Wait for the screen to load await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for agora offers to load await waitFor(() => expect( screen.queryByTitle('Loading active offers'), ).not.toBeInTheDocument(), ); // Wait for element to get token info and load expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); // We have an offer expect(screen.getByText('Token Offers')).toBeInTheDocument(); // We see all token names and tickers above their PartialOffers expect(await screen.findByText('Cachet (CACHET)')).toBeInTheDocument(); expect(await screen.findByText('Bull (BULL)')).toBeInTheDocument(); // We see the expected spot offer for CACHET const CACHET_SPOT_MIN_QTY = '.20'; const CACHET_SPOT_PRICE_MIN_BUY = '240.64 XEC'; const CACHET_SPOT_PRICE_FIAT_MIN_BUY = '$0.0072 USD'; // Quantities are not displayed until they load, so we await expect( await screen.findByText(`${CACHET_SPOT_MIN_QTY} CACHET`), ).toBeInTheDocument(); expect(screen.getByText(CACHET_SPOT_PRICE_MIN_BUY)).toBeInTheDocument(); expect( screen.getByText(CACHET_SPOT_PRICE_FIAT_MIN_BUY), ).toBeInTheDocument(); // Because both spot offers were created by the active Alpha wallet, // we see two cancel buttons expect( screen.getAllByRole('button', { name: 'Cancel your offer' })[1], ).toBeInTheDocument(); // If we select the offer created by the Beta wallet, we see a buy button await userEvent.click(screen.getByText('$0.36 USD')); // We also see updates to the rendered spot details const UPDATED_CACHET_SPOT_MIN_QTY = '.30'; const UPDATED_CACHET_SPOT_PRICE_MIN_BUY = '3.6k XEC'; const UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY = '$0.11 USD'; expect( screen.getByText(`${UPDATED_CACHET_SPOT_MIN_QTY} CACHET`), ).toBeInTheDocument(); expect( screen.getByText(UPDATED_CACHET_SPOT_PRICE_MIN_BUY), ).toBeInTheDocument(); expect( screen.getByText(UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY), ).toBeInTheDocument(); const buyCachetButton = screen.getByRole('button', { name: 'Buy Cachet (CACHET)', }); expect(buyCachetButton).toBeInTheDocument(); // Note I was not able to adjust the slider value in react testing library // We do the min buy await userEvent.click(buyCachetButton); // We see a confirmation modal expect( screen.getByText( `Buy ${UPDATED_CACHET_SPOT_MIN_QTY} Cachet (CACHET) for 3,601.92 XEC (${UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY})?`, ), ).toBeInTheDocument(); // We buy await userEvent.click(screen.getByText('OK')); // Notification on successful buy expect( await screen.findByText( `Bought ${UPDATED_CACHET_SPOT_MIN_QTY} Cachet (CACHET) for 3,601.92 XEC (${UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY})`, ), ).toBeInTheDocument(); // Note we can't test that offers are refreshed as we cannot dynamically adjust chronik mocks // Would need regtest integration to do this }); it('We get expected error if we try to buy an offer we cannot afford', async () => { // Need to mock agora API endpoints const mockedAgora = new MockAgora(); // mock await agora.offeredFungibleTokenIds() to return offers for both tokens mockedAgora.setOfferedFungibleTokenIds([ CACHET_TOKEN_ID, BULL_TOKEN_ID, ]); // then mock for each one agora.activeOffersByTokenId(offeredTokenId) mockedAgora.setActiveOffersByTokenId(CACHET_TOKEN_ID, [ agoraOfferCachetAlphaOne, agoraOfferCachetAlphaTwo, agoraOfferCachetBetaOne, ]); mockedAgora.setActiveOffersByTokenId(BULL_TOKEN_ID, [ agoraOfferBullAlphaOne, ]); // also mock await agora.activeOffersByPubKey(toHex(activePk)), for both walletse mockedAgora.setActiveOffersByPubKey( toHex(agoraPartialAlphaKeypair.pk), [ agoraOfferCachetAlphaOne, agoraOfferCachetAlphaTwo, agoraOfferBullAlphaOne, ], ); mockedAgora.setActiveOffersByPubKey(toHex(agoraPartialBetaKeypair.pk), [ agoraOfferCachetBetaOne, ]); const emptyWalletMockedChronik = await initializeCashtabStateForTests( [ { ...agoraPartialAlphaWallet, state: { ...agoraPartialAlphaWallet.state, nonSlpUtxos: [], }, }, agoraPartialBetaWallet, ], localforage, ); // Mock chronik calls used to build token cache to show // the user can load a page without having the token info cached for (const tokenCacheMock of [cachetCacheMocks, bullCacheMocks]) { emptyWalletMockedChronik.setMock('token', { input: tokenCacheMock.token.tokenId, output: tokenCacheMock.token, }); emptyWalletMockedChronik.setMock('tx', { input: tokenCacheMock.token.tokenId, output: tokenCacheMock.tx, }); } render( <CashtabTestWrapper chronik={emptyWalletMockedChronik} ecc={ecc} agora={mockedAgora} route={`/agora/`} />, ); // Wait for the screen to load await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for agora offers to load await waitFor(() => expect( screen.queryByTitle('Loading active offers'), ).not.toBeInTheDocument(), ); // Wait for element to get token info and load expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); // We have an offer expect(screen.getByText('Token Offers')).toBeInTheDocument(); // We see all token names and tickers above their PartialOffers expect(await screen.findByText('Cachet (CACHET)')).toBeInTheDocument(); expect(await screen.findByText('Bull (BULL)')).toBeInTheDocument(); // We see the expected spot offer for CACHET const CACHET_SPOT_MIN_QTY = '.20'; const CACHET_SPOT_PRICE_MIN_BUY = '240.64 XEC'; const CACHET_SPOT_PRICE_FIAT_MIN_BUY = '$0.0072 USD'; // Quantities are not displayed until they load, so we await expect( await screen.findByText(`${CACHET_SPOT_MIN_QTY} CACHET`), ).toBeInTheDocument(); expect(screen.getByText(CACHET_SPOT_PRICE_MIN_BUY)).toBeInTheDocument(); expect( screen.getByText(CACHET_SPOT_PRICE_FIAT_MIN_BUY), ).toBeInTheDocument(); // Because both spot offers were created by the active Alpha wallet, // we see two cancel buttons expect( screen.getAllByRole('button', { name: 'Cancel your offer' })[1], ).toBeInTheDocument(); // If we select the offer created by the Beta wallet, we see a buy button await userEvent.click(screen.getByText('$0.36 USD')); // We also see updates to the rendered spot details const UPDATED_CACHET_SPOT_MIN_QTY = '.30'; const UPDATED_CACHET_SPOT_PRICE_MIN_BUY = '3.6k XEC'; const UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY = '$0.11 USD'; expect( screen.getByText(`${UPDATED_CACHET_SPOT_MIN_QTY} CACHET`), ).toBeInTheDocument(); expect( screen.getByText(UPDATED_CACHET_SPOT_PRICE_MIN_BUY), ).toBeInTheDocument(); expect( screen.getByText(UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY), ).toBeInTheDocument(); const buyCachetButton = screen.getByRole('button', { name: 'Buy Cachet (CACHET)', }); expect(buyCachetButton).toBeInTheDocument(); // Note I was not able to adjust the slider value in react testing library // We do the min buy await userEvent.click(buyCachetButton); expect( screen.getByText( `Buy ${UPDATED_CACHET_SPOT_MIN_QTY} Cachet (CACHET) for 3,601.92 XEC (${UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY})?`, ), ).toBeInTheDocument(); // We buy await userEvent.click(screen.getByText('OK')); // Error notification for buy we can't afford expect( await screen.findByText( `Error: Insufficient utxos to accept this offer`, ), ).toBeInTheDocument(); }); }); diff --git a/cashtab/src/components/Etokens/__tests__/TokenActions.test.js b/cashtab/src/components/Etokens/__tests__/TokenActions.test.js index c1531aee4..1ef8d4bcf 100644 --- a/cashtab/src/components/Etokens/__tests__/TokenActions.test.js +++ b/cashtab/src/components/Etokens/__tests__/TokenActions.test.js @@ -1,1941 +1,1933 @@ // Copyright (c) 2024 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import userEvent from '@testing-library/user-event'; import { when } from 'jest-when'; import { initializeCashtabStateForTests, clearLocalForage, } from 'components/App/fixtures/helpers'; import CashtabTestWrapper from 'components/App/fixtures/CashtabTestWrapper'; import appConfig from 'config/app'; import 'fake-indexeddb/auto'; import localforage from 'localforage'; import { tokenTestWallet, supportedTokens, slp1FixedMocks, slp1VarMocks, alpMocks, slp1NftParentMocks, slp1NftParentWithChildrenMocks, slp1NftChildMocks, } from 'components/Etokens/fixtures/mocks'; import { cachedHeismanNftOne, heismanNftOneOffer, heismanNftOneCache, heismanCollectionCacheMocks, } from 'components/Agora/fixtures/mocks'; import CashtabCache from 'config/CashtabCache'; import { cashtabCacheToJSON } from 'helpers'; import { Ecc, initWasm } from 'ecash-lib'; import { MockAgora } from '../../../../../modules/mock-chronik-client'; import { Agora } from 'ecash-agora'; import { token as tokenConfig } from 'config/token'; import { explorer } from 'config/explorer'; describe('<Token /> available actions rendered', () => { let ecc; beforeAll(async () => { await initWasm(); ecc = new Ecc(); }); let mockedChronik; beforeEach(async () => { const mockedDate = new Date('2022-01-01T12:00:00.000Z'); jest.spyOn(global, 'Date').mockImplementation(() => mockedDate); // Mock the app with context at the Token Action screen mockedChronik = await initializeCashtabStateForTests( tokenTestWallet, localforage, ); // Build chronik mocks that Cashtab would use to add token info to cache for (const tokenMock of supportedTokens) { mockedChronik.setMock('token', { input: tokenMock.tokenId, output: tokenMock.token, }); mockedChronik.setMock('tx', { input: tokenMock.tokenId, output: tokenMock.tx, }); mockedChronik.setTokenId(tokenMock.tokenId); mockedChronik.setUtxosByTokenId(tokenMock.tokenId, { tokenId: tokenMock.tokenId, utxos: tokenMock.utxos, }); // Set empty tx history to mock no existing NFTs mockedChronik.setTxHistoryByTokenId(tokenMock.tokenId, []); } // Mock the fetch call to Cashtab's price API global.fetch = jest.fn(); const fiatCode = 'usd'; // Use usd until you mock getting settings from localforage const cryptoId = appConfig.coingeckoId; // Keep this in the code, because different URLs will have different outputs requiring different parsing const priceApiUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${cryptoId}&vs_currencies=${fiatCode}&include_last_updated_at=true`; const xecPrice = 0.00003; const priceResponse = { ecash: { usd: xecPrice, last_updated_at: 1706644626, }, }; when(fetch) .calledWith(priceApiUrl) .mockResolvedValue({ json: () => Promise.resolve(priceResponse), }); }); afterEach(async () => { jest.clearAllMocks(); await clearLocalForage(localforage); }); it('SLP1 fixed supply token', async () => { render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} route={`/send-token/${slp1FixedMocks.tokenId}`} />, ); const { tokenName } = slp1FixedMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // We can click an info icon to learn more about this token type await userEvent.click( await screen.findByRole('button', { name: 'Click for more info about this token type', }), ); expect( screen.getByText( `SLP 1 fungible token. Token may be of fixed supply if no mint batons exist. If you have a mint baton, you can mint more of this token at any time. May have up to 9 decimal places.`, ), ).toBeInTheDocument(); // Close out of the info modal await userEvent.click(screen.getByText('OK')); // The supply is correctly rendered as fixed expect( screen.getByText('2,999,998,798.000000000 (fixed)'), ).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // The sell switch is turned on by default expect(screen.getByTitle('Toggle Sell Token')).toHaveProperty( 'checked', true, ); // The send switch is present expect(screen.getByTitle('Toggle Send')).toBeInTheDocument(); // The Airdrop switch is present expect(screen.getByTitle('Toggle Airdrop')).toBeInTheDocument(); // The Burn switch is present expect(screen.getByTitle('Toggle Burn')).toBeInTheDocument(); // The Mint switch is not rendered expect(screen.queryByTitle('Toggle Mint')).not.toBeInTheDocument(); }); it('SLP1 variable supply token with mint baton', async () => { render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} route={`/send-token/${slp1VarMocks.tokenId}`} />, ); const { tokenName } = slp1VarMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // We can click an info icon to learn more about this token type await userEvent.click( await screen.findByRole('button', { name: 'Click for more info about this token type', }), ); expect( screen.getByText( `SLP 1 fungible token. Token may be of fixed supply if no mint batons exist. If you have a mint baton, you can mint more of this token at any time. May have up to 9 decimal places.`, ), ).toBeInTheDocument(); // Close out of the info modal await userEvent.click(screen.getByText('OK')); // The supply is correctly rendered as fixed expect( screen.getByText('18,446,744,073.709551615 (var.)'), ).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // The sell switch is turned on by default expect(screen.getByTitle('Toggle Sell Token')).toHaveProperty( 'checked', true, ); // The send switch is present expect(screen.getByTitle('Toggle Send')).toBeInTheDocument(); // The Airdrop switch is present expect(screen.getByTitle('Toggle Airdrop')).toBeInTheDocument(); // The Burn switch is present expect(screen.getByTitle('Toggle Burn')).toBeInTheDocument(); // The Mint switch is present and not disabled expect(screen.getByTitle('Toggle Mint')).toHaveProperty( 'disabled', false, ); }); it('We can list an SLP1 fungible token', async () => { // Mock Math.random() jest.spyOn(global.Math, 'random').mockReturnValue(0.5); // set a fixed value // SLP1 ad prep const adPrepHex = '0200000002666de5d5852807a13612b6ea0373643266d435822daeb39c29e5d4b67e893cda0100000064414feb64ffdf50b0eb40a6fe0c34da65e94e0cbbbc2e58f2b290f3b2bf31480b34a57c4862ee177129dc8a1ce645573cd240e5e83d336d19ff22c3a7675bc903564121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf7926180300000064410f0461f0e843cc5b78196e3fdb3b89d64948629645f3b44ea960c2a5ac8f5835189697165a01cc259a0f4eff931c83e110019ee5c7721a43e0dde11ba04e068d4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff040000000000000000406a04534c500001010453454e442020a0b9337a78603c6681ed2bc541593375535dcd9979196620ce71f233f2f6f80800000019d80000000800000000001d9600060500000000000017a914e49e695e2f466e34447cb253567b8b277b60e3908722020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac2c2e0f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const adPrepTxid = '280b6fda5a11a94145f3b4203fb4f199d875d3621c8e4cc9d63501e73b9649bc'; mockedChronik.setMock('broadcastTx', { input: adPrepHex, output: { txid: adPrepTxid }, }); // SLP1 ad list const adListHex = '0200000001bc49963be70135d6c94c8e1c62d375d899f1b43f20b4f34541a9115ada6f0b2801000000dd0441475230075041525449414c41b11b013fb8140dcce13f93ee99584b1c6b547ee076ed63f9ec0a6c0068ad84c5420ecd608af68134366576bae4196a83f6a8f521c50dea4acc75dda6215c7fec414c8c4c766a04534c500001010453454e442020a0b9337a78603c6681ed2bc541593375535dcd9979196620ce71f233f2f6f80800000000000000000300dbf30400000000003dc7010000000000d226af0c000000002099c53f031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02d01557f77ad075041525449414c88044147523087ffffffff020000000000000000376a04534c500001010453454e442020a0b9337a78603c6681ed2bc541593375535dcd9979196620ce71f233f2f6f80800000019d8000000220200000000000017a91472df09389a835adb0e13e32bf1c91144ed107eef8700000000'; const adListTxid = '823f652e22d154fc7bdd77ee9d9fa37c77e9649235f1430958bef68b7428b9ae'; mockedChronik.setMock('broadcastTx', { input: adListHex, output: { txid: adListTxid }, }); - // We need to give mockedChronik a plugin function - // This is required for creating a new Agora(mockedChronik) - mockedChronik.plugin = () => 'dummy plugin'; - // Mock response for agora select params check // Note // We obtain EXPECTED_OFFER_P2SH by adding // console.log(toHex(shaRmd160(agoraScript.bytecode))); // to ecash-agora lib and running this test // Note that Date() and Math.random() must be mocked to keep this deterministic const EXPECTED_OFFER_P2SH = '72df09389a835adb0e13e32bf1c91144ed107eef'; mockedChronik.setScript('p2sh', EXPECTED_OFFER_P2SH); // We mock no existing utxos mockedChronik.setUtxos('p2sh', EXPECTED_OFFER_P2SH, { utxos: [] }); const agora = new Agora(mockedChronik); render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} agora={agora} route={`/send-token/${slp1FixedMocks.tokenId}`} />, ); const { tokenName } = slp1FixedMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // Token image is rendered expect( screen.getByAltText(`icon for ${slp1FixedMocks.tokenId}`), ).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // On load, default action for SLP is to list it expect(screen.getByTitle('Toggle Sell Token')).toBeEnabled(); // The list button is disabled on load const listButton = screen.getByRole('button', { name: /List Vespene Gas/, }); expect(listButton).toHaveProperty('disabled', true); // The price input is disabled until qty values are entered const priceInput = screen.getByPlaceholderText( 'Enter list price (per token)', ); expect(priceInput).toHaveProperty('disabled', true); // Enter token balance as offered qty await userEvent.type(screen.getByPlaceholderText('Offered qty'), '111'); // Enter a min qty await userEvent.type(screen.getByPlaceholderText('Min buy'), '11'); // The price input is no longer disabled expect(priceInput).toHaveProperty('disabled', false); // We see expected error msg if we try to list the token at a price where the min buy would cost less than dust await userEvent.type(priceInput, '0.001'); expect( screen.getByText( 'Minimum buy costs 0.011 XEC, must be at least 5.46 XEC', ), ).toBeInTheDocument(); // The buy button is disabled with invalid price expect(listButton).toHaveProperty('disabled', true); // Increase the price to a valid one await userEvent.clear(priceInput); await userEvent.type(priceInput, '0.5'); // The list button is no longer disabled expect(listButton).toHaveProperty('disabled', false); // The fiat price is previewed correctly expect( screen.getByText('0.50 XEC ($0.000015 USD) per token'), ).toBeInTheDocument(); // We can also set the price in fiat currency await userEvent.selectOptions( screen.getByTestId('currency-select-dropdown'), screen.getByTestId('fiat-option'), ); // The price input is cleared when the user changes from XEC price to fiat price expect(priceInput).toHaveValue(null); // We list for $2 per token await userEvent.type(priceInput, '5'); // The fiat price is previewed correctly expect( screen.getByText('$5 USD (166,666.67 XEC) per token'), ).toBeInTheDocument(); // We enter a low price in fiat await userEvent.clear(priceInput); await userEvent.type(priceInput, '0.00005'); // The fiat price is previewed correctly expect( await screen.findByText('$0.00005 USD (1.67 XEC) per token'), ).toBeInTheDocument(); // Click the now-enabled list button await userEvent.click(listButton); // We see expected confirmation modal to list the Token expect(screen.getByText('List VSP?')).toBeInTheDocument(); expect( screen.getByText('Create the following sell offer?'), ).toBeInTheDocument(); // Offered qty (actual, calculated from AgoraOffer) const actualOfferedQty = '110.998061056'; expect(screen.getByText(actualOfferedQty)).toBeInTheDocument(); // Min by (actual, calculated from AgoraOffer) expect(screen.getByText('11.005853696')).toBeInTheDocument(); // Actual price calculated from AgoraOffer const actualPricePerTokenForMinBuy = '1.66 XEC'; expect( screen.getByText(actualPricePerTokenForMinBuy), ).toBeInTheDocument(); // User input price expect(screen.getByText('1.67 XEC')).toBeInTheDocument(); // We can cancel and not create this listing await userEvent.click(screen.getByText('Cancel')); // The confirmation modal is gone expect(screen.queryByText('List VSP?')).not.toBeInTheDocument(); // We change our mind and list it await userEvent.click(listButton); // We wait for the preview to be calculated again expect(await screen.findByText('List VSP?')).toBeInTheDocument(); await userEvent.click(screen.getByText('OK')); // We see expected toast notification for the ad setup tx expect( await screen.findByText( `Successful ad setup tx to offer ${actualOfferedQty} Vespene Gas for ${actualPricePerTokenForMinBuy} per token`, ), ).toBeInTheDocument(); // We see the expected toast notification for the successful listing tx expect( await screen.findByText( `${actualOfferedQty} Vespene Gas listed for ${actualPricePerTokenForMinBuy} per token`, ), ).toBeInTheDocument(); }); it('We can correctly render an SLP1 NFT Parent token with no NFT Mint inputs, then create some NFT Mint inputs', async () => { const hex = '0200000002cc04a35686950a66845ebf8e37677fffcc5ee0e2b63e3f05822838273149660c010000006441878aa7e698097a4961646a2da44f701d8895cb065113fcf1d2e9f073afbc37025a5587e121bd0311a24a7af60445abfc4de7e3675a3a9f51cffddc875d88fca24121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf7926180300000064412f509f90f23f4b85b27452e0f25d33cef07ad8fef898e2d308c43fb0dfd6f7e00f7201336be4089171ddc094a24688882b518ec0c6958c904df12d0239a7342f4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff150000000000000000d96a04534c500001810453454e44200c66493127382882053f3eb6e2e05eccff7f67378ebf5e84660a958656a304cc08000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000000108000000000000005222020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac0d070f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const txid = 'cdc6afbf1ddd796388692ec9106816be1f9229ece11e545c1cbe6854ccf087ec'; mockedChronik.setMock('broadcastTx', { input: hex, output: { txid }, }); render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} route={`/token/${slp1NftParentMocks.tokenId}`} />, ); const { tokenName } = slp1NftParentMocks.token.genesisInfo; // Wait for the component to finish loading await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // We can click an info icon to learn more about this token type await userEvent.click( await screen.findByRole('button', { name: 'Click for more info about this token type', }), ); expect( screen.getByText( `The parent tokens for an NFT collection. Can be used to mint NFTs. No decimal places. The supply of this token is the potential quantity of NFTs which could be minted. If no mint batons exist, the supply is fixed.`, ), ).toBeInTheDocument(); // Close out of the info modal await userEvent.click(screen.getByText('OK')); // The supply is correctly rendered expect(screen.getByText('100 (var.)')).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // The fan-out action is available expect( screen.getByTitle('Toggle NFT Parent Fan-out'), ).toBeInTheDocument(); // This action is checked by default if the user has no fanInputs expect(screen.getByTitle('Toggle NFT Parent Fan-out')).toHaveProperty( 'checked', true, ); // The mint NFT option is available expect(screen.getByTitle('Toggle Mint NFT')).toBeInTheDocument(); // The mint NFT option is disabled if there are no mint inputs expect(screen.getByTitle('Toggle Mint NFT')).toHaveProperty( 'disabled', true, ); // The mint NFT switch label explains why it is disabled expect(screen.getByText('(no NFT mint inputs)')).toBeInTheDocument(); // The Airdrop action is available expect(screen.getByTitle('Toggle Airdrop')).toBeInTheDocument(); // The Burn action is NOT available expect(screen.queryByTitle('Toggle Burn')).not.toBeInTheDocument(); // We can create NFT mint inputs by executing a fan-out tx await userEvent.click( screen.getByRole('button', { name: /Create NFT Mint Inputs/ }), ); // We see expected toast notification expect( await screen.findByText('NFT Mint inputs created'), ).toBeInTheDocument(); }); it('We can correctly render an SLP1 NFT Parent token with NFT Mint inputs, then mint an NFT', async () => { // We need to use a unique mockedChronik for this test, with at least one nft mint input utxo // Mock the app with context at the Token Action screen const mintNftMockedChronik = await initializeCashtabStateForTests( { ...tokenTestWallet, state: { ...tokenTestWallet.state, slpUtxos: [ ...tokenTestWallet.state.slpUtxos, { outpoint: { txid: '3333333333333333333333333333333333333333333333333333333333333333', outIdx: 1, }, blockHeight: 840012, isCoinbase: false, value: 546, isFinal: true, token: { tokenId: '0c66493127382882053f3eb6e2e05eccff7f67378ebf5e84660a958656a304cc', tokenType: { protocol: 'SLP', type: 'SLP_TOKEN_TYPE_NFT1_GROUP', number: 129, }, amount: '1', isMintBaton: false, }, path: 1899, }, ], }, }, localforage, ); // Build chronik mocks that Cashtab would use to add token info to cache for (const tokenMock of supportedTokens) { mintNftMockedChronik.setMock('token', { input: tokenMock.tokenId, output: tokenMock.token, }); mintNftMockedChronik.setMock('tx', { input: tokenMock.tokenId, output: tokenMock.tx, }); mintNftMockedChronik.setTokenId(tokenMock.tokenId); mintNftMockedChronik.setUtxosByTokenId(tokenMock.tokenId, { tokenId: tokenMock.tokenId, utxos: tokenMock.utxos, }); // Set empty tx history to mock no existing NFTs mintNftMockedChronik.setTxHistoryByTokenId(tokenMock.tokenId, []); } const hex = '020000000233333333333333333333333333333333333333333333333333333333333333330100000064412564b7504e0ec0a094aae832fee07ce86f21de56153a71c99bcc50a20d4f79ba264cccd4fc39d4840af59e0f013cb535b07ae31795197db0fcda47b8ef91973b4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf7926180300000064418758fd9e1a9eec69b262ba29227a1cbb0990dca35f7deadb91145af82e922cabe1efcb688c4a498fefbc903d6d4b5cdb8facdf624e7cbde95065b7ad014a54864121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff0300000000000000003c6a04534c500001410747454e4553495304414243310b426974636f696e204142430b636173687461622e636f6d4c0001004c0008000000000000000122020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac7a330f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const txid = 'd215995b67194576b66ef9c593a66d9255a3ec21e424ecfbb6046643b8e0dbe6'; mintNftMockedChronik.setMock('broadcastTx', { input: hex, output: { txid }, }); render( <CashtabTestWrapper chronik={mintNftMockedChronik} ecc={ecc} route={`/token/${slp1NftParentMocks.tokenId}`} />, ); const { tokenName } = slp1NftParentMocks.token.genesisInfo; // Wait for the component to finish loading await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // We can click an info icon to learn more about this token type await userEvent.click( await screen.findByRole('button', { name: 'Click for more info about this token type', }), ); expect( screen.getByText( `The parent tokens for an NFT collection. Can be used to mint NFTs. No decimal places. The supply of this token is the potential quantity of NFTs which could be minted. If no mint batons exist, the supply is fixed.`, ), ).toBeInTheDocument(); // Close out of the info modal await userEvent.click(screen.getByText('OK')); // The supply is correctly rendered expect(screen.getByText('100 (var.)')).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // The fan-out action is available expect( screen.getByTitle('Toggle NFT Parent Fan-out'), ).toBeInTheDocument(); // The fan-out action is NOT checked by default because we have a single fan input expect(screen.getByTitle('Toggle NFT Parent Fan-out')).toHaveProperty( 'checked', false, ); // The mint NFT option is available expect(screen.getByTitle('Toggle Mint NFT')).toBeInTheDocument(); // The mint NFT option is NOT disabled as we have a single mint input expect(screen.getByTitle('Toggle Mint NFT')).toHaveProperty( 'disabled', false, ); // The mint NFT switch label does not include the disabled explanation expect( screen.queryByText('(no NFT mint inputs)'), ).not.toBeInTheDocument(); // The mint NFT switch label shows available NFT mint inputs expect(screen.getByText('(1 input available)')).toBeInTheDocument(); // The Airdrop action is available expect(screen.getByTitle('Toggle Airdrop')).toBeInTheDocument(); // The Burn action is NOT available expect(screen.queryByTitle('Toggle Burn')).not.toBeInTheDocument(); // We can mint an NFT if we give it a name and a ticker await userEvent.type( await screen.findByPlaceholderText('Enter a name for your NFT'), 'Bitcoin ABC', ); // The mint button is disabled as the user has not entered a ticker expect(screen.getByRole('button', { name: /Mint NFT/ })).toHaveProperty( 'disabled', true, ); expect( screen.getByText('NFT must have a name and a ticker'), ).toBeInTheDocument(); // We give the NFT a ticker await userEvent.type( await screen.findByPlaceholderText('Enter a ticker for your NFT'), 'ABC1', ); // The mint button is no longer disabled expect(screen.getByRole('button', { name: /Mint NFT/ })).toHaveProperty( 'disabled', false, ); await userEvent.click(screen.getByRole('button', { name: /Mint NFT/ })); // We see a preview modal, click OK await userEvent.click(screen.getByText('OK')); // We see expected toast notification expect(await screen.findByText('NFT Minted!')).toBeInTheDocument(); }); it('We can render an SLP1 NFT Parent token with a minted NFT in its collection', async () => { // We need to use a unique mockedChronik for this test, with at least one nft mint input utxo // Mock the app with context at the Token Action screen // Note the Token page will render all NFTs in a collection based on whether or not they exist, // not based on whether or not they are in the user's wallet // The user actions available for the child NFTs depend on whether or not the NFTs exist in the user's wallet const renderChildNftsMockedChronik = await initializeCashtabStateForTests( { ...tokenTestWallet, state: { ...tokenTestWallet.state, slpUtxos: [ ...tokenTestWallet.state.slpUtxos, // Its parent NFT so this is cached slp1NftParentWithChildrenMocks.utxos[0], // A child NFT in the utxo set slp1NftChildMocks.utxos[0], ], }, }, localforage, ); // Build chronik mocks that Cashtab would use to add token info to cache for (const tokenMock of supportedTokens) { renderChildNftsMockedChronik.setMock('token', { input: tokenMock.tokenId, output: tokenMock.token, }); renderChildNftsMockedChronik.setMock('tx', { input: tokenMock.tokenId, output: tokenMock.tx, }); renderChildNftsMockedChronik.setTokenId(tokenMock.tokenId); renderChildNftsMockedChronik.setUtxosByTokenId(tokenMock.tokenId, { tokenId: tokenMock.tokenId, utxos: tokenMock.utxos, }); // Set tx history of parent tokenId to empty renderChildNftsMockedChronik.setTxHistoryByTokenId( tokenMock.tokenId, [], ); } // Set tx history of parent tokenId to include an NFT renderChildNftsMockedChronik.setTxHistoryByTokenId( slp1NftParentWithChildrenMocks.tokenId, [slp1NftChildMocks.tx], ); render( <CashtabTestWrapper chronik={renderChildNftsMockedChronik} ecc={ecc} route={`/token/${slp1NftParentWithChildrenMocks.tokenId}`} />, ); const { tokenName } = slp1NftParentWithChildrenMocks.token.genesisInfo; // Wait for the component to finish loading await waitFor(() => expect( screen.queryByTitle('Cashtab Loading'), ).not.toBeInTheDocument(), ); // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // We can click an info icon to learn more about this token type await userEvent.click( await screen.findByRole('button', { name: 'Click for more info about this token type', }), ); expect( screen.getByText( `The parent tokens for an NFT collection. Can be used to mint NFTs. No decimal places. The supply of this token is the potential quantity of NFTs which could be minted. If no mint batons exist, the supply is fixed.`, ), ).toBeInTheDocument(); // Close out of the info modal await userEvent.click(screen.getByText('OK')); // The wallet balance of this token is correctly rendered expect(screen.getByText('1 (fixed)')).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // The fan-out action is available expect( screen.getByTitle('Toggle NFT Parent Fan-out'), ).toBeInTheDocument(); // The fan-out action is NOT checked by default because we have a single fan input expect(screen.getByTitle('Toggle NFT Parent Fan-out')).toHaveProperty( 'checked', false, ); // The mint NFT option is available expect(screen.getByTitle('Toggle Mint NFT')).toBeInTheDocument(); // The mint NFT option is NOT disabled as we have a single mint input expect(screen.getByTitle('Toggle Mint NFT')).toHaveProperty( 'disabled', false, ); // The mint NFT switch label does not include the disabled explanation expect( screen.queryByText('(no NFT mint inputs)'), ).not.toBeInTheDocument(); // The mint NFT switch label shows available NFT mint inputs expect(screen.getByText('(1 input available)')).toBeInTheDocument(); // The Airdrop action is available expect(screen.getByTitle('Toggle Airdrop')).toBeInTheDocument(); // The Burn action is NOT available expect(screen.queryByTitle('Toggle Burn')).not.toBeInTheDocument(); // A child NFT is rendered expect(screen.getByText('NFTs in this Collection')).toBeInTheDocument(); // NFT image is rendered expect( screen.getByAltText(`icon for ${slp1NftChildMocks.tokenId}`), ).toBeInTheDocument(); // NFT name is rendered expect(screen.getByText('Gordon Chen')).toBeInTheDocument(); }); it('We can list an SLP1 NFT', async () => { const mockedAgora = new MockAgora(); mockedAgora.setOfferedGroupTokenIds([]); // It's not listed yet mockedAgora.setActiveOffersByTokenId(slp1NftChildMocks.tokenId, []); // activeOffersByPubKey // The test wallet is selling the Saturn V NFT mockedAgora.setActiveOffersByPubKey( tokenTestWallet.paths.get(appConfig.derivationPath).pk, [], ); // activeOffersByGroupTokenId does not need to be mocked since there are no offers here // NFT ad prep const adPrepHex = '0200000002268322a2a8e67fe9efdaf15c9eb7397fb640ae32d8a245c2933f9eb967ff9b5d010000006441e4365d350b1dfee55e60cc2600ba094ed0e05c1d6a297bd3fe3f0721b88d9ec09b7d114cf0aab08a3b264153858f1a48f839f3639a8a8f9b11214038080cb9e34121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf7926180300000064411e9913b28017832fa38944675eb8815411fd210f9dfc8f0aa806bed055f52b6592488fdd1f9be942c19dcb98d7ddd7c55bc8b1233a64ad3dfa1c65eebbd48f254121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff030000000000000000376a04534c500001410453454e44205d9bff67b99e3f93c245a2d832ae40b67f39b79e5cf1daefe97fe6a8a22283260800000000000000019a0400000000000017a91407d2b0e6ec7b96cbfbe4a7d54e28d28fbcf65e408710310f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const adPrepTxid = '7b4f2b1cf9716ead03f91910bd0c08956c381987e1cb3cd9f9b4d555a7b9ba25'; mockedChronik.setMock('broadcastTx', { input: adPrepHex, output: { txid: adPrepTxid }, }); // NFT ad list const adListHex = '020000000125bab9a755d5b4f9d93ccbe18719386c95080cbd1019f903ad6e71f91c2b4f7b01000000a70441475230074f4e4553484f544106bd7c3cc4f6aca45a7f97644b8cb5e745dee224246f38605171e8f9e0d6e036af3ea4853b08e1baa92e091bd0ceabf83d4a246e07e6b0b008a3e091b111f22a414c56222b50fe00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac7521031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dad074f4e4553484f5488044147523087ffffffff020000000000000000376a04534c500001410453454e44205d9bff67b99e3f93c245a2d832ae40b67f39b79e5cf1daefe97fe6a8a2228326080000000000000001220200000000000017a914729833ae294590bbcf28bfbb9ad54c01b6cdb6288700000000'; const adListTxid = '97cf0fed5062419ad456f22457cfeb3b15909f1de2350be48c53b24944e0de89'; mockedChronik.setMock('broadcastTx', { input: adListHex, output: { txid: adListTxid }, }); // NFT send const hex = '0200000002268322a2a8e67fe9efdaf15c9eb7397fb640ae32d8a245c2933f9eb967ff9b5d010000006441fff60607ba0fb6eda064075b321abc3980c249efcc0e91d4d95e464500a654476e59b76dd19bdd66f5d207a0d731550c93ce724a09e00a3bff3fcfbc08c970844121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf792618030000006441fe754300443dfb293619693087016c9d9a8437489d48cb7c0c3fcb6b5af6277833ff7156355aeb557145c4075b7917d90d79239ba7bf776a38fef935d8da2f7c4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff030000000000000000376a04534c500001410453454e44205d9bff67b99e3f93c245a2d832ae40b67f39b79e5cf1daefe97fe6a8a222832608000000000000000122020000000000001976a91495e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d88ac84330f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const txid = 'daa5872d1ef95a05bd3ee59fc532aa7921a54b783a5af68c5aa9146f61d2e134'; mockedChronik.setMock('broadcastTx', { input: hex, output: { txid }, }); render( <CashtabTestWrapper chronik={mockedChronik} agora={mockedAgora} ecc={ecc} route={`/send-token/${slp1NftChildMocks.tokenId}`} />, ); const { tokenName } = slp1NftChildMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // NFT image is rendered expect( screen.getByAltText(`icon for ${slp1NftChildMocks.tokenId}`), ).toBeInTheDocument(); // We can click an info icon to learn more about this token type await userEvent.click( await screen.findByRole('button', { name: 'Click for more info about this token type', }), ); expect( screen.getByText( `eCash NFT. NFT supply is always 1. This NFT may belong to an NFT collection.`, ), ).toBeInTheDocument(); // Close out of the info modal await userEvent.click(screen.getByText('OK')); // For an NFT, we render the NFT name, not balance, as it is always 1 if we can see this page expect(screen.getByText('Gordon Chen')).toBeInTheDocument(); // We see what collection this NFT is from expect(screen.getByText(/NFT from collection/)).toBeInTheDocument(); expect( screen.getByText('The Four Half-Coins of Jin-qua'), ).toBeInTheDocument(); // Token actions are available for NFTs expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // On load, default action for NFT is to list it expect(screen.getByTitle('Toggle Sell NFT')).toHaveProperty( 'checked', true, ); // We see a price input field for listing this NFT const priceInput = screen.getByPlaceholderText('Enter NFT list price'); expect(priceInput).toBeInTheDocument(); // We see expected error msg if we try to list the NFT for less than dust await userEvent.type(priceInput, '5.45'); expect( screen.getByText('List price cannot be less than dust (5.46 XEC).'), ).toBeInTheDocument(); // The List button is disabled on bad validation const listButton = screen.getByRole('button', { name: /List Gordon Chen/, }); expect(listButton).toHaveProperty('disabled', true); await userEvent.clear(priceInput); // No validation error if NFT list price is for more than dust await userEvent.type(priceInput, '10000'); expect( screen.queryByText( 'List price cannot be less than dust (5.46 XEC).', ), ).not.toBeInTheDocument(); // The List button is NOT disabled if price is greater than dust expect(listButton).toHaveProperty('disabled', false); // The fiat price is previewed correctly expect(screen.getByText('10,000 XEC = $ 0.30 USD')).toBeInTheDocument(); // We can also set the price in fiat currency await userEvent.selectOptions( screen.getByTestId('currency-select-dropdown'), screen.getByTestId('fiat-option'), ); // The price input is cleared when the user changes from XEC price to fiat price expect(priceInput).toHaveValue(null); // We list the NFT for $5 await userEvent.type(priceInput, '5'); // The fiat price is previewed correctly expect( screen.getByText(/\$ 5 USD = 166,666.67 XEC/), ).toBeInTheDocument(); // Click the now-enabled list button await userEvent.click(listButton); // We see expected confirmation modal to list the NFT expect(screen.getByText(/List GC for \$ 5 USD/)).toBeInTheDocument(); // We can cancel and not list the NFT await userEvent.click(screen.getByText('Cancel')); // The confirmation modal is gone expect( screen.queryByText(/List GC for \$ 5 USD/), ).not.toBeInTheDocument(); // We change our mind await userEvent.click(listButton); await userEvent.click(screen.getByText('OK')); // We see expected toast notification for the ad setup tx expect(await screen.findByText('Created NFT ad')).toBeInTheDocument(); // We see the expected toast notification for the successful listing tx expect( await screen.findByText(/NFT listed for 166,666.67 XEC/), ).toBeInTheDocument(); // Screen should check for new listings and show the listing on this page // Cannot test this without regtest, as we would need MockedAgora to show no // active offers on load, then 1 offer after listing // Can confirm in manual testing }); it('We can send an SLP1 NFT', async () => { const mockedAgora = new MockAgora(); mockedAgora.setOfferedGroupTokenIds([]); // It's not listed yet mockedAgora.setActiveOffersByTokenId(slp1NftChildMocks.tokenId, []); // NFT send const hex = '0200000002268322a2a8e67fe9efdaf15c9eb7397fb640ae32d8a245c2933f9eb967ff9b5d010000006441fff60607ba0fb6eda064075b321abc3980c249efcc0e91d4d95e464500a654476e59b76dd19bdd66f5d207a0d731550c93ce724a09e00a3bff3fcfbc08c970844121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf792618030000006441fe754300443dfb293619693087016c9d9a8437489d48cb7c0c3fcb6b5af6277833ff7156355aeb557145c4075b7917d90d79239ba7bf776a38fef935d8da2f7c4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff030000000000000000376a04534c500001410453454e44205d9bff67b99e3f93c245a2d832ae40b67f39b79e5cf1daefe97fe6a8a222832608000000000000000122020000000000001976a91495e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d88ac84330f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const txid = 'daa5872d1ef95a05bd3ee59fc532aa7921a54b783a5af68c5aa9146f61d2e134'; mockedChronik.setMock('broadcastTx', { input: hex, output: { txid }, }); render( <CashtabTestWrapper chronik={mockedChronik} agora={mockedAgora} ecc={ecc} route={`/send-token/${slp1NftChildMocks.tokenId}`} />, ); const { tokenName } = slp1NftChildMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // On load, default action for NFT is to list it const sellActionSwitch = screen.getByTitle('Toggle Sell NFT'); expect(sellActionSwitch).toHaveProperty('checked', true); // Sending is disabled const sendActionSwitch = screen.getByTitle('Toggle Send'); expect(sendActionSwitch).toHaveProperty('checked', false); // When we enable Sending, Selling is disabled, and Sending is enabled await userEvent.click(sendActionSwitch); expect(sendActionSwitch).toHaveProperty('checked', true); expect(sellActionSwitch).toHaveProperty('checked', false); // We see an Address input const addrInput = screen.getByPlaceholderText('Address'); expect(addrInput).toBeInTheDocument(); // Send button is disabled before address entry const sendButton = screen.getByRole('button', { name: /Send GC/, }); expect(sendButton).toHaveProperty('disabled', true); // We can enter an address await userEvent.type( addrInput, 'ecash:qz2708636snqhsxu8wnlka78h6fdp77ar59jrf5035', ); // Now the button is enabled expect(sendButton).toHaveProperty('disabled', false); // We can send an NFT await userEvent.click(sendButton); expect(await screen.findByText('NFT sent')).toBeInTheDocument(); }); it('SLP1 NFT page will update cashtab token cache for the NFT if it does not include groupTokenId, and for its parent if it is not in cache', async () => { // Use wallet with nft utxo as only utxo // Preset a cache without groupTokenId // Use existing tx and token mocks // We need to use a unique mockedChronik for this test, with a minted NFT utxo but no parent utxo // The user actions available for the child NFTs depend on whether or not the NFTs exist in the user's wallet const renderChildNftsMockedChronik = await initializeCashtabStateForTests( { ...tokenTestWallet, state: { ...tokenTestWallet.state, slpUtxos: [ // Only a child NFT in the utxo set slp1NftChildMocks.utxos[0], ], tokens: new Map([ [ '5d9bff67b99e3f93c245a2d832ae40b67f39b79e5cf1daefe97fe6a8a2228326', '1', ], ]), }, }, localforage, ); const mockCashtabCacheWithNft = new CashtabCache([ [ slp1NftChildMocks.tokenId, { // note that this mock DOES NOT include groupTokenId ...slp1NftChildMocks.token, genesisSupply: '1', genesisOutputScripts: [ '76a91495e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d88ac', ], genesisMintBatons: 0, }, ], ]); await localforage.setItem( 'cashtabCache', cashtabCacheToJSON(mockCashtabCacheWithNft), ); // Build chronik mocks that Cashtab would use to add token info to cache for (const tokenMock of supportedTokens) { renderChildNftsMockedChronik.setMock('token', { input: tokenMock.tokenId, output: tokenMock.token, }); renderChildNftsMockedChronik.setMock('tx', { input: tokenMock.tokenId, output: tokenMock.tx, }); renderChildNftsMockedChronik.setTokenId(tokenMock.tokenId); renderChildNftsMockedChronik.setUtxosByTokenId(tokenMock.tokenId, { tokenId: tokenMock.tokenId, utxos: tokenMock.utxos, }); // Set tx history of parent tokenId to empty renderChildNftsMockedChronik.setTxHistoryByTokenId( tokenMock.tokenId, [], ); } // Set tx history of parent tokenId to include an NFT renderChildNftsMockedChronik.setTxHistoryByTokenId( slp1NftParentWithChildrenMocks.tokenId, [slp1NftChildMocks.tx], ); render( <CashtabTestWrapper chronik={renderChildNftsMockedChronik} ecc={ecc} route={`/token/${slp1NftChildMocks.tokenId}`} />, ); const { tokenName } = slp1NftChildMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // NFT image is rendered expect( screen.getByAltText(`icon for ${slp1NftChildMocks.tokenId}`), ).toBeInTheDocument(); // We can click an info icon to learn more about this token type await userEvent.click( await screen.findByRole('button', { name: 'Click for more info about this token type', }), ); expect( screen.getByText( `eCash NFT. NFT supply is always 1. This NFT may belong to an NFT collection.`, ), ).toBeInTheDocument(); // Close out of the info modal await userEvent.click(screen.getByText('OK')); // The NFT Token name is the title expect(screen.getByText('Gordon Chen')).toBeInTheDocument(); // We see what collection this NFT is from expect(screen.getByText(/NFT from collection/)).toBeInTheDocument(); expect( screen.getByText('The Four Half-Coins of Jin-qua'), ).toBeInTheDocument(); // Token actions are available for NFTs expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // On load, the default action for an NFT is to list it const nftListInput = screen.getByPlaceholderText( 'Enter NFT list price', ); expect(nftListInput).toBeInTheDocument(); }); it('We show an agora query error if we cannot get active offers for an NFT token id', async () => { const heismanNftTokenId = heismanNftOneOffer.token.tokenId; // Mock the API calls for getting and caching this token's info mockedChronik.setMock('token', { input: heismanNftTokenId, output: heismanNftOneCache.token, }); mockedChronik.setMock('tx', { input: heismanNftTokenId, output: heismanNftOneCache.tx, }); // Also mock for the collection mockedChronik.setMock('token', { input: heismanCollectionCacheMocks.tokenId, output: heismanCollectionCacheMocks.token, }); mockedChronik.setMock('tx', { input: heismanCollectionCacheMocks.tokenId, output: heismanCollectionCacheMocks.tx, }); // Mock an error querying this NFT listing const mockedAgora = new MockAgora(); // then mock for each one agora.activeOffersByTokenId(offeredTokenId) mockedAgora.setActiveOffersByTokenId( heismanNftTokenId, new Error('some agora error'), ); render( <CashtabTestWrapper chronik={mockedChronik} agora={mockedAgora} ecc={ecc} route={`/send-token/${heismanNftTokenId}`} />, ); const { tokenName } = cachedHeismanNftOne.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // On load, we see expected agora query error expect( await screen.findByText('Error querying NFT offers'), ).toBeInTheDocument(); }); it('We show an agora oneshot listing for an SLP1 NFT if it is for sale', async () => { const heismanNftTokenId = heismanNftOneOffer.token.tokenId; // Mock the API calls for getting and caching this token's info mockedChronik.setMock('token', { input: heismanNftTokenId, output: heismanNftOneCache.token, }); mockedChronik.setMock('tx', { input: heismanNftTokenId, output: heismanNftOneCache.tx, }); // Also mock for the collection mockedChronik.setMock('token', { input: heismanCollectionCacheMocks.tokenId, output: heismanCollectionCacheMocks.token, }); mockedChronik.setMock('tx', { input: heismanCollectionCacheMocks.tokenId, output: heismanCollectionCacheMocks.tx, }); // Mock an error querying this NFT listing const mockedAgora = new MockAgora(); // then mock for each one agora.activeOffersByTokenId(offeredTokenId) mockedAgora.setActiveOffersByTokenId(heismanNftTokenId, [ heismanNftOneOffer, ]); render( <CashtabTestWrapper chronik={mockedChronik} agora={mockedAgora} ecc={ecc} route={`/send-token/${heismanNftTokenId}`} />, ); const { tokenName, tokenTicker } = cachedHeismanNftOne.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // On load, we can buy the offer expect( await screen.findByText(`Buy ${tokenName} (${tokenTicker})`), ).toBeInTheDocument(); }); it('ALP token', async () => { render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} route={`/send-token/${alpMocks.tokenId}`} />, ); const { tokenName } = alpMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // We can click an info icon to learn more about this token type await userEvent.click( await screen.findByRole('button', { name: 'Click for more info about this token type', }), ); expect( screen.getByText( 'ALP v1 fungible token. Token may be of fixed or variable supply. If you have a mint baton, you can mint more of this token at any time. May have up to 9 decimal places. ALP tokens use EMPP technology, which supports more token actions compared to SLP and more complex combinations of token and app actions. ALP token txs may have up to 127 outputs, though current OP_RETURN size de facto limits a single tx to 29 outputs.', ), ).toBeInTheDocument(); // Close out of the info modal await userEvent.click(screen.getByText('OK')); // The supply is correctly rendered expect(screen.getByText('111,367.0000 (var.)')).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // We can list, which is also the default action expect(screen.getByTitle('Toggle Sell Token')).toBeEnabled(); // We can send expect(screen.getByTitle('Toggle Send')).toBeInTheDocument(); // We can burn expect(screen.getByTitle('Toggle Burn')).toBeInTheDocument(); // Because we do not have the mint baton for this token, the Mint action is NOT available expect(screen.queryByTitle('Toggle Mint')).not.toBeInTheDocument(); }); it('We can send an ALP token', async () => { const mockedAgora = new MockAgora(); mockedAgora.setOfferedGroupTokenIds([]); // It's not listed yet mockedAgora.setActiveOffersByTokenId(alpMocks.tokenId, []); // ALP send const hex = '020000000288bb5c0d60e11b4038b00af152f9792fa954571ffdd2413a85f1c26bfd930c25010000006441999a894cafbab21d590da6ce07e572935144c480bce48c4df3efb74e9ee2fd3a4de61a40f93c28775c7b135a6a9ccba7d880bd5776d289b6c8ae5752afee24b34121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf792618030000006441f6e2b2a66d8676854e281f5af375bc56d4f359cb4be1e178d330720384da79a5216bd7a132bfd44654835c95a8d81b099b03e953d4a720187255ef1c9a1b646e4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff0400000000000000003a6a5037534c5032000453454e4449884c726ebb974b9b8345ee12b44cc48445562b970f776e307d16547ccdd77c02102700000000301b0f00000022020000000000001976a91495e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d88ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac18310f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const txid = '33313eaf3365d9bf440645c5fffa8ed91681d1e1464afe598a564cdc76855c04'; mockedChronik.setMock('broadcastTx', { input: hex, output: { txid }, }); // Mock NOT blacklisted when(fetch) .calledWith( `${tokenConfig.blacklistServerUrl}/blacklist/${alpMocks.tokenId}`, ) .mockResolvedValue({ json: () => Promise.resolve({ isBlacklisted: false }), }); render( <CashtabTestWrapper chronik={mockedChronik} agora={mockedAgora} ecc={ecc} route={`/send-token/${alpMocks.tokenId}`} />, ); const { tokenName } = alpMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // Wait for supply and actions to load // The supply is correctly rendered expect( await screen.findByText('111,367.0000 (var.)'), ).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // Click Send await userEvent.click(screen.getByTitle('Toggle Send')); // Wait for address input to render expect( await screen.findByPlaceholderText('Address'), ).toBeInTheDocument(); // On load, default action for ALP is to send it const sendActionSwitch = screen.getByTitle('Toggle Send'); await waitFor(() => expect(sendActionSwitch).toHaveProperty('checked', true), ); // We see an Address input const addrInput = screen.getByPlaceholderText('Address'); expect(addrInput).toBeInTheDocument(); // Send button is disabled before address and amount entry const sendButton = screen.getByRole('button', { name: /Send tCRD/, }); expect(sendButton).toBeDisabled(); // We can enter an address await userEvent.type( addrInput, 'ecash:qz2708636snqhsxu8wnlka78h6fdp77ar59jrf5035', ); const amountInputEl = screen.getByPlaceholderText('Amount'); const amountInput = '1'; await userEvent.type(amountInputEl, amountInput); // Now the button is enabled expect(sendButton).toBeEnabled(); // We can send an ALP token await userEvent.click(sendButton); const sendTokenSuccessNotification = await screen.findByText( 'eToken sent', ); expect(sendTokenSuccessNotification).toHaveAttribute( 'href', `${explorer.blockExplorerUrl}/tx/${txid}`, ); }); it('We can burn an ALP token with change', async () => { const mockedAgora = new MockAgora(); mockedAgora.setOfferedGroupTokenIds([]); // It's not listed yet mockedAgora.setActiveOffersByTokenId(alpMocks.tokenId, []); // ALP burn const hex = '020000000288bb5c0d60e11b4038b00af152f9792fa954571ffdd2413a85f1c26bfd930c250100000064416f667f359f04e273d524eac5fdaede0bfaf483daaf74f2ab5ba849c3a126b36b059003ef22b647d5265b74938e50c40505c1ad56474d0af2930192994011b9c84121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf792618030000006441ed0c24a83ec9137bc2cc367f674b1932de280f3bc2fbfd9cd70b840e61ccf5fa272e714ba06d3060574df97bc135acae2367d00fdd67ce2bbf347193a871348c4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff030000000000000000656a5030534c503200044255524e49884c726ebb974b9b8345ee12b44cc48445562b970f776e307d16547ccdd77c10270000000031534c5032000453454e4449884c726ebb974b9b8345ee12b44cc48445562b970f776e307d16547ccdd77c01301b0f00000022020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac28330f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const txid = 'f71293a94bd444c0b82ce6a6a8a1d2ae182f6a848cd2382bb6ca496955184fdf'; mockedChronik.setMock('broadcastTx', { input: hex, output: { txid }, }); // Mock NOT blacklisted when(fetch) .calledWith( `${tokenConfig.blacklistServerUrl}/blacklist/${alpMocks.tokenId}`, ) .mockResolvedValue({ json: () => Promise.resolve({ isBlacklisted: false }), }); render( <CashtabTestWrapper chronik={mockedChronik} agora={mockedAgora} ecc={ecc} route={`/send-token/${alpMocks.tokenId}`} />, ); const { tokenName } = alpMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // Wait for supply and actions to load // The supply is correctly rendered expect( await screen.findByText('111,367.0000 (var.)'), ).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // On load, default action for ALP is to list // Select burn await userEvent.click(screen.getByTitle('Toggle Burn')); await userEvent.type(screen.getByPlaceholderText('Burn Amount'), '1'); // Click the Burn button // Note we button title is the token ticker await userEvent.click( await screen.findByRole('button', { name: /Burn tCRD/ }), ); // We see a modal and enter the correct confirmation msg await userEvent.type( screen.getByPlaceholderText(`Type "burn tCRD" to confirm`), 'burn tCRD', ); // Click the Confirm button await userEvent.click(screen.getByRole('button', { name: /OK/ })); const burnTokenSuccessNotification = await screen.findByText( '🔥 Burn successful', ); await waitFor(() => expect(burnTokenSuccessNotification).toHaveAttribute( 'href', `${explorer.blockExplorerUrl}/tx/${txid}`, ), ); }); it('We can burn an ALP token without change', async () => { const mockedAgora = new MockAgora(); mockedAgora.setOfferedGroupTokenIds([]); // It's not listed yet mockedAgora.setActiveOffersByTokenId(alpMocks.tokenId, []); // ALP burn all const hex = '020000000288bb5c0d60e11b4038b00af152f9792fa954571ffdd2413a85f1c26bfd930c250100000064413919d2894e681586f285af178ef2c8d86b2f008e31519b1592c76cae7bee17eb4bb1558db35b225a15a2ba1c1f3d86564e12adfa0d5c012427f096398cdff20e4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf79261803000000644126a0f23966db5ba3212e4d5c545a186d407af4d110335e521c867e63549ade8d25da8a911343d9bf9275bbb58255cd445a1b3fc14ae35a89b8964cfbe47299aa4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff030000000000000000336a5030534c503200044255524e49884c726ebb974b9b8345ee12b44cc48445562b970f776e307d16547ccdd77c40420f00000022020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac8c330f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const txid = 'f413a14acc391c2541f0dea477cf7ee07cf6256bc3b201d6b276272f2fdda407'; mockedChronik.setMock('broadcastTx', { input: hex, output: { txid }, }); // Mock NOT blacklisted when(fetch) .calledWith( `${tokenConfig.blacklistServerUrl}/blacklist/${alpMocks.tokenId}`, ) .mockResolvedValue({ json: () => Promise.resolve({ isBlacklisted: false }), }); render( <CashtabTestWrapper chronik={mockedChronik} agora={mockedAgora} ecc={ecc} route={`/send-token/${alpMocks.tokenId}`} />, ); const { tokenName } = alpMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // Wait for supply and actions to load // The supply is correctly rendered expect( await screen.findByText('111,367.0000 (var.)'), ).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // On load, default action for ALP is to list // Select burn await userEvent.click(screen.getByTitle('Toggle Burn')); // Hit max for max burn await userEvent.click(screen.getByRole('button', { name: /max/ })); // Max is input const thisWalletAlpBalance = 100; expect(screen.getByPlaceholderText('Burn Amount')).toHaveValue( thisWalletAlpBalance, ); // Click the Burn button // Note we button title is the token ticker await userEvent.click( await screen.findByRole('button', { name: /Burn tCRD/ }), ); // We see a modal and enter the correct confirmation msg await userEvent.type( screen.getByPlaceholderText(`Type "burn tCRD" to confirm`), 'burn tCRD', ); // Click the Confirm button await userEvent.click(screen.getByRole('button', { name: /OK/ })); const burnTokenSuccessNotification = await screen.findByText( '🔥 Burn successful', ); await waitFor(() => expect(burnTokenSuccessNotification).toHaveAttribute( 'href', `${explorer.blockExplorerUrl}/tx/${txid}`, ), ); }); it('We can mint max one-output ALP token qty', async () => { const mockedAgora = new MockAgora(); mockedAgora.setOfferedGroupTokenIds([]); // It's not listed yet mockedAgora.setActiveOffersByTokenId(alpMocks.tokenId, []); // New mocked chronik since we change the wallet to include a mint baton for this token const walletWithAlpMintBaton = { ...tokenTestWallet, state: { ...tokenTestWallet.state, slpUtxos: [ ...tokenTestWallet.state.slpUtxos, { outpoint: { txid: '250c93fd6bc2f1853a41d2fd1f5754a92f79f952f10ab038401be1600d5cbb88', outIdx: 2, }, blockHeight: 836452, isCoinbase: false, value: 546, isFinal: true, token: { tokenId: '7cd7cd7c54167d306e770f972b564584c44cb412ee45839b4b97bb6e724c8849', tokenType: { protocol: 'ALP', type: 'ALP_TOKEN_TYPE_STANDARD', number: 0, }, amount: '0', isMintBaton: true, }, path: 1899, }, ], }, }; const mintAlpMockedChronik = await initializeCashtabStateForTests( walletWithAlpMintBaton, localforage, ); // Mock cache info mintAlpMockedChronik.setMock('token', { input: alpMocks.tokenId, output: alpMocks.token, }); mintAlpMockedChronik.setMock('tx', { input: alpMocks.tokenId, output: alpMocks.tx, }); mintAlpMockedChronik.setTokenId(alpMocks.tokenId); mintAlpMockedChronik.setUtxosByTokenId(alpMocks.tokenId, { tokenId: alpMocks.tokenId, utxos: alpMocks.utxos, }); // Set empty tx history mintAlpMockedChronik.setTxHistoryByTokenId(alpMocks.tokenId, []); // ALP mint const hex = '020000000288bb5c0d60e11b4038b00af152f9792fa954571ffdd2413a85f1c26bfd930c25020000006441acdadb019c561b7bfa761695503eb1250d3ae1f34e66eeb3c4c8fb561b4ec95291bde678871451316a8f0472922d25936dd341eb90eb6bb3ccde98b00a2138da4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf792618030000006441fc7a554a708c3e6a2fc72e7c96871521678d0a36e336a599b39eac6a36f4ecedcfd2a728c8e639b5946fde677f1afa9e31468531476dd66fce1adfc760e7e2ff4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff040000000000000000356a5032534c503200044d494e5449884c726ebb974b9b8345ee12b44cc48445562b970f776e307d16547ccdd77c01ffffffffffff0122020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac22310f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const txid = '28c733455a50be334948600bcdf0817610b0321ceba3da52c7c7ffec995320f0'; mintAlpMockedChronik.setMock('broadcastTx', { input: hex, output: { txid }, }); // Mock NOT blacklisted when(fetch) .calledWith( `${tokenConfig.blacklistServerUrl}/blacklist/${alpMocks.tokenId}`, ) .mockResolvedValue({ json: () => Promise.resolve({ isBlacklisted: false }), }); render( <CashtabTestWrapper chronik={mintAlpMockedChronik} agora={mockedAgora} ecc={ecc} route={`/send-token/${alpMocks.tokenId}`} />, ); const { tokenName } = alpMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // Wait for supply and actions to load // The supply is correctly rendered expect( await screen.findByText('111,367.0000 (var.)'), ).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // Select mint await userEvent.click(screen.getByTitle('Toggle Mint')); // Max qty await userEvent.click(screen.getByRole('button', { name: /max/ })); // Max is input // eslint-disable-next-line @typescript-eslint/no-loss-of-precision const maxMintQty = 28147497671.0655; expect(screen.getByPlaceholderText('Mint Amount')).toHaveValue( maxMintQty, ); // Click the Mint button // Note we button title is the token ticker await userEvent.click( await screen.findByRole('button', { name: /Mint tCRD/ }), ); const successNotification = await screen.findByText( '⚗️ Minted 28147497671.0655 tCRD', ); await waitFor(() => expect(successNotification).toHaveAttribute( 'href', `${explorer.blockExplorerUrl}/tx/${txid}`, ), ); }); it('We can list an ALP fungible token', async () => { // Mock Math.random() jest.spyOn(global.Math, 'random').mockReturnValue(0.5); // set a fixed value // ALP offer tx const offerHex = '020000000288bb5c0d60e11b4038b00af152f9792fa954571ffdd2413a85f1c26bfd930c25010000006441d32ae72fa880a40975a475147443a3a7fe10308178ad38d80e6a2428921732b0699849443d8e24124a8ee5b75f1e9f74628fdb8cd0c9704d8cd0c70df65828e94121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffffef76d01776229a95c45696cf68f2f98c8332d0c53e3f24e73fd9c6deaf7926180300000064415fc18bb026bc3122776e708b8cdba9225494c704c1feca7aefb36b592abed96568cd57cb3504769bc4019ec0f36990c28c57012cefe805e4d3b046cc308bc86b4121031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02dffffffff040000000000000000866a504b41475230075041525449414c01009b630800000000005532000000000000d6b24701000000002099c53f031d4603bdc23aca9432f903e3cf5975a3f655cc3fa5057c61d00dfc1ca5dfd02d37534c5032000453454e4449884c726ebb974b9b8345ee12b44cc48445562b970f776e307d16547ccdd77c0200420f000000400000000000220200000000000017a91450eb4978c85ec89b63e37e6b87409c9f5815c7058722020000000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac83300f00000000001976a91400549451e5c22b18686cacdf34dce649e5ec3be288ac00000000'; const offerTxid = 'e00be7011ee5d585cbd54049570ea0754ab0d5c05acf6cb01c25afa3aa61663d'; mockedChronik.setMock('broadcastTx', { input: offerHex, output: { txid: offerTxid }, }); - // We need to give mockedChronik a plugin function - // This is required for creating a new Agora(mockedChronik) - mockedChronik.plugin = () => 'dummy plugin'; - // Mock response for agora select params check // Note // We obtain EXPECTED_OFFER_P2SH by adding // console.log(toHex(shaRmd160(agoraScript.bytecode))); // to ecash-agora lib and running this test // Note that Date() and Math.random() must be mocked to keep this deterministic const EXPECTED_OFFER_P2SH = '50eb4978c85ec89b63e37e6b87409c9f5815c705'; mockedChronik.setScript('p2sh', EXPECTED_OFFER_P2SH); // We mock no existing utxos mockedChronik.setUtxos('p2sh', EXPECTED_OFFER_P2SH, { utxos: [] }); // Note that we cannot use mockedAgora to avoid agoraQueryErrors, as we need a proper // agora object to build the partial const agora = new Agora(mockedChronik); render( <CashtabTestWrapper chronik={mockedChronik} ecc={ecc} agora={agora} route={`/send-token/${alpMocks.tokenId}`} />, ); const { tokenName } = alpMocks.token.genesisInfo; // Wait for element to get token info and load expect( (await screen.findAllByText(new RegExp(tokenName)))[0], ).toBeInTheDocument(); // Token image is rendered expect( screen.getByAltText(`icon for ${alpMocks.tokenId}`), ).toBeInTheDocument(); // Token actions are available expect(screen.getByTitle('Token Actions')).toBeInTheDocument(); // On load, default action for ALP is to list it expect(screen.getByTitle('Toggle Sell Token')).toBeEnabled(); // The list button is disabled on load const listButton = screen.getByRole('button', { name: /List Test CRD/, }); expect(listButton).toBeDisabled(); // The price input is disabled until qty values are entered const priceInput = screen.getByPlaceholderText( 'Enter list price (per token)', ); expect(priceInput).toHaveProperty('disabled', true); // Enter token balance as offered qty await userEvent.type(screen.getByPlaceholderText('Offered qty'), '100'); // Enter a min qty await userEvent.type(screen.getByPlaceholderText('Min buy'), '1'); // The price input is no longer disabled expect(priceInput).toBeEnabled(); // We see expected error msg if we try to list the token at a price where the min buy would cost less than dust await userEvent.type(priceInput, '0.001'); expect( screen.getByText( 'Minimum buy costs 0.001 XEC, must be at least 5.46 XEC', ), ).toBeInTheDocument(); // The buy button is disabled with invalid price expect(listButton).toBeDisabled(); // Increase the price to a valid one await userEvent.clear(priceInput); await userEvent.type(priceInput, '33'); // The list button is no longer disabled expect(listButton).toBeEnabled(); // The fiat price is previewed correctly expect( screen.getByText('33.00 XEC ($0.00099 USD) per token'), ).toBeInTheDocument(); // We can also set the price in fiat currency await userEvent.selectOptions( screen.getByTestId('currency-select-dropdown'), screen.getByTestId('fiat-option'), ); // The price input is cleared when the user changes from XEC price to fiat price expect(priceInput).toHaveValue(null); // We list for $5 per token await userEvent.type(priceInput, '5'); // The fiat price is previewed correctly expect( screen.getByText('$5 USD (166,666.67 XEC) per token'), ).toBeInTheDocument(); // We enter a low price in fiat await userEvent.clear(priceInput); await userEvent.type(priceInput, '0.0005'); // The fiat price is previewed correctly expect( await screen.findByText('$0.0005 USD (16.67 XEC) per token'), ).toBeInTheDocument(); // Click the now-enabled list button expect(listButton).toBeEnabled(); await userEvent.click(listButton); // We see expected confirmation modal to list the Token expect(screen.getByText('List tCRD?')).toBeInTheDocument(); expect( screen.getByText('Create the following sell offer?'), ).toBeInTheDocument(); // Offered qty (actual, calculated from AgoraOffer) const actualOfferedQty = '99.9936'; expect(screen.getByText(actualOfferedQty)).toBeInTheDocument(); // Min by (actual, calculated from AgoraOffer) expect(screen.getByText('1.0240')).toBeInTheDocument(); // Actual price calculated from AgoraOffer const actualPricePerTokenForMinBuy = '16.67 XEC'; expect( screen.getAllByText(actualPricePerTokenForMinBuy)[0], ).toBeInTheDocument(); // User input price expect(screen.getAllByText('16.67 XEC')[1]).toBeInTheDocument(); // We can cancel and not create this listing await userEvent.click(screen.getByText('Cancel')); // The confirmation modal is gone expect(screen.queryByText('List tCRD?')).not.toBeInTheDocument(); // We change our mind and list it await userEvent.click(listButton); // We wait for the preview to be calculated again expect(await screen.findByText('List tCRD?')).toBeInTheDocument(); await userEvent.click(screen.getByText('OK')); // We see the expected toast notification for the successful listing tx expect( await screen.findByText( `${actualOfferedQty} Test CRD listed for ${actualPricePerTokenForMinBuy} per token`, ), ).toBeInTheDocument(); }); }); diff --git a/modules/mock-chronik-client/README.md b/modules/mock-chronik-client/README.md index 6e0b4d2ff..4260cc841 100644 --- a/modules/mock-chronik-client/README.md +++ b/modules/mock-chronik-client/README.md @@ -1,166 +1,170 @@ # mock-chronik-client Testing utility to mock the Chronik indexer client and support unit tests that need to mock chronik related API calls. ## Usage Import the MockChronikClient module via relative path along with any desired mock objects from `/mocks/mockChronikResponses`. ``` const { MockChronikClient } = require('/index'); // Import any mock responses from '/mocks/mockChronikResponses' const ecashaddr = require('ecashaddrjs'); const mockedChronik = new MockChronikClient(); ``` **_Mocking API Responses_** Chronik-client APIs which **don't** rely on a preceding `.script()` call can use the `.setMock()` function to inject the mock API response. This includes: - `.block()` - `.tx()` - `.token()` - `.blockchainInfo()` - `.broadcastTx()` - `.ws()` Example: mocking the chronik.token() call ``` const { mockTokenInfo } = require('/mocks/mockChronikResponses'); // Initialize chronik mock with token info mockedChronik.setMock('token', { input: 'some token ID', output: mockTokenInfo, }); // Execute the API call const result = await mockedChronik.token('some token ID'); assert.deepEqual(result, mockTokenInfo); ``` Chronik-client APIs which **do** rely on a preceding `.script()` call will need to firstly set the intended script (address type and hash) before setting the specific mock function. This includes: - `.script().utxos()` - `.script().history()` Example: mocking the chronik.script(type, hash).utxos() call ``` const { mockP2pkhUtxos } = require('/mocks/mockChronikResponses'); // Initialize chronik mock with script and utxo info const P2PKH_ADDRESS = 'ecash:qzth8qvakhr6y8zcefdrvx30zrdmt2z2gvp7zc5vj8'; const { type, hash } = ecashaddr.decode(P2PKH_ADDRESS, true); mockedChronik.setScript(type, hash); mockedChronik.setUtxos(type, hash, mockP2pkhUtxos); // Execute the API call const result = await mockedChronik.script(type, hash).utxos(); assert.deepEqual(result, mockP2pkhUtxos); ``` **_Mocking API Errors_** To test your app's behavior in handling an API error from Chronik, simply set an Error object as the mock output. Example: mocking an error from the chronik.broadcastTx() call ``` const mockInvalidRawTxHex = 'not a valid raw tx hex'; const expectedError = new Error('Bad response from Chronik'); mockedChronik.setMock('broadcastTx', { input: mockInvalidRawTxHex, output: expectedError, }); // Execute the API call await assert.rejects( async () => { await mockedChronik.broadcastTx(mockInvalidRawTxHex); }, expectedError, ); ``` ## Questions? If you have any implementation questions regarding this mock tool please check the test suite in `/test/index.test.js` or feel free to reach out to the development team via the [eCash Development Telegram](https://t.me/eCashDevelopment). ### Change Log 1.1.0 - Add support to in-node subscribeToBlocks method and check flag, isSubscribedBlocks 1.1.1 - Patch error tests 1.2.0 - Add support to calls by `address(address)` returning same as `script(type, hash)` 1.3.0 - Add support for `subscribeToAddress` and `unsubscribeFromAddress` websocket methods 1.4.0 - Add support for `ws.unsubscribe` method and fix errors in `ws` tests 1.4.1 - Patch repo path in package.json 1.5.0 - Add support for ws subscribe methods and shape found in in-node chronik-client 1.6.0 - Match shape of `subs` object in `ChronikClientNode` for `ChronikClientNode` ws methods and support unsubscribe from blocks 1.7.0 - Allow getting and setting utxos() and history() by tokenId 1.8.0 - Allow getting history without specifying pageNumber or pageSize 1.9.0 - Support `blockTxs` endpoint - Update websocket subs shape to match ChronikClientNode 1.9.1 - Upgrading npm dependencies [D16380](https://reviews.bitcoinabc.org/D16380) 1.10.0 - Allow getting and setting `history()` by `lokadId` [D16382](https://reviews.bitcoinabc.org/D16382) 1.10.1 - Return missing `numTxs` key from `history()` calls [D16617](https://reviews.bitcoinabc.org/D16617) 1.11.0 - Add support for `MockAgora`, a simple set-and-return mock for some `ecash-agora` class methods [D16737](https://reviews.bitcoinabc.org/D16737) 1.12.0 - Extend `MockAgora` support to cover `offeredFungibleTokenIds()` and `activeOffersByTokenId()` methods [D16929](https://reviews.bitcoinabc.org/D16929) 1.12.1 - Build published version with dependencies from `npm` [D17227](https://reviews.bitcoinabc.org/D17227) 1.12.2 - Add `MockAgora` to stub ts declarations [D17274](https://reviews.bitcoinabc.org/D17274) + +1.12.3 + +- Add dummy `plugin` method to allow construction of `new Agora()` from `ecash-agora` with a `MockChronikClient` [D17279](https://reviews.bitcoinabc.org/D17279) diff --git a/modules/mock-chronik-client/index.js b/modules/mock-chronik-client/index.js index 1701340ab..abcc09306 100644 --- a/modules/mock-chronik-client/index.js +++ b/modules/mock-chronik-client/index.js @@ -1,492 +1,496 @@ // Copyright (c) 2023 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. 'use strict'; const cashaddr = require('ecashaddrjs'); const CHRONIK_DEFAULT_PAGESIZE = 25; module.exports = { MockAgora: class { // Agora can make specialized chronik-client calls to a chronik-client instance // running the agora plugin // For the purposes of unit testing, we only need to re-create how this object // is initialized and support getting and setting of expected responses constructor() { // Use self since it is not a reserved term in js // Can access self from inside a method and still get the class const self = this; // API call mock return objects // Can be set with self.setMock self.mockedResponses = { offeredGroupTokenIds: {}, offeredFungibleTokenIds: {}, activeOffersByPubKey: {}, activeOffersByGroupTokenId: {}, activeOffersByTokenId: {}, }; // Allow user to set supported agora query responses self.setOfferedGroupTokenIds = function (response) { self.mockedResponses.offeredGroupTokenIds = response; }; self.setOfferedFungibleTokenIds = function (response) { self.mockedResponses.offeredFungibleTokenIds = response; }; self.setActiveOffersByPubKey = function (pubKey, response) { self.mockedResponses.activeOffersByPubKey[pubKey] = response; }; self.setActiveOffersByGroupTokenId = function ( groupTokenId, response, ) { self.mockedResponses.activeOffersByGroupTokenId[groupTokenId] = response; }; self.setActiveOffersByTokenId = function (tokenId, response) { self.mockedResponses.activeOffersByTokenId[tokenId] = response; }; // Checks whether the user set this mock response to be an error. // If so, throw it to simulate an API error response. function throwOrReturnValue(mockResponse) { if (mockResponse instanceof Error) { throw mockResponse; } return mockResponse; } self.offeredGroupTokenIds = async function () { return throwOrReturnValue( self.mockedResponses.offeredGroupTokenIds, ); }; self.offeredFungibleTokenIds = async function () { return throwOrReturnValue( self.mockedResponses.offeredFungibleTokenIds, ); }; self.activeOffersByPubKey = async function (pubKey) { return throwOrReturnValue( self.mockedResponses.activeOffersByPubKey[pubKey], ); }; self.activeOffersByGroupTokenId = async function (groupTokenId) { return throwOrReturnValue( self.mockedResponses.activeOffersByGroupTokenId[ groupTokenId ], ); }; self.activeOffersByTokenId = async function (tokenId) { return throwOrReturnValue( self.mockedResponses.activeOffersByTokenId[tokenId], ); }; } }, MockChronikClient: class { constructor() { // Use self since it is not a reserved term in js // Can access self from inside a method and still get the class const self = this; + // We need to give mockedChronik a plugin function + // This is required for creating a new Agora(mockedChronik) + self.plugin = () => 'dummy plugin'; + // API call mock return objects // Can be set with self.setMock self.mockedResponses = { block: {}, blockTxs: {}, blockchainInfo: {}, txHistory: [], tx: {}, token: {}, p2sh: {}, p2pkh: {}, broadcastTx: {}, }; self.mockedMethods = { p2pkh: {}, p2sh: {} }; self.manuallyClosed = false; // API call mock functions self.block = async function (blockHashOrHeight) { return throwOrReturnValue( self.mockedResponses.block[blockHashOrHeight], ); }; self.blockTxs = async function ( hashOrHeight, pageNumber = 0, pageSize = CHRONIK_DEFAULT_PAGESIZE, ) { if ( self.mockedResponses[hashOrHeight].txHistory instanceof Error ) { throw self.mockedResponses[hashOrHeight].txHistory; } return self.getTxHistory( pageNumber, pageSize, self.mockedResponses[hashOrHeight].txHistory, ); }; self.tx = async function (txid) { return throwOrReturnValue(self.mockedResponses.tx[txid]); }; self.token = async function (tokenId) { return throwOrReturnValue(self.mockedResponses.token[tokenId]); }; self.broadcastTx = async function (txHex) { return throwOrReturnValue( self.mockedResponses.broadcastTx[txHex], ); }; self.blockchainInfo = async function () { return throwOrReturnValue(self.mockedResponses.blockchainInfo); }; // Return assigned script mocks self.script = function (type, hash) { return self.mockedMethods[type][hash]; }; // Return assigned address mocks self.address = function (address) { return self.mockedMethods[address]; }; // Return assigned tokenId mocks self.tokenId = function (tokenId) { return self.mockedMethods[tokenId]; }; // Return assigned lokadId mocks self.lokadId = function (lokadId) { return self.mockedMethods[lokadId]; }; // Checks whether the user set this mock response to be an error. // If so, throw it to simulate an API error response. function throwOrReturnValue(mockResponse) { if (mockResponse instanceof Error) { throw mockResponse; } return mockResponse; } // Flags to check if ws methods have been called self.wsSubscribeCalled = false; self.wsWaitForOpenCalled = false; // Websocket mocks self.ws = function (wsObj) { if (wsObj !== null) { const returnedWs = { onMessage: wsObj.onMessage, // may be undefined onConnect: wsObj.onConnect, // may be undefined onReconnect: wsObj.onReconnect, // may be undefined onEnd: wsObj.onEnd, // may be undefined autoReconnect: wsObj.autoReconnect || true, // default to true if unset manuallyClosed: false, subs: { blocks: false, tokens: [], lokadIds: [], scripts: [], }, isSubscribedBlocks: false, waitForOpen: async function () { self.wsWaitForOpenCalled = true; }, // Note: subscribe is a legacy NNG method subscribe: function (type, hash) { this.subs.scripts.push({ scriptType: type, scriptPayload: hash, }); self.wsSubscribeCalled = true; }, // Note: unsubscribe is a legacy NNG method unsubscribe: function (type, hash) { const thisSubInSubsIndex = this.subs.scripts.findIndex( sub => sub.scriptType === type && sub.scriptPayload === hash, ); if (typeof thisSubInSubsIndex !== 'undefined') { // Remove from subs this.subs.scripts.splice(thisSubInSubsIndex, 1); } // Otherwise do nothing }, subscribeToScript: function (type, hash) { // in-node only if (Array.isArray(this.subs)) { this.subs = { scripts: [] }; } this.subs.scripts.push({ scriptType: type, payload: hash, }); self.wsSubscribeCalled = true; }, unsubscribeFromScript: function (type, hash) { const thisSubInSubsIndex = this.subs.scripts.findIndex( sub => sub.scriptType === type && sub.payload === hash, ); if (typeof thisSubInSubsIndex !== 'undefined') { // Remove from subs this.subs.scripts.splice(thisSubInSubsIndex, 1); } // Otherwise do nothing }, subscribeToAddress: function (address) { // in-node only if (Array.isArray(this.subs)) { this.subs = { scripts: [] }; } const { type, hash } = cashaddr.decode( address, true, ); this.subs.scripts.push({ scriptType: type, payload: hash, }); }, unsubscribeFromAddress: function (address) { const { type, hash } = cashaddr.decode( address, true, ); // Find the requested unsub script and remove it const unsubIndex = this.subs.scripts.findIndex( sub => sub.scriptType === type && sub.payload === hash, ); if (unsubIndex === -1) { // If we cannot find this subscription in this.subs, throw an error // We do not want an app developer thinking they have unsubscribed from something throw new Error( `No existing sub at ${type}, ${hash}`, ); } // Remove the requested subscription from this.subs this.subs.scripts.splice(unsubIndex, 1); }, subscribeToBlocks: function () { this.subs.blocks = true; }, unsubscribeFromBlocks: function () { this.subs.blocks = false; }, }; return returnedWs; } }; self.wsClose = function () { self.manuallyClosed = true; }; // Allow user to set expected chronik call response self.setMock = function (call, options) { // e.g. ('block', {input: '', output: ''}) const { input, output } = options; if (input) { self.mockedResponses[call][input] = output; } else { self.mockedResponses[call] = output; } }; // script calls need to be set differently self.setTxHistory = function (type, hash, txHistory) { self.mockedResponses[type][hash].txHistory = txHistory; }; self.setTxHistoryByAddress = function (address, txHistory) { self.mockedResponses[address].txHistory = txHistory; }; self.setTxHistoryByTokenId = function (tokenId, txHistory) { self.mockedResponses[tokenId].txHistory = txHistory; }; self.setTxHistoryByLokadId = function (lokadId, txHistory) { self.mockedResponses[lokadId].txHistory = txHistory; }; self.setTxHistoryByBlock = function (hashOrHeight, txHistory) { // Set all expected tx history as array where it can be accessed by mock method self.mockedResponses[hashOrHeight] = { txHistory }; }; /** * Set utxos to custom response; must be called after setScript * @param {string} type 'p2sh' or 'p2pkh' * @param {string} hash hash of an eCash address * @param {array} utxos mocked response of chronik.script(type,hash).utxos() */ self.setUtxos = function (type, hash, utxos) { self.mockedResponses[type][hash].utxos = utxos; }; /** * Set utxos to custom response; must be called after setAddress * @param {string} address 'p2sh' or 'p2pkh' address * @param {array} utxos mocked response of chronik.address(address).utxos() */ self.setUtxosByAddress = function (address, utxos) { self.mockedResponses[address].utxos = utxos; }; /** * Set utxos to custom response; must be called after setTokenId * @param {string} tokenId a tokenId * @param {array} utxos mocked response of chronik.tokenId(tokenId).utxos() */ self.setUtxosByTokenId = function (tokenId, utxos) { self.mockedResponses[tokenId].utxos = utxos; }; // Allow users to set expected chronik script call responses self.setScript = function (type, hash) { // Initialize object that will hold utxos if set self.mockedResponses[type][hash] = {}; self.mockedMethods[type][hash] = { history: async function ( pageNumber = 0, pageSize = CHRONIK_DEFAULT_PAGESIZE, ) { if ( self.mockedResponses[type][hash] .txHistory instanceof Error ) { throw self.mockedResponses[type][hash].txHistory; } return self.getTxHistory( pageNumber, pageSize, self.mockedResponses[type][hash].txHistory, ); }, utxos: async function () { return throwOrReturnValue( self.mockedResponses[type][hash].utxos, ); }, }; }; // Allow users to set expected chronik address call responses self.setAddress = function (address) { // Initialize object that will hold utxos if set self.mockedResponses[address] = {}; self.mockedMethods[address] = { history: async function ( pageNumber = 0, pageSize = CHRONIK_DEFAULT_PAGESIZE, ) { if ( self.mockedResponses[address].txHistory instanceof Error ) { throw self.mockedResponses[address].txHistory; } return self.getTxHistory( pageNumber, pageSize, self.mockedResponses[address].txHistory, ); }, utxos: async function () { return throwOrReturnValue( self.mockedResponses[address].utxos, ); }, }; }; // Allow users to set expected chronik tokenId call responses self.setTokenId = function (tokenId) { // Initialize object that will hold utxos if set self.mockedResponses[tokenId] = {}; self.mockedMethods[tokenId] = { history: async function ( pageNumber = 0, pageSize = CHRONIK_DEFAULT_PAGESIZE, ) { if ( self.mockedResponses[tokenId].txHistory instanceof Error ) { throw self.mockedResponses[tokenId].txHistory; } return self.getTxHistory( pageNumber, pageSize, self.mockedResponses[tokenId].txHistory, ); }, utxos: async function () { return throwOrReturnValue( self.mockedResponses[tokenId].utxos, ); }, }; }; // Allow users to set expected chronik lokadId call responses self.setLokadId = function (lokadId) { // Initialize object that will hold utxos if set self.mockedResponses[lokadId] = {}; self.mockedMethods[lokadId] = { history: async function ( pageNumber = 0, pageSize = CHRONIK_DEFAULT_PAGESIZE, ) { if ( self.mockedResponses[lokadId].txHistory instanceof Error ) { throw self.mockedResponses[lokadId].txHistory; } return self.getTxHistory( pageNumber, pageSize, self.mockedResponses[lokadId].txHistory, ); }, }; }; } // Method to get paginated tx history with same variables as chronik getTxHistory(pageNumber = 0, pageSize, txHistory) { // Return chronik shaped responses const startSliceOnePage = pageNumber * pageSize; const endSliceOnePage = startSliceOnePage + pageSize; const thisPage = txHistory.slice( startSliceOnePage, endSliceOnePage, ); const response = {}; response.txs = thisPage; response.numPages = Math.ceil(txHistory.length / pageSize); response.numTxs = txHistory.length; return response; } }, }; diff --git a/modules/mock-chronik-client/package-lock.json b/modules/mock-chronik-client/package-lock.json index 6613656ba..923a300a5 100644 --- a/modules/mock-chronik-client/package-lock.json +++ b/modules/mock-chronik-client/package-lock.json @@ -1,4749 +1,4749 @@ { "name": "mock-chronik-client", - "version": "1.12.2", + "version": "1.12.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mock-chronik-client", - "version": "1.12.2", + "version": "1.12.3", "license": "MIT", "dependencies": { "ecashaddrjs": "file:../ecashaddrjs" }, "devDependencies": { "mocha": "^10.2.0", "mocha-junit-reporter": "^2.2.1", "nyc": "^15.1.0" } }, "../ecashaddrjs": { "version": "1.5.8", "license": "MIT", "dependencies": { "big-integer": "1.6.36", "bs58check": "^3.0.1" }, "devDependencies": { "@babel/cli": "^7.21.0", "@babel/core": "^7.21.3", "@babel/preset-env": "^7.20.2", "babel-loader": "^9.1.2", "buffer": "^6.0.3", "chai": "^4.3.7", "debug": "^4.3.4", "eslint": "^8.37.0", "jsdoc": "^4.0.2", "mocha": "^10.2.0", "mocha-junit-reporter": "^2.2.0", "mocha-suppress-logs": "^0.3.1", "nyc": "^15.1.0", "random-js": "^2.1.0", "webpack": "^5.76.2", "webpack-cli": "^5.0.1" } }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/code-frame/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { "color-convert": "^1.9.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/code-frame/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/code-frame/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/code-frame/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/code-frame/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/@babel/code-frame/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/compat-data": { "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { "version": "7.22.11", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.10", "@babel/generator": "^7.22.10", "@babel/helper-compilation-targets": "^7.22.10", "@babel/helper-module-transforms": "^7.22.9", "@babel/helpers": "^7.22.11", "@babel/parser": "^7.22.11", "@babel/template": "^7.22.5", "@babel/traverse": "^7.22.11", "@babel/types": "^7.22.11", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/babel" } }, "node_modules/@babel/generator": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { "version": "7.22.10", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", "@babel/helper-validator-option": "^7.22.5", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-environment-visitor": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-module-imports": "^7.22.5", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "@babel/helper-validator-identifier": "^7.22.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-simple-access": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { "version": "7.22.11", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", "dev": true, "dependencies": { "@babel/template": "^7.22.5", "@babel/traverse": "^7.22.11", "@babel/types": "^7.22.11" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { "color-convert": "^1.9.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/@babel/parser": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", "@babel/parser": "^7.22.15", "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { "version": "7.23.2", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", "@babel/generator": "^7.23.0", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "@babel/parser": "^7.23.0", "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" }, "engines": { "node": ">=8" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { "p-locate": "^4.1.0" }, "engines": { "node": ">=8" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { "p-try": "^2.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { "p-limit": "^2.2.0" }, "engines": { "node": ">=8" } }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.19", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" }, "engines": { "node": ">= 8" } }, "node_modules/append-transform": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, "dependencies": { "default-require-extensions": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "node_modules/browserslist": { "version": "4.21.10", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/browserslist" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "dependencies": { "caniuse-lite": "^1.0.30001517", "electron-to-chromium": "^1.4.477", "node-releases": "^2.0.13", "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, "dependencies": { "hasha": "^5.0.0", "make-dir": "^3.0.0", "package-hash": "^4.0.0", "write-file-atomic": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { "version": "1.0.30001524", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz", "integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/browserslist" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ] }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", "dev": true, "engines": { "node": "*" } }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "funding": [ { "type": "individual", "url": "https://paulmillr.com/funding/" } ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/crypt": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", "dev": true, "engines": { "node": "*" } }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/debug/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", "dev": true, "dependencies": { "strip-bom": "^4.0.0" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, "engines": { "node": ">=0.3.1" } }, "node_modules/ecashaddrjs": { "resolved": "../ecashaddrjs", "link": true }, "node_modules/electron-to-chromium": { "version": "1.4.504", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.504.tgz", "integrity": "sha512-cSMwIAd8yUh54VwitVRVvHK66QqHWE39C3DRj8SWiXitEpVSY3wNPD9y1pxQtLIi4w3UdzF9klLsmuPshz09DQ==", "dev": true }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" }, "engines": { "node": ">=4" } }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", "pkg-dir": "^4.1.0" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, "bin": { "flat": "cli.js" } }, "node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^3.0.2" }, "engines": { "node": ">=8.0.0" } }, "node_modules/fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ] }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, "os": [ "darwin" ], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "engines": { "node": ">=8.0.0" } }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, "engines": { "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { "is-glob": "^4.0.1" }, "engines": { "node": ">= 6" } }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, "dependencies": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, "bin": { "he": "bin/he" } }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" } }, "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, "engines": { "node": ">=8" } }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-hook": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, "dependencies": { "append-transform": "^2.0.0" }, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-instrument": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "dependencies": { "@babel/core": "^7.7.5", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" }, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-processinfo": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, "dependencies": { "archy": "^1.0.0", "cross-spawn": "^7.0.3", "istanbul-lib-coverage": "^3.2.0", "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^8.3.2" }, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" } }, "node_modules/istanbul-lib-report/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/istanbul-lib-report/node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { "semver": "^7.5.3" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/istanbul-lib-report/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-report/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" }, "engines": { "node": ">=10" } }, "node_modules/istanbul-reports": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, "bin": { "jsesc": "bin/jsesc" }, "engines": { "node": ">=4" } }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" }, "engines": { "node": ">=6" } }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { "p-locate": "^5.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { "yallist": "^3.0.2" } }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "dependencies": { "semver": "^6.0.0" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "dev": true, "dependencies": { "charenc": "0.0.2", "crypt": "0.0.2", "is-buffer": "~1.1.6" } }, "node_modules/minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" } }, "node_modules/mkdirp": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, "bin": { "mkdirp": "dist/cjs/src/bin.js" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.2.0", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", "minimatch": "5.0.1", "ms": "2.1.3", "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "bin": { "_mocha": "bin/_mocha", "mocha": "bin/mocha.js" }, "engines": { "node": ">= 14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mochajs" } }, "node_modules/mocha-junit-reporter": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==", "dev": true, "dependencies": { "debug": "^4.3.4", "md5": "^2.3.0", "mkdirp": "^3.0.0", "strip-ansi": "^6.0.1", "xml": "^1.0.1" }, "peerDependencies": { "mocha": ">=2.2.5" } }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/nanoid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", "dev": true, "dependencies": { "process-on-spawn": "^1.0.0" }, "engines": { "node": ">=8" } }, "node_modules/node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", "dev": true, "dependencies": { "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "caching-transform": "^4.0.0", "convert-source-map": "^1.7.0", "decamelize": "^1.2.0", "find-cache-dir": "^3.2.0", "find-up": "^4.1.0", "foreground-child": "^2.0.0", "get-package-type": "^0.1.0", "glob": "^7.1.6", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-hook": "^3.0.0", "istanbul-lib-instrument": "^4.0.0", "istanbul-lib-processinfo": "^2.0.2", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.2", "make-dir": "^3.0.0", "node-preload": "^0.2.1", "p-map": "^3.0.0", "process-on-spawn": "^1.0.0", "resolve-from": "^5.0.0", "rimraf": "^3.0.0", "signal-exit": "^3.0.2", "spawn-wrap": "^2.0.0", "test-exclude": "^6.0.0", "yargs": "^15.0.2" }, "bin": { "nyc": "bin/nyc.js" }, "engines": { "node": ">=8.9" } }, "node_modules/nyc/node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "node_modules/nyc/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/nyc/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { "p-locate": "^4.1.0" }, "engines": { "node": ">=8" } }, "node_modules/nyc/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { "p-try": "^2.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/nyc/node_modules/p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { "p-limit": "^2.2.0" }, "engines": { "node": ">=8" } }, "node_modules/nyc/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=8" } }, "node_modules/nyc/node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "node_modules/nyc/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" }, "engines": { "node": ">=8" } }, "node_modules/nyc/node_modules/yargs-parser": { "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" }, "engines": { "node": ">=6" } }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" } }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { "p-limit": "^3.0.2" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, "dependencies": { "aggregate-error": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/package-hash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, "dependencies": { "graceful-fs": "^4.1.15", "hasha": "^5.0.0", "lodash.flattendeep": "^4.4.0", "release-zalgo": "^1.0.0" }, "engines": { "node": ">=8" } }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "dependencies": { "find-up": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/pkg-dir/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/pkg-dir/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { "p-locate": "^4.1.0" }, "engines": { "node": ">=8" } }, "node_modules/pkg-dir/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { "p-try": "^2.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/pkg-dir/node_modules/p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { "p-limit": "^2.2.0" }, "engines": { "node": ">=8" } }, "node_modules/process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", "dev": true, "dependencies": { "fromentries": "^1.2.0" }, "engines": { "node": ">=8" } }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "dependencies": { "safe-buffer": "^5.1.0" } }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { "picomatch": "^2.2.1" }, "engines": { "node": ">=8.10.0" } }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, "dependencies": { "es6-error": "^4.0.1" }, "engines": { "node": ">=4" } }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ] }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, "dependencies": { "foreground-child": "^2.0.0", "is-windows": "^1.0.2", "make-dir": "^3.0.0", "rimraf": "^3.0.0", "signal-exit": "^3.0.2", "which": "^2.0.1" }, "engines": { "node": ">=8" } }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" }, "engines": { "node": ">=8" } }, "node_modules/test-exclude/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/test-exclude/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, "dependencies": { "is-typedarray": "^1.0.0" } }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/browserslist" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" }, "bin": { "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" } }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "dev": true }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "node_modules/xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", "dev": true }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" }, "engines": { "node": ">=10" } }, "node_modules/yargs-parser": { "version": "20.2.4", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/yargs-unparser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" }, "engines": { "node": ">=10" } }, "node_modules/yargs-unparser/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/yargs-unparser/node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } } }, "dependencies": { "@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, "@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" }, "dependencies": { "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { "color-name": "1.1.3" } }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, "@babel/compat-data": { "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", "dev": true }, "@babel/core": { "version": "7.22.11", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.10", "@babel/generator": "^7.22.10", "@babel/helper-compilation-targets": "^7.22.10", "@babel/helper-module-transforms": "^7.22.9", "@babel/helpers": "^7.22.11", "@babel/parser": "^7.22.11", "@babel/template": "^7.22.5", "@babel/traverse": "^7.22.11", "@babel/types": "^7.22.11", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "@babel/generator": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" } }, "@babel/helper-compilation-targets": { "version": "7.22.10", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", "dev": true, "requires": { "@babel/compat-data": "^7.22.9", "@babel/helper-validator-option": "^7.22.5", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "@babel/helper-environment-visitor": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "requires": { "@babel/types": "^7.22.5" } }, "@babel/helper-module-transforms": { "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-module-imports": "^7.22.5", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "@babel/helper-validator-identifier": "^7.22.5" } }, "@babel/helper-simple-access": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "requires": { "@babel/types": "^7.22.5" } }, "@babel/helper-split-export-declaration": { "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true }, "@babel/helper-validator-identifier": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "dev": true }, "@babel/helpers": { "version": "7.22.11", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", "dev": true, "requires": { "@babel/template": "^7.22.5", "@babel/traverse": "^7.22.11", "@babel/types": "^7.22.11" } }, "@babel/highlight": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { "color-name": "1.1.3" } }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, "@babel/parser": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true }, "@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { "@babel/code-frame": "^7.22.13", "@babel/parser": "^7.22.15", "@babel/types": "^7.22.15" } }, "@babel/traverse": { "version": "7.23.2", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "requires": { "@babel/code-frame": "^7.22.13", "@babel/generator": "^7.23.0", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "@babel/parser": "^7.23.0", "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "requires": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" }, "dependencies": { "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { "sprintf-js": "~1.0.2" } }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" } }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" } }, "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" } } } }, "@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, "@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "requires": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.9" } }, "@jridgewell/resolve-uri": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true }, "@jridgewell/set-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true }, "@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "@jridgewell/trace-mapping": { "version": "0.3.19", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" } }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "append-transform": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, "requires": { "default-require-extensions": "^3.0.0" } }, "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", "dev": true }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { "balanced-match": "^1.0.0" } }, "braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { "fill-range": "^7.1.1" } }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "browserslist": { "version": "4.21.10", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "dev": true, "requires": { "caniuse-lite": "^1.0.30001517", "electron-to-chromium": "^1.4.477", "node-releases": "^2.0.13", "update-browserslist-db": "^1.0.11" } }, "caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, "requires": { "hasha": "^5.0.0", "make-dir": "^3.0.0", "package-hash": "^4.0.0", "write-file-atomic": "^3.0.0" } }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "caniuse-lite": { "version": "1.0.30001524", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz", "integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==", "dev": true }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "dependencies": { "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" } } } }, "charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", "dev": true }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", "fsevents": "~2.3.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" } }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "crypt": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", "dev": true }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" }, "dependencies": { "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, "default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", "dev": true, "requires": { "strip-bom": "^4.0.0" } }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "ecashaddrjs": { "version": "file:../ecashaddrjs", "requires": { "@babel/cli": "^7.21.0", "@babel/core": "^7.21.3", "@babel/preset-env": "^7.20.2", "babel-loader": "^9.1.2", "big-integer": "1.6.36", "bs58check": "^3.0.1", "buffer": "^6.0.3", "chai": "^4.3.7", "debug": "^4.3.4", "eslint": "^8.37.0", "jsdoc": "^4.0.2", "mocha": "^10.2.0", "mocha-junit-reporter": "^2.2.0", "mocha-suppress-logs": "^0.3.1", "nyc": "^15.1.0", "random-js": "^2.1.0", "webpack": "^5.76.2", "webpack-cli": "^5.0.1" } }, "electron-to-chromium": { "version": "1.4.504", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.504.tgz", "integrity": "sha512-cSMwIAd8yUh54VwitVRVvHK66QqHWE39C3DRj8SWiXitEpVSY3wNPD9y1pxQtLIi4w3UdzF9klLsmuPshz09DQ==", "dev": true }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" } }, "find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, "requires": { "commondir": "^1.0.1", "make-dir": "^3.0.2", "pkg-dir": "^4.1.0" } }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, "foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "requires": { "cross-spawn": "^7.0.0", "signal-exit": "^3.0.2" } }, "fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", "dev": true }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "optional": true }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, "dependencies": { "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } } } }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, "requires": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" } }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" } }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { "binary-extensions": "^2.0.0" } }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" } }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", "dev": true }, "istanbul-lib-hook": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, "requires": { "append-transform": "^2.0.0" } }, "istanbul-lib-instrument": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "requires": { "@babel/core": "^7.7.5", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" } }, "istanbul-lib-processinfo": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", "dev": true, "requires": { "archy": "^1.0.0", "cross-spawn": "^7.0.3", "istanbul-lib-coverage": "^3.2.0", "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^8.3.2" } }, "istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "dependencies": { "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { "yallist": "^4.0.0" } }, "make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "requires": { "semver": "^7.5.3" } }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" } }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" } }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } }, "istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "requires": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" } }, "istanbul-reports": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "requires": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" } }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" } }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { "p-locate": "^5.0.0" } }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { "yallist": "^3.0.2" } }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { "semver": "^6.0.0" } }, "md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "dev": true, "requires": { "charenc": "0.0.2", "crypt": "0.0.2", "is-buffer": "~1.1.6" } }, "minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, "mkdirp": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true }, "mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, "requires": { "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.2.0", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", "minimatch": "5.0.1", "ms": "2.1.3", "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" } }, "mocha-junit-reporter": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==", "dev": true, "requires": { "debug": "^4.3.4", "md5": "^2.3.0", "mkdirp": "^3.0.0", "strip-ansi": "^6.0.1", "xml": "^1.0.1" } }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "nanoid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true }, "node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", "dev": true, "requires": { "process-on-spawn": "^1.0.0" } }, "node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", "dev": true, "requires": { "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "caching-transform": "^4.0.0", "convert-source-map": "^1.7.0", "decamelize": "^1.2.0", "find-cache-dir": "^3.2.0", "find-up": "^4.1.0", "foreground-child": "^2.0.0", "get-package-type": "^0.1.0", "glob": "^7.1.6", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-hook": "^3.0.0", "istanbul-lib-instrument": "^4.0.0", "istanbul-lib-processinfo": "^2.0.2", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.2", "make-dir": "^3.0.0", "node-preload": "^0.2.1", "p-map": "^3.0.0", "process-on-spawn": "^1.0.0", "resolve-from": "^5.0.0", "rimraf": "^3.0.0", "signal-exit": "^3.0.2", "spawn-wrap": "^2.0.0", "test-exclude": "^6.0.0", "yargs": "^15.0.2" }, "dependencies": { "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" } }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" } }, "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" } }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "yargs-parser": { "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } } } }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" } }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { "yocto-queue": "^0.1.0" } }, "p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { "p-limit": "^3.0.2" } }, "p-map": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, "requires": { "aggregate-error": "^3.0.0" } }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "package-hash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, "requires": { "graceful-fs": "^4.1.15", "hasha": "^5.0.0", "lodash.flattendeep": "^4.4.0", "release-zalgo": "^1.0.0" } }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { "find-up": "^4.0.0" }, "dependencies": { "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { "p-locate": "^4.1.0" } }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" } }, "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { "p-limit": "^2.2.0" } } } }, "process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", "dev": true, "requires": { "fromentries": "^1.2.0" } }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { "safe-buffer": "^5.1.0" } }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" } }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, "requires": { "es6-error": "^4.0.1" } }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" } }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" } }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { "shebang-regex": "^3.0.0" } }, "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, "requires": { "foreground-child": "^2.0.0", "is-windows": "^1.0.2", "make-dir": "^3.0.0", "rimraf": "^3.0.0", "signal-exit": "^3.0.2", "which": "^2.0.1" } }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" } }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { "has-flag": "^4.0.0" } }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" }, "dependencies": { "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } } } }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" } }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, "requires": { "is-typedarray": "^1.0.0" } }, "update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" } }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" } }, "which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", "dev": true }, "workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", "dev": true }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "yargs-parser": { "version": "20.2.4", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true }, "yargs-unparser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" }, "dependencies": { "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true } } }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true } } } diff --git a/modules/mock-chronik-client/package.json b/modules/mock-chronik-client/package.json index d48c4d2f8..49224ec4f 100644 --- a/modules/mock-chronik-client/package.json +++ b/modules/mock-chronik-client/package.json @@ -1,32 +1,32 @@ { "dependencies": { "ecashaddrjs": "file:../ecashaddrjs" }, "name": "mock-chronik-client", "description": "Testing utility to mock the Chronik indexer client and support unit tests that need to mock chronik related API calls.", - "version": "1.12.2", + "version": "1.12.3", "main": "index.js", "devDependencies": { "mocha": "^10.2.0", "mocha-junit-reporter": "^2.2.1", "nyc": "^15.1.0" }, "scripts": { "test": "mocha", "coverage": "nyc mocha", "junit": "mocha test --reporter mocha-junit-reporter" }, "repository": { "type": "git", "url": "git+https://github.com/Bitcoin-ABC/bitcoin-abc.git", "directory": "modules/mock-chronik-client" }, "keywords": [ "chronik-client", "chronik", "mock" ], "author": "Bitcoin ABC", "license": "MIT", "homepage": "https://github.com/Bitcoin-ABC/bitcoin-abc#readme" }