diff --git a/.changeset/quick-frogs-fold.md b/.changeset/quick-frogs-fold.md new file mode 100644 index 0000000000..4b79d1ec92 --- /dev/null +++ b/.changeset/quick-frogs-fold.md @@ -0,0 +1,6 @@ +--- +"@latticexyz/config": patch +"@latticexyz/store": patch +--- + +Fixed a few type issues with `namespaceLabel` in tables and added/clarified TSDoc for config input/output objects. diff --git a/.changeset/real-waves-bathe.md b/.changeset/real-waves-bathe.md new file mode 100644 index 0000000000..a60720e49e --- /dev/null +++ b/.changeset/real-waves-bathe.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/create-mud": patch +--- + +Added `@latticexyz/explorer` and `@latticexyz/store-indexer` to all MUD templates. Updated default `worlds.json` config and world address. diff --git a/.changeset/swift-rabbits-appear.md b/.changeset/swift-rabbits-appear.md new file mode 100644 index 0000000000..398f1afeff --- /dev/null +++ b/.changeset/swift-rabbits-appear.md @@ -0,0 +1,7 @@ +--- +"@latticexyz/explorer": minor +--- + +Initial release of the @latticexyz/explorer package. Explorer is a standalone tool designed to explore and manage worlds. This initial release supports local worlds, with plans to extend support to any world in the future. + +Read more on how to get started or contribute in the [Explorer README](https://github.com/latticexyz/mud/blob/main/packages/explorer/README.md). diff --git a/.changeset/tasty-toys-deliver.md b/.changeset/tasty-toys-deliver.md new file mode 100644 index 0000000000..5a438a7b31 --- /dev/null +++ b/.changeset/tasty-toys-deliver.md @@ -0,0 +1,7 @@ +--- +"@latticexyz/cli": patch +"@latticexyz/world": patch +--- + +Add a strongly typed `namespaceLabel` to the system config output. +It corresponds to the `label` of the namespace the system belongs to and can't be set manually. diff --git a/docs/pages/config/reference.mdx b/docs/pages/config/reference.mdx index 17c45833d8..2224348b8b 100644 --- a/docs/pages/config/reference.mdx +++ b/docs/pages/config/reference.mdx @@ -143,7 +143,7 @@ The following options are available in both single- and multiple-namespace modes - Customize how to deploy this table. + Customize how to deploy the world. Directory, relative to project root, to write the deployment artifacts to. Defaults to `"deploys"`. diff --git a/examples/local-explorer/.eslintrc b/examples/local-explorer/.eslintrc new file mode 100644 index 0000000000..2e5dee437f --- /dev/null +++ b/examples/local-explorer/.eslintrc @@ -0,0 +1,14 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + ], + "env": { + "browser": true, + "node": true, + }, +} diff --git a/examples/local-explorer/.gitattributes b/examples/local-explorer/.gitattributes new file mode 100644 index 0000000000..9c70dc52f0 --- /dev/null +++ b/examples/local-explorer/.gitattributes @@ -0,0 +1,3 @@ +# suppress diffs for generated files +**/pnpm-lock.yaml linguist-generated=true +**/codegen/**/*.sol linguist-generated=true diff --git a/examples/local-explorer/.gitignore b/examples/local-explorer/.gitignore new file mode 100644 index 0000000000..5eb2fb939a --- /dev/null +++ b/examples/local-explorer/.gitignore @@ -0,0 +1,2 @@ +indexer.db +node_modules diff --git a/examples/local-explorer/mprocs.yaml b/examples/local-explorer/mprocs.yaml new file mode 100644 index 0000000000..ab44f56d3e --- /dev/null +++ b/examples/local-explorer/mprocs.yaml @@ -0,0 +1,20 @@ +procs: + client: + cwd: packages/client + shell: pnpm run dev + contracts: + cwd: packages/contracts + shell: pnpm mud dev-contracts --rpc http://127.0.0.1:8545 + anvil: + cwd: packages/contracts + shell: anvil --base-fee 0 --block-time 2 + indexer: + cwd: packages/contracts + shell: rimraf $SQLITE_FILENAME && pnpm sqlite-indexer + env: + RPC_HTTP_URL: "http://127.0.0.1:8545" + FOLLOW_BLOCK_TAG: "latest" + SQLITE_FILENAME: "indexer.db" + explorer: + cwd: packages/contracts + shell: pnpm explorer diff --git a/examples/local-explorer/package.json b/examples/local-explorer/package.json new file mode 100644 index 0000000000..bdcc33e917 --- /dev/null +++ b/examples/local-explorer/package.json @@ -0,0 +1,31 @@ +{ + "name": "local-explorer", + "private": true, + "scripts": { + "build": "pnpm recursive run build", + "dev": "mprocs", + "dev:client": "pnpm --filter 'client' run dev", + "dev:contracts": "pnpm --filter 'contracts' dev", + "foundry:up": "curl -L https://foundry.paradigm.xyz | bash && bash $HOME/.foundry/bin/foundryup", + "mud:up": "pnpm mud set-version --tag main && pnpm install", + "prepare": "(forge --version || pnpm foundry:up)", + "test": "pnpm recursive run test" + }, + "devDependencies": { + "@latticexyz/cli": "link:../../packages/cli", + "@latticexyz/common": "link:../../packages/common", + "@latticexyz/explorer": "link:../../packages/explorer", + "@latticexyz/store-indexer": "link:../../packages/store-indexer", + "@types/debug": "4.1.7", + "@typescript-eslint/eslint-plugin": "7.1.1", + "@typescript-eslint/parser": "7.1.1", + "eslint": "8.57.0", + "mprocs": "^0.6.4", + "rimraf": "^3.0.2", + "typescript": "5.4.2" + }, + "engines": { + "node": "^18", + "pnpm": "^8 || ^9" + } +} diff --git a/examples/local-explorer/packages/client/.eslintrc b/examples/local-explorer/packages/client/.eslintrc new file mode 100644 index 0000000000..930af95967 --- /dev/null +++ b/examples/local-explorer/packages/client/.eslintrc @@ -0,0 +1,7 @@ +{ + "extends": ["../../.eslintrc", "plugin:react/recommended", "plugin:react-hooks/recommended"], + "plugins": ["react", "react-hooks"], + "rules": { + "react/react-in-jsx-scope": "off" + } +} diff --git a/examples/local-explorer/packages/client/.gitignore b/examples/local-explorer/packages/client/.gitignore new file mode 100644 index 0000000000..0ca39c007c --- /dev/null +++ b/examples/local-explorer/packages/client/.gitignore @@ -0,0 +1,3 @@ +node_modules +dist +.DS_Store diff --git a/examples/local-explorer/packages/client/index.html b/examples/local-explorer/packages/client/index.html new file mode 100644 index 0000000000..c3f06eb806 --- /dev/null +++ b/examples/local-explorer/packages/client/index.html @@ -0,0 +1,12 @@ + + + + + + a minimal MUD client + + +
+ + + diff --git a/examples/local-explorer/packages/client/package.json b/examples/local-explorer/packages/client/package.json new file mode 100644 index 0000000000..cb8f88f0b0 --- /dev/null +++ b/examples/local-explorer/packages/client/package.json @@ -0,0 +1,36 @@ +{ + "name": "client", + "version": "0.0.0", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "build": "vite build", + "dev": "wait-port localhost:8545 && vite", + "preview": "vite preview", + "test": "tsc --noEmit" + }, + "dependencies": { + "@latticexyz/common": "link:../../../../packages/common", + "@latticexyz/dev-tools": "link:../../../../packages/dev-tools", + "@latticexyz/react": "link:../../../../packages/react", + "@latticexyz/schema-type": "link:../../../../packages/schema-type", + "@latticexyz/store-sync": "link:../../../../packages/store-sync", + "@latticexyz/utils": "link:../../../../packages/utils", + "@latticexyz/world": "link:../../../../packages/world", + "contracts": "workspace:*", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "rxjs": "7.5.5", + "viem": "2.19.8" + }, + "devDependencies": { + "@types/react": "18.2.22", + "@types/react-dom": "18.2.7", + "@vitejs/plugin-react": "^3.1.0", + "eslint-plugin-react": "7.31.11", + "eslint-plugin-react-hooks": "4.6.0", + "vite": "^4.2.1", + "wait-port": "^1.0.4" + } +} diff --git a/examples/local-explorer/packages/client/src/App.tsx b/examples/local-explorer/packages/client/src/App.tsx new file mode 100644 index 0000000000..82c60486a9 --- /dev/null +++ b/examples/local-explorer/packages/client/src/App.tsx @@ -0,0 +1,105 @@ +import { useMUD } from "./MUDContext"; + +const styleUnset = { all: "unset" } as const; + +export const App = () => { + const { + network: { tables, useStore }, + systemCalls: { addTask, toggleTask, deleteTask }, + } = useMUD(); + + const tasks = useStore((state) => { + const records = Object.values(state.getRecords(tables.Tasks)); + records.sort((a, b) => Number(a.value.createdAt - b.value.createdAt)); + return records; + }); + + return ( + <> + + + {tasks.map((task) => ( + + + + + + ))} + + + + + + + +
+ 0n} + title={task.value.completedAt === 0n ? "Mark task as completed" : "Mark task as incomplete"} + onChange={async (event) => { + event.preventDefault(); + const checkbox = event.currentTarget; + + checkbox.disabled = true; + try { + await toggleTask(task.key.id); + } finally { + checkbox.disabled = false; + } + }} + /> + {task.value.completedAt > 0n ? {task.value.description} : <>{task.value.description}} + +
+ + +
{ + event.preventDefault(); + const form = event.currentTarget; + const fieldset = form.querySelector("fieldset"); + if (!(fieldset instanceof HTMLFieldSetElement)) return; + + const formData = new FormData(form); + const desc = formData.get("description"); + if (typeof desc !== "string") return; + + fieldset.disabled = true; + try { + await addTask(desc); + form.reset(); + } finally { + fieldset.disabled = false; + } + }} + > +
+ {" "} + +
+
+
+ + ); +}; diff --git a/examples/local-explorer/packages/client/src/MUDContext.tsx b/examples/local-explorer/packages/client/src/MUDContext.tsx new file mode 100644 index 0000000000..7b5637f6a6 --- /dev/null +++ b/examples/local-explorer/packages/client/src/MUDContext.tsx @@ -0,0 +1,21 @@ +import { createContext, ReactNode, useContext } from "react"; +import { SetupResult } from "./mud/setup"; + +const MUDContext = createContext(null); + +type Props = { + children: ReactNode; + value: SetupResult; +}; + +export const MUDProvider = ({ children, value }: Props) => { + const currentValue = useContext(MUDContext); + if (currentValue) throw new Error("MUDProvider can only be used once"); + return {children}; +}; + +export const useMUD = () => { + const value = useContext(MUDContext); + if (!value) throw new Error("Must be used within a MUDProvider"); + return value; +}; diff --git a/examples/local-explorer/packages/client/src/index.tsx b/examples/local-explorer/packages/client/src/index.tsx new file mode 100644 index 0000000000..c9b662c9f0 --- /dev/null +++ b/examples/local-explorer/packages/client/src/index.tsx @@ -0,0 +1,34 @@ +import ReactDOM from "react-dom/client"; +import { App } from "./App"; +import { setup } from "./mud/setup"; +import { MUDProvider } from "./MUDContext"; +import mudConfig from "contracts/mud.config"; + +const rootElement = document.getElementById("react-root"); +if (!rootElement) throw new Error("React root not found"); +const root = ReactDOM.createRoot(rootElement); + +// TODO: figure out if we actually want this to be async or if we should render something else in the meantime +setup().then(async (result) => { + root.render( + + + , + ); + + // https://vitejs.dev/guide/env-and-mode.html + if (import.meta.env.DEV) { + const { mount: mountDevTools } = await import("@latticexyz/dev-tools"); + mountDevTools({ + config: mudConfig, + publicClient: result.network.publicClient, + walletClient: result.network.walletClient, + latestBlock$: result.network.latestBlock$, + storedBlockLogs$: result.network.storedBlockLogs$, + worldAddress: result.network.worldContract.address, + worldAbi: result.network.worldContract.abi, + write$: result.network.write$, + useStore: result.network.useStore, + }); + } +}); diff --git a/examples/local-explorer/packages/client/src/mud/createSystemCalls.ts b/examples/local-explorer/packages/client/src/mud/createSystemCalls.ts new file mode 100644 index 0000000000..d4fb0c8d50 --- /dev/null +++ b/examples/local-explorer/packages/client/src/mud/createSystemCalls.ts @@ -0,0 +1,56 @@ +/* + * Create the system calls that the client can use to ask + * for changes in the World state (using the System contracts). + */ + +import { Hex } from "viem"; +import { SetupNetworkResult } from "./setupNetwork"; + +export type SystemCalls = ReturnType; + +export function createSystemCalls( + /* + * The parameter list informs TypeScript that: + * + * - The first parameter is expected to be a + * SetupNetworkResult, as defined in setupNetwork.ts + * + * Out of this parameter, we only care about two fields: + * - worldContract (which comes from getContract, see + * https://github.com/latticexyz/mud/blob/main/templates/react/packages/client/src/mud/setupNetwork.ts#L63-L69). + * + * - waitForTransaction (which comes from syncToRecs, see + * https://github.com/latticexyz/mud/blob/main/templates/react/packages/client/src/mud/setupNetwork.ts#L77-L83). + * + * - From the second parameter, which is a ClientComponent, + * we only care about Counter. This parameter comes to use + * through createClientComponents.ts, but it originates in + * syncToRecs + * (https://github.com/latticexyz/mud/blob/main/templates/react/packages/client/src/mud/setupNetwork.ts#L77-L83). + */ + { tables, useStore, worldContract, waitForTransaction }: SetupNetworkResult, +) { + const addTask = async (label: string) => { + const tx = await worldContract.write.app__addTask([label]); + await waitForTransaction(tx); + }; + + const toggleTask = async (id: Hex) => { + const isComplete = (useStore.getState().getValue(tables.Tasks, { id })?.completedAt ?? 0n) > 0n; + const tx = isComplete + ? await worldContract.write.app__resetTask([id]) + : await worldContract.write.app__completeTask([id]); + await waitForTransaction(tx); + }; + + const deleteTask = async (id: Hex) => { + const tx = await worldContract.write.app__deleteTask([id]); + await waitForTransaction(tx); + }; + + return { + addTask, + toggleTask, + deleteTask, + }; +} diff --git a/examples/local-explorer/packages/client/src/mud/getNetworkConfig.ts b/examples/local-explorer/packages/client/src/mud/getNetworkConfig.ts new file mode 100644 index 0000000000..976cf3399a --- /dev/null +++ b/examples/local-explorer/packages/client/src/mud/getNetworkConfig.ts @@ -0,0 +1,76 @@ +/* + * Network specific configuration for the client. + * By default connect to the anvil test network. + * + */ + +/* + * By default the template just creates a temporary wallet + * (called a burner wallet). + */ +import { getBurnerPrivateKey } from "@latticexyz/common"; + +/* + * Import the addresses of the World, possibly on multiple chains, + * from packages/contracts/worlds.json. When the contracts package + * deploys a new `World`, it updates this file. + */ +import worlds from "contracts/worlds.json"; + +/* + * The supported chains. + */ +import { supportedChains } from "./supportedChains"; + +export async function getNetworkConfig() { + const params = new URLSearchParams(window.location.search); + + /* + * The chain ID is the first item available from this list: + * 1. chainId query parameter + * 2. chainid query parameter + * 3. The VITE_CHAIN_ID environment variable set when the + * vite dev server was started or client was built + * 4. The default, 31337 (anvil) + */ + const chainId = Number(params.get("chainId") || params.get("chainid") || import.meta.env.VITE_CHAIN_ID || 31337); + + /* + * Find the chain (unless it isn't in the list of supported chains). + */ + const chainIndex = supportedChains.findIndex((c) => c.id === chainId); + const chain = supportedChains[chainIndex]; + if (!chain) { + throw new Error(`Chain ${chainId} not found`); + } + + /* + * Get the address of the World. If you want to use a + * different address than the one in worlds.json, + * provide it as worldAddress in the query string. + */ + const world = worlds[chain.id.toString()]; + const worldAddress = params.get("worldAddress") || world?.address; + if (!worldAddress) { + throw new Error(`No world address found for chain ${chainId}. Did you run \`mud deploy\`?`); + } + + /* + * MUD clients use events to synchronize the database, meaning + * they need to look as far back as when the World was started. + * The block number for the World start can be specified either + * on the URL (as initialBlockNumber) or in the worlds.json + * file. If neither has it, it starts at the first block, zero. + */ + const initialBlockNumber = params.has("initialBlockNumber") + ? Number(params.get("initialBlockNumber")) + : world?.blockNumber ?? 0n; + + return { + privateKey: getBurnerPrivateKey(), + chainId, + chain, + worldAddress, + initialBlockNumber, + }; +} diff --git a/examples/local-explorer/packages/client/src/mud/setup.ts b/examples/local-explorer/packages/client/src/mud/setup.ts new file mode 100644 index 0000000000..6d07134612 --- /dev/null +++ b/examples/local-explorer/packages/client/src/mud/setup.ts @@ -0,0 +1,18 @@ +/* + * This file sets up all the definitions required for a MUD client. + */ + +import { createSystemCalls } from "./createSystemCalls"; +import { setupNetwork } from "./setupNetwork"; + +export type SetupResult = Awaited>; + +export async function setup() { + const network = await setupNetwork(); + const systemCalls = createSystemCalls(network); + + return { + network, + systemCalls, + }; +} diff --git a/examples/local-explorer/packages/client/src/mud/setupNetwork.ts b/examples/local-explorer/packages/client/src/mud/setupNetwork.ts new file mode 100644 index 0000000000..dba19e3f4c --- /dev/null +++ b/examples/local-explorer/packages/client/src/mud/setupNetwork.ts @@ -0,0 +1,101 @@ +/* + * The MUD client code is built on top of viem + * (https://viem.sh/docs/getting-started.html). + * This line imports the functions we need from it. + */ +import { + createPublicClient, + fallback, + webSocket, + http, + createWalletClient, + Hex, + ClientConfig, + getContract, +} from "viem"; +import { syncToZustand } from "@latticexyz/store-sync/zustand"; +import { getNetworkConfig } from "./getNetworkConfig"; +import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; +import { createBurnerAccount, transportObserver, ContractWrite } from "@latticexyz/common"; +import { transactionQueue, writeObserver } from "@latticexyz/common/actions"; +import { Subject, share } from "rxjs"; + +/* + * Import our MUD config, which includes strong types for + * our tables and other config options. We use this to generate + * things like RECS components and get back strong types for them. + * + * See https://mud.dev/templates/typescript/contracts#mudconfigts + * for the source of this information. + */ +import mudConfig from "contracts/mud.config"; + +export type SetupNetworkResult = Awaited>; + +export async function setupNetwork() { + const networkConfig = await getNetworkConfig(); + + /* + * Create a viem public (read only) client + * (https://viem.sh/docs/clients/public.html) + */ + const clientOptions = { + chain: networkConfig.chain, + transport: transportObserver(fallback([webSocket(), http()])), + pollingInterval: 1000, + } as const satisfies ClientConfig; + + const publicClient = createPublicClient(clientOptions); + + /* + * Create an observable for contract writes that we can + * pass into MUD dev tools for transaction observability. + */ + const write$ = new Subject(); + + /* + * Create a temporary wallet and a viem client for it + * (see https://viem.sh/docs/clients/wallet.html). + */ + const burnerAccount = createBurnerAccount(networkConfig.privateKey as Hex); + const burnerWalletClient = createWalletClient({ + ...clientOptions, + account: burnerAccount, + }) + .extend(transactionQueue()) + .extend(writeObserver({ onWrite: (write) => write$.next(write) })); + + /* + * Create an object for communicating with the deployed World. + */ + const worldContract = getContract({ + address: networkConfig.worldAddress as Hex, + abi: IWorldAbi, + client: { public: publicClient, wallet: burnerWalletClient }, + }); + + /* + * Sync on-chain state into RECS and keeps our client in sync. + * Uses the MUD indexer if available, otherwise falls back + * to the viem publicClient to make RPC calls to fetch MUD + * events from the chain. + */ + const { tables, useStore, latestBlock$, storedBlockLogs$, waitForTransaction } = await syncToZustand({ + config: mudConfig, + address: networkConfig.worldAddress as Hex, + publicClient, + startBlock: BigInt(networkConfig.initialBlockNumber), + }); + + return { + tables, + useStore, + publicClient, + walletClient: burnerWalletClient, + latestBlock$, + storedBlockLogs$, + waitForTransaction, + worldContract, + write$: write$.asObservable().pipe(share()), + }; +} diff --git a/examples/local-explorer/packages/client/src/mud/supportedChains.ts b/examples/local-explorer/packages/client/src/mud/supportedChains.ts new file mode 100644 index 0000000000..03cefdf77f --- /dev/null +++ b/examples/local-explorer/packages/client/src/mud/supportedChains.ts @@ -0,0 +1,19 @@ +/* + * The supported chains. + * By default, there are only two chains here: + * + * - mudFoundry, the chain running on anvil that pnpm dev + * starts by default. It is similar to the viem anvil chain + * (see https://viem.sh/docs/clients/test.html), but with the + * basefee set to zero to avoid transaction fees. + * - latticeTestnet, our public test network. + * + */ + +import { MUDChain, mudFoundry, redstone, garnet } from "@latticexyz/common/chains"; + +/* + * See https://mud.dev/tutorials/minimal/deploy#run-the-user-interface + * for instructions on how to add networks. + */ +export const supportedChains: MUDChain[] = [mudFoundry, redstone, garnet]; diff --git a/examples/local-explorer/packages/client/tsconfig.json b/examples/local-explorer/packages/client/tsconfig.json new file mode 100644 index 0000000000..555d4a7c24 --- /dev/null +++ b/examples/local-explorer/packages/client/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["vite/client"], + "target": "ESNext", + "lib": ["ESNext", "DOM"], + "jsx": "react-jsx", + "jsxImportSource": "react" + }, + "include": ["src"] +} diff --git a/examples/local-explorer/packages/client/vite.config.ts b/examples/local-explorer/packages/client/vite.config.ts new file mode 100644 index 0000000000..a47d70e7a1 --- /dev/null +++ b/examples/local-explorer/packages/client/vite.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [react()], + server: { + port: 3000, + fs: { + strict: false, + }, + }, + build: { + target: "es2022", + minify: true, + sourcemap: true, + }, +}); diff --git a/examples/local-explorer/packages/contracts/.env b/examples/local-explorer/packages/contracts/.env new file mode 100644 index 0000000000..84889c8ef2 --- /dev/null +++ b/examples/local-explorer/packages/contracts/.env @@ -0,0 +1,11 @@ +# This .env file is for demonstration purposes only. +# +# This should usually be excluded via .gitignore and the env vars attached to +# your deployment environment, but we're including this here for ease of local +# development. Please do not commit changes to this file! +# +# Enable debug logs for MUD CLI +DEBUG=mud:* +# +# Anvil default private key: +PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 diff --git a/examples/local-explorer/packages/contracts/.gitignore b/examples/local-explorer/packages/contracts/.gitignore new file mode 100644 index 0000000000..aea4e54c82 --- /dev/null +++ b/examples/local-explorer/packages/contracts/.gitignore @@ -0,0 +1,9 @@ +out/ +cache/ +node_modules/ +bindings/ +artifacts/ +broadcast/ + +# Ignore MUD deploy artifacts +deploys/**/*.json diff --git a/examples/local-explorer/packages/contracts/.prettierrc b/examples/local-explorer/packages/contracts/.prettierrc new file mode 100644 index 0000000000..bf689e8c48 --- /dev/null +++ b/examples/local-explorer/packages/contracts/.prettierrc @@ -0,0 +1,8 @@ +{ + "plugins": ["prettier-plugin-solidity"], + "printWidth": 120, + "semi": true, + "tabWidth": 2, + "useTabs": false, + "bracketSpacing": true +} diff --git a/examples/local-explorer/packages/contracts/.solhint.json b/examples/local-explorer/packages/contracts/.solhint.json new file mode 100644 index 0000000000..f3e0b01ffc --- /dev/null +++ b/examples/local-explorer/packages/contracts/.solhint.json @@ -0,0 +1,12 @@ +{ + "extends": ["solhint:recommended", "mud"], + "plugins": ["mud"], + "rules": { + "compiler-version": ["error", ">=0.8.0"], + "avoid-low-level-calls": "off", + "no-inline-assembly": "off", + "func-visibility": ["warn", { "ignoreConstructors": true }], + "no-empty-blocks": "off", + "no-complex-fallback": "off" + } +} diff --git a/examples/local-explorer/packages/contracts/foundry.toml b/examples/local-explorer/packages/contracts/foundry.toml new file mode 100644 index 0000000000..3256b0c658 --- /dev/null +++ b/examples/local-explorer/packages/contracts/foundry.toml @@ -0,0 +1,29 @@ +[profile.default] +solc = "0.8.24" +ffi = false +fuzz_runs = 256 +optimizer = true +optimizer_runs = 3000 +verbosity = 2 +src = "src" +test = "test" +out = "out" +allow_paths = [ + # pnpm symlinks to the project root's node_modules + "../../node_modules", + # template uses linked mud packages from within the mud monorepo + "../../../../packages", + # projects created from this template and using linked mud packages + "../../../mud/packages", +] +extra_output_files = [ + "abi", + "evm.bytecode" +] +fs_permissions = [{ access = "read", path = "./"}] + +[profile.garnet] +eth_rpc_url = "https://rpc.garnetchain.com" + +[profile.redstone] +eth_rpc_url = "https://rpc.redstonechain.com" diff --git a/examples/local-explorer/packages/contracts/mud.config.ts b/examples/local-explorer/packages/contracts/mud.config.ts new file mode 100644 index 0000000000..a0828bf515 --- /dev/null +++ b/examples/local-explorer/packages/contracts/mud.config.ts @@ -0,0 +1,16 @@ +import { defineWorld } from "@latticexyz/world"; + +export default defineWorld({ + namespace: "app", + tables: { + Tasks: { + schema: { + id: "bytes32", + createdAt: "uint256", + completedAt: "uint256", + description: "string", + }, + key: ["id"], + }, + }, +}); diff --git a/examples/local-explorer/packages/contracts/package.json b/examples/local-explorer/packages/contracts/package.json new file mode 100644 index 0000000000..3b5fa8c383 --- /dev/null +++ b/examples/local-explorer/packages/contracts/package.json @@ -0,0 +1,35 @@ +{ + "name": "contracts", + "version": "0.0.0", + "private": true, + "license": "MIT", + "scripts": { + "build": "mud build", + "clean": "forge clean && rimraf src/codegen", + "deploy:garnet": "mud deploy --profile=garnet", + "deploy:local": "mud deploy", + "deploy:redstone": "mud deploy --profile=redstone", + "dev": "mud dev-contracts", + "lint": "pnpm run prettier && pnpm run solhint", + "prettier": "prettier --write 'src/**/*.sol'", + "solhint": "solhint --config ./.solhint.json 'src/**/*.sol' --fix", + "test": "tsc --noEmit && mud test" + }, + "dependencies": { + "@latticexyz/cli": "link:../../../../packages/cli", + "@latticexyz/schema-type": "link:../../../../packages/schema-type", + "@latticexyz/store": "link:../../../../packages/store", + "@latticexyz/world": "link:../../../../packages/world", + "@latticexyz/world-modules": "link:../../../../packages/world-modules" + }, + "devDependencies": { + "@types/node": "^18.15.11", + "ds-test": "https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0", + "forge-std": "https://github.com/foundry-rs/forge-std.git#74cfb77e308dd188d2f58864aaf44963ae6b88b1", + "prettier": "3.2.5", + "prettier-plugin-solidity": "1.3.1", + "solhint": "^3.3.7", + "solhint-config-mud": "link:../../../../packages/solhint-config-mud", + "solhint-plugin-mud": "link:../../../../packages/solhint-plugin-mud" + } +} diff --git a/examples/local-explorer/packages/contracts/remappings.txt b/examples/local-explorer/packages/contracts/remappings.txt new file mode 100644 index 0000000000..c4d992480e --- /dev/null +++ b/examples/local-explorer/packages/contracts/remappings.txt @@ -0,0 +1,3 @@ +ds-test/=node_modules/ds-test/src/ +forge-std/=node_modules/forge-std/src/ +@latticexyz/=node_modules/@latticexyz/ diff --git a/examples/local-explorer/packages/contracts/script/PostDeploy.s.sol b/examples/local-explorer/packages/contracts/script/PostDeploy.s.sol new file mode 100644 index 0000000000..93da4ddd8c --- /dev/null +++ b/examples/local-explorer/packages/contracts/script/PostDeploy.s.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import { Script } from "forge-std/Script.sol"; +import { console } from "forge-std/console.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; + +import { IWorld } from "../src/codegen/world/IWorld.sol"; +import { Tasks, TasksData } from "../src/codegen/index.sol"; + +contract PostDeploy is Script { + function run(address worldAddress) external { + // Specify a store so that you can use tables directly in PostDeploy + StoreSwitch.setStoreAddress(worldAddress); + + // Load the private key from the `PRIVATE_KEY` environment variable (in .env) + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + + // Start broadcasting transactions from the deployer account + vm.startBroadcast(deployerPrivateKey); + + // We can set table records directly + Tasks.set("1", TasksData({ description: "Walk the dog", createdAt: block.timestamp, completedAt: 0 })); + + // Or we can call our own systems + IWorld(worldAddress).app__addTask("Take out the trash"); + + bytes32 key = IWorld(worldAddress).app__addTask("Do the dishes"); + IWorld(worldAddress).app__completeTask(key); + + vm.stopBroadcast(); + } +} diff --git a/examples/local-explorer/packages/contracts/src/codegen/index.sol b/examples/local-explorer/packages/contracts/src/codegen/index.sol new file mode 100644 index 0000000000..6337bbbc62 --- /dev/null +++ b/examples/local-explorer/packages/contracts/src/codegen/index.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +import { Tasks, TasksData } from "./tables/Tasks.sol"; diff --git a/examples/local-explorer/packages/contracts/src/codegen/tables/Tasks.sol b/examples/local-explorer/packages/contracts/src/codegen/tables/Tasks.sol new file mode 100644 index 0000000000..48535061b8 --- /dev/null +++ b/examples/local-explorer/packages/contracts/src/codegen/tables/Tasks.sol @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +// Import store internals +import { IStore } from "@latticexyz/store/src/IStore.sol"; +import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol"; +import { StoreCore } from "@latticexyz/store/src/StoreCore.sol"; +import { Bytes } from "@latticexyz/store/src/Bytes.sol"; +import { Memory } from "@latticexyz/store/src/Memory.sol"; +import { SliceLib } from "@latticexyz/store/src/Slice.sol"; +import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol"; +import { FieldLayout } from "@latticexyz/store/src/FieldLayout.sol"; +import { Schema } from "@latticexyz/store/src/Schema.sol"; +import { EncodedLengths, EncodedLengthsLib } from "@latticexyz/store/src/EncodedLengths.sol"; +import { ResourceId } from "@latticexyz/store/src/ResourceId.sol"; + +struct TasksData { + uint256 createdAt; + uint256 completedAt; + string description; +} + +library Tasks { + // Hex below is the result of `WorldResourceIdLib.encode({ namespace: "app", name: "Tasks", typeId: RESOURCE_TABLE });` + ResourceId constant _tableId = ResourceId.wrap(0x746261707000000000000000000000005461736b730000000000000000000000); + + FieldLayout constant _fieldLayout = + FieldLayout.wrap(0x0040020120200000000000000000000000000000000000000000000000000000); + + // Hex-encoded key schema of (bytes32) + Schema constant _keySchema = Schema.wrap(0x002001005f000000000000000000000000000000000000000000000000000000); + // Hex-encoded value schema of (uint256, uint256, string) + Schema constant _valueSchema = Schema.wrap(0x004002011f1fc500000000000000000000000000000000000000000000000000); + + /** + * @notice Get the table's key field names. + * @return keyNames An array of strings with the names of key fields. + */ + function getKeyNames() internal pure returns (string[] memory keyNames) { + keyNames = new string[](1); + keyNames[0] = "id"; + } + + /** + * @notice Get the table's value field names. + * @return fieldNames An array of strings with the names of value fields. + */ + function getFieldNames() internal pure returns (string[] memory fieldNames) { + fieldNames = new string[](3); + fieldNames[0] = "createdAt"; + fieldNames[1] = "completedAt"; + fieldNames[2] = "description"; + } + + /** + * @notice Register the table with its config. + */ + function register() internal { + StoreSwitch.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Register the table with its config. + */ + function _register() internal { + StoreCore.registerTable(_tableId, _fieldLayout, _keySchema, _valueSchema, getKeyNames(), getFieldNames()); + } + + /** + * @notice Get createdAt. + */ + function getCreatedAt(bytes32 id) internal view returns (uint256 createdAt) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Get createdAt. + */ + function _getCreatedAt(bytes32 id) internal view returns (uint256 createdAt) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 0, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Set createdAt. + */ + function setCreatedAt(bytes32 id, uint256 createdAt) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((createdAt)), _fieldLayout); + } + + /** + * @notice Set createdAt. + */ + function _setCreatedAt(bytes32 id, uint256 createdAt) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setStaticField(_tableId, _keyTuple, 0, abi.encodePacked((createdAt)), _fieldLayout); + } + + /** + * @notice Get completedAt. + */ + function getCompletedAt(bytes32 id) internal view returns (uint256 completedAt) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreSwitch.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Get completedAt. + */ + function _getCompletedAt(bytes32 id) internal view returns (uint256 completedAt) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes32 _blob = StoreCore.getStaticField(_tableId, _keyTuple, 1, _fieldLayout); + return (uint256(bytes32(_blob))); + } + + /** + * @notice Set completedAt. + */ + function setCompletedAt(bytes32 id, uint256 completedAt) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setStaticField(_tableId, _keyTuple, 1, abi.encodePacked((completedAt)), _fieldLayout); + } + + /** + * @notice Set completedAt. + */ + function _setCompletedAt(bytes32 id, uint256 completedAt) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setStaticField(_tableId, _keyTuple, 1, abi.encodePacked((completedAt)), _fieldLayout); + } + + /** + * @notice Get description. + */ + function getDescription(bytes32 id) internal view returns (string memory description) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes memory _blob = StoreSwitch.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Get description. + */ + function _getDescription(bytes32 id) internal view returns (string memory description) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + bytes memory _blob = StoreCore.getDynamicField(_tableId, _keyTuple, 0); + return (string(_blob)); + } + + /** + * @notice Set description. + */ + function setDescription(bytes32 id, string memory description) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setDynamicField(_tableId, _keyTuple, 0, bytes((description))); + } + + /** + * @notice Set description. + */ + function _setDescription(bytes32 id, string memory description) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setDynamicField(_tableId, _keyTuple, 0, bytes((description))); + } + + /** + * @notice Get the length of description. + */ + function lengthDescription(bytes32 id) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + uint256 _byteLength = StoreSwitch.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get the length of description. + */ + function _lengthDescription(bytes32 id) internal view returns (uint256) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + uint256 _byteLength = StoreCore.getDynamicFieldLength(_tableId, _keyTuple, 0); + unchecked { + return _byteLength / 1; + } + } + + /** + * @notice Get an item of description. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function getItemDescription(bytes32 id, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + unchecked { + bytes memory _blob = StoreSwitch.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Get an item of description. + * @dev Reverts with Store_IndexOutOfBounds if `_index` is out of bounds for the array. + */ + function _getItemDescription(bytes32 id, uint256 _index) internal view returns (string memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + unchecked { + bytes memory _blob = StoreCore.getDynamicFieldSlice(_tableId, _keyTuple, 0, _index * 1, (_index + 1) * 1); + return (string(_blob)); + } + } + + /** + * @notice Push a slice to description. + */ + function pushDescription(bytes32 id, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Push a slice to description. + */ + function _pushDescription(bytes32 id, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.pushToDynamicField(_tableId, _keyTuple, 0, bytes((_slice))); + } + + /** + * @notice Pop a slice from description. + */ + function popDescription(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Pop a slice from description. + */ + function _popDescription(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.popFromDynamicField(_tableId, _keyTuple, 0, 1); + } + + /** + * @notice Update a slice of description at `_index`. + */ + function updateDescription(bytes32 id, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreSwitch.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Update a slice of description at `_index`. + */ + function _updateDescription(bytes32 id, uint256 _index, string memory _slice) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + unchecked { + bytes memory _encoded = bytes((_slice)); + StoreCore.spliceDynamicData(_tableId, _keyTuple, 0, uint40(_index * 1), uint40(_encoded.length), _encoded); + } + } + + /** + * @notice Get the full data. + */ + function get(bytes32 id) internal view returns (TasksData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + (bytes memory _staticData, EncodedLengths _encodedLengths, bytes memory _dynamicData) = StoreSwitch.getRecord( + _tableId, + _keyTuple, + _fieldLayout + ); + return decode(_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Get the full data. + */ + function _get(bytes32 id) internal view returns (TasksData memory _table) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + (bytes memory _staticData, EncodedLengths _encodedLengths, bytes memory _dynamicData) = StoreCore.getRecord( + _tableId, + _keyTuple, + _fieldLayout + ); + return decode(_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Set the full data using individual values. + */ + function set(bytes32 id, uint256 createdAt, uint256 completedAt, string memory description) internal { + bytes memory _staticData = encodeStatic(createdAt, completedAt); + + EncodedLengths _encodedLengths = encodeLengths(description); + bytes memory _dynamicData = encodeDynamic(description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Set the full data using individual values. + */ + function _set(bytes32 id, uint256 createdAt, uint256 completedAt, string memory description) internal { + bytes memory _staticData = encodeStatic(createdAt, completedAt); + + EncodedLengths _encodedLengths = encodeLengths(description); + bytes memory _dynamicData = encodeDynamic(description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** + * @notice Set the full data using the data struct. + */ + function set(bytes32 id, TasksData memory _table) internal { + bytes memory _staticData = encodeStatic(_table.createdAt, _table.completedAt); + + EncodedLengths _encodedLengths = encodeLengths(_table.description); + bytes memory _dynamicData = encodeDynamic(_table.description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Set the full data using the data struct. + */ + function _set(bytes32 id, TasksData memory _table) internal { + bytes memory _staticData = encodeStatic(_table.createdAt, _table.completedAt); + + EncodedLengths _encodedLengths = encodeLengths(_table.description); + bytes memory _dynamicData = encodeDynamic(_table.description); + + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.setRecord(_tableId, _keyTuple, _staticData, _encodedLengths, _dynamicData, _fieldLayout); + } + + /** + * @notice Decode the tightly packed blob of static data using this table's field layout. + */ + function decodeStatic(bytes memory _blob) internal pure returns (uint256 createdAt, uint256 completedAt) { + createdAt = (uint256(Bytes.getBytes32(_blob, 0))); + + completedAt = (uint256(Bytes.getBytes32(_blob, 32))); + } + + /** + * @notice Decode the tightly packed blob of dynamic data using the encoded lengths. + */ + function decodeDynamic( + EncodedLengths _encodedLengths, + bytes memory _blob + ) internal pure returns (string memory description) { + uint256 _start; + uint256 _end; + unchecked { + _end = _encodedLengths.atIndex(0); + } + description = (string(SliceLib.getSubslice(_blob, _start, _end).toBytes())); + } + + /** + * @notice Decode the tightly packed blobs using this table's field layout. + * @param _staticData Tightly packed static fields. + * @param _encodedLengths Encoded lengths of dynamic fields. + * @param _dynamicData Tightly packed dynamic fields. + */ + function decode( + bytes memory _staticData, + EncodedLengths _encodedLengths, + bytes memory _dynamicData + ) internal pure returns (TasksData memory _table) { + (_table.createdAt, _table.completedAt) = decodeStatic(_staticData); + + (_table.description) = decodeDynamic(_encodedLengths, _dynamicData); + } + + /** + * @notice Delete all data for given keys. + */ + function deleteRecord(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreSwitch.deleteRecord(_tableId, _keyTuple); + } + + /** + * @notice Delete all data for given keys. + */ + function _deleteRecord(bytes32 id) internal { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + StoreCore.deleteRecord(_tableId, _keyTuple, _fieldLayout); + } + + /** + * @notice Tightly pack static (fixed length) data using this table's schema. + * @return The static data, encoded into a sequence of bytes. + */ + function encodeStatic(uint256 createdAt, uint256 completedAt) internal pure returns (bytes memory) { + return abi.encodePacked(createdAt, completedAt); + } + + /** + * @notice Tightly pack dynamic data lengths using this table's schema. + * @return _encodedLengths The lengths of the dynamic fields (packed into a single bytes32 value). + */ + function encodeLengths(string memory description) internal pure returns (EncodedLengths _encodedLengths) { + // Lengths are effectively checked during copy by 2**40 bytes exceeding gas limits + unchecked { + _encodedLengths = EncodedLengthsLib.pack(bytes(description).length); + } + } + + /** + * @notice Tightly pack dynamic (variable length) data using this table's schema. + * @return The dynamic data, encoded into a sequence of bytes. + */ + function encodeDynamic(string memory description) internal pure returns (bytes memory) { + return abi.encodePacked(bytes((description))); + } + + /** + * @notice Encode all of a record's fields. + * @return The static (fixed length) data, encoded into a sequence of bytes. + * @return The lengths of the dynamic fields (packed into a single bytes32 value). + * @return The dynamic (variable length) data, encoded into a sequence of bytes. + */ + function encode( + uint256 createdAt, + uint256 completedAt, + string memory description + ) internal pure returns (bytes memory, EncodedLengths, bytes memory) { + bytes memory _staticData = encodeStatic(createdAt, completedAt); + + EncodedLengths _encodedLengths = encodeLengths(description); + bytes memory _dynamicData = encodeDynamic(description); + + return (_staticData, _encodedLengths, _dynamicData); + } + + /** + * @notice Encode keys as a bytes32 array using this table's field layout. + */ + function encodeKeyTuple(bytes32 id) internal pure returns (bytes32[] memory) { + bytes32[] memory _keyTuple = new bytes32[](1); + _keyTuple[0] = id; + + return _keyTuple; + } +} diff --git a/examples/local-explorer/packages/contracts/src/codegen/world/ITasksSystem.sol b/examples/local-explorer/packages/contracts/src/codegen/world/ITasksSystem.sol new file mode 100644 index 0000000000..7ac8215848 --- /dev/null +++ b/examples/local-explorer/packages/contracts/src/codegen/world/ITasksSystem.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +/** + * @title ITasksSystem + * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) + * @dev This interface is automatically generated from the corresponding system contract. Do not edit manually. + */ +interface ITasksSystem { + function app__addTask(string memory description) external returns (bytes32 id); + + function app__completeTask(bytes32 id) external; + + function app__resetTask(bytes32 id) external; + + function app__deleteTask(bytes32 id) external; +} diff --git a/examples/local-explorer/packages/contracts/src/codegen/world/IWorld.sol b/examples/local-explorer/packages/contracts/src/codegen/world/IWorld.sol new file mode 100644 index 0000000000..71352c40ca --- /dev/null +++ b/examples/local-explorer/packages/contracts/src/codegen/world/IWorld.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +/* Autogenerated file. Do not edit manually. */ + +import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol"; +import { ITasksSystem } from "./ITasksSystem.sol"; + +/** + * @title IWorld + * @author MUD (https://mud.dev) by Lattice (https://lattice.xyz) + * @notice This interface integrates all systems and associated function selectors + * that are dynamically registered in the World during deployment. + * @dev This is an autogenerated file; do not edit manually. + */ +interface IWorld is IBaseWorld, ITasksSystem {} diff --git a/examples/local-explorer/packages/contracts/src/systems/TasksSystem.sol b/examples/local-explorer/packages/contracts/src/systems/TasksSystem.sol new file mode 100644 index 0000000000..e9a5654f2c --- /dev/null +++ b/examples/local-explorer/packages/contracts/src/systems/TasksSystem.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import { System } from "@latticexyz/world/src/System.sol"; +import { Tasks, TasksData } from "../codegen/index.sol"; + +contract TasksSystem is System { + function addTask(string memory description) public returns (bytes32 id) { + id = keccak256(abi.encode(block.prevrandao, _msgSender(), description)); + Tasks.set(id, TasksData({ description: description, createdAt: block.timestamp, completedAt: 0 })); + } + + function completeTask(bytes32 id) public { + Tasks.setCompletedAt(id, block.timestamp); + } + + function resetTask(bytes32 id) public { + Tasks.setCompletedAt(id, 0); + } + + function deleteTask(bytes32 id) public { + Tasks.deleteRecord(id); + } +} diff --git a/examples/local-explorer/packages/contracts/test/TasksTest.t.sol b/examples/local-explorer/packages/contracts/test/TasksTest.t.sol new file mode 100644 index 0000000000..69afe731f2 --- /dev/null +++ b/examples/local-explorer/packages/contracts/test/TasksTest.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.24; + +import "forge-std/Test.sol"; +import { MudTest } from "@latticexyz/world/test/MudTest.t.sol"; + +import { IWorld } from "../src/codegen/world/IWorld.sol"; +import { Tasks, TasksData } from "../src/codegen/index.sol"; + +contract TasksTest is MudTest { + function testWorldExists() public { + uint256 codeSize; + address addr = worldAddress; + assembly { + codeSize := extcodesize(addr) + } + assertTrue(codeSize > 0); + } + + function testTasks() public { + // Expect task to exist that we created during PostDeploy script + TasksData memory task = Tasks.get("1"); + assertEq(task.description, "Walk the dog"); + assertEq(task.completedAt, 0); + + // Expect the task to be completed after calling completeTask from our TasksSystem + IWorld(worldAddress).app__completeTask("1"); + assertEq(Tasks.getCompletedAt("1"), block.timestamp); + } +} diff --git a/examples/local-explorer/packages/contracts/tsconfig.json b/examples/local-explorer/packages/contracts/tsconfig.json new file mode 100644 index 0000000000..4082f16a5d --- /dev/null +++ b/examples/local-explorer/packages/contracts/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/examples/local-explorer/packages/contracts/worlds.json b/examples/local-explorer/packages/contracts/worlds.json new file mode 100644 index 0000000000..6fed600155 --- /dev/null +++ b/examples/local-explorer/packages/contracts/worlds.json @@ -0,0 +1,5 @@ +{ + "31337": { + "address": "0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b" + } +} \ No newline at end of file diff --git a/examples/local-explorer/packages/contracts/worlds.json.d.ts b/examples/local-explorer/packages/contracts/worlds.json.d.ts new file mode 100644 index 0000000000..494829c2f9 --- /dev/null +++ b/examples/local-explorer/packages/contracts/worlds.json.d.ts @@ -0,0 +1,2 @@ +declare const worlds: Partial>; +export default worlds; diff --git a/examples/local-explorer/pnpm-lock.yaml b/examples/local-explorer/pnpm-lock.yaml new file mode 100644 index 0000000000..2cee66fc43 --- /dev/null +++ b/examples/local-explorer/pnpm-lock.yaml @@ -0,0 +1,3321 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@latticexyz/cli': + specifier: link:../../packages/cli + version: link:../../packages/cli + '@latticexyz/common': + specifier: link:../../packages/common + version: link:../../packages/common + '@latticexyz/explorer': + specifier: link:../../packages/explorer + version: link:../../packages/explorer + '@latticexyz/store-indexer': + specifier: link:../../packages/store-indexer + version: link:../../packages/store-indexer + '@types/debug': + specifier: 4.1.7 + version: 4.1.7 + '@typescript-eslint/eslint-plugin': + specifier: 7.1.1 + version: 7.1.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/parser': + specifier: 7.1.1 + version: 7.1.1(eslint@8.57.0)(typescript@5.4.2) + eslint: + specifier: 8.57.0 + version: 8.57.0 + mprocs: + specifier: ^0.6.4 + version: 0.6.4 + rimraf: + specifier: ^3.0.2 + version: 3.0.2 + typescript: + specifier: 5.4.2 + version: 5.4.2 + + packages/client: + dependencies: + '@latticexyz/common': + specifier: link:../../../../packages/common + version: link:../../../../packages/common + '@latticexyz/dev-tools': + specifier: link:../../../../packages/dev-tools + version: link:../../../../packages/dev-tools + '@latticexyz/react': + specifier: link:../../../../packages/react + version: link:../../../../packages/react + '@latticexyz/schema-type': + specifier: link:../../../../packages/schema-type + version: link:../../../../packages/schema-type + '@latticexyz/store-sync': + specifier: link:../../../../packages/store-sync + version: link:../../../../packages/store-sync + '@latticexyz/utils': + specifier: link:../../../../packages/utils + version: link:../../../../packages/utils + '@latticexyz/world': + specifier: link:../../../../packages/world + version: link:../../../../packages/world + contracts: + specifier: workspace:* + version: link:../contracts + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + rxjs: + specifier: 7.5.5 + version: 7.5.5 + viem: + specifier: 2.19.8 + version: 2.19.8(typescript@5.4.2)(zod@3.23.8) + devDependencies: + '@types/react': + specifier: 18.2.22 + version: 18.2.22 + '@types/react-dom': + specifier: 18.2.7 + version: 18.2.7 + '@vitejs/plugin-react': + specifier: ^3.1.0 + version: 3.1.0(vite@4.5.3(@types/node@18.19.44)) + eslint-plugin-react: + specifier: 7.31.11 + version: 7.31.11(eslint@8.57.0) + eslint-plugin-react-hooks: + specifier: 4.6.0 + version: 4.6.0(eslint@8.57.0) + vite: + specifier: ^4.2.1 + version: 4.5.3(@types/node@18.19.44) + wait-port: + specifier: ^1.0.4 + version: 1.1.0 + + packages/contracts: + dependencies: + '@latticexyz/cli': + specifier: link:../../../../packages/cli + version: link:../../../../packages/cli + '@latticexyz/schema-type': + specifier: link:../../../../packages/schema-type + version: link:../../../../packages/schema-type + '@latticexyz/store': + specifier: link:../../../../packages/store + version: link:../../../../packages/store + '@latticexyz/world': + specifier: link:../../../../packages/world + version: link:../../../../packages/world + '@latticexyz/world-modules': + specifier: link:../../../../packages/world-modules + version: link:../../../../packages/world-modules + devDependencies: + '@types/node': + specifier: ^18.15.11 + version: 18.19.44 + ds-test: + specifier: https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0 + version: https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0 + forge-std: + specifier: https://github.com/foundry-rs/forge-std.git#74cfb77e308dd188d2f58864aaf44963ae6b88b1 + version: https://codeload.github.com/foundry-rs/forge-std/tar.gz/74cfb77e308dd188d2f58864aaf44963ae6b88b1 + prettier: + specifier: 3.2.5 + version: 3.2.5 + prettier-plugin-solidity: + specifier: 1.3.1 + version: 1.3.1(prettier@3.2.5) + solhint: + specifier: ^3.3.7 + version: 3.6.2(typescript@5.4.2) + solhint-config-mud: + specifier: link:../../../../packages/solhint-config-mud + version: link:../../../../packages/solhint-config-mud + solhint-plugin-mud: + specifier: link:../../../../packages/solhint-plugin-mud + version: link:../../../../packages/solhint-plugin-mud + +packages: + + '@adraffy/ens-normalize@1.10.0': + resolution: {integrity: sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.25.2': + resolution: {integrity: sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.25.2': + resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.25.0': + resolution: {integrity: sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.2': + resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.25.2': + resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.24.8': + resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.24.8': + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.8': + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.25.0': + resolution: {integrity: sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.25.3': + resolution: {integrity: sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.24.7': + resolution: {integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.24.7': + resolution: {integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.25.0': + resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.25.3': + resolution: {integrity: sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.25.2': + resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==} + engines: {node: '>=6.9.0'} + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.0': + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@humanwhocodes/config-array@0.11.14': + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@noble/curves@1.4.0': + resolution: {integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@scure/base@1.1.7': + resolution: {integrity: sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==} + + '@scure/bip32@1.4.0': + resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} + + '@scure/bip39@1.3.0': + resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} + + '@solidity-parser/parser@0.16.2': + resolution: {integrity: sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==} + + '@solidity-parser/parser@0.17.0': + resolution: {integrity: sha512-Nko8R0/kUo391jsEHHxrGM07QFdnPGvlmox4rmH0kNiNAashItAilhy4Mv4pK5gQmW5f4sXAF58fwJbmlkGcVw==} + + '@types/debug@4.1.7': + resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/ms@0.7.34': + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + + '@types/node@18.19.44': + resolution: {integrity: sha512-ZsbGerYg72WMXUIE9fYxtvfzLEuq6q8mKERdWFnqTmOvudMxnz+CBNRoOwJ2kNpFOncrKjT1hZwxjlFgQ9qvQA==} + + '@types/prop-types@15.7.12': + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + + '@types/react-dom@18.2.7': + resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==} + + '@types/react@18.2.22': + resolution: {integrity: sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==} + + '@types/scheduler@0.23.0': + resolution: {integrity: sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@typescript-eslint/eslint-plugin@7.1.1': + resolution: {integrity: sha512-zioDz623d0RHNhvx0eesUmGfIjzrk18nSBC8xewepKXbBvN/7c1qImV7Hg8TI1URTxKax7/zxfxj3Uph8Chcuw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@7.1.1': + resolution: {integrity: sha512-ZWUFyL0z04R1nAEgr9e79YtV5LbafdOtN7yapNbn1ansMyaegl2D4bL7vHoJ4HPSc4CaLwuCVas8CVuneKzplQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@7.1.1': + resolution: {integrity: sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/type-utils@7.1.1': + resolution: {integrity: sha512-5r4RKze6XHEEhlZnJtR3GYeCh1IueUHdbrukV2KSlLXaTjuSfeVF8mZUVPLovidCuZfbVjfhi4c0DNSa/Rdg5g==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@7.1.1': + resolution: {integrity: sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/typescript-estree@7.1.1': + resolution: {integrity: sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@7.1.1': + resolution: {integrity: sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/visitor-keys@7.1.1': + resolution: {integrity: sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + '@vitejs/plugin-react@3.1.0': + resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.1.0-beta.0 + + abitype@1.0.5: + resolution: {integrity: sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + antlr4@4.13.2: + resolution: {integrity: sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg==} + engines: {node: '>=16'} + + antlr4ts@0.5.0-alpha.4: + resolution: {integrity: sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + + ast-parents@0.0.1: + resolution: {integrity: sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.23.3: + resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001651: + resolution: {integrity: sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + ds-test@https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0: + resolution: {tarball: https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0} + version: 1.0.0 + + electron-to-chromium@1.5.6: + resolution: {integrity: sha512-jwXWsM5RPf6j9dPYzaorcBSUg6AiqocPEyMpkchkvntaH9HGfOOMZwxMJjDY/XEs3T5dM7uyH1VhRMkqUU9qVw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@4.6.0: + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + + eslint-plugin-react@7.31.11: + resolution: {integrity: sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + forge-std@https://codeload.github.com/foundry-rs/forge-std/tar.gz/74cfb77e308dd188d2f58864aaf44963ae6b88b1: + resolution: {tarball: https://codeload.github.com/foundry-rs/forge-std/tar.gz/74cfb77e308dd188d2f58864aaf44963ae6b88b1} + version: 1.6.0 + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.15.0: + resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isows@1.0.4: + resolution: {integrity: sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ==} + peerDependencies: + ws: '*' + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + + mprocs@0.6.4: + resolution: {integrity: sha512-Y4eqnAjp3mjy0eT+zPoMQ+P/ISOzjgRG/4kh4I5cRA4Tv0rPxTCBRadn3+j+boMF5id7IoLhrVq9NFWFPuzD9A==} + engines: {node: '>=0.10.0'} + hasBin: true + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.hasown@1.1.4: + resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss@8.4.41: + resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-plugin-solidity@1.3.1: + resolution: {integrity: sha512-MN4OP5I2gHAzHZG1wcuJl0FsLS3c4Cc5494bbg+6oQWBPuEamjwDvmGfFMZ6NFzsh3Efd9UUxeT7ImgjNH4ozA==} + engines: {node: '>=16'} + peerDependencies: + prettier: '>=2.3.0' + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + engines: {node: '>=14'} + hasBin: true + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.5.5: + resolution: {integrity: sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==} + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + solhint@3.6.2: + resolution: {integrity: sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ==} + hasBin: true + + solidity-comments-extractor@0.0.8: + resolution: {integrity: sha512-htM7Vn6LhHreR+EglVMd2s+sZhcXAirB1Zlyrv5zBuTxieCvjfnRpd7iZk75m/u6NOlEyQ94C6TWbBn2cY7w8g==} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + table@6.8.2: + resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==} + engines: {node: '>=10.0.0'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + viem@2.19.8: + resolution: {integrity: sha512-2SkT6kHgp1MZnPl+fJ8kT2Eozv2tOuri30DI5dSnOecJpvachZY5PdgCdvXw7AUZCwNUkLX9ZEpKqyhqjQoUPg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + vite@4.5.3: + resolution: {integrity: sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + wait-port@1.1.0: + resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==} + engines: {node: '>=10'} + hasBin: true + + webauthn-p256@0.0.5: + resolution: {integrity: sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg==} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@adraffy/ens-normalize@1.10.0': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 + + '@babel/compat-data@7.25.2': {} + + '@babel/core@7.25.2': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.0 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helpers': 7.25.0 + '@babel/parser': 7.25.3 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + convert-source-map: 2.0.0 + debug: 4.3.6 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.25.0': + dependencies: + '@babel/types': 7.25.2 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-compilation-targets@7.25.2': + dependencies: + '@babel/compat-data': 7.25.2 + '@babel/helper-validator-option': 7.24.8 + browserslist: 4.23.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.24.8': {} + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.24.8': {} + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/helper-validator-option@7.24.8': {} + + '@babel/helpers@7.25.0': + dependencies: + '@babel/template': 7.25.0 + '@babel/types': 7.25.2 + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + + '@babel/parser@7.25.3': + dependencies: + '@babel/types': 7.25.2 + + '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/template@7.25.0': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.25.3 + '@babel/types': 7.25.2 + + '@babel/traverse@7.25.3': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.0 + '@babel/parser': 7.25.3 + '@babel/template': 7.25.0 + '@babel/types': 7.25.2 + debug: 4.3.6 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.25.2': + dependencies: + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.11.0': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.3.6 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.0': {} + + '@humanwhocodes/config-array@0.11.14': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.6 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@noble/curves@1.4.0': + dependencies: + '@noble/hashes': 1.4.0 + + '@noble/hashes@1.4.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@scure/base@1.1.7': {} + + '@scure/bip32@1.4.0': + dependencies: + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.7 + + '@scure/bip39@1.3.0': + dependencies: + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.7 + + '@solidity-parser/parser@0.16.2': + dependencies: + antlr4ts: 0.5.0-alpha.4 + + '@solidity-parser/parser@0.17.0': {} + + '@types/debug@4.1.7': + dependencies: + '@types/ms': 0.7.34 + + '@types/json-schema@7.0.15': {} + + '@types/ms@0.7.34': {} + + '@types/node@18.19.44': + dependencies: + undici-types: 5.26.5 + + '@types/prop-types@15.7.12': {} + + '@types/react-dom@18.2.7': + dependencies: + '@types/react': 18.2.22 + + '@types/react@18.2.22': + dependencies: + '@types/prop-types': 15.7.12 + '@types/scheduler': 0.23.0 + csstype: 3.1.3 + + '@types/scheduler@0.23.0': {} + + '@types/semver@7.5.8': {} + + '@typescript-eslint/eslint-plugin@7.1.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2))(eslint@8.57.0)(typescript@5.4.2)': + dependencies: + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 7.1.1(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/scope-manager': 7.1.1 + '@typescript-eslint/type-utils': 7.1.1(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/utils': 7.1.1(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/visitor-keys': 7.1.1 + debug: 4.3.6 + eslint: 8.57.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.4.2) + optionalDependencies: + typescript: 5.4.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.2)': + dependencies: + '@typescript-eslint/scope-manager': 7.1.1 + '@typescript-eslint/types': 7.1.1 + '@typescript-eslint/typescript-estree': 7.1.1(typescript@5.4.2) + '@typescript-eslint/visitor-keys': 7.1.1 + debug: 4.3.6 + eslint: 8.57.0 + optionalDependencies: + typescript: 5.4.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@7.1.1': + dependencies: + '@typescript-eslint/types': 7.1.1 + '@typescript-eslint/visitor-keys': 7.1.1 + + '@typescript-eslint/type-utils@7.1.1(eslint@8.57.0)(typescript@5.4.2)': + dependencies: + '@typescript-eslint/typescript-estree': 7.1.1(typescript@5.4.2) + '@typescript-eslint/utils': 7.1.1(eslint@8.57.0)(typescript@5.4.2) + debug: 4.3.6 + eslint: 8.57.0 + ts-api-utils: 1.3.0(typescript@5.4.2) + optionalDependencies: + typescript: 5.4.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@7.1.1': {} + + '@typescript-eslint/typescript-estree@7.1.1(typescript@5.4.2)': + dependencies: + '@typescript-eslint/types': 7.1.1 + '@typescript-eslint/visitor-keys': 7.1.1 + debug: 4.3.6 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.4.2) + optionalDependencies: + typescript: 5.4.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@7.1.1(eslint@8.57.0)(typescript@5.4.2)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 7.1.1 + '@typescript-eslint/types': 7.1.1 + '@typescript-eslint/typescript-estree': 7.1.1(typescript@5.4.2) + eslint: 8.57.0 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@7.1.1': + dependencies: + '@typescript-eslint/types': 7.1.1 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.2.0': {} + + '@vitejs/plugin-react@3.1.0(vite@4.5.3(@types/node@18.19.44))': + dependencies: + '@babel/core': 7.25.2 + '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2) + magic-string: 0.27.0 + react-refresh: 0.14.2 + vite: 4.5.3(@types/node@18.19.44) + transitivePeerDependencies: + - supports-color + + abitype@1.0.5(typescript@5.4.2)(zod@3.23.8): + optionalDependencies: + typescript: 5.4.2 + zod: 3.23.8 + + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn@8.12.1: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.1 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-regex@5.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + antlr4@4.13.2: {} + + antlr4ts@0.5.0-alpha.4: {} + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + + array-includes@3.1.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + is-string: 1.0.7 + + array-union@2.1.0: {} + + array.prototype.flat@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + + array.prototype.flatmap@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 + + arraybuffer.prototype.slice@1.0.3: + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + + ast-parents@0.0.1: {} + + astral-regex@2.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.0.0 + + balanced-match@1.0.2: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.23.3: + dependencies: + caniuse-lite: 1.0.30001651 + electron-to-chromium: 1.5.6 + node-releases: 2.0.18 + update-browserslist-db: 1.1.0(browserslist@4.23.3) + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001651: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + commander@10.0.1: {} + + commander@9.5.0: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cosmiconfig@8.3.6(typescript@5.4.2): + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.4.2 + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.1.3: {} + + data-view-buffer@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-offset@1.0.0: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + debug@4.3.6: + dependencies: + ms: 2.1.2 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + ds-test@https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0: {} + + electron-to-chromium@1.5.6: {} + + emoji-regex@8.0.0: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.23.3: + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.2 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.6 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + + es-object-atoms@1.0.0: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.0.3: + dependencies: + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.0.2: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.2.1: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + escalade@3.1.2: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): + dependencies: + eslint: 8.57.0 + + eslint-plugin-react@7.31.11(eslint@8.57.0): + dependencies: + array-includes: 3.1.8 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + eslint: 8.57.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.8 + object.fromentries: 2.0.8 + object.hasown: 1.1.4 + object.values: 1.2.0 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.11 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.0: + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.11.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.6 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 3.4.3 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.0.1: {} + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.1: {} + + for-each@0.3.3: + dependencies: + is-callable: 1.2.7 + + forge-std@https://codeload.github.com/foundry-rs/forge-std/tar.gz/74cfb77e308dd188d2f58864aaf44963ae6b88b1: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + functions-have-names: 1.2.3 + + functions-have-names@1.2.3: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + get-symbol-description@1.0.2: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + + globals@11.12.0: {} + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.0.1 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + graphemer@1.4.0: {} + + has-bigints@1.0.2: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.0.3 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + ignore@5.3.2: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + internal-slot@1.0.7: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.0.6 + + is-array-buffer@3.0.4: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + + is-arrayish@0.2.1: {} + + is-bigint@1.0.4: + dependencies: + has-bigints: 1.0.2 + + is-boolean-object@1.1.2: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.15.0: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.1: + dependencies: + is-typed-array: 1.1.13 + + is-date-object@1.0.5: + dependencies: + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-negative-zero@2.0.3: {} + + is-number-object@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + is-regex@1.1.4: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-shared-array-buffer@1.0.3: + dependencies: + call-bind: 1.0.7 + + is-string@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-symbol@1.0.4: + dependencies: + has-symbols: 1.0.3 + + is-typed-array@1.1.13: + dependencies: + which-typed-array: 1.1.15 + + is-weakref@1.0.2: + dependencies: + call-bind: 1.0.7 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + isows@1.0.4(ws@8.17.1): + dependencies: + ws: 8.17.1 + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@2.5.2: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.8 + array.prototype.flat: 1.3.2 + object.assign: 4.1.5 + object.values: 1.2.0 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lines-and-columns@1.2.4: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lodash.truncate@4.4.2: {} + + lodash@4.17.21: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.27.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + merge2@1.4.1: {} + + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + + minimatch@9.0.3: + dependencies: + brace-expansion: 2.0.1 + + mprocs@0.6.4: {} + + ms@2.1.2: {} + + nanoid@3.3.7: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.18: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.2: {} + + object-keys@1.1.1: {} + + object.assign@4.1.5: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + + object.entries@1.1.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + + object.hasown@1.1.4: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + + object.values@1.2.0: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.24.7 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + picocolors@1.0.1: {} + + picomatch@2.3.1: {} + + pluralize@8.0.0: {} + + possible-typed-array-names@1.0.0: {} + + postcss@8.4.41: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + + prelude-ls@1.2.1: {} + + prettier-plugin-solidity@1.3.1(prettier@3.2.5): + dependencies: + '@solidity-parser/parser': 0.17.0 + prettier: 3.2.5 + semver: 7.6.3 + solidity-comments-extractor: 0.0.8 + + prettier@2.8.8: + optional: true + + prettier@3.2.5: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-is@16.13.1: {} + + react-refresh@0.14.2: {} + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + regexp.prototype.flags@1.5.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.15.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.0.4: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup@3.29.4: + optionalDependencies: + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.5.5: + dependencies: + tslib: 2.6.3 + + safe-array-concat@1.1.2: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + + safe-regex-test@1.0.3: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-regex: 1.1.4 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + semver@7.6.3: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.2 + + slash@3.0.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + solhint@3.6.2(typescript@5.4.2): + dependencies: + '@solidity-parser/parser': 0.16.2 + ajv: 6.12.6 + antlr4: 4.13.2 + ast-parents: 0.0.1 + chalk: 4.1.2 + commander: 10.0.1 + cosmiconfig: 8.3.6(typescript@5.4.2) + fast-diff: 1.3.0 + glob: 8.1.0 + ignore: 5.3.2 + js-yaml: 4.1.0 + lodash: 4.17.21 + pluralize: 8.0.0 + semver: 7.6.3 + strip-ansi: 6.0.1 + table: 6.8.2 + text-table: 0.2.0 + optionalDependencies: + prettier: 2.8.8 + transitivePeerDependencies: + - typescript + + solidity-comments-extractor@0.0.8: {} + + source-map-js@1.2.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string.prototype.matchall@4.0.11: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.7 + regexp.prototype.flags: 1.5.2 + set-function-name: 2.0.2 + side-channel: 1.0.6 + + string.prototype.trim@1.2.9: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + + string.prototype.trimend@1.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-json-comments@3.1.1: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + table@6.8.2: + dependencies: + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + text-table@0.2.0: {} + + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@1.3.0(typescript@5.4.2): + dependencies: + typescript: 5.4.2 + + tslib@2.6.3: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + typed-array-buffer@1.0.2: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + + typed-array-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + + typed-array-byte-offset@1.0.2: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + + typed-array-length@1.0.6: + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 + + typescript@5.4.2: {} + + unbox-primitive@1.0.2: + dependencies: + call-bind: 1.0.7 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + + undici-types@5.26.5: {} + + update-browserslist-db@1.1.0(browserslist@4.23.3): + dependencies: + browserslist: 4.23.3 + escalade: 3.1.2 + picocolors: 1.0.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + viem@2.19.8(typescript@5.4.2)(zod@3.23.8): + dependencies: + '@adraffy/ens-normalize': 1.10.0 + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.3.0 + abitype: 1.0.5(typescript@5.4.2)(zod@3.23.8) + isows: 1.0.4(ws@8.17.1) + webauthn-p256: 0.0.5 + ws: 8.17.1 + optionalDependencies: + typescript: 5.4.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + vite@4.5.3(@types/node@18.19.44): + dependencies: + esbuild: 0.18.20 + postcss: 8.4.41 + rollup: 3.29.4 + optionalDependencies: + '@types/node': 18.19.44 + fsevents: 2.3.3 + + wait-port@1.1.0: + dependencies: + chalk: 4.1.2 + commander: 9.5.0 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + + webauthn-p256@0.0.5: + dependencies: + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + + which-boxed-primitive@1.0.2: + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + + which-typed-array@1.1.15: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + + ws@8.17.1: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zod@3.23.8: + optional: true diff --git a/examples/local-explorer/pnpm-workspace.yaml b/examples/local-explorer/pnpm-workspace.yaml new file mode 100644 index 0000000000..924b55f42e --- /dev/null +++ b/examples/local-explorer/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - packages/* diff --git a/examples/local-explorer/tsconfig.json b/examples/local-explorer/tsconfig.json new file mode 100644 index 0000000000..258a47be3f --- /dev/null +++ b/examples/local-explorer/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@latticexyz/common/tsconfig.base.json" +} diff --git a/packages/cli/src/deploy/common.ts b/packages/cli/src/deploy/common.ts index f5b768c1b4..30c762843c 100644 --- a/packages/cli/src/deploy/common.ts +++ b/packages/cli/src/deploy/common.ts @@ -82,13 +82,18 @@ export type Library = DeterministicContract & { }; export type System = DeterministicContract & { + // labels readonly label: string; + readonly namespaceLabel: string; + // resource ID readonly namespace: string; readonly name: string; readonly systemId: Hex; + // access readonly allowAll: boolean; readonly allowedAddresses: readonly Hex[]; readonly allowedSystemIds: readonly Hex[]; + // world registration // TODO: replace this with system manifest data readonly worldFunctions: readonly WorldFunction[]; // human readable ABIs to register onchain @@ -98,7 +103,7 @@ export type System = DeterministicContract & { export type DeployedSystem = Omit< System, - "label" | "abi" | "worldAbi" | "prepareDeploy" | "deployedBytecodeSize" | "allowedSystemIds" + "label" | "namespaceLabel" | "abi" | "worldAbi" | "prepareDeploy" | "deployedBytecodeSize" | "allowedSystemIds" > & { address: Address; }; diff --git a/packages/config/src/common.ts b/packages/config/src/common.ts index e3f672051f..0e63a6069c 100644 --- a/packages/config/src/common.ts +++ b/packages/config/src/common.ts @@ -24,16 +24,39 @@ export type Schema = { }; export type Table = { - // labels - readonly namespaceLabel: string; + /** + * Human-readable label for this table. Used as config keys, library names, and filenames. + * Labels are not length constrained like resource names, but special characters should be avoided to be compatible with the filesystem, Solidity compiler, etc. + */ readonly label: string; - // resource ID + /** + * Human-readable label for this table's namespace. Used for namespace config keys and directory names. + */ + readonly namespaceLabel: string; + /** + * Table type used in table's resource ID and determines how storage and events are used by this table. + */ readonly type: satisfy; + /** + * Table namespace used in table's resource ID and determines access control. + */ readonly namespace: string; + /** + * Table name used in table's resource ID. + */ readonly name: string; + /** + * Table's resource ID. + */ readonly tableId: Hex; - // key/value schema + /** + * Schema definition for this table's records. + */ readonly schema: Schema; + /** + * Primary key for records of this table. An array of zero or more schema field names. + * Using an empty array acts like a singleton, where only one record can exist for this table. + */ readonly key: readonly string[]; }; diff --git a/packages/explorer/.eslintrc.json b/packages/explorer/.eslintrc.json new file mode 100644 index 0000000000..bffb357a71 --- /dev/null +++ b/packages/explorer/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/packages/explorer/.gitignore b/packages/explorer/.gitignore new file mode 100644 index 0000000000..fd3dbb571a --- /dev/null +++ b/packages/explorer/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/explorer/.prettierrc.cjs b/packages/explorer/.prettierrc.cjs new file mode 100644 index 0000000000..80ae83be6f --- /dev/null +++ b/packages/explorer/.prettierrc.cjs @@ -0,0 +1,9 @@ +const baseConfig = require("../../.prettierrc.js"); + +/** @type {import('prettier').Config} */ +module.exports = { + ...baseConfig, + plugins: ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"], + importOrder: ["^[react]", "^@(?!/)", "^@/", "^[./]"], + importOrderSortSpecifiers: true, +}; diff --git a/packages/explorer/README.md b/packages/explorer/README.md new file mode 100644 index 0000000000..f234f5ef0d --- /dev/null +++ b/packages/explorer/README.md @@ -0,0 +1,85 @@ +# Explorer + +Explorer is a GUI tool designed for visually exploring and manipulating the state of worlds. + +## Getting started + +1. **Install the package** + + ```sh + pnpm add @latticexyz/explorer + ``` + +2. **Start a local development chain** + + Ensure you have a local development chain running. + +3. **Run the explorer** + + ```sh + npx @latticexyz/explorer --worldAddress + ``` + + Alternatively, if you have a worlds configuration file: + + ```sh + npx @latticexyz/explorer --worldsConfigPath + ``` + + Note: You can use `@latticexyz/store-indexer` for indexing your world's data. + +## CLI arguments + +The explorer accepts the following CLI arguments: + +| Argument | Description | Default value | +| ----------------- | -------------------------------------------------------------------------- | ------------- | +| `worldAddress` | The address of the world to explore | None | +| `worldsFile` | Path to a worlds configuration file (used to resolve world address) | None | +| `indexerDatabase` | Path to your SQLite indexer database | "indexer.db" | +| `chainId` | The chain ID of the network | 31337 | +| `port` | The port on which to run the explorer | 13690 | +| `env` | The environment to run the explorer in (e.g., "development", "production") | "production" | + +## Example setup + +An example setup is provided in the `examples/local-explorer` directory, demonstrating a full setup for using the explorer in a local development environment: + +1. **Setup** + + ```sh + cd examples/local-explorer && pnpm install + ``` + +2. **Run** + + ```sh + pnpm dev + ``` + + This command starts all necessary processes, including a local chain, indexer, and the explorer. + +## Contributing + +To contribute to or modify the explorer, the easiest way is to run the example setup in `development` mode: + +1. **Setup** + + Navigate to the `examples/local-explorer` directory and locate the `mprocs.yaml` file. + +2. **Configure** + + In `mprocs.yaml`, ensure the explorer command is set up correctly. For example: + + ```yaml + explorer: + shell: pnpm explorer --worldsConfigPath packages/contracts/worlds.json --env development + ``` + +3. **Run** + + ```sh + pnpm dev + ``` + + Files can now be edited in the `packages/explorer` directory, and changes will be reflected in the running explorer instance. diff --git a/packages/explorer/bin/explorer.ts b/packages/explorer/bin/explorer.ts new file mode 100755 index 0000000000..1dda756974 --- /dev/null +++ b/packages/explorer/bin/explorer.ts @@ -0,0 +1,112 @@ +#!/usr/bin/env node +import { watchFile } from "fs"; +import { readFile } from "fs/promises"; +import minimist from "minimist"; +import path from "path"; +import process from "process"; +import { fileURLToPath } from "url"; +import { ChildProcess, spawn } from "child_process"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const argv = minimist(process.argv.slice(2)); +const port = argv.port || process.env.PORT || 13690; +const chainId = argv.chainId || process.env.CHAIN_ID || 31337; +const env = argv.env || process.env.NODE_ENV || "production"; +const indexerDatabase = argv.indexerDatabase || process.env.INDEXER_DATABASE || "indexer.db"; +const worldsFile = argv.worldsFile || process.env.WORLDS_FILE || "worlds.json"; + +let worldAddress = argv.worldAddress || process.env.WORLD_ADDRESS || null; +let explorerProcess: ChildProcess; + +async function startExplorer() { + let command, args; + + if (env === "production") { + command = "pnpm"; + args = ["start"]; + } else { + command = "pnpm"; + args = ["dev"]; + } + + explorerProcess = spawn(command, args, { + cwd: __dirname, + stdio: "inherit", + env: { + ...process.env, + PORT: port, + WORLD_ADDRESS: worldAddress, + INDEXER_DATABASE: path.join(process.cwd(), indexerDatabase), + }, + }); +} + +async function readWorldsJson() { + try { + const data = await readFile(worldsFile, "utf8"); + if (data) { + const worlds = JSON.parse(data); + const world = worlds[chainId]; + if (world) { + return world.address; + } else { + console.error(`World not found for chain ID ${chainId}`); + return null; + } + } + } catch (error) { + console.error("Error reading worlds.json:", error); + return null; + } +} + +async function restartExplorer() { + if (explorerProcess) { + explorerProcess.kill(); + } + await startExplorer(); +} + +function watchWorldsJson() { + if (!worldsFile) { + return; + } + + watchFile(worldsFile, async () => { + const newWorldAddress = await readWorldsJson(); + if (worldAddress && worldAddress !== newWorldAddress) { + console.log("\nWorld address changed, restarting explorer..."); + + worldAddress = newWorldAddress; + await restartExplorer(); + } + }); +} + +process.on("SIGINT", () => { + if (explorerProcess) { + explorerProcess.kill(); + } + process.exit(); +}); + +async function main() { + // If world address is not provided, try to read it from worlds.json + if (!worldAddress) { + worldAddress = await readWorldsJson(); + + // If world address is still not found, throw an error + if (!worldAddress) { + throw new Error( + `No world address found in "${worldsFile}" file. Either run \`mud deploy\` to create one or provide one with \`--worldAddress\`.`, + ); + } + } + + watchWorldsJson(); + await startExplorer(); +} + +main().catch(console.error); diff --git a/packages/explorer/components.json b/packages/explorer/components.json new file mode 100644 index 0000000000..7559f63f10 --- /dev/null +++ b/packages/explorer/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/app/globals.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} diff --git a/packages/explorer/next.config.mjs b/packages/explorer/next.config.mjs new file mode 100644 index 0000000000..6eab04cefe --- /dev/null +++ b/packages/explorer/next.config.mjs @@ -0,0 +1,14 @@ +export default function config() { + /** + * @type {import('next').NextConfig} + */ + const nextConfig = { + output: "standalone", + webpack: (config) => { + config.externals.push("pino-pretty", "lokijs", "encoding"); + return config; + }, + }; + + return nextConfig; +} diff --git a/packages/explorer/package.json b/packages/explorer/package.json new file mode 100644 index 0000000000..120615479b --- /dev/null +++ b/packages/explorer/package.json @@ -0,0 +1,75 @@ +{ + "name": "@latticexyz/explorer", + "version": "2.1.1", + "description": "Explorer is a tool for visually exploring and manipulating the state of worlds", + "type": "module", + "bin": { + "explorer": "./dist/explorer.js" + }, + "files": [ + "dist", + ".next/standalone/packages/explorer" + ], + "scripts": { + "build": "pnpm run build:explorer && pnpm run build:bin", + "build:bin": "tsup", + "build:explorer": "next build && cp -r .next/static .next/standalone/packages/explorer/.next", + "clean": "pnpm run clean:explorer && pnpm run clean:bin", + "clean:bin": "rimraf dist", + "clean:explorer": "rimraf .next .turbo", + "dev": "next dev", + "lint": "next lint", + "start": "node .next/standalone/packages/explorer/server.js" + }, + "dependencies": { + "@hookform/resolvers": "^3.9.0", + "@latticexyz/common": "workspace:*", + "@latticexyz/protocol-parser": "workspace:*", + "@latticexyz/schema-type": "workspace:*", + "@latticexyz/store": "workspace:*", + "@latticexyz/store-sync": "workspace:*", + "@latticexyz/world": "workspace:*", + "@radix-ui/react-checkbox": "^1.1.1", + "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-toast": "^1.2.1", + "@radix-ui/themes": "^3.0.5", + "@tanstack/react-query": "^5.51.3", + "@tanstack/react-table": "^8.19.3", + "@wagmi/core": "^2.12.1", + "better-sqlite3": "^8.6.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "lucide-react": "^0.408.0", + "minimist": "^1.2.8", + "next": "14.2.5", + "query-string": "^9.1.0", + "react": "^18", + "react-dom": "^18", + "react-hook-form": "^7.52.1", + "sonner": "^1.5.0", + "tailwind-merge": "^1.12.0", + "tailwindcss-animate": "^1.0.7", + "tsup": "^6.7.0", + "viem": "catalog:", + "wagmi": "^2.11.1", + "zod": "3.23.8", + "zustand": "^4.3.7" + }, + "devDependencies": { + "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "@types/better-sqlite3": "^7.6.4", + "@types/minimist": "^1.2.5", + "@types/node": "^18.15.11", + "@types/react": "18.2.22", + "@types/react-dom": "18.2.7", + "eslint-config-next": "14.2.3", + "postcss": "^8", + "prettier": "3.2.5", + "prettier-plugin-tailwindcss": "^0.6.5", + "tailwindcss": "^3.4.1" + } +} diff --git a/packages/explorer/postcss.config.mjs b/packages/explorer/postcss.config.mjs new file mode 100644 index 0000000000..1a69fd2a45 --- /dev/null +++ b/packages/explorer/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/packages/explorer/src/app/Providers.tsx b/packages/explorer/src/app/Providers.tsx new file mode 100644 index 0000000000..8bdff0fc7b --- /dev/null +++ b/packages/explorer/src/app/Providers.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { WagmiProvider } from "wagmi"; +import { injected, metaMask, safe } from "wagmi/connectors"; +import { ReactNode } from "react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { createConfig, http } from "@wagmi/core"; +import { localhost } from "@wagmi/core/chains"; + +const queryClient = new QueryClient(); + +export const wagmiConfig = createConfig({ + chains: [localhost], + connectors: [ + injected(), + metaMask({ + dappMetadata: { + name: "Explorer", + }, + }), + safe(), + ], + transports: { + [localhost.id]: http(), + }, +}); + +export function Providers({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/packages/explorer/src/app/api/rows/route.ts b/packages/explorer/src/app/api/rows/route.ts new file mode 100644 index 0000000000..7882ae3364 --- /dev/null +++ b/packages/explorer/src/app/api/rows/route.ts @@ -0,0 +1,39 @@ +import { getDatabase } from "../utils/getDatabase"; + +export const dynamic = "force-dynamic"; + +type Row = { + [key: string]: string; +}; + +type RowsResponse = Row[] | undefined; + +function doesTableExist(table: string) { + const db = getDatabase(); + const result = db?.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(table); + + return Boolean(result); +} + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const table = searchParams.get("table"); + + try { + if (!table || !doesTableExist(table)) { + return Response.json({ error: "table does not exist" }, { status: 400 }); + } + + const db = getDatabase(); + const query = `SELECT * FROM "${table}" LIMIT 30`; + const rows = db?.prepare(query).all() as RowsResponse; + + return Response.json({ rows }); + } catch (error: unknown) { + if (error instanceof Error) { + return Response.json({ error: error.message }, { status: 400 }); + } else { + return Response.json({ error: "An unknown error occurred" }, { status: 400 }); + } + } +} diff --git a/packages/explorer/src/app/api/schema/route.ts b/packages/explorer/src/app/api/schema/route.ts new file mode 100644 index 0000000000..c031eb3a12 --- /dev/null +++ b/packages/explorer/src/app/api/schema/route.ts @@ -0,0 +1,27 @@ +import { getDatabase } from "../utils/getDatabase"; + +export const dynamic = "force-dynamic"; + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const table = searchParams.get("table"); + + if (!table) { + return new Response(JSON.stringify({ error: "table is required" }), { + status: 400, + }); + } + + try { + const db = getDatabase(); + const schema = db?.prepare("SELECT * FROM pragma_table_info(?)").all(table); + + return new Response(JSON.stringify({ schema }), { status: 200 }); + } catch (error: unknown) { + if (error instanceof Error) { + return Response.json({ error: error.message }, { status: 400 }); + } else { + return Response.json({ error: "An unknown error occurred" }, { status: 400 }); + } + } +} diff --git a/packages/explorer/src/app/api/table/route.ts b/packages/explorer/src/app/api/table/route.ts new file mode 100644 index 0000000000..8d455a0fba --- /dev/null +++ b/packages/explorer/src/app/api/table/route.ts @@ -0,0 +1,39 @@ +import { Hex } from "viem"; +import { KeySchema, ValueSchema } from "@latticexyz/protocol-parser/internal"; +import { getDatabase } from "../utils/getDatabase"; + +export const dynamic = "force-dynamic"; + +export type TableConfig = { + address: Hex; + id: string; + key_schema: KeySchema; + last_error: string | null; + name: string; + namespace: string; + schema_version: number; + table_id: Hex; + value_schema: ValueSchema; +}; + +export async function GET(req: Request) { + const { searchParams } = new URL(req.url); + const table = searchParams.get("table") as Hex; + + if (!table) { + return Response.json({ error: "table is required" }, { status: 400 }); + } + + try { + const db = getDatabase(); + const tableData = db?.prepare("SELECT * FROM __mudStoreTables WHERE id = ?").get(table) as TableConfig; + + return Response.json({ table: tableData }); + } catch (error: unknown) { + if (error instanceof Error) { + return Response.json({ error: error.message }, { status: 400 }); + } else { + return Response.json({ error: "An unknown error occurred" }, { status: 400 }); + } + } +} diff --git a/packages/explorer/src/app/api/tables/route.ts b/packages/explorer/src/app/api/tables/route.ts new file mode 100644 index 0000000000..2d71f8bc66 --- /dev/null +++ b/packages/explorer/src/app/api/tables/route.ts @@ -0,0 +1,22 @@ +import { getDatabase } from "../utils/getDatabase"; + +export const dynamic = "force-dynamic"; + +export type TableRow = { + name: string; +}; + +export async function GET() { + try { + const db = getDatabase(); + const tables = db?.prepare("SELECT name FROM sqlite_master WHERE type='table'").all() as TableRow[]; + + return Response.json({ tables }); + } catch (error: unknown) { + if (error instanceof Error) { + return Response.json({ error: error.message }, { status: 400 }); + } else { + return Response.json({ error: "An unknown error occurred" }, { status: 400 }); + } + } +} diff --git a/packages/explorer/src/app/api/utils/getDatabase.ts b/packages/explorer/src/app/api/utils/getDatabase.ts new file mode 100644 index 0000000000..b819af85e8 --- /dev/null +++ b/packages/explorer/src/app/api/utils/getDatabase.ts @@ -0,0 +1,16 @@ +import sqliteDB, { Database } from "better-sqlite3"; +import fs from "fs"; + +export function getDatabase(): Database | null { + const dbPath = process.env.INDEXER_DATABASE as string; + if (!fs.existsSync(dbPath)) { + return null; + } + + const db = new sqliteDB(dbPath); + if (!db) { + return null; + } + + return db; +} diff --git a/packages/explorer/src/app/api/world/route.ts b/packages/explorer/src/app/api/world/route.ts new file mode 100644 index 0000000000..f9f8b45fd5 --- /dev/null +++ b/packages/explorer/src/app/api/world/route.ts @@ -0,0 +1,64 @@ +import { AbiFunction, Address, Hex, createWalletClient, http, parseAbi } from "viem"; +import { getBlockNumber, getLogs } from "viem/actions"; +import { getRpcUrl } from "@latticexyz/common/foundry"; +import { helloStoreEvent } from "@latticexyz/store"; +import { helloWorldEvent } from "@latticexyz/world"; +import { getWorldAbi } from "@latticexyz/world/internal"; + +export const dynamic = "force-dynamic"; + +async function getClient() { + const profile = process.env.FOUNDRY_PROFILE; + const rpc = await getRpcUrl(profile); + const client = createWalletClient({ + transport: http(rpc), + }); + + return client; +} + +async function getParameters(worldAddress: Address) { + const client = await getClient(); + const toBlock = await getBlockNumber(client); + const logs = await getLogs(client, { + strict: true, + address: worldAddress, + events: parseAbi([helloStoreEvent, helloWorldEvent] as const), + fromBlock: "earliest", + toBlock, + }); + const fromBlock = logs[0].blockNumber; + + return { fromBlock, toBlock }; +} + +export async function GET(req: Request) { + const { searchParams } = new URL(req.url); + const worldAddress = searchParams.get("address") as Hex; + + if (!worldAddress) { + return Response.json({ error: "address is required" }, { status: 400 }); + } + + try { + const client = await getClient(); + const { fromBlock, toBlock } = await getParameters(worldAddress); + const worldAbiResponse = await getWorldAbi({ + client, + worldAddress, + fromBlock, + toBlock, + }); + const abi = worldAbiResponse + .filter((entry): entry is AbiFunction => entry.type === "function") + .sort((a, b) => a.name.localeCompare(b.name)); + + return Response.json({ abi }); + } catch (error: unknown) { + if (error instanceof Error) { + return Response.json({ error: error.message }, { status: 400 }); + } else { + return Response.json({ error: "An unknown error occurred" }, { status: 400 }); + } + } +} diff --git a/packages/explorer/src/app/error.tsx b/packages/explorer/src/app/error.tsx new file mode 100644 index 0000000000..08d42ee6ce --- /dev/null +++ b/packages/explorer/src/app/error.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { ExternalLink, RefreshCwIcon } from "lucide-react"; +import Link from "next/link"; +import { Button } from "../components/ui/Button"; +import { useWorldUrl } from "../hooks/useWorldUrl"; + +type Props = { + error: Error & { digest?: string }; + reset: () => void; +}; + +export default function Error({ reset }: Props) { + const getUrl = useWorldUrl(); + return ( +
+

