diff --git a/package.json b/package.json index 8567c5e..fb47f7e 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "eslint:ci": "eslint --no-eslintrc --c .eslintrc.json '*.{js,json}' '{src,tests}/**/*.{ts,tsx}'", "pretest": "tsc --noEmit", "test": "vitest --ui --coverage", - "test:ci": "vitest" + "test:ci": "vitest", + "test:debug": "DEBUG=vitest:web-worker vitest" }, "engines": { "node": ">=12.7.0" @@ -57,6 +58,7 @@ "@rollup/plugin-commonjs": "^25.0.3", "@rollup/plugin-node-resolve": "^15.1.0", "@rollup/plugin-typescript": "^11.1.2", + "@testing-library/jest-dom": "^6.0.1", "@testing-library/react": "^14.0.0", "@types/react": "^18.2.15", "@types/rollup-plugin-peer-deps-external": "^2.2.1", @@ -64,6 +66,7 @@ "@typescript-eslint/parser": "^6.1.0", "@vitest/coverage-v8": "^0.34.1", "@vitest/ui": "^0.34.1", + "@vitest/web-worker": "^0.34.2", "eslint": "^8.21.0", "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-alias": "^1.1.2", @@ -79,9 +82,12 @@ "rollup-plugin-peer-deps-external": "^2.2.4", "snarkyjs": "^0.12.1", "typescript": "^5.1.6", + "vite": "^4.4.9", "vite-plugin-top-level-await": "^1.3.1", "vite-plugin-wasm": "^3.2.2", "vitest": "^0.34.1", + "vitest-canvas-mock": "^0.3.3", + "vitest-fetch-mock": "^0.2.2", "zod": "^3.21.4", "zustand": "^4.3.9" }, @@ -91,5 +97,6 @@ "snarkyjs": ">=0.11.0", "zod": ">=3.21.0", "zustand": ">=4.3.9" - } + }, + "license": "MIT" } diff --git a/setupVitest.js b/setupVitest.js new file mode 100644 index 0000000..382ef31 --- /dev/null +++ b/setupVitest.js @@ -0,0 +1,11 @@ +import matchers from "@testing-library/jest-dom/matchers"; +import { expect, vi } from "vitest"; +import createFetchMock from "vitest-fetch-mock"; +import "vitest-canvas-mock"; + +expect.extend(matchers); + +const fetchMocker = createFetchMock(vi); + +// sets globalThis.fetch and globalThis.fetchMock to our mocked version +fetchMocker.enableMocks(); diff --git a/src/hooks.ts b/src/hooks.ts index f1d780d..df17585 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -27,7 +27,7 @@ export const createZKState = ( const useZKStore = create(createState); const zkAppWorkerClient = new ZkAppWorkerClient(worker); - const useInitZkStore = () => { + const useInitZKStore = () => { const isInitialized = useContractStore((state) => state.isInitialized); const setProof = useContractStore((state) => state.setProof); @@ -80,5 +80,15 @@ export const createZKState = ( }; }; - return { useInitZkStore, useZKStore, useGetLatestProof }; + const useProof = () => useContractStore((state) => state.proof); + const useIsInitialized = () => + useContractStore((state) => state.isInitialized); + + return { + useInitZKStore, + useZKStore, + useGetLatestProof, + useProof, + useIsInitialized, + }; }; diff --git a/src/index.ts b/src/index.ts index 7399a29..0e5e234 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,2 @@ -export * from "./store"; export * from "./hooks"; export { initZKWorker } from "./zkAppWorker"; diff --git a/src/zkAppWorker.ts b/src/zkAppWorker.ts index a94589e..bd7396e 100644 --- a/src/zkAppWorker.ts +++ b/src/zkAppWorker.ts @@ -13,6 +13,8 @@ import { } from "./types"; import { INITIAL_STATE, MERKLE_TREE_HEIGHT, MerkleWitness20 } from "./utils"; +let post = postMessage; + const state: WorkerState = { tree: new MerkleTree(MERKLE_TREE_HEIGHT), transitionIndex: 0n, // should increment on state transition and is used to index the tree @@ -43,7 +45,7 @@ setInterval(() => { id: 0, data: proof.toJSON(), }; - postMessage(message); + post(message); state.executingUpdate = false; }) @@ -136,28 +138,35 @@ export interface ZkappWorkerReponse { data: unknown; } -export const initZKWorker = () => { +const onMessage = async (event: MessageEvent) => { + try { + const returnData = await workerFunctions[event.data.fn](event.data.args); + + const message: ZkappWorkerReponse = { + resType: "function-call", + id: event.data.id, + data: returnData, + }; + post(message); + } catch (error) { + console.warn("Worker Error:", error); + } +}; + +/** + * Sets the message listener in the worker. Read the README for informations on how to use in your own code. + * + * @param testRef reference to the worker file when testing the library, do not use when developing + */ +export const initZKWorker = (testRef?: Window & typeof globalThis) => { console.info("[zk-states worker] adding event listener"); - addEventListener( - "message", - async (event: MessageEvent) => { - try { - const returnData = await workerFunctions[event.data.fn]( - event.data.args, - ); - - const message: ZkappWorkerReponse = { - resType: "function-call", - id: event.data.id, - data: returnData, - }; - postMessage(message); - } catch (error) { - console.warn("Worker Error:", error); - } - }, - ); + if (testRef) { + post = testRef.postMessage; + testRef.onmessage = onMessage; + } else { + onmessage = onMessage; + } console.info("[zk-states worker] added worker service listener."); }; diff --git a/tests/index.test.tsx b/tests/index.test.tsx index 446a364..1c518b2 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -1,14 +1,71 @@ import React from "react"; -import { render } from "@testing-library/react"; -import { it } from "vitest"; +import "@vitest/web-worker"; +import { render, waitFor } from "@testing-library/react"; +import type { JsonProof } from "snarkyjs"; +import { describe, expect, it } from "vitest"; +import { createZKState } from "zk-states"; -// TODO: fix wasm error when importing zk-states library -it("initializes state in a react component", async () => { - const Component = () => { - return <>hello world!; - }; +interface ZKState { + num: number; + incNum: () => void; +} - const { findByText } = render(); +const { + useInitZKStore, + useGetLatestProof, + useZKStore, + useProof, + useIsInitialized, +} = createZKState( + new Worker(new URL("./worker.ts", import.meta.url), { + type: "module", + }), + (set) => ({ + num: 0, + incNum: () => set((state) => ({ num: state.num + 1 })), + }), +); - await findByText("hello world!"); +describe("createZKState", () => { + it("returns the expected hooks", () => { + expect(useInitZKStore).toBeDefined(); + expect(useGetLatestProof).toBeDefined(); + expect(useProof).toBeDefined(); + expect(useIsInitialized).toBeDefined(); + }); + + it("renders the correct initial global state value in a React component", async () => { + const expectedValue = 0; + + const Component = () => { + const num = useZKStore((state) => state.num); + return
num: {num}
; + }; + + const { findByText } = render(); + + await findByText(`num: ${expectedValue}`); + }); + + it("correctly initializes ZK store with useInitZKStore", async () => { + let proof: JsonProof | undefined; + + const Component = () => { + const isInitialized = useIsInitialized(); + proof = useProof(); + useInitZKStore(); + return
isInitialized: {isInitialized ? "true" : "false"}
; + }; + + const { getByText } = render(); + + await waitFor( + () => { + expect(getByText("isInitialized: true")).toBeInTheDocument(); + }, + { timeout: 300000 }, + ); + + expect(proof).toBeDefined(); + }); }); diff --git a/tests/tsconfig.json b/tests/tsconfig.json index 867292b..734b605 100644 --- a/tests/tsconfig.json +++ b/tests/tsconfig.json @@ -1,4 +1,7 @@ { "extends": "../tsconfig.json", - "include": ["./**/*"] + "include": ["./**/*"], + "compilerOptions": { + "types": ["@testing-library/jest-dom"] + } } diff --git a/tests/worker.ts b/tests/worker.ts new file mode 100644 index 0000000..b3304b2 --- /dev/null +++ b/tests/worker.ts @@ -0,0 +1,3 @@ +import { initZKWorker } from "zk-states"; + +initZKWorker(self); diff --git a/vitest.config.ts b/vitest.config.ts index 78962ff..f3a8ae3 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,13 +1,30 @@ +import { resolve } from "path"; import topLevelAwait from "vite-plugin-top-level-await"; import wasm from "vite-plugin-wasm"; import { defineConfig } from "vitest/config"; export default defineConfig({ - plugins: [wasm(), topLevelAwait()], + plugins: [ + wasm(), + topLevelAwait(), + { + name: "isolation", + configureServer(server) { + server.middlewares.use((_req, res, next) => { + res.setHeader("Cross-Origin-Opener-Policy", "same-origin"); + res.setHeader("Cross-Origin-Embedder-Policy", "require-corp"); + next(); + }); + }, + }, + ], resolve: { alias: [ - { find: /^zk-states$/, replacement: "./src/index.ts" }, - { find: /^zk-states(.*)$/, replacement: "./src/$1.ts" }, + { + find: /^zk-states$/, + replacement: resolve(__dirname, "src/index.ts"), + }, + { find: /^zk-states(.*)$/, replacement: resolve(__dirname, "src/$1.ts") }, ], }, test: { @@ -22,5 +39,19 @@ export default defineConfig({ reporter: ["text", "json", "html", "text-summary"], reportsDirectory: "./coverage/", }, + setupFiles: ["./setupVitest.js", "@vitest/web-worker"], + server: { + deps: { + inline: ["vitest-canvas-mock"], + }, + }, + // For this config, check https://github.com/vitest-dev/vitest/issues/740 + threads: false, + environmentOptions: { + jsdom: { + resources: "usable", + }, + }, + testTimeout: 600000, // 10 minutes (some operations are pretty slow, so keep it high) }, }); diff --git a/yarn.lock b/yarn.lock index d861fde..50ad1f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,6 +12,13 @@ __metadata: languageName: node linkType: hard +"@adobe/css-tools@npm:^4.0.1": + version: 4.3.1 + resolution: "@adobe/css-tools@npm:4.3.1" + checksum: ad43456379ff391132aff687ece190cb23ea69395e23c9b96690eeabe2468da89a4aaf266e4f8b6eaab53db3d1064107ce0f63c3a974e864f4a04affc768da3f + languageName: node + linkType: hard + "@ampproject/remapping@npm:^2.2.1": version: 2.2.1 resolution: "@ampproject/remapping@npm:2.2.1" @@ -58,6 +65,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.9.2": + version: 7.22.10 + resolution: "@babel/runtime@npm:7.22.10" + dependencies: + regenerator-runtime: ^0.14.0 + checksum: 524d41517e68953dbc73a4f3616b8475e5813f64e28ba89ff5fca2c044d535c2ea1a3f310df1e5bb06162e1f0b401b5c4af73fe6e2519ca2450d9d8c44cf268d + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -637,6 +653,36 @@ __metadata: languageName: node linkType: hard +"@testing-library/jest-dom@npm:^6.0.1": + version: 6.0.1 + resolution: "@testing-library/jest-dom@npm:6.0.1" + dependencies: + "@adobe/css-tools": ^4.0.1 + "@babel/runtime": ^7.9.2 + aria-query: ^5.0.0 + chalk: ^3.0.0 + css.escape: ^1.5.1 + dom-accessibility-api: ^0.5.6 + lodash: ^4.17.15 + redent: ^3.0.0 + peerDependencies: + "@jest/globals": ">= 28" + "@types/jest": ">= 28" + jest: ">= 28" + vitest: ">= 0.32" + peerDependenciesMeta: + "@jest/globals": + optional: true + "@types/jest": + optional: true + jest: + optional: true + vitest: + optional: true + checksum: b5c35e2a79d646753f0305bf7f461c5d6fb873016527a78606a20a5fcc7e55a2f5e9eb394d376c47739cee566af133c6f53d8f49681d623db1cf6f236c5971df + languageName: node + linkType: hard + "@testing-library/react@npm:^14.0.0": version: 14.0.0 resolution: "@testing-library/react@npm:14.0.0" @@ -1050,6 +1096,17 @@ __metadata: languageName: node linkType: hard +"@vitest/web-worker@npm:^0.34.2": + version: 0.34.2 + resolution: "@vitest/web-worker@npm:0.34.2" + dependencies: + debug: ^4.3.4 + peerDependencies: + vitest: ">=0.34.0" + checksum: d5753c6fc8b780171829d8d71dcdb95f52942b277e1cc1faf50748f4004b0c84ced69c58a1e605a4ec7717dcc0dfee3970e77c1ab08ff7b1ef2e564e7f52d40c + languageName: node + linkType: hard + "abab@npm:^2.0.6": version: 2.0.6 resolution: "abab@npm:2.0.6" @@ -1210,6 +1267,15 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:^5.0.0": + version: 5.3.0 + resolution: "aria-query@npm:5.3.0" + dependencies: + dequal: ^2.0.3 + checksum: 305bd73c76756117b59aba121d08f413c7ff5e80fa1b98e217a3443fcddb9a232ee790e24e432b59ae7625aebcf4c47cb01c2cac872994f0b426f5bdfcd96ba9 + languageName: node + linkType: hard + "array-buffer-byte-length@npm:^1.0.0": version: 1.0.0 resolution: "array-buffer-byte-length@npm:1.0.0" @@ -1431,6 +1497,16 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^3.0.0": + version: 3.0.0 + resolution: "chalk@npm:3.0.0" + dependencies: + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: 8e3ddf3981c4da405ddbd7d9c8d91944ddf6e33d6837756979f7840a29272a69a5189ecae0ff84006750d6d1e92368d413335eab4db5476db6e6703a1d1e0505 + languageName: node + linkType: hard + "chalk@npm:^4.0.0, chalk@npm:^4.1.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -1487,7 +1563,7 @@ __metadata: languageName: node linkType: hard -"color-name@npm:~1.1.4": +"color-name@npm:^1.1.4, color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 @@ -1540,6 +1616,15 @@ __metadata: languageName: node linkType: hard +"cross-fetch@npm:^3.0.6": + version: 3.1.8 + resolution: "cross-fetch@npm:3.1.8" + dependencies: + node-fetch: ^2.6.12 + checksum: 78f993fa099eaaa041122ab037fe9503ecbbcb9daef234d1d2e0b9230a983f64d645d088c464e21a247b825a08dc444a6e7064adfa93536d3a9454b4745b3632 + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -1551,6 +1636,20 @@ __metadata: languageName: node linkType: hard +"css.escape@npm:^1.5.1": + version: 1.5.1 + resolution: "css.escape@npm:1.5.1" + checksum: f6d38088d870a961794a2580b2b2af1027731bb43261cfdce14f19238a88664b351cc8978abc20f06cc6bbde725699dec8deb6fe9816b139fc3f2af28719e774 + languageName: node + linkType: hard + +"cssfontparser@npm:^1.2.1": + version: 1.2.1 + resolution: "cssfontparser@npm:1.2.1" + checksum: 952d487cddab591fb944f2a4c326a7736bc963784a6d92b6ad4051f3bf5ee49a732eff62e29a52ff085197cb07f5bd66525a2245ded7fd356113ac81be9238b9 + languageName: node + linkType: hard + "cssstyle@npm:^3.0.0": version: 3.0.0 resolution: "cssstyle@npm:3.0.0" @@ -1686,6 +1785,13 @@ __metadata: languageName: node linkType: hard +"dequal@npm:^2.0.3": + version: 2.0.3 + resolution: "dequal@npm:2.0.3" + checksum: 8679b850e1a3d0ebbc46ee780d5df7b478c23f335887464023a631d1b9af051ad4a6595a44220f9ff8ff95a8ddccf019b5ad778a976fd7bbf77383d36f412f90 + languageName: node + linkType: hard + "detect-gpu@npm:^5.0.5": version: 5.0.36 resolution: "detect-gpu@npm:5.0.36" @@ -1729,7 +1835,7 @@ __metadata: languageName: node linkType: hard -"dom-accessibility-api@npm:^0.5.9": +"dom-accessibility-api@npm:^0.5.6, dom-accessibility-api@npm:^0.5.9": version: 0.5.16 resolution: "dom-accessibility-api@npm:0.5.16" checksum: 005eb283caef57fc1adec4d5df4dd49189b628f2f575af45decb210e04d634459e3f1ee64f18b41e2dcf200c844bc1d9279d80807e686a30d69a4756151ad248 @@ -3118,6 +3224,16 @@ __metadata: languageName: node linkType: hard +"jest-canvas-mock@npm:~2.5.2": + version: 2.5.2 + resolution: "jest-canvas-mock@npm:2.5.2" + dependencies: + cssfontparser: ^1.2.1 + moo-color: ^1.0.2 + checksum: a3004d2e96473049045e49dcf98e5ea6011494048ab42b5422b3089d9ff406aaca8353e79587055d840fa145541668eb8f78613765f252ad5901a8217e91ea5d + languageName: node + linkType: hard + "js-sha256@npm:^0.9.0": version: 0.9.0 resolution: "js-sha256@npm:0.9.0" @@ -3245,6 +3361,13 @@ __metadata: languageName: node linkType: hard +"lodash@npm:^4.17.15": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 + languageName: node + linkType: hard + "loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -3389,6 +3512,13 @@ __metadata: languageName: node linkType: hard +"min-indent@npm:^1.0.0": + version: 1.0.1 + resolution: "min-indent@npm:1.0.1" + checksum: bfc6dd03c5eaf623a4963ebd94d087f6f4bbbfd8c41329a7f09706b0cb66969c4ddd336abeb587bc44bc6f08e13bf90f0b374f9d71f9f01e04adc2cd6f083ef1 + languageName: node + linkType: hard + "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -3521,6 +3651,15 @@ __metadata: languageName: node linkType: hard +"moo-color@npm:^1.0.2": + version: 1.0.3 + resolution: "moo-color@npm:1.0.3" + dependencies: + color-name: ^1.1.4 + checksum: 02bf59b6bbd5e86641bc062e2dc0843e6e579e18ef67e1c8e93bfc01945df578f20e66ce16aa9632db2aa0e16806e0914a26eb345a804f45fff1ae12a8906a29 + languageName: node + linkType: hard + "mrmime@npm:^1.0.0": version: 1.0.1 resolution: "mrmime@npm:1.0.1" @@ -3572,7 +3711,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.1": +"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.12": version: 2.6.12 resolution: "node-fetch@npm:2.6.12" dependencies: @@ -4035,6 +4174,16 @@ __metadata: languageName: node linkType: hard +"redent@npm:^3.0.0": + version: 3.0.0 + resolution: "redent@npm:3.0.0" + dependencies: + indent-string: ^4.0.0 + strip-indent: ^3.0.0 + checksum: fa1ef20404a2d399235e83cc80bd55a956642e37dd197b4b612ba7327bf87fa32745aeb4a1634b2bab25467164ab4ed9c15be2c307923dd08b0fe7c52431ae6b + languageName: node + linkType: hard + "reflect-metadata@npm:^0.1.13": version: 0.1.13 resolution: "reflect-metadata@npm:0.1.13" @@ -4049,6 +4198,13 @@ __metadata: languageName: node linkType: hard +"regenerator-runtime@npm:^0.14.0": + version: 0.14.0 + resolution: "regenerator-runtime@npm:0.14.0" + checksum: 1c977ad82a82a4412e4f639d65d22be376d3ebdd30da2c003eeafdaaacd03fc00c2320f18120007ee700900979284fc78a9f00da7fb593f6e6eeebc673fba9a3 + languageName: node + linkType: hard + "regexp.prototype.flags@npm:^1.4.3, regexp.prototype.flags@npm:^1.5.0": version: 1.5.0 resolution: "regexp.prototype.flags@npm:1.5.0" @@ -4545,6 +4701,15 @@ __metadata: languageName: node linkType: hard +"strip-indent@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-indent@npm:3.0.0" + dependencies: + min-indent: ^1.0.0 + checksum: 18f045d57d9d0d90cd16f72b2313d6364fd2cb4bf85b9f593523ad431c8720011a4d5f08b6591c9d580f446e78855c5334a30fb91aa1560f5d9f95ed1b4a0530 + languageName: node + linkType: hard + "strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -4933,7 +5098,7 @@ __metadata: languageName: node linkType: hard -"vite@npm:^3.0.0 || ^4.0.0": +"vite@npm:^3.0.0 || ^4.0.0, vite@npm:^4.4.9": version: 4.4.9 resolution: "vite@npm:4.4.9" dependencies: @@ -4973,6 +5138,28 @@ __metadata: languageName: node linkType: hard +"vitest-canvas-mock@npm:^0.3.3": + version: 0.3.3 + resolution: "vitest-canvas-mock@npm:0.3.3" + dependencies: + jest-canvas-mock: ~2.5.2 + peerDependencies: + vitest: "*" + checksum: bf564fe0cb701f228eb20cacdbd3abb9eb83c948bebbbea7fe907082ef830b2f8d7865277eade77fd84806bd1ddccdf1426c85ad716b8da8a977ae6b82e4f85f + languageName: node + linkType: hard + +"vitest-fetch-mock@npm:^0.2.2": + version: 0.2.2 + resolution: "vitest-fetch-mock@npm:0.2.2" + dependencies: + cross-fetch: ^3.0.6 + peerDependencies: + vitest: ">=0.16.0" + checksum: fa160f301171cd45dbf7d782880b6b6063fc74b9dd1965ef9206545e812ca8696e6be76662afbac822c6bf850fbb66cf8fb066af646e0e159f5a87ab25c97a02 + languageName: node + linkType: hard + "vitest@npm:^0.34.1": version: 0.34.1 resolution: "vitest@npm:0.34.1" @@ -5262,6 +5449,7 @@ __metadata: "@rollup/plugin-commonjs": ^25.0.3 "@rollup/plugin-node-resolve": ^15.1.0 "@rollup/plugin-typescript": ^11.1.2 + "@testing-library/jest-dom": ^6.0.1 "@testing-library/react": ^14.0.0 "@types/react": ^18.2.15 "@types/rollup-plugin-peer-deps-external": ^2.2.1 @@ -5269,6 +5457,7 @@ __metadata: "@typescript-eslint/parser": ^6.1.0 "@vitest/coverage-v8": ^0.34.1 "@vitest/ui": ^0.34.1 + "@vitest/web-worker": ^0.34.2 eslint: ^8.21.0 eslint-config-prettier: ^8.5.0 eslint-import-resolver-alias: ^1.1.2 @@ -5284,9 +5473,12 @@ __metadata: rollup-plugin-peer-deps-external: ^2.2.4 snarkyjs: ^0.12.1 typescript: ^5.1.6 + vite: ^4.4.9 vite-plugin-top-level-await: ^1.3.1 vite-plugin-wasm: ^3.2.2 vitest: ^0.34.1 + vitest-canvas-mock: ^0.3.3 + vitest-fetch-mock: ^0.2.2 zod: ^3.21.4 zustand: ^4.3.9 peerDependencies: