From 2629456873cd9b293d36b49a0f49cffbf13f4ec4 Mon Sep 17 00:00:00 2001 From: Bonnie57 <146059114+bonnie57@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:30:36 +0800 Subject: [PATCH] Add integration test in react sdk (#214) * Modify calling signTypedData by wallet client * Upload pnpm-lock.yaml * Remove useless code * Set up test env * Modify error handle logic * Modify timeout time * Add util methods and wrapper * Add dispute integration test * Make test serially execute * Add ipAsset integration test * Add ipAccount integration * Add useLicense integration test * Add permission, royalty, nftClient integration test in react sdk * Modify test description * Add nftClient integration test * Move babel config into package.json * Return false when license Terms are already attached to IP Asset --- packages/core-sdk/src/resources/license.ts | 4 +- .../core-sdk/src/types/resources/dispute.ts | 1 - .../core-sdk/test/integration/dispute.test.ts | 6 +- .../test/integration/ipAccount.test.ts | 2 - .../test/integration/nftClient.test.ts | 21 + .../core-sdk/test/integration/utils/util.ts | 8 +- .../test/unit/resources/license.test.ts | 21 +- packages/react-sdk/.env.example | 2 + .../react-sdk/generator/templates/resource.js | 10 +- packages/react-sdk/jest.config.js | 9 + packages/react-sdk/package.json | 32 +- .../react-sdk/src/resources/useDispute.ts | 28 +- .../react-sdk/src/resources/useIpAccount.ts | 28 +- .../react-sdk/src/resources/useIpAsset.ts | 88 +- .../react-sdk/src/resources/useLicense.ts | 76 +- .../react-sdk/src/resources/useNftClient.ts | 10 +- .../react-sdk/src/resources/usePermission.ts | 67 +- .../react-sdk/src/resources/useRoyalty.ts | 55 +- packages/react-sdk/src/util.ts | 7 + .../test/integration/useDispute.test.ts | 69 + .../test/integration/useIpAccount.test.ts | 149 ++ .../test/integration/useIpAsset.test.ts | 327 ++++ .../test/integration/useLicense.test.ts | 144 ++ .../test/integration/useNftClient.test.ts | 29 + .../test/integration/usePermission.test.ts | 160 ++ .../test/integration/useRoyalty.test.ts | 258 ++++ .../test/integration/utils/Wrapper.tsx | 18 + .../react-sdk/test/integration/utils/util.ts | 63 + .../react-sdk/test/jest-environment-jsdom.ts | 55 + packages/react-sdk/test/jest-setup.ts | 1 + pnpm-lock.yaml | 1321 ++++++++++++++++- 31 files changed, 2807 insertions(+), 262 deletions(-) create mode 100644 packages/core-sdk/test/integration/nftClient.test.ts create mode 100644 packages/react-sdk/.env.example create mode 100644 packages/react-sdk/jest.config.js create mode 100644 packages/react-sdk/src/util.ts create mode 100644 packages/react-sdk/test/integration/useDispute.test.ts create mode 100644 packages/react-sdk/test/integration/useIpAccount.test.ts create mode 100644 packages/react-sdk/test/integration/useIpAsset.test.ts create mode 100644 packages/react-sdk/test/integration/useLicense.test.ts create mode 100644 packages/react-sdk/test/integration/useNftClient.test.ts create mode 100644 packages/react-sdk/test/integration/usePermission.test.ts create mode 100644 packages/react-sdk/test/integration/useRoyalty.test.ts create mode 100644 packages/react-sdk/test/integration/utils/Wrapper.tsx create mode 100644 packages/react-sdk/test/integration/utils/util.ts create mode 100644 packages/react-sdk/test/jest-environment-jsdom.ts create mode 100644 packages/react-sdk/test/jest-setup.ts diff --git a/packages/core-sdk/src/resources/license.ts b/packages/core-sdk/src/resources/license.ts index ee35db2e..ae88194b 100644 --- a/packages/core-sdk/src/resources/license.ts +++ b/packages/core-sdk/src/resources/license.ts @@ -186,9 +186,7 @@ export class LicenseClient { licenseTermsId: request.licenseTermsId, }); if (isAttachedLicenseTerms) { - throw new Error( - `License terms id ${request.licenseTermsId} is already attached to the IP with id ${request.ipId}.`, - ); + return { txHash: "", success: false }; } const txHash = await this.licensingModuleClient.attachLicenseTerms({ ipId: request.ipId, diff --git a/packages/core-sdk/src/types/resources/dispute.ts b/packages/core-sdk/src/types/resources/dispute.ts index a650b561..c2000bc3 100644 --- a/packages/core-sdk/src/types/resources/dispute.ts +++ b/packages/core-sdk/src/types/resources/dispute.ts @@ -14,7 +14,6 @@ export type RaiseDisputeRequest = { export type RaiseDisputeResponse = { txHash: string; disputeId?: bigint; - arbitrationPolicy?: Address; }; export type CancelDisputeRequest = { diff --git a/packages/core-sdk/test/integration/dispute.test.ts b/packages/core-sdk/test/integration/dispute.test.ts index 6d837303..7bb2ce56 100644 --- a/packages/core-sdk/test/integration/dispute.test.ts +++ b/packages/core-sdk/test/integration/dispute.test.ts @@ -18,7 +18,7 @@ describe("Dispute Functions", () => { before(async () => { clientA = getStoryClientInSepolia(); - clientB = getStoryClientInSepolia(process.env.SEPOLIA_WALLET_PRIVATE_KEY2 as Address); + clientB = getStoryClientInSepolia(); const mockERC20 = new MockERC20(); await mockERC20.approve(arbitrationPolicyAddress); const tokenId = await getTokenId(); @@ -33,7 +33,7 @@ describe("Dispute Functions", () => { ).ipId!; }); - it("raise a dispute", async () => { + it("should not throw error when raise a dispute", async () => { const raiseDisputeRequest: RaiseDisputeRequest = { targetIpId: ipIdB, arbitrationPolicy: arbitrationPolicyAddress, @@ -50,7 +50,7 @@ describe("Dispute Functions", () => { expect(response.disputeId).to.be.a("bigint"); }); - it("cancel a dispute", async () => { + it("should not throw error when cancel a dispute", async () => { const cancelDispute: CancelDisputeRequest = { disputeId: disputeId, txOptions: { diff --git a/packages/core-sdk/test/integration/ipAccount.test.ts b/packages/core-sdk/test/integration/ipAccount.test.ts index 590e4c9c..5bfd10a8 100644 --- a/packages/core-sdk/test/integration/ipAccount.test.ts +++ b/packages/core-sdk/test/integration/ipAccount.test.ts @@ -14,7 +14,6 @@ import { accessControllerAddress, coreMetadataModuleAddress, } from "../../src/abi/generated"; -import { privateKeyToAccount } from "viem/accounts"; import { getDeadline } from "../../src/utils/sign"; chai.use(chaiAsPromised); @@ -61,7 +60,6 @@ describe("Ip Account functions", () => { }); it("should not throw error when executeWithSig setting permission", async () => { - const account = privateKeyToAccount(process.env.SEPOLIA_WALLET_PRIVATE_KEY as Hex); const state = await client.ipAccount.getIpAccountNonce(ipId); const expectedState = state + 1n; const deadline = getDeadline(60000n); diff --git a/packages/core-sdk/test/integration/nftClient.test.ts b/packages/core-sdk/test/integration/nftClient.test.ts new file mode 100644 index 00000000..cb557014 --- /dev/null +++ b/packages/core-sdk/test/integration/nftClient.test.ts @@ -0,0 +1,21 @@ +import { expect } from "chai"; +import { StoryClient } from "../../src"; +import { getStoryClientInSepolia } from "./utils/util"; + +describe("nftClient Functions", () => { + let client: StoryClient; + before(async () => { + client = getStoryClientInSepolia(); + }); + it("should success when create nft collection", async () => { + const txData = await client.nftClient.createNFTCollection({ + name: "test-collection", + symbol: "TEST", + maxSupply: 100, + txOptions: { + waitForTransaction: true, + }, + }); + expect(txData.nftContract).to.be.a("string").and.not.empty; + }); +}); diff --git a/packages/core-sdk/test/integration/utils/util.ts b/packages/core-sdk/test/integration/utils/util.ts index 9ece9950..414b6f0b 100644 --- a/packages/core-sdk/test/integration/utils/util.ts +++ b/packages/core-sdk/test/integration/utils/util.ts @@ -41,15 +41,11 @@ export const getTokenId = async (nftContract?: Address): Promise { +export const getStoryClientInSepolia = (): StoryClient => { const config: StoryConfig = { chainId: "sepolia", transport: http(RPC), - account: privateKeyToAccount(privateKey || (process.env.SEPOLIA_WALLET_PRIVATE_KEY as Address)), + account: privateKeyToAccount(process.env.SEPOLIA_WALLET_PRIVATE_KEY as Address), }; return StoryClient.newClient(config); }; - -export const getBlockTimestamp = async (): Promise => { - return (await publicClient.getBlock()).timestamp; -}; diff --git a/packages/core-sdk/test/unit/resources/license.test.ts b/packages/core-sdk/test/unit/resources/license.test.ts index 579997c7..5d6edb6d 100644 --- a/packages/core-sdk/test/unit/resources/license.test.ts +++ b/packages/core-sdk/test/unit/resources/license.test.ts @@ -291,23 +291,20 @@ describe("Test LicenseClient", () => { } }); - it("should throw attached error when call attachLicenseTerms given licenseTermsId is already attached", async () => { + it("should return txHash of empty and success of false when call attachLicenseTerms given licenseTermsId is already attached", async () => { sinon.stub(licenseClient.ipAssetRegistryClient, "isRegistered").resolves(true); sinon.stub(licenseClient.piLicenseTemplateReadOnlyClient, "exists").resolves(true); sinon .stub(licenseClient.licenseRegistryReadOnlyClient, "hasIpAttachedLicenseTerms") .resolves(true); - - try { - await licenseClient.attachLicenseTerms({ - ipId: zeroAddress, - licenseTermsId: "1", - }); - } catch (error) { - expect((error as Error).message).equal( - "Failed to attach license terms: License terms id 1 is already attached to the IP with id 0x0000000000000000000000000000000000000000.", - ); - } + const result = await licenseClient.attachLicenseTerms({ + ipId: zeroAddress, + licenseTermsId: "1", + }); + expect(result).to.deep.equal({ + txHash: "", + success: false, + }); }); it("should return txHash when call attachLicenseTerms given args is correct", async () => { diff --git a/packages/react-sdk/.env.example b/packages/react-sdk/.env.example new file mode 100644 index 00000000..b915d5a4 --- /dev/null +++ b/packages/react-sdk/.env.example @@ -0,0 +1,2 @@ +SEPOLIA_TEST_WALLET_ADDRESS = 0x0000000000000000000000000000000000000000 +SEPOLIA_WALLET_PRIVATE_KEY = 0x0000000000000000000000000000000000000000 \ No newline at end of file diff --git a/packages/react-sdk/generator/templates/resource.js b/packages/react-sdk/generator/templates/resource.js index 0bc69e71..7d9154ad 100644 --- a/packages/react-sdk/generator/templates/resource.js +++ b/packages/react-sdk/generator/templates/resource.js @@ -10,11 +10,10 @@ const methodTemplate = `<%=comments%>\nconst <%=method.name %> = async (<% metho setLoadings((prev ) => ({ ...prev, <%=method.name %>: false })); return response; }catch(e){ - if(e instanceof Error){ - setErrors((prev) => ({ ...prev, <%=method.name %>: e.message })); - setLoadings((prev) => ({ ...prev, <%=method.name %>: false })); - } - throw new Error(\`unhandled error type\`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, <%=method.name %>: errorMessage })); + setLoadings((prev) => ({ ...prev, <%=method.name %>: false })); + throw new Error(errorMessage); } }; `; @@ -28,6 +27,7 @@ const startTemplate = `import { <% types.forEach((type,index)=>{%>\n<%=type %><% import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; + import { handleError } from "../util"; const use<%=name %> = () => { const client = useStoryContext(); const [loadings,setLoadings] = useState>({<% methodNames.forEach((name,index)=>{%><%=name %>: false<%=index === methodNames.length - 1 ? '' : ',' %> <%})%>}); diff --git a/packages/react-sdk/jest.config.js b/packages/react-sdk/jest.config.js new file mode 100644 index 00000000..c9ae5171 --- /dev/null +++ b/packages/react-sdk/jest.config.js @@ -0,0 +1,9 @@ +/** @type {import('jest').Config} */ +const config = { + verbose: true, + testEnvironment: "./test/jest-environment-jsdom.ts", + setupFiles: ["./test/jest-setup.ts"], + testTimeout: 1000 * 60, +}; + +module.exports = config; diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index 944efa8e..21273033 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -14,6 +14,7 @@ "scripts": { "generate": "node ./generator/index.js && npm run fix", "build": "pnpm run fix && preconstruct build", + "test": "jest -i", "fix": "pnpm run format:fix && pnpm run lint:fix", "format": "prettier --check .", "format:fix": "prettier --write .", @@ -43,7 +44,14 @@ ], "babel": { "presets": [ - "@babel/preset-env", + [ + "@babel/preset-env", + { + "targets": { + "node": "current" + } + } + ], "@babel/preset-typescript", [ "@babel/preset-react", @@ -57,19 +65,29 @@ "dependencies": { "@story-protocol/core-sdk": "1.0.0-rc.14", "react": "^18.3.1", - "viem": "^2.8.12", - "@types/react": "^18.3.3" + "viem": "^2.8.12" }, "devDependencies": { - "@babel/preset-env": "^7.22.20", + "@babel/core": "^7.23.0", + "@babel/preset-env": "^7.24.4", "@babel/preset-react": "^7.24.7", - "@babel/preset-typescript": "^7.23.0", + "@babel/preset-typescript": "^7.24.1", "@preconstruct/cli": "^2.8.1", "@story-protocol/eslint-config": "workspace:*", "@story-protocol/prettier-config": "workspace:*", "@story-protocol/tsconfig": "workspace:*", + "@testing-library/react": "^16.0.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.8.2", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "babel-jest": "^29.7.0", + "dotenv": "^16.3.1", + "ejs": "^3.1.10", + "eslint-plugin-jest-dom": "^5.4.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "ts-node": "^10.9.1", - "typescript": "^5.4.5", - "ejs": "^3.1.10" + "typescript": "^5.4.5" } } diff --git a/packages/react-sdk/src/resources/useDispute.ts b/packages/react-sdk/src/resources/useDispute.ts index e4bbc07c..5013182f 100644 --- a/packages/react-sdk/src/resources/useDispute.ts +++ b/packages/react-sdk/src/resources/useDispute.ts @@ -9,6 +9,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useDispute = () => { const client = useStoryContext(); @@ -49,11 +50,10 @@ const useDispute = () => { setLoadings((prev) => ({ ...prev, raiseDispute: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, raiseDispute: e.message })); - setLoadings((prev) => ({ ...prev, raiseDispute: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, raiseDispute: errorMessage })); + setLoadings((prev) => ({ ...prev, raiseDispute: false })); + throw new Error(errorMessage); } }; @@ -79,11 +79,10 @@ const useDispute = () => { setLoadings((prev) => ({ ...prev, cancelDispute: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, cancelDispute: e.message })); - setLoadings((prev) => ({ ...prev, cancelDispute: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, cancelDispute: errorMessage })); + setLoadings((prev) => ({ ...prev, cancelDispute: false })); + throw new Error(errorMessage); } }; @@ -107,11 +106,10 @@ const useDispute = () => { setLoadings((prev) => ({ ...prev, resolveDispute: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, resolveDispute: e.message })); - setLoadings((prev) => ({ ...prev, resolveDispute: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, resolveDispute: errorMessage })); + setLoadings((prev) => ({ ...prev, resolveDispute: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/useIpAccount.ts b/packages/react-sdk/src/resources/useIpAccount.ts index 9177757b..f1a596b9 100644 --- a/packages/react-sdk/src/resources/useIpAccount.ts +++ b/packages/react-sdk/src/resources/useIpAccount.ts @@ -8,6 +8,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useIpAccount = () => { const client = useStoryContext(); @@ -41,11 +42,10 @@ const useIpAccount = () => { setLoadings((prev) => ({ ...prev, execute: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, execute: e.message })); - setLoadings((prev) => ({ ...prev, execute: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, execute: errorMessage })); + setLoadings((prev) => ({ ...prev, execute: false })); + throw new Error(errorMessage); } }; @@ -70,11 +70,10 @@ const useIpAccount = () => { setLoadings((prev) => ({ ...prev, executeWithSig: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, executeWithSig: e.message })); - setLoadings((prev) => ({ ...prev, executeWithSig: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, executeWithSig: errorMessage })); + setLoadings((prev) => ({ ...prev, executeWithSig: false })); + throw new Error(errorMessage); } }; @@ -92,11 +91,10 @@ const useIpAccount = () => { setLoadings((prev) => ({ ...prev, getIpAccountNonce: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, getIpAccountNonce: e.message })); - setLoadings((prev) => ({ ...prev, getIpAccountNonce: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, getIpAccountNonce: errorMessage })); + setLoadings((prev) => ({ ...prev, getIpAccountNonce: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/useIpAsset.ts b/packages/react-sdk/src/resources/useIpAsset.ts index 0a752bf0..962e08fe 100644 --- a/packages/react-sdk/src/resources/useIpAsset.ts +++ b/packages/react-sdk/src/resources/useIpAsset.ts @@ -15,6 +15,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useIpAsset = () => { const client = useStoryContext(); @@ -59,11 +60,10 @@ const useIpAsset = () => { setLoadings((prev) => ({ ...prev, register: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, register: e.message })); - setLoadings((prev) => ({ ...prev, register: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, register: errorMessage })); + setLoadings((prev) => ({ ...prev, register: false })); + throw new Error(errorMessage); } }; @@ -90,11 +90,10 @@ const useIpAsset = () => { setLoadings((prev) => ({ ...prev, registerDerivative: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, registerDerivative: e.message })); - setLoadings((prev) => ({ ...prev, registerDerivative: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, registerDerivative: errorMessage })); + setLoadings((prev) => ({ ...prev, registerDerivative: false })); + throw new Error(errorMessage); } }; @@ -130,17 +129,16 @@ const useIpAsset = () => { })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - registerDerivativeWithLicenseTokens: e.message, - })); - setLoadings((prev) => ({ - ...prev, - registerDerivativeWithLicenseTokens: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + registerDerivativeWithLicenseTokens: errorMessage, + })); + setLoadings((prev) => ({ + ...prev, + registerDerivativeWithLicenseTokens: false, + })); + throw new Error(errorMessage); } }; @@ -183,17 +181,16 @@ const useIpAsset = () => { })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - mintAndRegisterIpAssetWithPilTerms: e.message, - })); - setLoadings((prev) => ({ - ...prev, - mintAndRegisterIpAssetWithPilTerms: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + mintAndRegisterIpAssetWithPilTerms: errorMessage, + })); + setLoadings((prev) => ({ + ...prev, + mintAndRegisterIpAssetWithPilTerms: false, + })); + throw new Error(errorMessage); } }; @@ -227,17 +224,13 @@ const useIpAsset = () => { setLoadings((prev) => ({ ...prev, registerIpAndAttachPilTerms: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - registerIpAndAttachPilTerms: e.message, - })); - setLoadings((prev) => ({ - ...prev, - registerIpAndAttachPilTerms: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + registerIpAndAttachPilTerms: errorMessage, + })); + setLoadings((prev) => ({ ...prev, registerIpAndAttachPilTerms: false })); + throw new Error(errorMessage); } }; @@ -269,11 +262,10 @@ const useIpAsset = () => { setLoadings((prev) => ({ ...prev, registerDerivativeIp: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, registerDerivativeIp: e.message })); - setLoadings((prev) => ({ ...prev, registerDerivativeIp: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, registerDerivativeIp: errorMessage })); + setLoadings((prev) => ({ ...prev, registerDerivativeIp: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/useLicense.ts b/packages/react-sdk/src/resources/useLicense.ts index 9397cd36..2cf563f1 100644 --- a/packages/react-sdk/src/resources/useLicense.ts +++ b/packages/react-sdk/src/resources/useLicense.ts @@ -13,6 +13,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useLicense = () => { const client = useStoryContext(); @@ -58,17 +59,16 @@ const useLicense = () => { })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - registerNonComSocialRemixingPIL: e.message, - })); - setLoadings((prev) => ({ - ...prev, - registerNonComSocialRemixingPIL: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + registerNonComSocialRemixingPIL: errorMessage, + })); + setLoadings((prev) => ({ + ...prev, + registerNonComSocialRemixingPIL: false, + })); + throw new Error(errorMessage); } }; @@ -91,11 +91,13 @@ const useLicense = () => { setLoadings((prev) => ({ ...prev, registerCommercialUsePIL: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, registerCommercialUsePIL: e.message })); - setLoadings((prev) => ({ ...prev, registerCommercialUsePIL: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + registerCommercialUsePIL: errorMessage, + })); + setLoadings((prev) => ({ ...prev, registerCommercialUsePIL: false })); + throw new Error(errorMessage); } }; @@ -119,14 +121,13 @@ const useLicense = () => { setLoadings((prev) => ({ ...prev, registerCommercialRemixPIL: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - registerCommercialRemixPIL: e.message, - })); - setLoadings((prev) => ({ ...prev, registerCommercialRemixPIL: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + registerCommercialRemixPIL: errorMessage, + })); + setLoadings((prev) => ({ ...prev, registerCommercialRemixPIL: false })); + throw new Error(errorMessage); } }; @@ -149,11 +150,10 @@ const useLicense = () => { setLoadings((prev) => ({ ...prev, attachLicenseTerms: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, attachLicenseTerms: e.message })); - setLoadings((prev) => ({ ...prev, attachLicenseTerms: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, attachLicenseTerms: errorMessage })); + setLoadings((prev) => ({ ...prev, attachLicenseTerms: false })); + throw new Error(errorMessage); } }; @@ -189,11 +189,10 @@ const useLicense = () => { setLoadings((prev) => ({ ...prev, mintLicenseTokens: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, mintLicenseTokens: e.message })); - setLoadings((prev) => ({ ...prev, mintLicenseTokens: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, mintLicenseTokens: errorMessage })); + setLoadings((prev) => ({ ...prev, mintLicenseTokens: false })); + throw new Error(errorMessage); } }; @@ -214,11 +213,10 @@ const useLicense = () => { setLoadings((prev) => ({ ...prev, getLicenseTerms: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, getLicenseTerms: e.message })); - setLoadings((prev) => ({ ...prev, getLicenseTerms: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, getLicenseTerms: errorMessage })); + setLoadings((prev) => ({ ...prev, getLicenseTerms: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/useNftClient.ts b/packages/react-sdk/src/resources/useNftClient.ts index 9a9fb082..bc165f10 100644 --- a/packages/react-sdk/src/resources/useNftClient.ts +++ b/packages/react-sdk/src/resources/useNftClient.ts @@ -5,6 +5,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useNftClient = () => { const client = useStoryContext(); @@ -38,11 +39,10 @@ const useNftClient = () => { setLoadings((prev) => ({ ...prev, createNFTCollection: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, createNFTCollection: e.message })); - setLoadings((prev) => ({ ...prev, createNFTCollection: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, createNFTCollection: errorMessage })); + setLoadings((prev) => ({ ...prev, createNFTCollection: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/usePermission.ts b/packages/react-sdk/src/resources/usePermission.ts index 31b122a1..6b723159 100644 --- a/packages/react-sdk/src/resources/usePermission.ts +++ b/packages/react-sdk/src/resources/usePermission.ts @@ -9,6 +9,7 @@ import { import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const usePermission = () => { const client = useStoryContext(); @@ -57,11 +58,10 @@ const usePermission = () => { setLoadings((prev) => ({ ...prev, setPermission: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, setPermission: e.message })); - setLoadings((prev) => ({ ...prev, setPermission: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, setPermission: errorMessage })); + setLoadings((prev) => ({ ...prev, setPermission: false })); + throw new Error(errorMessage); } }; @@ -90,17 +90,13 @@ const usePermission = () => { setLoadings((prev) => ({ ...prev, createSetPermissionSignature: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - createSetPermissionSignature: e.message, - })); - setLoadings((prev) => ({ - ...prev, - createSetPermissionSignature: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + createSetPermissionSignature: errorMessage, + })); + setLoadings((prev) => ({ ...prev, createSetPermissionSignature: false })); + throw new Error(errorMessage); } }; @@ -124,11 +120,10 @@ const usePermission = () => { setLoadings((prev) => ({ ...prev, setAllPermissions: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, setAllPermissions: e.message })); - setLoadings((prev) => ({ ...prev, setAllPermissions: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, setAllPermissions: errorMessage })); + setLoadings((prev) => ({ ...prev, setAllPermissions: false })); + throw new Error(errorMessage); } }; @@ -156,11 +151,10 @@ const usePermission = () => { setLoadings((prev) => ({ ...prev, setBatchPermissions: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, setBatchPermissions: e.message })); - setLoadings((prev) => ({ ...prev, setBatchPermissions: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, setBatchPermissions: errorMessage })); + setLoadings((prev) => ({ ...prev, setBatchPermissions: false })); + throw new Error(errorMessage); } }; @@ -196,17 +190,16 @@ const usePermission = () => { })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ - ...prev, - createBatchPermissionSignature: e.message, - })); - setLoadings((prev) => ({ - ...prev, - createBatchPermissionSignature: false, - })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ + ...prev, + createBatchPermissionSignature: errorMessage, + })); + setLoadings((prev) => ({ + ...prev, + createBatchPermissionSignature: false, + })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/resources/useRoyalty.ts b/packages/react-sdk/src/resources/useRoyalty.ts index 4a7945e6..e02e53fe 100644 --- a/packages/react-sdk/src/resources/useRoyalty.ts +++ b/packages/react-sdk/src/resources/useRoyalty.ts @@ -14,6 +14,7 @@ import { Hex, Address } from "viem"; import { useState } from "react"; import { useStoryContext } from "../StoryProtocolContext"; +import { handleError } from "../util"; const useRoyalty = () => { const client = useStoryContext(); @@ -53,11 +54,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, collectRoyaltyTokens: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, collectRoyaltyTokens: e.message })); - setLoadings((prev) => ({ ...prev, collectRoyaltyTokens: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, collectRoyaltyTokens: errorMessage })); + setLoadings((prev) => ({ ...prev, collectRoyaltyTokens: false })); + throw new Error(errorMessage); } }; @@ -81,11 +81,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, payRoyaltyOnBehalf: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, payRoyaltyOnBehalf: e.message })); - setLoadings((prev) => ({ ...prev, payRoyaltyOnBehalf: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, payRoyaltyOnBehalf: errorMessage })); + setLoadings((prev) => ({ ...prev, payRoyaltyOnBehalf: false })); + throw new Error(errorMessage); } }; @@ -109,11 +108,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, claimableRevenue: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, claimableRevenue: e.message })); - setLoadings((prev) => ({ ...prev, claimableRevenue: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, claimableRevenue: errorMessage })); + setLoadings((prev) => ({ ...prev, claimableRevenue: false })); + throw new Error(errorMessage); } }; @@ -138,11 +136,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, claimRevenue: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, claimRevenue: e.message })); - setLoadings((prev) => ({ ...prev, claimRevenue: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, claimRevenue: errorMessage })); + setLoadings((prev) => ({ ...prev, claimRevenue: false })); + throw new Error(errorMessage); } }; @@ -164,11 +161,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, snapshot: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, snapshot: e.message })); - setLoadings((prev) => ({ ...prev, snapshot: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, snapshot: errorMessage })); + setLoadings((prev) => ({ ...prev, snapshot: false })); + throw new Error(errorMessage); } }; @@ -189,11 +185,10 @@ const useRoyalty = () => { setLoadings((prev) => ({ ...prev, getRoyaltyVaultAddress: false })); return response; } catch (e) { - if (e instanceof Error) { - setErrors((prev) => ({ ...prev, getRoyaltyVaultAddress: e.message })); - setLoadings((prev) => ({ ...prev, getRoyaltyVaultAddress: false })); - } - throw new Error(`unhandled error type`); + const errorMessage = handleError(e); + setErrors((prev) => ({ ...prev, getRoyaltyVaultAddress: errorMessage })); + setLoadings((prev) => ({ ...prev, getRoyaltyVaultAddress: false })); + throw new Error(errorMessage); } }; diff --git a/packages/react-sdk/src/util.ts b/packages/react-sdk/src/util.ts new file mode 100644 index 00000000..362bc955 --- /dev/null +++ b/packages/react-sdk/src/util.ts @@ -0,0 +1,7 @@ +export const handleError = (error: unknown): string => { + if (error instanceof Error) { + return error.message; + } else { + return "Unhandled error type"; + } +}; diff --git a/packages/react-sdk/test/integration/useDispute.test.ts b/packages/react-sdk/test/integration/useDispute.test.ts new file mode 100644 index 00000000..47dedf5f --- /dev/null +++ b/packages/react-sdk/test/integration/useDispute.test.ts @@ -0,0 +1,69 @@ +import { renderHook, act, waitFor } from "@testing-library/react"; +import { Address } from "viem"; +import { RaiseDisputeResponse, useDispute } from "../../src"; +import { useIpAsset } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { mockERC721Address, getTokenId } from "./utils/util"; + +const arbitrationPolicyAddress = "0xc07Bc791CF55E718BA7D70cE650B3152BbE3325e"; +describe("useDispute Functions", () => { + const { + result: { current: disputeHook }, + } = renderHook(() => useDispute(), { wrapper: Wrapper }); + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + let ipId: Address; + let disputedId: number; + + beforeAll(async () => { + const tokenId = await getTokenId(); + await act(async () => { + ipId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + }); + + it("should success when call raise dispute", async () => { + let result: RaiseDisputeResponse; + await act(async () => { + result = await disputeHook.raiseDispute({ + targetIpId: ipId, + arbitrationPolicy: arbitrationPolicyAddress, + linkToDisputeEvidence: "foo", + targetTag: "PLAGIARISM", + txOptions: { + waitForTransaction: true, + }, + }); + disputedId = Number(result.disputeId); + }); + await waitFor(() => { + expect(result).toEqual( + expect.objectContaining({ + txHash: expect.any(String), + disputeId: expect.any(BigInt), + }) + ); + }); + }); + + it("should success when cancel dispute", async () => { + await act(async () => { + await expect( + disputeHook.cancelDispute({ disputeId: disputedId }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/useIpAccount.test.ts b/packages/react-sdk/test/integration/useIpAccount.test.ts new file mode 100644 index 00000000..c1214358 --- /dev/null +++ b/packages/react-sdk/test/integration/useIpAccount.test.ts @@ -0,0 +1,149 @@ +import { renderHook, act } from "@testing-library/react"; +import { mockERC721Address, getTokenId, walletClient } from "./utils/util"; +import { + AccessPermission, + getPermissionSignature, + useIpAccount, + useIpAsset, +} from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { + Address, + Hex, + encodeFunctionData, + getAddress, + toFunctionSelector, +} from "viem"; +const coreMetadataModuleAddress = "0xDa498A3f7c8a88cb72201138C366bE3778dB9575"; +const permissionAddress = "0xF9936a224b3Deb6f9A4645ccAfa66f7ECe83CF0A"; +const accessControllerAbi = [ + { + inputs: [ + { + internalType: "address", + name: "ipId", + type: "address", + }, + { + internalType: "address", + name: "signer", + type: "address", + }, + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "bytes4", + name: "func", + type: "bytes4", + }, + { + internalType: "uint8", + name: "permission", + type: "uint8", + }, + ], + name: "setPermission", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; +describe("useIpAccount Functions", () => { + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + const { + result: { current: ipAccountHook }, + } = renderHook(() => useIpAccount(), { wrapper: Wrapper }); + + let ipId: Address; + let data: Hex; + beforeAll(async () => { + const tokenId = await getTokenId(); + await act(async () => { + ipId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + data = encodeFunctionData({ + abi: accessControllerAbi, + functionName: "setPermission", + args: [ + getAddress(ipId), + getAddress(process.env.SEPOLIA_TEST_WALLET_ADDRESS as Hex), + getAddress(coreMetadataModuleAddress), + toFunctionSelector("function setAll(address,string,bytes32,bytes32)"), + AccessPermission.ALLOW, + ], + }); + }); + + it("should success when call execute", async () => { + await act(async () => { + await expect( + ipAccountHook.execute({ + to: permissionAddress, + value: 0, + data, + ipId: ipId, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); + + it("should success when executeWithSig setting permission", async () => { + const state = await ipAccountHook.getIpAccountNonce(ipId); + const expectedState = state + 1n; + const deadline = Date.now() + 60000; + const signature = await getPermissionSignature({ + ipId, + wallet: walletClient, + permissions: [ + { + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Hex, + to: coreMetadataModuleAddress, + permission: AccessPermission.ALLOW, + func: "function setAll(address,string,bytes32,bytes32)", + }, + ], + nonce: expectedState, + + chainId: 11155111n, + deadline: BigInt(deadline), + }); + await act(async () => { + await expect( + ipAccountHook.executeWithSig({ + ipId: ipId, + value: 0, + to: permissionAddress, + data: data, + deadline: deadline, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Hex, + signature: signature, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/useIpAsset.test.ts b/packages/react-sdk/test/integration/useIpAsset.test.ts new file mode 100644 index 00000000..8696b7f3 --- /dev/null +++ b/packages/react-sdk/test/integration/useIpAsset.test.ts @@ -0,0 +1,327 @@ +import { renderHook, act, waitFor } from "@testing-library/react"; +import { Address, toHex } from "viem"; +import { + CreateIpAssetWithPilTermsResponse, + PIL_TYPE, + RegisterIpResponse, + useLicense, + useNftClient, +} from "../../src"; +import { useIpAsset } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { mockERC721Address, getTokenId, mockERC20Address } from "./utils/util"; +describe("useIpAsset Functions", () => { + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + const { + result: { current: licenseHook }, + } = renderHook(() => useLicense(), { wrapper: Wrapper }); + const { + result: { current: nftClientHook }, + } = renderHook(() => useNftClient(), { wrapper: Wrapper }); + + let childIpId: Address; + let noCommercialLicenseTermsId: bigint; + let parentIpId: Address; + it("should success when call register", async () => { + const tokenId = await getTokenId(); + let result: RegisterIpResponse; + await act(async () => { + result = await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }); + childIpId = result.ipId!; + }); + await waitFor(() => { + expect(result).toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + + it("should success when call register given metadata", async () => { + const tokenId = await getTokenId(); + await act(async () => { + await expect( + ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + metadata: { + metadataURI: "test-uri", + metadataHash: toHex("test-metadata-hash", { size: 32 }), + nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), + }, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + + it("should success when registering derivative", async () => { + const tokenId = await getTokenId(); + await act(async () => { + parentIpId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + await act(async () => { + noCommercialLicenseTermsId = ( + await licenseHook.registerNonComSocialRemixingPIL({ + txOptions: { + waitForTransaction: true, + }, + }) + ).licenseTermsId!; + }); + await act(async () => { + await licenseHook.attachLicenseTerms({ + ipId: parentIpId, + licenseTermsId: noCommercialLicenseTermsId, + txOptions: { + waitForTransaction: true, + }, + }); + }); + await act(async () => { + await expect( + ipAssetHook.registerDerivative({ + parentIpIds: [parentIpId], + childIpId: childIpId, + licenseTermsIds: [noCommercialLicenseTermsId], + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); + + it("should success when registering derivative with license tokens", async () => { + const tokenId = await getTokenId(); + let ipId: Address; + let licenseTokenId: bigint; + await act(async () => { + ipId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + await act(async () => { + licenseTokenId = ( + await licenseHook.mintLicenseTokens({ + licenseTermsId: noCommercialLicenseTermsId, + licensorIpId: parentIpId, + txOptions: { + waitForTransaction: true, + }, + }) + ).licenseTokenIds![0]!; + }); + await act(async () => { + await expect( + ipAssetHook.registerDerivativeWithLicenseTokens({ + childIpId: ipId, + licenseTokenIds: [licenseTokenId], + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); + + describe("NFT Client (SPG)", () => { + let nftContract: Address; + beforeAll(async () => { + await act(async () => { + nftContract = ( + await nftClientHook.createNFTCollection({ + name: "test-collection", + symbol: "TEST", + maxSupply: 100, + txOptions: { + waitForTransaction: true, + }, + }) + ).nftContract!; + }); + }); + describe("should success when mint and register ip and attach pil terms", () => { + it("Non-Commercial Remix", async () => { + await act(async () => { + await expect( + ipAssetHook.mintAndRegisterIpAssetWithPilTerms({ + nftContract, + pilType: PIL_TYPE.NON_COMMERCIAL_REMIX, + metadata: { + metadataURI: "test-uri", + metadataHash: toHex("test-metadata-hash", { size: 32 }), + nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), + }, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + + it("Commercial Use", async () => { + await act(async () => { + await expect( + ipAssetHook.mintAndRegisterIpAssetWithPilTerms({ + nftContract, + pilType: PIL_TYPE.COMMERCIAL_USE, + commercialRevShare: 10, + mintingFee: "100", + currency: mockERC20Address, + metadata: { + metadataURI: "test-uri", + metadataHash: toHex("test-metadata-hash", { size: 32 }), + nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), + }, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + + it("Commercial Remix", async () => { + await act(async () => { + await expect( + ipAssetHook.mintAndRegisterIpAssetWithPilTerms({ + nftContract, + pilType: PIL_TYPE.COMMERCIAL_REMIX, + commercialRevShare: 10, + mintingFee: "100", + currency: mockERC20Address, + metadata: { + metadataURI: "test-uri", + metadataHash: toHex("test-metadata-hash", { size: 32 }), + nftMetadataHash: toHex("test-nft-metadata-hash", { size: 32 }), + }, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + }); + + it("should success when register derivative ip", async () => { + const tokenChildId = await getTokenId(nftContract); + let createIpAssetWithPilTermsResponse: CreateIpAssetWithPilTermsResponse; + await act(async () => { + createIpAssetWithPilTermsResponse = + await ipAssetHook.mintAndRegisterIpAssetWithPilTerms({ + nftContract, + pilType: PIL_TYPE.NON_COMMERCIAL_REMIX, + commercialRevShare: 10, + mintingFee: "100", + currency: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }); + }); + await act(async () => { + await expect( + ipAssetHook.registerDerivativeIp({ + nftContract: nftContract, + tokenId: tokenChildId!, + derivData: { + parentIpIds: [createIpAssetWithPilTermsResponse.ipId!], + licenseTermsIds: [ + createIpAssetWithPilTermsResponse.licenseTermsId!, + ], + }, + deadline: 1000n, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + ipId: expect.any(String), + }) + ); + }); + }); + + it("should success when register ip and attach pil terms", async () => { + const tokenId = await getTokenId(nftContract); + const deadline = 1000n; + await act(async () => { + await expect( + ipAssetHook.registerIpAndAttachPilTerms({ + nftContract: nftContract, + tokenId: tokenId!, + deadline, + pilType: PIL_TYPE.NON_COMMERCIAL_REMIX, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + ipId: expect.any(String), + txHash: expect.any(String), + }) + ); + }); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/useLicense.test.ts b/packages/react-sdk/test/integration/useLicense.test.ts new file mode 100644 index 00000000..06903ade --- /dev/null +++ b/packages/react-sdk/test/integration/useLicense.test.ts @@ -0,0 +1,144 @@ +import { renderHook } from "@testing-library/react"; +import { useIpAsset, useLicense } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { act } from "react"; +import { getTokenId, mockERC20Address, mockERC721Address } from "./utils/util"; +import { Hex } from "viem"; + +describe("useLicense Functions", () => { + const { + result: { current: licenseHook }, + } = renderHook(() => useLicense(), { wrapper: Wrapper }); + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + describe("registering license with different types", () => { + it("should success when register license with non commercial social remixing PIL", async () => { + await act(async () => { + await expect( + licenseHook.registerNonComSocialRemixingPIL({ + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + licenseTermsId: expect.any(BigInt), + }) + ); + }); + }); + + it("should success when register license with commercial use", async () => { + await act(async () => { + await expect( + licenseHook.registerCommercialUsePIL({ + mintingFee: "1", + currency: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + licenseTermsId: expect.any(BigInt), + }) + ); + }); + }); + + it("should success when register license with commercial Remix use", async () => { + await act(async () => { + await expect( + licenseHook.registerCommercialRemixPIL({ + mintingFee: "1", + commercialRevShare: 100, + currency: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + licenseTermsId: expect.any(BigInt), + }) + ); + }); + }); + }); + + describe("attach License Terms and mint license tokens", () => { + let ipId: Hex; + let licenseId: bigint; + let tokenId; + beforeAll(async () => { + tokenId = await getTokenId(); + await act(async () => { + ipId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + await act(async () => { + licenseId = ( + await licenseHook.registerNonComSocialRemixingPIL({ + txOptions: { + waitForTransaction: true, + }, + }) + ).licenseTermsId!; + }); + }); + + it("should success when attach License Terms", async () => { + await act(async () => { + await expect( + licenseHook.attachLicenseTerms({ + ipId: ipId, + licenseTermsId: licenseId, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: expect.any(Boolean), + }) + ); + }); + }); + + it("should success when minting license tokens", async () => { + await act(async () => { + await expect( + licenseHook.mintLicenseTokens({ + licenseTermsId: licenseId, + licensorIpId: ipId, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + licenseTokenIds: expect.any(Array), + }) + ); + }); + }); + + it("should success when get license terms", async () => { + await act(async () => { + await expect(licenseHook.getLicenseTerms(licenseId)).resolves.toEqual( + expect.any(Object) + ); + }); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/useNftClient.test.ts b/packages/react-sdk/test/integration/useNftClient.test.ts new file mode 100644 index 00000000..ff3fcb13 --- /dev/null +++ b/packages/react-sdk/test/integration/useNftClient.test.ts @@ -0,0 +1,29 @@ +import { renderHook } from "@testing-library/react"; +import { useNftClient } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { act } from "react"; + +describe("useNftClient Functions", () => { + const { + result: { current: nftClientHook }, + } = renderHook(() => useNftClient(), { wrapper: Wrapper }); + + it("should success when create nft collection", async () => { + await act(async () => { + await expect( + nftClientHook.createNFTCollection({ + name: "test-collection", + symbol: "TEST", + maxSupply: 100, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + nftContract: expect.any(String), + }) + ); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/usePermission.test.ts b/packages/react-sdk/test/integration/usePermission.test.ts new file mode 100644 index 00000000..f02b4422 --- /dev/null +++ b/packages/react-sdk/test/integration/usePermission.test.ts @@ -0,0 +1,160 @@ +import { act, renderHook } from "@testing-library/react"; +import { useIpAsset, usePermission } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { Address } from "viem"; +import { getTokenId, mockERC721Address } from "./utils/util"; + +describe("usePermission Functions", () => { + const { + result: { current: permissionHook }, + } = renderHook(() => usePermission(), { wrapper: Wrapper }); + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + + let ipId: Address; + beforeAll(async () => { + const tokenId = await getTokenId(); + await act(async () => { + ipId = ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + }); + it("should success when call setPermission", async () => { + await act(async () => { + await expect( + permissionHook.setPermission({ + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: true, + }) + ); + }); + }); + + it("should success when call setAllPermissions", async () => { + await act(async () => { + await expect( + permissionHook.setAllPermissions({ + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + permission: 1, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: true, + }) + ); + }); + }); + + it("should success when call createSetPermissionSignature", async () => { + await act(async () => { + await expect( + permissionHook.createSetPermissionSignature({ + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + deadline: 60000n, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: true, + }) + ); + }); + }); + + it("should success when call setBatchPermissions", async () => { + await act(async () => { + await expect( + permissionHook.setBatchPermissions({ + permissions: [ + { + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + func: "function setAll(address,string,bytes32,bytes32)", + }, + { + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + func: "function freezeMetadata(address)", + }, + ], + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: true, + }) + ); + }); + }); + + it("should success when call createBatchPermissionSignature", async () => { + await act(async () => { + await expect( + permissionHook.createBatchPermissionSignature({ + ipId: ipId, + permissions: [ + { + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + func: "function setAll(address,string,bytes32,bytes32)", + }, + { + ipId: ipId, + signer: process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + to: "0x2ac240293f12032E103458451dE8A8096c5A72E8", + permission: 1, + func: "function freezeMetadata(address)", + }, + ], + deadline: 60000n, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + success: true, + }) + ); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/useRoyalty.test.ts b/packages/react-sdk/test/integration/useRoyalty.test.ts new file mode 100644 index 00000000..2625f412 --- /dev/null +++ b/packages/react-sdk/test/integration/useRoyalty.test.ts @@ -0,0 +1,258 @@ +import { act, renderHook, waitFor } from "@testing-library/react"; +import { useIpAccount, useIpAsset, useLicense, useRoyalty } from "../../src"; +import Wrapper from "./utils/Wrapper"; +import { Address, encodeFunctionData } from "viem"; +import { getTokenId, mockERC20Address, mockERC721Address } from "./utils/util"; + +describe("useRoyalty Functions", () => { + const { + result: { current: royaltyHook }, + } = renderHook(() => useRoyalty(), { wrapper: Wrapper }); + const { + result: { current: ipAssetHook }, + } = renderHook(() => useIpAsset(), { wrapper: Wrapper }); + const { + result: { current: licenseHook }, + } = renderHook(() => useLicense(), { wrapper: Wrapper }); + const { + result: { current: ipAccountHook }, + } = renderHook(() => useIpAccount(), { wrapper: Wrapper }); + let ipId1: Address; + let ipId2: Address; + + const getIpId = async (): Promise
=> { + const tokenId = await getTokenId(); + return await act(async () => { + return ( + await ipAssetHook.register({ + nftContract: mockERC721Address, + tokenId: tokenId!, + txOptions: { + waitForTransaction: true, + }, + }) + ).ipId!; + }); + }; + const getCommercialPolicyId = async (): Promise => { + return await act(async () => { + return ( + await licenseHook.registerCommercialUsePIL({ + mintingFee: "1", + currency: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }) + ).licenseTermsId!; + }); + }; + + const attachLicenseTerms = async (ipId: Address, licenseTermsId: bigint) => { + await act(async () => { + await licenseHook.attachLicenseTerms({ + ipId, + licenseTermsId: licenseTermsId, + txOptions: { + waitForTransaction: true, + }, + }); + }); + }; + beforeAll(async () => { + ipId1 = await getIpId(); + ipId2 = await getIpId(); + const licenseTermsId = await getCommercialPolicyId(); + await attachLicenseTerms(ipId1, licenseTermsId); + await act(async () => { + await ipAssetHook.registerDerivative({ + childIpId: ipId2, + parentIpIds: [ipId1], + licenseTermsIds: [licenseTermsId], + txOptions: { + waitForTransaction: true, + }, + }); + }); + }); + + it("should success when collect royalty tokens", async () => { + await act(async () => { + await expect( + royaltyHook.collectRoyaltyTokens({ + parentIpId: ipId1, + royaltyVaultIpId: ipId2, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + royaltyTokensCollected: expect.any(BigInt), + }) + ); + }); + }); + + it("should success when pay royalty on behalf", async () => { + await act(async () => { + await expect( + royaltyHook.payRoyaltyOnBehalf({ + receiverIpId: ipId1, + payerIpId: ipId2, + token: mockERC20Address, + amount: "10", + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + }) + ); + }); + }); + + it("should success when snapshot", async () => { + await act(async () => { + await expect( + royaltyHook.snapshot({ + royaltyVaultIpId: ipId1, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + snapshotId: expect.any(BigInt), + }) + ); + }); + }); + it("should success when claimable revenue", async () => { + await act(async () => { + await expect( + royaltyHook.claimableRevenue({ + royaltyVaultIpId: ipId1, + account: ipId1, + snapshotId: "1", + token: mockERC20Address, + }) + ).resolves.toEqual(expect.any(BigInt)); + }); + }); + + it("should success when claim revenue by ipAccount", async () => { + await act(async () => { + await expect( + royaltyHook.claimRevenue({ + royaltyVaultIpId: ipId1, + snapshotIds: ["1"], + account: ipId1, + token: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + claimableToken: expect.any(BigInt), + }) + ); + }); + }); + + it("should success when claim revenue by ipAccount by EOA", async () => { + const proxyAddress = await royaltyHook.getRoyaltyVaultAddress(ipId1); + //1.transfer token to eoa + await act(async () => { + await ipAccountHook.execute({ + to: proxyAddress, + value: 0, + ipId: ipId1, + txOptions: { + waitForTransaction: true, + }, + data: encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "transfer", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ], + functionName: "transfer", + args: [ + process.env.SEPOLIA_TEST_WALLET_ADDRESS as Address, + BigInt(10 * 10 ** 6), + ], + }), + }); + }); + //2. transfer token to royaltyVault,revenue token + await act(async () => { + await royaltyHook.payRoyaltyOnBehalf({ + receiverIpId: ipId1, + payerIpId: ipId2, + token: mockERC20Address, + amount: "10", + txOptions: { + waitForTransaction: true, + }, + }); + }); + //3. snapshot + let snapshotId: bigint; + await act(async () => { + snapshotId = ( + await royaltyHook.snapshot({ + royaltyVaultIpId: ipId1, + txOptions: { + waitForTransaction: true, + }, + }) + ).snapshotId!; + }); + //4. claim revenue + await act(async () => { + await expect( + royaltyHook.claimRevenue({ + royaltyVaultIpId: ipId1, + snapshotIds: [snapshotId], + token: mockERC20Address, + txOptions: { + waitForTransaction: true, + }, + }) + ).resolves.toEqual( + expect.objectContaining({ + txHash: expect.any(String), + claimableToken: expect.any(BigInt), + }) + ); + }); + }); +}); diff --git a/packages/react-sdk/test/integration/utils/Wrapper.tsx b/packages/react-sdk/test/integration/utils/Wrapper.tsx new file mode 100644 index 00000000..2fb69051 --- /dev/null +++ b/packages/react-sdk/test/integration/utils/Wrapper.tsx @@ -0,0 +1,18 @@ +import { Address, http } from "viem"; +import { StoryProvider } from "../../../src"; +import { privateKeyToAccount } from "viem/accounts"; +import { ReactNode } from "react"; +type Props = { children: ReactNode }; +const Wrapper = ({ children }: Props) => ( + + {children} + +); +export default Wrapper; diff --git a/packages/react-sdk/test/integration/utils/util.ts b/packages/react-sdk/test/integration/utils/util.ts new file mode 100644 index 00000000..aec30725 --- /dev/null +++ b/packages/react-sdk/test/integration/utils/util.ts @@ -0,0 +1,63 @@ +import { privateKeyToAccount } from "viem/accounts"; +import { + http, + createPublicClient, + createWalletClient, + Hex, + Address, + Chain, +} from "viem"; +import { sepolia } from "viem/chains"; + +import { SupportedChainIds } from "@story-protocol/core-sdk"; +export const RPC = "http://127.0.0.1:8545"; +export const mockERC721Address = "0x7ee32b8B515dEE0Ba2F25f612A04a731eEc24F49"; +export const mockERC20Address = "0xB132A6B7AE652c974EE1557A3521D53d18F6739f"; + +function chainStringToViemChain(chainId: SupportedChainIds): Chain { + switch (chainId) { + case "11155111": + case "sepolia": + return sepolia; + default: + throw new Error(`chainId ${chainId as string} not supported`); + } +} +const baseConfig = { + chain: chainStringToViemChain("sepolia"), + transport: http(RPC), +} as const; +export const publicClient = createPublicClient(baseConfig); +export const walletClient = createWalletClient({ + ...baseConfig, + account: privateKeyToAccount(process.env.SEPOLIA_WALLET_PRIVATE_KEY as Hex), +}); + +export const getTokenId = async ( + nftContract?: Address +): Promise => { + const { request } = await publicClient.simulateContract({ + abi: [ + { + inputs: [{ internalType: "address", name: "to", type: "address" }], + name: "mint", + outputs: [ + { internalType: "uint256", name: "tokenId", type: "uint256" }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ], + address: nftContract || mockERC721Address, + functionName: "mint", + args: [process.env.SEPOLIA_TEST_WALLET_ADDRESS as Hex], + account: walletClient.account, + }); + const hash = await walletClient.writeContract(request); + const { logs } = await publicClient.waitForTransactionReceipt({ + hash, + }); + if (logs[0].topics[3]) { + return parseInt(logs[0].topics[3], 16); + } +}; diff --git a/packages/react-sdk/test/jest-environment-jsdom.ts b/packages/react-sdk/test/jest-environment-jsdom.ts new file mode 100644 index 00000000..6596f25e --- /dev/null +++ b/packages/react-sdk/test/jest-environment-jsdom.ts @@ -0,0 +1,55 @@ +import $JSDOMEnvironment, { + TestEnvironment as $TestEnvironment, +} from "jest-environment-jsdom"; +import { TextDecoder, TextEncoder } from "util"; + +/** + * This patched JSDOMEnvironment serves as a proxy for the default JSDOMEnvironment from Jest. + * It patches the global objects TextEncoder, TextDecoder, and Uint8Array + * which are missing, or improperly implemented (Uint8Array is a node Buffer) in the JSDOM environment. + * + * A proxy pattern is used due to ES5 transpilation limitations, as direct + * class extension isn't possible in ES5. The class delegates other methods + * and properties to the original JSDOMEnvironment. + * + * We use this wrapper for full compatibility with browser global + * objects in our Jest testing environment. + */ +class JSDOMEnvironment { + private environment: $JSDOMEnvironment; + + constructor(...args: ConstructorParameters) { + this.environment = new $JSDOMEnvironment(...args); + this.environment.global.TextEncoder = TextEncoder; + this.environment.global.TextDecoder = + TextDecoder as typeof global.TextDecoder; + this.environment.global.Uint8Array = Uint8Array; + this.environment.global.Request = Request; + this.environment.global.Response = Response; + this.environment.global.Headers = Headers; + this.environment.global.fetch = fetch; + + return new Proxy(this, { + get: (target, prop, receiver) => { + if (Reflect.has(target, prop)) { + return Reflect.get(target, prop, receiver); + } else { + return Reflect.get(target.environment, prop, receiver); + } + }, + set: (target, prop, value) => { + if (Reflect.has(target, prop)) { + return Reflect.set(target, prop, value); + } else { + return Reflect.set(target.environment, prop, value); + } + }, + }); + } +} + +const TestEnvironment = + $TestEnvironment === $JSDOMEnvironment ? JSDOMEnvironment : $TestEnvironment; + +export default JSDOMEnvironment; +export { TestEnvironment }; diff --git a/packages/react-sdk/test/jest-setup.ts b/packages/react-sdk/test/jest-setup.ts new file mode 100644 index 00000000..3d0d62ec --- /dev/null +++ b/packages/react-sdk/test/jest-setup.ts @@ -0,0 +1 @@ +import "dotenv/config"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2fc77308..6ab49a01 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -142,9 +142,6 @@ importers: '@story-protocol/core-sdk': specifier: 1.0.0-rc.14 version: 1.0.0-rc.14(typescript@5.4.5) - '@types/react': - specifier: ^18.3.3 - version: 18.3.3 react: specifier: ^18.3.1 version: 18.3.1 @@ -152,14 +149,17 @@ importers: specifier: ^2.8.12 version: 2.9.16(typescript@5.4.5)(zod@3.22.4) devDependencies: + '@babel/core': + specifier: ^7.23.0 + version: 7.24.4 '@babel/preset-env': - specifier: ^7.22.20 + specifier: ^7.24.4 version: 7.24.4(@babel/core@7.24.4) '@babel/preset-react': specifier: ^7.24.7 version: 7.24.7(@babel/core@7.24.4) '@babel/preset-typescript': - specifier: ^7.23.0 + specifier: ^7.24.1 version: 7.24.1(@babel/core@7.24.4) '@preconstruct/cli': specifier: ^2.8.1 @@ -173,9 +173,39 @@ importers: '@story-protocol/tsconfig': specifier: workspace:* version: link:../tsconfig + '@testing-library/react': + specifier: ^16.0.0 + version: 16.0.0(@testing-library/dom@10.1.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.2.0)(react@18.3.1) + '@types/jest': + specifier: ^29.5.12 + version: 29.5.12 + '@types/node': + specifier: ^20.8.2 + version: 20.12.7 + '@types/react': + specifier: ^18.3.3 + version: 18.3.3 + '@types/react-dom': + specifier: ^18.3.0 + version: 18.3.0 + babel-jest: + specifier: ^29.7.0 + version: 29.7.0(@babel/core@7.24.4) + dotenv: + specifier: ^16.3.1 + version: 16.4.5 ejs: specifier: ^3.1.10 version: 3.1.10 + eslint-plugin-jest-dom: + specifier: ^5.4.0 + version: 5.4.0(@testing-library/dom@10.1.0)(eslint@8.57.0) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + jest-environment-jsdom: + specifier: ^29.7.0 + version: 29.7.0 ts-node: specifier: ^10.9.1 version: 10.9.2(@types/node@20.12.7)(typescript@5.4.5) @@ -259,15 +289,15 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.4 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.4) '@babel/helpers': 7.24.4 - '@babel/parser': 7.24.4 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 + '@babel/parser': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 convert-source-map: 2.0.0 debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 @@ -772,6 +802,15 @@ packages: '@babel/helper-plugin-utils': 7.24.0 dev: true + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.4): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.4 + '@babel/helper-plugin-utils': 7.24.7 + dev: true + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.4): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: @@ -2494,11 +2533,77 @@ packages: engines: {node: '>=12'} dev: true + /@istanbuljs/load-nyc-config@1.1.0: + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + dev: true + /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} dev: true + /@jest/console@29.7.0: + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/core@29.7.0(ts-node@10.9.2): + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /@jest/create-cache-key-function@29.7.0: resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2516,6 +2621,23 @@ packages: jest-mock: 29.7.0 dev: true + /@jest/expect-utils@29.7.0: + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + dev: true + + /@jest/expect@29.7.0: + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/fake-timers@29.7.0: resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2528,6 +2650,55 @@ packages: jest-util: 29.7.0 dev: true + /@jest/globals@29.7.0: + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters@29.7.0: + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 20.12.7 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.2.0 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2535,6 +2706,58 @@ packages: '@sinclair/typebox': 0.27.8 dev: true + /@jest/source-map@29.6.3: + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + + /@jest/test-result@29.7.0: + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + dev: true + + /@jest/test-sequencer@29.7.0: + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/transform@29.7.0: + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.24.4 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.5 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/types@26.6.2: resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} engines: {node: '>= 10.14.2'} @@ -3827,6 +4050,48 @@ packages: react: 18.3.1 dev: true + /@testing-library/dom@10.1.0: + resolution: {integrity: sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA==} + engines: {node: '>=18'} + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/runtime': 7.24.4 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + dev: true + + /@testing-library/react@16.0.0(@testing-library/dom@10.1.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.2.0)(react@18.3.1): + resolution: {integrity: sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 + '@types/react-dom': ^18.0.0 + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.4 + '@testing-library/dom': 10.1.0 + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.2.0(react@18.3.1) + dev: true + + /@tootallnate/once@2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: true + /@tsconfig/node10@1.0.11: resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} dev: true @@ -3843,6 +4108,39 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true + /@types/aria-query@5.0.4: + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + dev: true + + /@types/babel__core@7.20.5: + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + dependencies: + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + dev: true + + /@types/babel__generator@7.6.8: + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + dependencies: + '@babel/types': 7.24.7 + dev: true + + /@types/babel__template@7.4.4: + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + dependencies: + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + dev: true + + /@types/babel__traverse@7.20.6: + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + dependencies: + '@babel/types': 7.24.7 + dev: true + /@types/chai-as-promised@7.1.8: resolution: {integrity: sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==} dependencies: @@ -3888,6 +4186,12 @@ packages: resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==} dev: true + /@types/graceful-fs@4.1.9: + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + dependencies: + '@types/node': 20.12.7 + dev: true + /@types/har-format@1.2.15: resolution: {integrity: sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==} dev: true @@ -3908,6 +4212,21 @@ packages: '@types/istanbul-lib-report': 3.0.3 dev: true + /@types/jest@29.5.12: + resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /@types/jsdom@20.0.1: + resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} + dependencies: + '@types/node': 20.12.7 + '@types/tough-cookie': 4.0.5 + parse5: 7.1.2 + dev: true + /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: false @@ -3952,14 +4271,20 @@ packages: /@types/prop-types@15.7.12: resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - dev: false + dev: true + + /@types/react-dom@18.3.0: + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + dependencies: + '@types/react': 18.3.3 + dev: true /@types/react@18.3.3: resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 - dev: false + dev: true /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} @@ -3990,6 +4315,10 @@ packages: resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} dev: true + /@types/tough-cookie@4.0.5: + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + dev: true + /@types/trusted-types@2.0.7: resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} dev: true @@ -4625,6 +4954,11 @@ packages: tslib: 1.14.1 dev: true + /abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + dev: true + /abitype@0.10.3(typescript@5.4.5): resolution: {integrity: sha512-tRN+7XIa7J9xugdbRzFv/95ka5ivR/sRe01eiWvM0HWWjHuigSZEACgKa0sj4wGuekTDtghCx+5Izk/cOi78pQ==} peerDependencies: @@ -4711,6 +5045,13 @@ packages: negotiator: 0.6.3 dev: true + /acorn-globals@7.0.1: + resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + dependencies: + acorn: 8.11.3 + acorn-walk: 8.3.2 + dev: true + /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -4728,6 +5069,15 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -4750,6 +5100,13 @@ packages: engines: {node: '>=6'} dev: true + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + /ansi-fragments@0.2.1: resolution: {integrity: sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==} dependencies: @@ -4815,6 +5172,12 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: true + /array-back@3.1.0: resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==} engines: {node: '>=6'} @@ -4933,7 +5296,6 @@ packages: /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false /at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} @@ -4969,21 +5331,62 @@ packages: '@babel/core': 7.24.4 dev: true - /babel-plugin-macros@3.1.0: - resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} - engines: {node: '>=10', npm: '>=6'} - dependencies: - '@babel/runtime': 7.24.4 - cosmiconfig: 7.1.0 - resolve: 1.22.8 - dev: true - - /babel-plugin-polyfill-corejs2@0.4.10(@babel/core@7.24.4): - resolution: {integrity: sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==} + /babel-jest@29.7.0(@babel/core@7.24.4): + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + '@babel/core': ^7.8.0 dependencies: - '@babel/compat-data': 7.24.4 + '@babel/core': 7.24.4 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.24.4) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.24.7 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.6 + dev: true + + /babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + dependencies: + '@babel/runtime': 7.24.4 + cosmiconfig: 7.1.0 + resolve: 1.22.8 + dev: true + + /babel-plugin-polyfill-corejs2@0.4.10(@babel/core@7.24.4): + resolution: {integrity: sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + dependencies: + '@babel/compat-data': 7.24.4 '@babel/core': 7.24.4 '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.4) semver: 6.3.1 @@ -5022,6 +5425,37 @@ packages: - '@babel/core' dev: true + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.4): + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.4 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.4) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.4) + dev: true + + /babel-preset-jest@29.6.3(@babel/core@7.24.4): + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.4 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.4) + dev: true + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -5322,6 +5756,11 @@ packages: resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} dev: true + /char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + dev: true + /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true @@ -5403,6 +5842,10 @@ packages: consola: 3.2.3 dev: true + /cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + dev: true + /cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -5475,6 +5918,15 @@ packages: engines: {node: '>=6'} dev: true + /co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true + + /collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + dev: true + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -5503,7 +5955,6 @@ packages: engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 - dev: false /command-exists@1.2.9: resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} @@ -5641,6 +6092,25 @@ packages: hasBin: true dev: true + /create-jest@29.7.0(@types/node@20.12.7)(ts-node@10.9.2): + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true @@ -5686,8 +6156,24 @@ packages: optional: true dev: true + /cssom@0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + dev: true + + /cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + dev: true + + /cssstyle@2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + dependencies: + cssom: 0.3.8 + dev: true + /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dev: true /csv-generate@3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} @@ -5711,6 +6197,15 @@ packages: stream-transform: 2.1.3 dev: true + /data-urls@3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + dev: true + /data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -5802,6 +6297,10 @@ packages: engines: {node: '>=10'} dev: true + /decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dev: true + /decode-uri-component@0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} engines: {node: '>=0.10'} @@ -5811,6 +6310,15 @@ packages: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true + /dedent@1.5.3: + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dev: true + /deep-eql@4.1.3: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} @@ -5865,7 +6373,6 @@ packages: /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - dev: false /denodeify@1.2.1: resolution: {integrity: sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==} @@ -5885,6 +6392,11 @@ packages: prop-types: 15.8.1 dev: true + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: true + /destr@2.0.3: resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} dev: true @@ -5909,6 +6421,16 @@ packages: hasBin: true dev: true + /detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -5947,6 +6469,18 @@ packages: dependencies: esutils: 2.0.3 + /dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dev: true + + /domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead + dependencies: + webidl-conversions: 7.0.0 + dev: true + /dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: @@ -6013,6 +6547,11 @@ packages: minimalistic-crypto-utils: 1.0.1 dev: true + /emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + dev: true + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -6059,6 +6598,11 @@ packages: strip-ansi: 6.0.1 dev: true + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + dev: true + /envinfo@7.12.0: resolution: {integrity: sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==} engines: {node: '>=4'} @@ -6227,6 +6771,18 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + /escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + dev: true + /eslint-config-prettier@8.10.0(eslint@8.57.0): resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true @@ -6319,6 +6875,22 @@ packages: - supports-color dev: false + /eslint-plugin-jest-dom@5.4.0(@testing-library/dom@10.1.0)(eslint@8.57.0): + resolution: {integrity: sha512-yBqvFsnpS5Sybjoq61cJiUsenRkC9K32hYQBFS9doBR7nbQZZ5FyO+X7MlmfM1C48Ejx/qTuOCgukDUNyzKZ7A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6', yarn: '>=1'} + peerDependencies: + '@testing-library/dom': ^8.0.0 || ^9.0.0 || ^10.0.0 + eslint: ^6.8.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + peerDependenciesMeta: + '@testing-library/dom': + optional: true + dependencies: + '@babel/runtime': 7.24.4 + '@testing-library/dom': 10.1.0 + eslint: 8.57.0 + requireindex: 1.2.0 + dev: true + /eslint-plugin-tsdoc@0.2.17: resolution: {integrity: sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==} dependencies: @@ -6533,6 +7105,22 @@ packages: strip-final-newline: 3.0.0 dev: true + /exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: true + + /expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + dev: true + /extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} dev: true @@ -6753,7 +7341,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: false /fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} @@ -6852,6 +7439,11 @@ packages: has-symbols: 1.0.3 hasown: 2.0.2 + /get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + dev: true + /get-port-please@3.1.2: resolution: {integrity: sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==} dev: true @@ -7114,6 +7706,13 @@ packages: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true + /html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + dependencies: + whatwg-encoding: 2.0.0 + dev: true + /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true @@ -7135,11 +7734,32 @@ packages: toidentifier: 1.0.1 dev: true + /http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + /http-shutdown@1.2.2: resolution: {integrity: sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} dev: true + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + /human-id@1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} dev: true @@ -7179,6 +7799,13 @@ packages: safer-buffer: 2.1.2 dev: true + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + /idb-keyval@6.2.1: resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} dev: true @@ -7220,6 +7847,15 @@ packages: parent-module: 1.0.1 resolve-from: 4.0.0 + /import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -7356,6 +7992,11 @@ packages: engines: {node: '>=8'} dev: true + /is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + dev: true + /is-generator-function@1.0.10: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} @@ -7433,6 +8074,10 @@ packages: isobject: 3.0.1 dev: true + /is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: true + /is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} dependencies: @@ -7569,6 +8214,32 @@ packages: engines: {node: '>=8'} dev: true + /istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.24.4 + '@babel/parser': 7.24.7 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-instrument@6.0.2: + resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==} + engines: {node: '>=10'} + dependencies: + '@babel/core': 7.24.4 + '@babel/parser': 7.24.7 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + dev: true + /istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} @@ -7578,6 +8249,17 @@ packages: supports-color: 7.2.0 dev: true + /istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4(supports-color@8.1.1) + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + /istanbul-reports@3.1.7: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} @@ -7591,10 +8273,168 @@ packages: engines: {node: '>=10'} hasBin: true dependencies: - async: 3.2.5 - chalk: 4.1.2 - filelist: 1.0.4 - minimatch: 3.1.2 + async: 3.2.5 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + dev: true + + /jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + dev: true + + /jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.3 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-cli@29.7.0(@types/node@20.12.7)(ts-node@10.9.2): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /jest-config@29.7.0(@types/node@20.12.7)(ts-node@10.9.2): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.24.4 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + babel-jest: 29.7.0(@babel/core@7.24.4) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + ts-node: 10.9.2(@types/node@20.12.7)(typescript@5.4.5) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /jest-environment-jsdom@29.7.0: + resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/jsdom': 20.0.1 + '@types/node': 20.12.7 + jest-mock: 29.7.0 + jest-util: 29.7.0 + jsdom: 20.0.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate dev: true /jest-environment-node@29.7.0: @@ -7614,6 +8454,43 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 20.12.7 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + /jest-message-util@29.7.0: resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -7638,6 +8515,135 @@ packages: jest-util: 29.7.0 dev: true + /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.7.0 + dev: true + + /jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + dev: true + + /jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + chalk: 4.1.2 + cjs-module-lexer: 1.3.1 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.24.4 + '@babel/generator': 7.24.7 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.4) + '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.4) + '@babel/types': 7.24.7 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.4) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + dev: true + /jest-util@29.7.0: resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -7662,6 +8668,20 @@ packages: pretty-format: 29.7.0 dev: true + /jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.12.7 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + dev: true + /jest-worker@26.6.2: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} @@ -7681,6 +8701,27 @@ packages: supports-color: 8.1.1 dev: true + /jest@29.7.0(@types/node@20.12.7)(ts-node@10.9.2): + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /jiti@1.21.0: resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} hasBin: true @@ -7759,6 +8800,47 @@ packages: - supports-color dev: true + /jsdom@20.0.3: + resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} + engines: {node: '>=14'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + abab: 2.0.6 + acorn: 8.11.3 + acorn-globals: 7.0.1 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.4.3 + domexception: 4.0.0 + escodegen: 2.1.0 + form-data: 4.0.0 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.10 + parse5: 7.1.2 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 4.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + ws: 8.13.0 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -8091,6 +9173,11 @@ packages: dependencies: yallist: 4.0.0 + /lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + dev: true + /magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} dependencies: @@ -8750,6 +9837,10 @@ packages: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} dev: true + /nwsapi@2.2.10: + resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==} + dev: true + /ob1@0.80.8: resolution: {integrity: sha512-QHJQk/lXMmAW8I7AIM3in1MSlwe1umR72Chhi8B7Xnq6mzjhBKkA6Fy/zAhQnGkA4S912EPCEvTij5yh+EQTAA==} engines: {node: '>=18'} @@ -9045,6 +10136,12 @@ packages: lines-and-columns: 1.2.4 dev: true + /parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.5.0 + dev: true + /parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -9242,6 +10339,15 @@ packages: react-is: 17.0.2 dev: true + /pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + dev: true + /pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -9297,6 +10403,10 @@ packages: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true + /psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + dev: true + /pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} dependencies: @@ -9308,6 +10418,10 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + /pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + dev: true + /qr-code-styling@1.6.0-rc.1: resolution: {integrity: sha512-ModRIiW6oUnsP18QzrRYZSc/CFKFKIdj7pUs57AEVH20ajlglRpN3HukjHk0UbNMTlKGuaYl7Gt6/O5Gg2NU2Q==} dependencies: @@ -9344,6 +10458,10 @@ packages: strict-uri-encode: 2.0.0 dev: true + /querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + dev: true + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -9686,6 +10804,22 @@ packages: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true + /requireindex@1.2.0: + resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==} + engines: {node: '>=0.10.5'} + dev: true + + /requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: true + + /resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + /resolve-from@3.0.0: resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==} engines: {node: '>=4'} @@ -9700,6 +10834,11 @@ packages: engines: {node: '>=8'} dev: true + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + /resolve@1.19.0: resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} dependencies: @@ -9812,6 +10951,13 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true + /saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + dependencies: + xmlchars: 2.2.0 + dev: true + /scheduler@0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: @@ -10072,6 +11218,13 @@ packages: atomic-sleep: 1.0.0 dev: true + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: @@ -10200,6 +11353,14 @@ packages: resolution: {integrity: sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==} dev: true + /string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -10275,6 +11436,11 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + /strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + dev: true + /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -10336,6 +11502,10 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + /symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: true + /system-architecture@0.1.0: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} engines: {node: '>=18'} @@ -10435,10 +11605,27 @@ packages: engines: {node: '>=0.6'} dev: true + /tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + dev: true + /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: true + /tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + dependencies: + punycode: 2.3.1 + dev: true + /trim-newlines@3.0.1: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} @@ -10613,6 +11800,11 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} @@ -10772,6 +11964,11 @@ packages: engines: {node: '>= 4.0.0'} dev: true + /universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + dev: true + /universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -10882,6 +12079,13 @@ packages: dependencies: punycode: 2.3.1 + /url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: true + /use-sync-external-store@1.2.0(react@18.3.1): resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: @@ -11027,6 +12231,13 @@ packages: engines: {node: '>=0.10.0'} dev: true + /w3c-xmlserializer@4.0.0: + resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} + engines: {node: '>=14'} + dependencies: + xml-name-validator: 4.0.0 + dev: true + /wagmi@2.5.19(@tanstack/react-query@5.29.2)(react-native@0.73.6)(react@18.3.1)(typescript@5.4.5)(viem@2.9.16): resolution: {integrity: sha512-fy6s3qTuXpfrrghhoNXuV92yqOqJI7m/9iLIejHxEYxiddVDTR8BVdkt0BuBQZzoXSAutDkyIlJbtFcpX5dfrQ==} peerDependencies: @@ -11104,10 +12315,35 @@ packages: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: true + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: true + + /whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + dependencies: + iconv-lite: 0.6.3 + dev: true + /whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} dev: true + /whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + dev: true + + /whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: true + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: @@ -11201,6 +12437,14 @@ packages: signal-exit: 3.0.7 dev: true + /write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + /ws@6.2.2: resolution: {integrity: sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==} peerDependencies: @@ -11256,6 +12500,15 @@ packages: utf-8-validate: optional: true + /xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + dev: true + + /xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: true + /xmlhttprequest-ssl@2.0.0: resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} engines: {node: '>=0.4.0'}