400

+

Something went wrong :(

+ +
+ + + + + +
+
+ ); +} diff --git a/packages/explorer/src/app/favicon.ico b/packages/explorer/src/app/favicon.ico new file mode 100644 index 0000000000..718d6fea48 Binary files /dev/null and b/packages/explorer/src/app/favicon.ico differ diff --git a/packages/explorer/src/app/globals.css b/packages/explorer/src/app/globals.css new file mode 100644 index 0000000000..e3adb31bcb --- /dev/null +++ b/packages/explorer/src/app/globals.css @@ -0,0 +1,69 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 20 14.3% 4.1%; + --card: 0 0% 100%; + --card-foreground: 20 14.3% 4.1%; + --popover: 0 0% 100%; + --popover-foreground: 20 14.3% 4.1%; + --primary: 24.6 95% 53.1%; + --primary-foreground: 60 9.1% 97.8%; + --secondary: 60 4.8% 95.9%; + --secondary-foreground: 24 9.8% 10%; + --muted: 60 4.8% 95.9%; + --muted-foreground: 25 5.3% 44.7%; + --accent: 60 4.8% 95.9%; + --accent-foreground: 24 9.8% 10%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 60 9.1% 97.8%; + --border: 20 5.9% 90%; + --input: 20 5.9% 90%; + --ring: 24.6 95% 53.1%; + --radius: 0.5rem; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + } + + .dark { + --background: 20 14.3% 4.1%; + --foreground: 60 9.1% 97.8%; + --card: 20 14.3% 4.1%; + --card-foreground: 60 9.1% 97.8%; + --popover: 20 14.3% 4.1%; + --popover-foreground: 60 9.1% 97.8%; + --primary: 20.5 90.2% 48.2%; + --primary-foreground: 60 9.1% 97.8%; + --secondary: 12 6.5% 15.1%; + --secondary-foreground: 60 9.1% 97.8%; + --muted: 12 6.5% 15.1%; + --muted-foreground: 24 5.4% 63.9%; + --accent: 12 6.5% 15.1%; + --accent-foreground: 60 9.1% 97.8%; + --destructive: 0 72.2% 50.6%; + --destructive-foreground: 60 9.1% 97.8%; + --border: 12 6.5% 15.1%; + --input: 12 6.5% 15.1%; + --ring: 20.5 90.2% 48.2%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/packages/explorer/src/app/layout.tsx b/packages/explorer/src/app/layout.tsx new file mode 100644 index 0000000000..da37183d91 --- /dev/null +++ b/packages/explorer/src/app/layout.tsx @@ -0,0 +1,51 @@ +import type { Metadata } from "next"; +import { Inter, JetBrains_Mono } from "next/font/google"; +import { Toaster } from "sonner"; +import { Theme } from "@radix-ui/themes"; +import "@radix-ui/themes/styles.css"; +import { Navigation } from "../components/Navigation"; +import { Providers } from "./Providers"; +import "./globals.css"; + +const inter = Inter({ + subsets: ["latin"], + display: "swap", + variable: "--font-inter", +}); + +const jetbrains = JetBrains_Mono({ + subsets: ["latin"], + variable: "--font-jetbrains-mono", +}); + +export const metadata: Metadata = { + title: "Explorer", + description: "Explorer is a tool for visually exploring and manipulating the state of worlds", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + +
+ + {children} +
+ +
+
+ + + ); +} diff --git a/packages/explorer/src/app/not-found.tsx b/packages/explorer/src/app/not-found.tsx new file mode 100644 index 0000000000..85030ebf80 --- /dev/null +++ b/packages/explorer/src/app/not-found.tsx @@ -0,0 +1,28 @@ +"use client"; + +import { ExternalLink } from "lucide-react"; +import Link from "next/link"; +import { Button } from "../components/ui/Button"; +import { useWorldUrl } from "../hooks/useWorldUrl"; + +export default function NotFound() { + const getUrl = useWorldUrl(); + return ( +
+

404

+

Page not found

+

Sorry, we couldn’t find the page you’re looking for.

+
+ + + +
+
+ ); +} diff --git a/packages/explorer/src/app/worlds/[worldAddress]/explorer/DataExplorer.tsx b/packages/explorer/src/app/worlds/[worldAddress]/explorer/DataExplorer.tsx new file mode 100644 index 0000000000..b2f728990d --- /dev/null +++ b/packages/explorer/src/app/worlds/[worldAddress]/explorer/DataExplorer.tsx @@ -0,0 +1,27 @@ +"use client"; + +import { useSearchParams } from "next/navigation"; +import { useQuery } from "@tanstack/react-query"; +import { TableSelector } from "./TableSelector"; +import { TablesViewer } from "./TablesViewer"; + +export function DataExplorer() { + const searchParams = useSearchParams(); + const { data: tables } = useQuery({ + queryKey: ["tables"], + queryFn: async () => { + const response = await fetch("/api/tables"); + return response.json(); + }, + select: (data) => data.tables.map((table: { name: string }) => table.name), + refetchInterval: 15000, + }); + const selectedTable = searchParams.get("table") || (tables?.length > 0 ? tables[0] : null); + + return ( + <> + + + + ); +} diff --git a/packages/explorer/src/app/worlds/[worldAddress]/explorer/EditableTableCell.tsx b/packages/explorer/src/app/worlds/[worldAddress]/explorer/EditableTableCell.tsx new file mode 100644 index 0000000000..4040cc825b --- /dev/null +++ b/packages/explorer/src/app/worlds/[worldAddress]/explorer/EditableTableCell.tsx @@ -0,0 +1,141 @@ +import { Loader } from "lucide-react"; +import { useParams } from "next/navigation"; +import { toast } from "sonner"; +import { Hex } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { useChainId } from "wagmi"; +import { ChangeEvent, useState } from "react"; +import { encodeField, getFieldIndex } from "@latticexyz/protocol-parser/internal"; +import { SchemaAbiType } from "@latticexyz/schema-type/internal"; +import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { waitForTransactionReceipt, writeContract } from "@wagmi/core"; +import { Checkbox } from "../../../../components/ui/Checkbox"; +import { ACCOUNT_PRIVATE_KEYS } from "../../../../consts"; +import { camelCase, cn } from "../../../../lib/utils"; +import { useStore } from "../../../../store"; +import { wagmiConfig } from "../../../Providers"; +import { TableConfig } from "../../../api/table/route"; + +type Props = { + name: string; + value: string; + keyTuple: string[]; + config: TableConfig; +}; + +export function EditableTableCell({ name, config, keyTuple, value: defaultValue }: Props) { + const queryClient = useQueryClient(); + const chainId = useChainId(); + const { account } = useStore(); + const { worldAddress } = useParams(); + + const [value, setValue] = useState(defaultValue); + + const tableId = config?.table_id; + const fieldType = config?.value_schema[camelCase(name)] as SchemaAbiType; + + const { mutate, isPending } = useMutation({ + mutationFn: async (newValue: unknown) => { + const fieldIndex = getFieldIndex(config?.value_schema, camelCase(name)); + const encodedField = encodeField(fieldType, newValue); + const txHash = await writeContract(wagmiConfig, { + account: privateKeyToAccount(ACCOUNT_PRIVATE_KEYS[account]), + abi: IBaseWorldAbi, + address: worldAddress as Hex, + functionName: "setField", + args: [tableId, keyTuple, fieldIndex, encodedField], + }); + + const receipt = await waitForTransactionReceipt(wagmiConfig, { + hash: txHash, + pollingInterval: 100, + }); + + return { txHash, receipt }; + }, + onMutate: () => { + const toastId = toast.loading("Transaction submitted"); + return { toastId }; + }, + onSuccess: ({ txHash }, newValue, { toastId }) => { + setValue(newValue); + toast.success(`Transaction successful with hash: ${txHash}`, { + id: toastId, + }); + queryClient.invalidateQueries({ + queryKey: [ + "balance", + { + address: account, + chainId, + }, + ], + }); + }, + onError: (error, _, context) => { + console.error("Error:", error); + toast.error(error.message || "Something went wrong. Please try again.", { + id: context?.toastId, + }); + setValue(defaultValue); + }, + }); + + const handleSubmit = (newValue: unknown) => { + if (newValue !== defaultValue) { + mutate(newValue); + } + }; + + if (fieldType === "bool") { + return ( + <> + { + const newValue = checked ? "1" : "0"; + handleSubmit(newValue); + }} + disabled={isPending} + /> + {isPending && } + + ); + } + + return ( +
+ {!isPending && ( +
{ + evt.preventDefault(); + handleSubmit(value); + }} + > + ) => { + setValue(evt.target.value); + }} + onBlur={(evt) => handleSubmit(evt.target.value)} + value={String(value)} + disabled={isPending} + /> +
+ )} + + {isPending && ( + <> + {String(value)} + + + )} +
+ ); +} diff --git a/packages/explorer/src/app/worlds/[worldAddress]/explorer/TableSelector.tsx b/packages/explorer/src/app/worlds/[worldAddress]/explorer/TableSelector.tsx new file mode 100644 index 0000000000..7fe71a6c55 --- /dev/null +++ b/packages/explorer/src/app/worlds/[worldAddress]/explorer/TableSelector.tsx @@ -0,0 +1,42 @@ +import { Lock } from "lucide-react"; +import { useParams } from "next/navigation"; +import { internalTableNames } from "@latticexyz/store-sync/sqlite"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../../../components/ui/Select"; + +type Props = { + value: string | undefined; + options: string[]; +}; + +export function TableSelector({ value, options }: Props) { + const { worldAddress } = useParams(); + return ( +
+ +
+ ); +} diff --git a/packages/explorer/src/app/worlds/[worldAddress]/explorer/TablesViewer.tsx b/packages/explorer/src/app/worlds/[worldAddress]/explorer/TablesViewer.tsx new file mode 100644 index 0000000000..5de665d0e3 --- /dev/null +++ b/packages/explorer/src/app/worlds/[worldAddress]/explorer/TablesViewer.tsx @@ -0,0 +1,249 @@ +import { ArrowUpDown, Loader } from "lucide-react"; +import { useState } from "react"; +import { internalTableNames } from "@latticexyz/store-sync/sqlite"; +import { useQuery } from "@tanstack/react-query"; +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { Button } from "../../../../components/ui/Button"; +import { Checkbox } from "../../../../components/ui/Checkbox"; +import { Input } from "../../../../components/ui/Input"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../../../../components/ui/Table"; +import { bufferToBigInt } from "../utils/bufferToBigInt"; +import { EditableTableCell } from "./EditableTableCell"; + +type Props = { + table: string | undefined; +}; + +export function TablesViewer({ table: selectedTable }: Props) { + const [sorting, setSorting] = useState([]); + const [columnFilters, setColumnFilters] = useState([]); + const [columnVisibility, setColumnVisibility] = useState({}); + const [rowSelection, setRowSelection] = useState({}); + const [globalFilter, setGlobalFilter] = useState(""); + const [showAllColumns, setShowAllColumns] = useState(false); + + const { data: schema } = useQuery({ + queryKey: ["schema", { table: selectedTable }], + queryFn: async () => { + const response = await fetch(`/api/schema?table=${selectedTable}`); + return response.json(); + }, + select: (data) => { + return data.schema.filter((column: { name: string }) => { + if (showAllColumns) { + return true; + } + return !column.name.startsWith("__"); + }); + }, + }); + + const { data: rows } = useQuery({ + queryKey: ["rows", { table: selectedTable }], + queryFn: async () => { + const response = await fetch(`/api/rows?table=${selectedTable}`); + return response.json(); + }, + select: (data) => { + return data.rows.map((row: object) => { + return Object.fromEntries( + Object.entries(row).map(([key, value]) => { + if (value?.type === "Buffer") { + return [key, bufferToBigInt(value?.data)]; + } + return [key, value]; + }), + ); + }); + }, + enabled: Boolean(selectedTable), + refetchInterval: 1000, + }); + + const { data: mudTableConfig } = useQuery({ + queryKey: ["table", { selectedTable }], + queryFn: async () => { + const response = await fetch(`/api/table?table=${selectedTable}`); + return response.json(); + }, + select: (data) => { + return { + ...data.table, + key_schema: JSON.parse(data.table.key_schema).json, + value_schema: JSON.parse(data.table.value_schema).json, + }; + }, + enabled: Boolean(selectedTable), + }); + + const columns: ColumnDef<{}>[] = schema?.map(({ name, type }: { name: string; type: string }) => { + return { + accessorKey: name, + header: ({ + column, + }: { + column: { + toggleSorting: (ascending: boolean) => void; + getIsSorted: () => "asc" | "desc" | undefined; + }; + }) => { + return ( + + ); + }, + cell: ({ + row, + }: { + row: { + getValue: (name: string) => string; + }; + }) => { + const keysSchema = Object.keys(mudTableConfig?.key_schema || {}); + const keyTuple = keysSchema.map((key) => row.getValue(key)); + const value = row.getValue(name); + if ((selectedTable && (internalTableNames as string[]).includes(selectedTable)) || keysSchema.includes(name)) { + return value?.toString(); + } + + return ; + }, + }; + }); + + const table = useReactTable({ + data: rows, + columns, + initialState: { + pagination: { + pageSize: 50, + }, + }, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + onGlobalFilterChange: setGlobalFilter, + globalFilterFn: "includesString", + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + globalFilter, + }, + }); + + if (!schema || !rows) { + return ( +
+ +
+ ); + } + + return ( + <> +
+ table.setGlobalFilter(event.target.value)} + className="max-w-sm rounded border px-2 py-1" + /> + +
+ { + setShowAllColumns(!showAllColumns); + }} + /> +
+ +
+
+
+ +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + {flexRender(cell.column.columnDef.cell, cell.getContext())} + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+
+ + +
+
+ + ); +} diff --git a/packages/explorer/src/app/worlds/[worldAddress]/explorer/page.tsx b/packages/explorer/src/app/worlds/[worldAddress]/explorer/page.tsx new file mode 100644 index 0000000000..b019622675 --- /dev/null +++ b/packages/explorer/src/app/worlds/[worldAddress]/explorer/page.tsx @@ -0,0 +1,12 @@ +import { Suspense } from "react"; +import { DataExplorer } from "./DataExplorer"; + +export default function ExplorerPage() { + return ( +
+ + + +
+ ); +} diff --git a/packages/explorer/src/app/worlds/[worldAddress]/interact/Form.tsx b/packages/explorer/src/app/worlds/[worldAddress]/interact/Form.tsx new file mode 100644 index 0000000000..cf01952cd9 --- /dev/null +++ b/packages/explorer/src/app/worlds/[worldAddress]/interact/Form.tsx @@ -0,0 +1,81 @@ +"use client"; + +import { Coins, Eye, Send } from "lucide-react"; +import { AbiFunction } from "viem"; +import { useDeferredValue, useState } from "react"; +import { Input } from "../../../../components/ui/Input"; +import { useHashState } from "../../../../hooks/useHashState"; +import { cn } from "../../../../lib/utils"; +import { FunctionField } from "./FunctionField"; + +type Props = { + abi: AbiFunction[]; +}; + +export function Form({ abi }: Props) { + const [hash] = useHashState(); + const [filterValue, setFilterValue] = useState(""); + const deferredFilterValue = useDeferredValue(filterValue); + const filteredFunctions = abi.filter((item) => item.name.toLowerCase().includes(deferredFilterValue.toLowerCase())); + + return ( +
+
+
+

Jump to:

+ + { + setFilterValue(evt.target.value); + }} + /> + + +
+
+ +
+ {filteredFunctions.map((abi) => { + return ; + })} +
+
+ ); +} diff --git a/packages/explorer/src/app/worlds/[worldAddress]/interact/FunctionField.tsx b/packages/explorer/src/app/worlds/[worldAddress]/interact/FunctionField.tsx new file mode 100644 index 0000000000..3134127c10 --- /dev/null +++ b/packages/explorer/src/app/worlds/[worldAddress]/interact/FunctionField.tsx @@ -0,0 +1,113 @@ +"use client"; + +import { Coins, Eye, Send } from "lucide-react"; +import { AbiFunction } from "viem"; +import { z } from "zod"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Button } from "../../../../components/ui/Button"; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../../../../components/ui/Form"; +import { Input } from "../../../../components/ui/Input"; +import { Separator } from "../../../../components/ui/Separator"; +import { useContractMutation } from "./useContractMutation"; + +export enum FunctionType { + READ, + WRITE, +} + +type Props = { + abi: AbiFunction; +}; + +const formSchema = z.object({ + inputs: z.array(z.string()), + value: z.string().optional(), +}); + +export function FunctionField({ abi }: Props) { + const operationType: FunctionType = + abi.stateMutability === "view" || abi.stateMutability === "pure" ? FunctionType.READ : FunctionType.WRITE; + const [result, setResult] = useState(null); + const mutation = useContractMutation({ abi, operationType }); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + inputs: [], + }, + }); + + async function onSubmit(values: z.infer) { + const mutationResult = await mutation.mutateAsync({ + inputs: values.inputs, + value: values.value, + }); + + if (operationType === FunctionType.READ && "result" in mutationResult) { + setResult(JSON.stringify(mutationResult.result, null, 2)); + } + } + + const inputsLabel = abi?.inputs.map((input) => input.type).join(", "); + return ( +
+ +

+ {abi?.name} + {inputsLabel && ` (${inputsLabel})`} + + {abi.stateMutability === "payable" && } + {(abi.stateMutability === "view" || abi.stateMutability === "pure") && ( + + )} + {abi.stateMutability === "nonpayable" && } + +

+ + {abi?.inputs.map((input, index) => ( + ( + + {input.name} + + + + + + )} + /> + ))} + + {abi.stateMutability === "payable" && ( + ( + + ETH value + + + + + + )} + /> + )} + + + + {result &&
{result}
} + + + + + ); +} diff --git a/packages/explorer/src/app/worlds/[worldAddress]/interact/page.tsx b/packages/explorer/src/app/worlds/[worldAddress]/interact/page.tsx new file mode 100644 index 0000000000..46df999240 --- /dev/null +++ b/packages/explorer/src/app/worlds/[worldAddress]/interact/page.tsx @@ -0,0 +1,24 @@ +import { headers } from "next/headers"; +import { Hex } from "viem"; +import { Form } from "./Form"; + +async function getABI(worldAddress: Hex) { + const headersList = headers(); + const protocol = headersList.get("x-forwarded-proto"); + const host = headersList.get("host"); + + const res = await fetch(`${protocol}://${host}/api/world?${new URLSearchParams({ address: worldAddress })}`); + const data = await res.json(); + + if (!res.ok) { + throw new Error(data.error); + } + + return data; +} + +export default async function InteractPage({ params }: { params: { worldAddress: Hex } }) { + const { worldAddress } = params; + const data = await getABI(worldAddress); + return
; +} diff --git a/packages/explorer/src/app/worlds/[worldAddress]/interact/useContractMutation.ts b/packages/explorer/src/app/worlds/[worldAddress]/interact/useContractMutation.ts new file mode 100644 index 0000000000..d14a3ea16c --- /dev/null +++ b/packages/explorer/src/app/worlds/[worldAddress]/interact/useContractMutation.ts @@ -0,0 +1,83 @@ +import { useParams } from "next/navigation"; +import { toast } from "sonner"; +import { Abi, AbiFunction, Hex } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { useChainId } from "wagmi"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { readContract, waitForTransactionReceipt, writeContract } from "@wagmi/core"; +import { ACCOUNT_PRIVATE_KEYS } from "../../../../consts"; +import { useStore } from "../../../../store"; +import { wagmiConfig } from "../../../Providers"; +import { FunctionType } from "./FunctionField"; + +type UseContractMutationProps = { + abi: AbiFunction; + operationType: FunctionType; +}; + +export function useContractMutation({ abi, operationType }: UseContractMutationProps) { + const queryClient = useQueryClient(); + const chainId = useChainId(); + const { account } = useStore(); + const { worldAddress } = useParams(); + + return useMutation({ + mutationFn: async ({ inputs, value }: { inputs: unknown[]; value?: string }) => { + if (operationType === FunctionType.READ) { + const result = await readContract(wagmiConfig, { + abi: [abi] as Abi, + address: worldAddress as Hex, + functionName: abi.name, + args: inputs, + }); + + return { result }; + } else { + const txHash = await writeContract(wagmiConfig, { + account: privateKeyToAccount(ACCOUNT_PRIVATE_KEYS[account]), + abi: [abi] as Abi, + address: worldAddress as Hex, + functionName: abi.name, + args: inputs, + ...(value && { value: BigInt(value) }), + }); + + const receipt = await waitForTransactionReceipt(wagmiConfig, { + hash: txHash, + pollingInterval: 100, + }); + + return { txHash, receipt }; + } + }, + onMutate: () => { + if (operationType === FunctionType.WRITE) { + const toastId = toast.loading("Transaction submitted"); + return { toastId }; + } + }, + onSuccess: (data, _, context) => { + if (operationType === FunctionType.WRITE && "txHash" in data) { + toast.success(`Transaction successful with hash: ${data.txHash}`, { + id: context?.toastId, + }); + } + + queryClient.invalidateQueries({ + queryKey: [ + "balance", + { + address: account, + chainId, + }, + ], + }); + }, + onError: (error: Error, _, context) => { + console.error("Error:", error); + toast.error(error.message || "Something went wrong. Please try again.", { + id: context?.toastId, + }); + }, + }); +} diff --git a/packages/explorer/src/app/worlds/[worldAddress]/utils/bufferToBigInt.ts b/packages/explorer/src/app/worlds/[worldAddress]/utils/bufferToBigInt.ts new file mode 100644 index 0000000000..302d98d3d1 --- /dev/null +++ b/packages/explorer/src/app/worlds/[worldAddress]/utils/bufferToBigInt.ts @@ -0,0 +1,3 @@ +export function bufferToBigInt(bufferData: number[]) { + return BigInt(Buffer.from(bufferData).toString()); +} diff --git a/packages/explorer/src/components/AccountSelect.tsx b/packages/explorer/src/components/AccountSelect.tsx new file mode 100644 index 0000000000..9127682ad8 --- /dev/null +++ b/packages/explorer/src/components/AccountSelect.tsx @@ -0,0 +1,41 @@ +import { Address, formatEther } from "viem"; +import { useBalance } from "wagmi"; +import { ACCOUNTS } from "../consts"; +import { useStore } from "../store"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/Select"; +import { TruncatedHex } from "./ui/TruncatedHex"; + +function AccountSelectItem({ address, name }: { address: Address; name: string }) { + const balance = useBalance({ + address, + query: { + refetchInterval: 15000, + }, + }); + const balanceValue = balance.data?.value; + return ( + + {name} + {balanceValue !== undefined && ` (${formatEther(balanceValue)} ETH)`}{" "} + + () + + + ); +} + +export function AccountSelect() { + const { account, setAccount } = useStore(); + return ( + + ); +} diff --git a/packages/explorer/src/components/LatestBlock.tsx b/packages/explorer/src/components/LatestBlock.tsx new file mode 100644 index 0000000000..a518c86822 --- /dev/null +++ b/packages/explorer/src/components/LatestBlock.tsx @@ -0,0 +1,25 @@ +import { useBlockNumber } from "wagmi"; + +export function LatestBlock() { + const { data: block } = useBlockNumber({ + watch: true, + }); + + if (block === undefined || block === 0n) { + return; + } + + return ( +
+
+ + {block.toString()} +
+
+ ); +} diff --git a/packages/explorer/src/components/Navigation.tsx b/packages/explorer/src/components/Navigation.tsx new file mode 100644 index 0000000000..567d772c79 --- /dev/null +++ b/packages/explorer/src/components/Navigation.tsx @@ -0,0 +1,47 @@ +"use client"; + +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { LatestBlock } from "../components/LatestBlock"; +import { Separator } from "../components/ui/Separator"; +import { useWorldUrl } from "../hooks/useWorldUrl"; +import { cn } from "../lib/utils"; +import { AccountSelect } from "./AccountSelect"; + +export function Navigation() { + const pathname = usePathname(); + const getLinkUrl = useWorldUrl(); + + return ( +
+
+
+ + Data explorer + + + + Interact + +
+ +
+ + +
+
+ + +
+ ); +} diff --git a/packages/explorer/src/components/ui/Button.tsx b/packages/explorer/src/components/ui/Button.tsx new file mode 100644 index 0000000000..151aab1e65 --- /dev/null +++ b/packages/explorer/src/components/ui/Button.tsx @@ -0,0 +1,49 @@ +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cn } from "../../lib/utils"; + +const buttonVariants = cva( + cn( + "inline-flex items-center justify-center rounded-md", + "whitespace-nowrap text-sm font-medium", + "ring-offset-background transition-colors", + "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", + "disabled:pointer-events-none disabled:opacity-50", + ), + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export type ButtonProps = React.ButtonHTMLAttributes & + VariantProps & { asChild?: boolean }; + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ; + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/packages/explorer/src/components/ui/Checkbox.tsx b/packages/explorer/src/components/ui/Checkbox.tsx new file mode 100644 index 0000000000..25db55d579 --- /dev/null +++ b/packages/explorer/src/components/ui/Checkbox.tsx @@ -0,0 +1,30 @@ +"use client"; + +import { Check } from "lucide-react"; +import * as React from "react"; +import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; +import { cn } from "../../lib/utils"; + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)); +Checkbox.displayName = CheckboxPrimitive.Root.displayName; + +export { Checkbox }; diff --git a/packages/explorer/src/components/ui/DropdownMenu.tsx b/packages/explorer/src/components/ui/DropdownMenu.tsx new file mode 100644 index 0000000000..58c83651a7 --- /dev/null +++ b/packages/explorer/src/components/ui/DropdownMenu.tsx @@ -0,0 +1,188 @@ +"use client"; + +import { Check, ChevronRight, Circle } from "lucide-react"; +import * as React from "react"; +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import { cn } from "../../lib/utils"; + +const DropdownMenu = DropdownMenuPrimitive.Root; + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; + +const DropdownMenuGroup = DropdownMenuPrimitive.Group; + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; + +const DropdownMenuSub = DropdownMenuPrimitive.Sub; + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)); +DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName; + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName; + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + +)); +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)); +DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName; + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)); +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + +)); +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; + +const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes) => { + return ; +}; +DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +}; diff --git a/packages/explorer/src/components/ui/Form.tsx b/packages/explorer/src/components/ui/Form.tsx new file mode 100644 index 0000000000..bafa560b09 --- /dev/null +++ b/packages/explorer/src/components/ui/Form.tsx @@ -0,0 +1,130 @@ +"use client"; + +import * as React from "react"; +import { Controller, ControllerProps, FieldPath, FieldValues, FormProvider, useFormContext } from "react-hook-form"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { Slot } from "@radix-ui/react-slot"; +import { Label } from "../../components/ui/Label"; +import { cn } from "../../lib/utils"; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext({} as FormFieldContextValue); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error("useFormField should be used within "); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext({} as FormItemContextValue); + +const FormItem = React.forwardRef>( + ({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
+ + ); + }, +); +FormItem.displayName = "FormItem"; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return